Teact: Support autoFocus attribute (#6948)

This commit is contained in:
Alexander Zinchuk 2026-05-15 18:37:55 +02:00
parent a99d7bbdf9
commit 480b09e9aa
11 changed files with 193 additions and 290 deletions

View File

@ -99,61 +99,19 @@
} }
.day-button { .day-button {
--button-text-color: var(--color-text);
position: relative; position: relative;
margin: 0.125rem 0.625rem;
border-radius: 4rem;
font-weight: var(--font-weight-medium); font-weight: var(--font-weight-medium);
font-variant-numeric: tabular-nums;
outline: none !important;
&::before {
content: "";
display: block;
padding-top: 100%;
}
&.weekday { &.weekday {
height: 1rem; height: 1rem;
margin-bottom: 0; margin-bottom: 0;
} }
&.clickable { &.selected {
cursor: var(--custom-cursor, pointer); --button-text-color: revert-layer;
&:hover {
background-color: var(--color-interactive-element-hover);
}
&.selected {
color: white;
background-color: var(--color-primary);
}
}
&.disabled {
pointer-events: none;
opacity: 0.25;
}
span {
position: absolute;
top: 0;
left: 0;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
font-size: 0.875rem;
}
@media (max-width: 600px) {
margin: 0.25rem 0.375rem;
} }
} }
@ -163,12 +121,15 @@
justify-content: center; justify-content: center;
min-height: 17rem; min-height: 17rem;
margin: 1.5rem -0.5rem 0.5rem; margin: 0rem -0.5rem 1rem;
} }
.calendar-grid { .calendar-grid {
display: grid; display: grid;
grid-auto-rows: 1fr;
grid-template-columns: repeat(7, 1fr); grid-template-columns: repeat(7, 1fr);
place-items: center;
width: 100%; width: 100%;
} }
} }

View File

@ -203,7 +203,7 @@ const CalendarModal = ({
message: lang('MessageScheduledRepeatPremium'), message: lang('MessageScheduledRepeatPremium'),
action: { action: {
action: 'openPremiumModal', action: 'openPremiumModal',
payload: { }, payload: {},
}, },
actionText: lang('PremiumMore'), actionText: lang('PremiumMore'),
}); });
@ -399,6 +399,8 @@ const CalendarModal = ({
color="translucent" color="translucent"
iconName="previous" iconName="previous"
disabled={shouldDisablePrevMonth} disabled={shouldDisablePrevMonth}
noFastClick
noPreventDefault
onClick={shouldDisablePrevMonth ? undefined : handlePrevMonth} onClick={shouldDisablePrevMonth ? undefined : handlePrevMonth}
/> />
@ -408,6 +410,8 @@ const CalendarModal = ({
color="translucent" color="translucent"
iconName="next" iconName="next"
disabled={shouldDisableNextMonth} disabled={shouldDisableNextMonth}
noFastClick
noPreventDefault
onClick={shouldDisableNextMonth ? undefined : handleNextMonth} onClick={shouldDisableNextMonth ? undefined : handleNextMonth}
/> />
</div> </div>
@ -417,35 +421,53 @@ const CalendarModal = ({
<div className="calendar-grid"> <div className="calendar-grid">
{WEEKDAY_LETTERS.map((day) => ( {WEEKDAY_LETTERS.map((day) => (
<div className="day-button faded weekday"> <div className="day-button faded weekday">
<span>{oldLang(day)}</span> {oldLang(day)}
</div> </div>
))} ))}
{prevMonthGrid.map((gridDate) => ( {prevMonthGrid.map((gridDate) => (
<div className="day-button disabled"><span>{gridDate}</span></div> <Button
))} key={`prev-month-${gridDate}`}
{currentMonthGrid.map((gridDate) => ( round
<div size="smaller"
role="button" color="translucent"
tabIndex={0} disabled
onClick={() => handleDateSelect(gridDate)}
className={buildClassName(
'day-button',
'div-button',
isDisabledDay(
currentYear, currentMonth, gridDate, minDate, maxDate,
)
? 'disabled'
: gridDate ? 'clickable' : '',
selectedDay === formatDay(currentYear, currentMonth, gridDate) && 'selected',
)}
> >
{Boolean(gridDate) && ( {gridDate}
<span>{gridDate}</span> </Button>
)}
</div>
))} ))}
{currentMonthGrid.map((gridDate) => {
const isSelected = selectedDay === formatDay(currentYear, currentMonth, gridDate);
return (
<Button
key={`current-month-${gridDate}`}
round
autoFocus={isSelected}
ariaSelected={isSelected}
onClick={() => handleDateSelect(gridDate)}
disabled={isDisabledDay(
currentYear, currentMonth, gridDate, minDate, maxDate,
)}
noFastClick
noPreventDefault
nonInteractive={!gridDate}
size="smaller"
color={isSelected ? 'primary' : 'translucent'}
className={buildClassName('day-button div-button', isSelected && 'selected')}
>
{gridDate}
</Button>
);
})}
{nextMonthGrid.map((gridDate) => ( {nextMonthGrid.map((gridDate) => (
<div className="day-button disabled"><span>{gridDate}</span></div> <Button
key={`next-month-${gridDate}`}
round
size="smaller"
color="translucent"
disabled
>
{gridDate}
</Button>
))} ))}
</div> </div>
</div> </div>

View File

@ -292,8 +292,7 @@
padding-bottom: 1.25rem; padding-bottom: 1.25rem;
.unpin-all-button { .unpin-all-button {
color: var(--color-primary); --button-text-color: var(--color-primary);
text-transform: capitalize;
.icon-unpin { .icon-unpin {
margin-inline-start: -0.4375rem; margin-inline-start: -0.4375rem;
@ -304,27 +303,6 @@
transition: color 0.15s; transition: color 0.15s;
} }
@media (hover: hover) {
&:hover {
color: var(--color-white);
.icon-unpin {
color: var(--color-white);
}
}
}
@media (max-width: 600px) {
&:active,
&:focus {
color: var(--color-white);
.icon-unpin {
color: var(--color-white);
}
}
}
} }
.composer-button { .composer-button {
@ -339,20 +317,7 @@
} }
.open-chat-button { .open-chat-button {
color: var(--color-primary); --button-text-color: var(--color-primary);
@media (hover: hover) {
&:hover {
color: var(--color-white);
}
}
@media (max-width: 600px) {
&:active,
&:focus {
color: var(--color-white);
}
}
} }
.mask-image-disabled &::before { .mask-image-disabled &::before {

View File

@ -608,6 +608,7 @@ function MiddleColumn({
fluid fluid
color="secondary" color="secondary"
className="composer-button unpin-all-button" className="composer-button unpin-all-button"
noForcedUpperCase
onClick={handleOpenUnpinModal} onClick={handleOpenUnpinModal}
iconName="unpin" iconName="unpin"
> >

View File

@ -154,7 +154,6 @@ const PollModal = ({
const lang = useLang(); const lang = useLang();
const questionInputRef = useRef<HTMLInputElement>();
const mainButtonRef = useRef<HTMLButtonElement>(); const mainButtonRef = useRef<HTMLButtonElement>();
const optionListRef = useRef<HTMLDivElement>(); const optionListRef = useRef<HTMLDivElement>();
const durationMenuRef = useRef<HTMLDivElement>(); const durationMenuRef = useRef<HTMLDivElement>();
@ -206,14 +205,6 @@ const PollModal = ({
true, true,
); );
useEffect(() => {
if (!isOpen) {
return;
}
questionInputRef.current?.focus();
}, [isOpen]);
useEffect(() => { useEffect(() => {
if (isChannel) { if (isChannel) {
setIsPublic(false); setIsPublic(false);
@ -578,11 +569,11 @@ const PollModal = ({
<IslandTitle>{lang('PollModalQuestionTitle')}</IslandTitle> <IslandTitle>{lang('PollModalQuestionTitle')}</IslandTitle>
<Island> <Island>
<InputText <InputText
ref={questionInputRef}
className={styles.input} className={styles.input}
label={lang('AskAQuestion')} label={lang('AskAQuestion')}
value={question} value={question}
maxLength={MAX_QUESTION_LENGTH} maxLength={MAX_QUESTION_LENGTH}
autoFocus
error={hasSubmitted && !trimmedQuestion ? lang('PollsChooseQuestion') : undefined} error={hasSubmitted && !trimmedQuestion ? lang('PollsChooseQuestion') : undefined}
onChange={handleQuestionChange} onChange={handleQuestionChange}
/> />

View File

@ -27,6 +27,8 @@
@layer ui.button { @layer ui.button {
.Button { .Button {
--premium-gradient: linear-gradient(88.39deg, #6c93ff -2.56%, #976fff 51.27%, #df69d1 107.39%); --premium-gradient: linear-gradient(88.39deg, #6c93ff -2.56%, #976fff 51.27%, #df69d1 107.39%);
--button-text-color: white;
--button-background-color: transparent;
cursor: var(--custom-cursor, pointer); cursor: var(--custom-cursor, pointer);
@ -46,11 +48,11 @@
font-weight: var(--font-weight-medium); font-weight: var(--font-weight-medium);
line-height: 1.2; line-height: 1.2;
color: white; color: var(--button-text-color);
text-decoration: none !important; text-decoration: none !important;
text-transform: uppercase; text-transform: uppercase;
background-color: transparent; background-color: var(--button-background-color);
background-size: cover; background-size: cover;
outline: none !important; outline: none !important;
@ -88,66 +90,71 @@
} }
} }
@include active-styles() {
color:
var(
--button-active-text-color,
var(--button-text-color)
);
background-color:
var(
--button-active-background-color,
var(--button-background-color)
);
}
@include no-ripple-styles() {
color:
var(
--button-no-ripple-text-color,
var(
--button-active-text-color,
var(--button-text-color)
)
);
background-color:
var(
--button-no-ripple-background-color,
var(
--button-active-background-color,
var(--button-background-color)
)
);
}
&.primary { &.primary {
--ripple-color: rgba(0, 0, 0, 0.08); --ripple-color: rgba(0, 0, 0, 0.08);
--button-text-color: var(--color-white);
color: var(--color-white); --button-background-color: var(--color-primary);
background-color: var(--color-primary); --button-active-text-color: var(--color-white);
--button-active-background-color: rgba(var(--color-primary-shade-rgb), 0.9);
@include active-styles() { --button-no-ripple-background-color: var(--color-primary-shade-darker);
background-color: rgba(var(--color-primary-shade-rgb), 0.9);
}
@include no-ripple-styles() {
background-color: var(--color-primary-shade-darker);
}
} }
&.secondary { &.secondary {
--ripple-color: rgba(0, 0, 0, 0.08); --ripple-color: rgba(0, 0, 0, 0.08);
--button-text-color: rgba(var(--color-text-secondary-rgb), 0.75);
color: rgba(var(--color-text-secondary-rgb), 0.75); --button-background-color: var(--color-background);
background-color: var(--color-background); --button-active-text-color: white;
--button-active-background-color: var(--color-primary);
@include active-styles() { --button-no-ripple-background-color: var(--color-primary-shade);
color: white;
background-color: var(--color-primary);
}
@include no-ripple-styles() {
background-color: var(--color-primary-shade);
}
} }
&.gray { &.gray {
--ripple-color: rgba(0, 0, 0, 0.08); --ripple-color: rgba(0, 0, 0, 0.08);
--button-text-color: var(--color-text-secondary);
color: var(--color-text-secondary); --button-background-color: var(--color-background);
background-color: var(--color-background); --button-active-text-color: var(--color-primary);
--button-no-ripple-background-color: var(--color-chat-hover);
@include active-styles() {
color: var(--color-primary);
}
@include no-ripple-styles() {
background-color: var(--color-chat-hover);
}
} }
&.danger { &.danger {
--ripple-color: rgba(var(--color-error-rgb), 0.16); --ripple-color: rgba(var(--color-error-rgb), 0.16);
--button-text-color: var(--color-error);
color: var(--color-error); --button-background-color: var(--color-background);
background-color: var(--color-background); --button-active-text-color: var(--color-white);
--button-active-background-color: var(--color-error);
@include active-styles() { --button-no-ripple-background-color: var(--color-error-shade);
color: var(--color-white);
background-color: var(--color-error);
}
@include no-ripple-styles() {
background-color: var(--color-error-shade);
}
} }
&.faded { &.faded {
@ -165,154 +172,102 @@
&.translucent-primary, &.translucent-primary,
&.translucent { &.translucent {
--ripple-color: var(--color-interactive-element-hover); --ripple-color: var(--color-interactive-element-hover);
--button-text-color: var(--color-text-secondary);
color: var(--color-text-secondary); --button-background-color: transparent;
background-color: transparent; --button-active-background-color: var(--color-interactive-element-hover);
--button-no-ripple-background-color: rgba(var(--color-text-secondary-rgb), 0.16);
@include active-styles() {
background-color: var(--color-interactive-element-hover);
}
@include no-ripple-styles() {
background-color: rgba(var(--color-text-secondary-rgb), 0.16);
}
&.activated { &.activated {
color: var(--color-primary); --button-active-text-color: var(--color-primary);
color: var(--button-active-text-color, var(--color-primary));
} }
} }
&.translucent-white { &.translucent-white {
--ripple-color: rgba(255, 255, 255, 0.08); --ripple-color: rgba(255, 255, 255, 0.08);
--button-text-color: rgba(255, 255, 255, 0.5);
color: rgba(255, 255, 255, 0.5); --button-background-color: transparent;
background-color: transparent; --button-active-text-color: white;
--button-active-background-color: rgba(255, 255, 255, 0.08);
@include active-styles() { --button-no-ripple-background-color: rgba(255, 255, 255, 0.16);
color: white;
background-color: rgba(255, 255, 255, 0.08);
}
@include no-ripple-styles() {
background-color: rgba(255, 255, 255, 0.16);
}
} }
&.translucent-black { &.translucent-black {
--ripple-color: rgba(0, 0, 0, 0.08); --ripple-color: rgba(0, 0, 0, 0.08);
--button-text-color: rgba(0, 0, 0, 0.8);
color: rgba(0, 0, 0, 0.8); --button-background-color: transparent;
background-color: transparent; --button-active-background-color: rgba(0, 0, 0, 0.08);
--button-no-ripple-background-color: rgba(0, 0, 0, 0.16);
@include active-styles() {
background-color: rgba(0, 0, 0, 0.08);
}
@include no-ripple-styles() {
background-color: rgba(0, 0, 0, 0.16);
}
} }
&.translucent-primary { &.translucent-primary {
color: var(--color-primary); --button-text-color: var(--color-primary);
} }
&.translucent-bordered { &.translucent-bordered {
--ripple-color: rgba(0, 0, 0, 0.08); --ripple-color: rgba(0, 0, 0, 0.08);
--button-text-color: var(--accent-color);
--button-background-color: transparent;
--button-active-text-color: var(--color-white);
--button-active-background-color: var(--accent-color);
--button-no-ripple-background-color: var(--active-color);
border: 1px solid var(--accent-color); border: 1px solid var(--accent-color);
color: var(--accent-color);
background-color: transparent;
@include active-styles() {
color: var(--color-white);
background-color: var(--accent-color);
}
@include no-ripple-styles() {
background-color: var(--active-color);
}
} }
&.adaptive { &.adaptive {
--ripple-color: var(--accent-background-active-color); --ripple-color: var(--accent-background-active-color);
--button-text-color: var(--accent-color);
color: var(--accent-color); --button-background-color: var(--accent-background-color);
background-color: var(--accent-background-color); --button-active-background-color: var(--accent-background-active-color);
--button-no-ripple-background-color: var(--accent-background-active-color);
@include active-styles() {
background-color: var(--accent-background-active-color);
}
@include no-ripple-styles() {
background-color: var(--accent-background-active-color);
}
} }
&.dark { &.dark {
--ripple-color: rgba(255, 255, 255, 0.08); --ripple-color: rgba(255, 255, 255, 0.08);
--button-text-color: white;
color: white; --button-background-color: rgba(0, 0, 0, 0.75);
background-color: rgba(0, 0, 0, 0.75); --button-active-text-color: white;
--button-active-background-color: rgba(0, 0, 0, 0.85);
@include active-styles() { --button-no-ripple-background-color: rgba(0, 0, 0, 0.95);
color: white;
background-color: rgba(0, 0, 0, 0.85);
}
@include no-ripple-styles() {
background-color: rgba(0, 0, 0, 0.95);
}
} }
&.green { &.green {
--ripple-color: rgba(0, 0, 0, 0.08); --ripple-color: rgba(0, 0, 0, 0.08);
--button-text-color: var(--color-white);
color: var(--color-white); --button-background-color: var(--color-green);
background-color: var(--color-green); --button-active-text-color: var(--color-white);
--button-active-background-color: var(--color-green-darker);
@include active-styles() { --button-no-ripple-background-color: var(--color-green);
background-color: var(--color-green-darker);
}
@include no-ripple-styles() {
background-color: var(--color-green);
}
} }
&.stars { &.stars {
--ripple-color: rgba(0, 0, 0, 0.08); --ripple-color: rgba(0, 0, 0, 0.08);
--button-text-color: var(--color-white);
color: var(--color-white); --button-background-color: #ffb727;
background-color: #ffb727; --button-active-text-color: var(--color-white);
--button-active-background-color: #ffb727cc;
--button-no-ripple-background-color: #ffb727;
.theme-dark & { .theme-dark & {
background-color: #cf8920; --button-background-color: #cf8920;
}
@include active-styles() {
background-color: #ffb727cc;
}
@include no-ripple-styles() {
background-color: #ffb727;
} }
} }
&.bluredStarsBadge { &.bluredStarsBadge {
color: var(--color-white); --button-text-color: var(--color-white);
background: rgba(0, 0, 0, 0.2) !important; background: rgba(0, 0, 0, 0.2) !important;
backdrop-filter: blur(50px); backdrop-filter: blur(50px);
} }
&.transparentBlured { &.transparentBlured {
color: white; --button-text-color: white;
background-color: rgba(255, 255, 255, 0.1); --button-background-color: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(0.5rem); --button-active-background-color: rgba(255, 255, 255, 0.2);
&:hover { backdrop-filter: blur(0.5rem);
background-color: rgba(255, 255, 255, 0.2);
}
} }
&.smaller { &.smaller {
@ -501,35 +456,23 @@
} }
&.text { &.text {
background-color: transparent; --button-background-color: transparent;
&.primary { &.primary {
color: var(--color-primary); --button-text-color: var(--color-primary);
background-color: transparent; --button-active-text-color: var(--color-primary);
--button-active-background-color: rgba(var(--color-primary-shade-rgb), 0.08);
@include active-styles() { --button-no-ripple-background-color: rgba(var(--color-primary-shade-rgb), 0.16);
background-color: rgba(var(--color-primary-shade-rgb), 0.08);
}
@include no-ripple-styles() {
background-color: rgba(var(--color-primary-shade-rgb), 0.16);
}
} }
&.secondary { &.secondary {
color: var(--color-text-secondary); --button-text-color: var(--color-text-secondary);
background-color: transparent;
} }
&.danger { &.danger {
@include active-styles() { --button-active-text-color: var(--color-error);
color: var(--color-error); --button-active-background-color: rgba(var(--color-error-rgb), 0.08);
background-color: rgba(var(--color-error-rgb), 0.08); --button-no-ripple-background-color: rgba(var(--color-error-rgb), 0.16);
}
@include no-ripple-styles() {
background-color: rgba(var(--color-error-rgb), 0.16);
}
} }
} }
} }

View File

@ -40,6 +40,7 @@ export type OwnProps = {
isLoading?: boolean; isLoading?: boolean;
ariaLabel?: string; ariaLabel?: string;
ariaControls?: string; ariaControls?: string;
ariaSelected?: boolean;
hasPopup?: boolean; hasPopup?: boolean;
href?: string; href?: string;
download?: string; download?: string;
@ -60,6 +61,7 @@ export type OwnProps = {
noForcedUpperCase?: boolean; noForcedUpperCase?: boolean;
shouldStopPropagation?: boolean; shouldStopPropagation?: boolean;
style?: string; style?: string;
autoFocus?: boolean;
iconName?: IconName; iconName?: IconName;
iconAlignment?: 'top' | 'bottom' | 'start' | 'end'; iconAlignment?: 'top' | 'bottom' | 'start' | 'end';
iconClassName?: string; iconClassName?: string;
@ -98,6 +100,7 @@ const Button = ({
noSparkleAnimation, noSparkleAnimation,
ariaLabel, ariaLabel,
ariaControls, ariaControls,
ariaSelected,
hasPopup, hasPopup,
href, href,
download, download,
@ -114,6 +117,7 @@ const Button = ({
shouldStopPropagation, shouldStopPropagation,
noForcedUpperCase, noForcedUpperCase,
style, style,
autoFocus,
iconName, iconName,
iconAlignment = 'start', iconAlignment = 'start',
iconClassName, iconClassName,
@ -241,6 +245,7 @@ const Button = ({
title={ariaLabel} title={ariaLabel}
download={download} download={download}
tabIndex={tabIndex} tabIndex={tabIndex}
autoFocus={autoFocus}
dir={isRtl ? 'rtl' : undefined} dir={isRtl ? 'rtl' : undefined}
aria-label={ariaLabel} aria-label={ariaLabel}
aria-controls={ariaControls} aria-controls={ariaControls}
@ -268,9 +273,12 @@ const Button = ({
onMouseLeave={onMouseLeave && !isNotInteractive ? onMouseLeave : undefined} onMouseLeave={onMouseLeave && !isNotInteractive ? onMouseLeave : undefined}
onTransitionEnd={onTransitionEnd} onTransitionEnd={onTransitionEnd}
onFocus={onFocus && !isNotInteractive ? onFocus : undefined} onFocus={onFocus && !isNotInteractive ? onFocus : undefined}
disabled={disabled}
autoFocus={autoFocus}
aria-label={ariaLabel} aria-label={ariaLabel}
aria-controls={ariaControls} aria-controls={ariaControls}
aria-haspopup={hasPopup} aria-haspopup={hasPopup}
aria-selected={ariaSelected}
title={ariaLabel} title={ariaLabel}
tabIndex={tabIndex} tabIndex={tabIndex}
dir={isRtl ? 'rtl' : undefined} dir={isRtl ? 'rtl' : undefined}

View File

@ -68,6 +68,7 @@ const ConfirmDialog: FC<OwnProps> = ({
onClose={onClose} onClose={onClose}
isNativeDialog isNativeDialog
onCloseAnimationEnd={onCloseAnimationEnd} onCloseAnimationEnd={onCloseAnimationEnd}
noTitleAutoFocus
> >
{text && text.split('\\n').map((textPart) => ( {text && text.split('\\n').map((textPart) => (
<p>{textPart}</p> <p>{textPart}</p>
@ -85,10 +86,15 @@ const ConfirmDialog: FC<OwnProps> = ({
onClick={confirmHandler} onClick={confirmHandler}
color={confirmIsDestructive ? 'danger' : 'primary'} color={confirmIsDestructive ? 'danger' : 'primary'}
disabled={isConfirmDisabled} disabled={isConfirmDisabled}
autoFocus={!confirmIsDestructive}
> >
{confirmLabel || lang('GeneralConfirm')} {confirmLabel || lang('GeneralConfirm')}
</Button> </Button>
{!isOnlyConfirm && <Button className="confirm-dialog-button" isText onClick={onClose}>{lang('Cancel')}</Button>} {!isOnlyConfirm && (
<Button className="confirm-dialog-button" isText onClick={onClose} autoFocus={confirmIsDestructive}>
{lang('Cancel')}
</Button>
)}
</div> </div>
</Modal> </Modal>
); );

View File

@ -24,6 +24,7 @@ type OwnProps = {
maxLength?: number; maxLength?: number;
tabIndex?: number; tabIndex?: number;
title?: string; title?: string;
autoFocus?: boolean;
teactExperimentControlled?: boolean; teactExperimentControlled?: boolean;
inputMode?: 'text' | 'none' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search'; inputMode?: 'text' | 'none' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search';
onChange?: (e: ChangeEvent<HTMLInputElement>) => void; onChange?: (e: ChangeEvent<HTMLInputElement>) => void;
@ -51,6 +52,7 @@ const InputText = ({
maxLength, maxLength,
tabIndex, tabIndex,
title, title,
autoFocus,
teactExperimentControlled, teactExperimentControlled,
onChange, onChange,
onInput, onInput,
@ -99,6 +101,7 @@ const InputText = ({
title={title} title={title}
teactExperimentControlled={teactExperimentControlled} teactExperimentControlled={teactExperimentControlled}
onClick={onClick} onClick={onClick}
autoFocus={autoFocus}
/> />
{labelText && ( {labelText && (
<label htmlFor={id}>{labelText}</label> <label htmlFor={id}>{labelText}</label>

View File

@ -44,6 +44,7 @@ export type OwnProps = {
noBackdrop?: boolean; noBackdrop?: boolean;
noBackdropClose?: boolean; noBackdropClose?: boolean;
isNativeDialog?: boolean; isNativeDialog?: boolean;
noTitleAutoFocus?: boolean;
children: React.ReactNode; children: React.ReactNode;
style?: string; style?: string;
dialogStyle?: string; dialogStyle?: string;
@ -68,6 +69,7 @@ const Modal = (props: OwnProps) => {
noBackdropClose, noBackdropClose,
noFreezeOnClose, noFreezeOnClose,
isNativeDialog, isNativeDialog,
noTitleAutoFocus,
onClose, onClose,
onCloseAnimationEnd, onCloseAnimationEnd,
onEnter, onEnter,
@ -251,7 +253,7 @@ const Modal = (props: OwnProps) => {
return title ? ( return title ? (
<div className={buildClassName('modal-header', headerClassName, isCondensedHeader && 'modal-header-condensed')}> <div className={buildClassName('modal-header', headerClassName, isCondensedHeader && 'modal-header-condensed')}>
{closeButton} {closeButton}
<div className="modal-title">{title}</div> <div className="modal-title" autoFocus={!noTitleAutoFocus}>{title}</div>
</div> </div>
) : closeButton; ) : closeButton;
} }

View File

@ -52,6 +52,7 @@ const MAPPED_ATTRIBUTES: Partial<Record<string, string>> = {
autoCorrect: 'autocorrect', autoCorrect: 'autocorrect',
autoPlay: 'autoplay', autoPlay: 'autoplay',
spellCheck: 'spellcheck', spellCheck: 'spellcheck',
autoFocus: 'autofocus',
}; };
const INDEX_KEY_PREFIX = '__indexKey#'; const INDEX_KEY_PREFIX = '__indexKey#';
const SELECTION_STATE_ATTRIBUTE = '__teactSelectionState'; const SELECTION_STATE_ATTRIBUTE = '__teactSelectionState';