This commit is contained in:
@@ -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<HTMLInputElement> | undefined, node: HTMLInputElemen
|
||||
return;
|
||||
}
|
||||
|
||||
(ref as MutableRefObject<HTMLInputElement | null>).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<HTMLDivElement | null>(null);
|
||||
const popupRef = useRef<HTMLDivElement | null>(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<number | null>(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({
|
||||
<div className="datepicker-time-root">
|
||||
<div className="datepicker-time-column">
|
||||
<span className="datepicker-time-title">Hours</span>
|
||||
<div className="datepicker-time-list" role="listbox" aria-label="Hours">
|
||||
<div
|
||||
className="datepicker-time-list"
|
||||
role="listbox"
|
||||
aria-label="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}
|
||||
|
||||
Reference in New Issue
Block a user