Skip to content

Commit f0789f4

Browse files
morethanwordsclaude
andcommitted
Birthday popup: a month passed on the way keeps the day, and the month field keeps the focus with the a11y layer
Two problems in the month field taking the keyboard (#744): - A month passed on the way to another clamped the day for good: with 31, typing "aug" goes through April and left August 30, as "july" did through June, or arrows through a shorter month. The day as it was entered is kept apart now; every check clamps from it, and the field shows it again once the month or the year fits it. - With the a11y layer the months menu took the focus as it opened on the field's focus, so the keys never reached the field, and that menu's options are not walked by the menu's arrow keys either. createButtonMenuSelect takes activateFocus: false, and the month field keeps the focus and takes the keys itself; Escape still closes the menu first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent a499b10 commit f0789f4

2 files changed

Lines changed: 17 additions & 5 deletions

File tree

‎src/components/buttonMenuSelect.tsx‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,9 @@ export function createButtonMenuSelect<T>(props: ComponentProps<typeof ButtonMen
284284
floatingDirection?: FloatingMenuDirection
285285
floatingOptions?: FloatingMenuPositionOptions & {constrainHeight?: boolean}
286286
onToggleMenu?: (open: boolean) => void
287+
// * false keeps the focus where it is instead of handing it to the menu (the a11y layer does): for a field that
288+
// * opens the menu as it gets the focus and takes the keys itself
289+
activateFocus?: boolean
287290
direction: ButtonMenuDirection
288291
}) {
289292
type MenuInstance = {
@@ -337,7 +340,8 @@ export function createButtonMenuSelect<T>(props: ComponentProps<typeof ButtonMen
337340
'direction',
338341
'floatingDirection',
339342
'floatingOptions',
340-
'onToggleMenu'
343+
'onToggleMenu',
344+
'activateFocus'
341345
])
342346

343347
return <ButtonMenuSelectInner {...innerProps} />
@@ -405,7 +409,7 @@ export function createButtonMenuSelect<T>(props: ComponentProps<typeof ButtonMen
405409
menu.removeTimeout = undefined;
406410
removeMenu(menu)
407411
}, 300);
408-
})
412+
}, undefined, props.activateFocus)
409413
opened = true
410414
}
411415

‎src/components/popups/birthday.tsx‎

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,11 @@ export default async function showBirthdayPopup(props: {
111111
const nowMonth = now.getMonth() + 1;
112112
const nowDay = now.getDate();
113113

114+
// * the day as it was entered: a month or year too short for it clamps what the field shows, and the next one
115+
// * is checked against this, not the clamp (31, then April on the way to August, is August 31)
116+
let enteredDay = dayField.value;
114117
function updateDayInput() {
115-
const value = dayField.value;
116-
const cleanValue = value.replace(/[^0-9]/g, '');
118+
const cleanValue = enteredDay.replace(/[^0-9]/g, '');
117119
if(!cleanValue) {
118120
setDay(undefined);
119121
return;
@@ -133,6 +135,7 @@ export default async function showBirthdayPopup(props: {
133135
dayField.setValueSilently('1');
134136
setDay(1);
135137
} else {
138+
if(dayField.value !== enteredDay) dayField.setValueSilently(enteredDay);
136139
setDay(valueNum);
137140
}
138141
}
@@ -182,7 +185,10 @@ export default async function showBirthdayPopup(props: {
182185
if(option) selectMonth(option);
183186
}
184187

185-
dayField.input.addEventListener('input', updateDayInput);
188+
dayField.input.addEventListener('input', () => {
189+
enteredDay = dayField.value;
190+
updateDayInput();
191+
});
186192
monthField.input.addEventListener('beforeinput', (e: InputEvent) => {
187193
cancelEvent(e);
188194
if(e.inputType === 'insertText' && e.data) {
@@ -217,6 +223,8 @@ export default async function showBirthdayPopup(props: {
217223
return availableMonths();
218224
},
219225
single: true,
226+
// * the field opens it as it gets the focus and takes the keys itself: typing, arrows
227+
activateFocus: false,
220228
optionKey: (v) => v.value.toString(),
221229
renderOption: (props) => (
222230
<>

0 commit comments

Comments
 (0)