diff --git a/src/components/DatePicker.tsx b/src/components/DatePicker.tsx index 012aeeb..f96603d 100644 --- a/src/components/DatePicker.tsx +++ b/src/components/DatePicker.tsx @@ -12,7 +12,6 @@ import { type FocusEvent, type FocusEventHandler, type KeyboardEvent as ReactKeyboardEvent, - type MutableRefObject, type ReactNode, type Ref, useCallback, @@ -143,7 +142,7 @@ function createDateTimeFromPickerValue(value: PickerValue): Date { } function startOfDay(value: Date): Date { - const candidate = new Date(value.getTime()); + const candidate = new Date(value); candidate.setHours(0, 0, 0, 0); return candidate; } @@ -256,7 +255,7 @@ function assignRef(ref: Ref | undefined, node: HTMLInputElemen return; } - (ref as MutableRefObject).current = node; + (ref as { current: HTMLInputElement | null }).current = node; } function tokenizeFormat(format: string): RawFormatPart[] { @@ -616,7 +615,11 @@ function isWithinRange( return true; } -function applySegmentDigits(baseValue: PickerValue, kind: SegmentKind, digits: string): PickerValue { +function applySegmentDigits( + baseValue: PickerValue, + kind: SegmentKind, + digits: string, +): PickerValue { const parsedDigits = Number(digits); if (!Number.isFinite(parsedDigits)) { return clonePickerValue(baseValue); @@ -644,7 +647,8 @@ function applySegmentDigits(baseValue: PickerValue, kind: SegmentKind, digits: s const maxDayForCurrentMonth = daysInMonth(year, month); day = clampNumber(day, 1, maxDayForCurrentMonth); - const nextDate = createValidatedDate(year, month, day) ?? createDateAtLocalMidnight(year, month - 1, day); + const nextDate = + createValidatedDate(year, month, day) ?? createDateAtLocalMidnight(year, month - 1, day); return { date: nextDate, hour, @@ -770,9 +774,11 @@ export function DatePicker({ const inputWrapperRef = useRef(null); const popupRef = useRef(null); const changeHandledRef = useRef(false); - const bufferedDigitsRef = useRef<{ segmentIndex: number; digits: string; timestamp: number } | null>( - null, - ); + const bufferedDigitsRef = useRef<{ + segmentIndex: number; + digits: string; + timestamp: number; + } | null>(null); const activeSegmentIndexRef = useRef(0); const pendingSelectionTimerRef = useRef(null); @@ -897,7 +903,7 @@ export function DatePicker({ const monthGrid = useMemo(() => buildMonthGrid(viewMonth, weekStart), [viewMonth, weekStart]); const recalculatePopupPosition = useCallback(() => { - if (!isOpen || !inputWrapperRef.current || !popupRef.current || typeof window === 'undefined') { + if (!isOpen || !inputWrapperRef.current || !popupRef.current || !globalThis.window) { return; } @@ -920,7 +926,10 @@ export function DatePicker({ ? anchorRect.top - popupRect.height - POPUP_GAP : anchorRect.bottom + POPUP_GAP; - top = Math.max(POPUP_MARGIN, Math.min(top, viewportHeight - POPUP_MARGIN - popupRect.height)); + top = Math.max( + POPUP_MARGIN, + Math.min(top, viewportHeight - POPUP_MARGIN - popupRect.height), + ); setPopupPosition({ top, @@ -937,7 +946,7 @@ export function DatePicker({ recalculatePopupPosition(); - if (typeof window === 'undefined') { + if (!globalThis.window) { return; } @@ -965,7 +974,10 @@ export function DatePicker({ return; } - if (popupRef.current?.contains(eventTarget) || inputWrapperRef.current?.contains(eventTarget)) { + if ( + popupRef.current?.contains(eventTarget) || + inputWrapperRef.current?.contains(eventTarget) + ) { return; } @@ -992,7 +1004,7 @@ export function DatePicker({ useEffect(() => { return () => { if (pendingSelectionTimerRef.current != null) { - window.clearTimeout(pendingSelectionTimerRef.current); + globalThis.window.clearTimeout(pendingSelectionTimerRef.current); } }; }, []); @@ -1007,15 +1019,15 @@ export function DatePicker({ const clampedIndex = clampNumber(segmentIndex, 0, segments.length - 1); activeSegmentIndexRef.current = clampedIndex; - if (typeof window === 'undefined') { + if (!globalThis.window) { return; } if (pendingSelectionTimerRef.current != null) { - window.clearTimeout(pendingSelectionTimerRef.current); + globalThis.window.clearTimeout(pendingSelectionTimerRef.current); } - pendingSelectionTimerRef.current = window.setTimeout(() => { + pendingSelectionTimerRef.current = globalThis.window.setTimeout(() => { const inputNode = internalInputRef.current; const targetSegment = segments[clampedIndex]; if (!inputNode || !targetSegment) { @@ -1109,7 +1121,8 @@ export function DatePicker({ return; } - const baseValue = parsePickerValueWithFormat(displayValue, formatConfig) ?? clampedSelectedValue; + const baseValue = + parsePickerValueWithFormat(displayValue, formatConfig) ?? clampedSelectedValue; const nextUnclamped = applySegmentDigits(baseValue, segment.kind, digits); const nextClamped = clampPickerToRange( nextUnclamped, @@ -1145,7 +1158,7 @@ export function DatePicker({ (segmentIndex: number, moveToNext: boolean) => { const buffered = bufferedDigitsRef.current; const segment = formatConfig.segments[segmentIndex]; - if (!buffered || buffered.segmentIndex !== segmentIndex || !segment) { + if (buffered?.segmentIndex !== segmentIndex || !segment) { if (moveToNext) { selectSegment(segmentIndex + 1); } @@ -1234,7 +1247,12 @@ export function DatePicker({ hour: selectedHour, minute: selectedMinute, }; - const nextValue = clampPickerToRange(candidate, normalizedMinValue, normalizedMaxValue, type); + const nextValue = clampPickerToRange( + candidate, + normalizedMinValue, + normalizedMaxValue, + type, + ); commitValue(formatPickerValueWithFormat(nextValue, formatConfig)); setViewMonth(startOfMonth(normalizedDate)); }, @@ -1341,7 +1359,12 @@ export function DatePicker({ return; } - const clamped = clampPickerToRange(parsed, normalizedMinValue, normalizedMaxValue, type); + const clamped = clampPickerToRange( + parsed, + normalizedMinValue, + normalizedMaxValue, + type, + ); event.preventDefault(); commitValue(formatPickerValueWithFormat(clamped, formatConfig)); }, @@ -1663,7 +1686,11 @@ export function DatePicker({
Hours -
+
{HOURS.map((hour) => { const hourDisabled = !isHourSelectableForRange( selectedDate, @@ -1717,7 +1744,8 @@ export function DatePicker({ type="button" className={joinClassNames( 'datepicker-time-option', - minute === selectedMinute && 'is-selected', + minute === selectedMinute && + 'is-selected', )} onClick={() => handleMinuteCommit(minute)} disabled={minuteDisabled}