@refraktor/dates 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/style.css +2 -2
- package/package.json +33 -4
- package/.turbo/turbo-build.log +0 -4
- package/refraktor-dates-0.0.1-alpha.0.tgz +0 -0
- package/src/components/date-input/date-input.tsx +0 -379
- package/src/components/date-input/date-input.types.ts +0 -161
- package/src/components/date-input/index.ts +0 -13
- package/src/components/date-picker/date-picker.tsx +0 -649
- package/src/components/date-picker/date-picker.types.ts +0 -145
- package/src/components/date-picker/index.ts +0 -15
- package/src/components/dates-provider/context.ts +0 -18
- package/src/components/dates-provider/dates-provider.tsx +0 -136
- package/src/components/dates-provider/index.ts +0 -10
- package/src/components/dates-provider/types.ts +0 -33
- package/src/components/dates-provider/use-dates.ts +0 -5
- package/src/components/index.ts +0 -9
- package/src/components/month-input/index.ts +0 -13
- package/src/components/month-input/month-input.tsx +0 -366
- package/src/components/month-input/month-input.types.ts +0 -139
- package/src/components/month-picker/index.ts +0 -14
- package/src/components/month-picker/month-picker.tsx +0 -458
- package/src/components/month-picker/month-picker.types.ts +0 -117
- package/src/components/picker-shared/index.ts +0 -7
- package/src/components/picker-shared/picker-header.tsx +0 -178
- package/src/components/picker-shared/picker-header.types.ts +0 -49
- package/src/components/picker-shared/picker.styles.ts +0 -69
- package/src/components/picker-shared/picker.types.ts +0 -4
- package/src/components/time-input/index.ts +0 -6
- package/src/components/time-input/time-input.tsx +0 -48
- package/src/components/time-input/time-input.types.ts +0 -30
- package/src/components/time-picker/index.ts +0 -10
- package/src/components/time-picker/time-picker.tsx +0 -1088
- package/src/components/time-picker/time-picker.types.ts +0 -166
- package/src/components/year-input/index.ts +0 -13
- package/src/components/year-input/year-input.tsx +0 -350
- package/src/components/year-input/year-input.types.ts +0 -118
- package/src/components/year-picker/index.ts +0 -15
- package/src/components/year-picker/year-picker.tsx +0 -504
- package/src/components/year-picker/year-picker.types.ts +0 -108
- package/src/index.ts +0 -3
- package/src/style.css +0 -1
- package/tsconfig.json +0 -13
|
@@ -1,649 +0,0 @@
|
|
|
1
|
-
import { useId, useUncontrolled } from "@refraktor/utils";
|
|
2
|
-
import { KeyboardEvent, useEffect, useMemo, useState } from "react";
|
|
3
|
-
import {
|
|
4
|
-
createClassNamesConfig,
|
|
5
|
-
createComponentConfig,
|
|
6
|
-
factory,
|
|
7
|
-
useTheme,
|
|
8
|
-
useClassNames,
|
|
9
|
-
useProps
|
|
10
|
-
} from "@refraktor/core";
|
|
11
|
-
import {
|
|
12
|
-
getGridColumns,
|
|
13
|
-
getPickerSizeStyles,
|
|
14
|
-
PickerHeader
|
|
15
|
-
} from "../picker-shared";
|
|
16
|
-
import { useDates } from "../dates-provider";
|
|
17
|
-
import { MonthPicker } from "../month-picker";
|
|
18
|
-
import {
|
|
19
|
-
DatePickerClassNames,
|
|
20
|
-
DatePickerFactoryPayload,
|
|
21
|
-
DatePickerNavigationDirection,
|
|
22
|
-
DatePickerProps
|
|
23
|
-
} from "./date-picker.types";
|
|
24
|
-
|
|
25
|
-
const DAYS_IN_WEEK = 7;
|
|
26
|
-
const CONSISTENT_WEEK_COUNT = 6;
|
|
27
|
-
const DEFAULT_MONTH_PICKER_COLUMNS = 3;
|
|
28
|
-
const DEFAULT_YEAR_PICKER_YEARS_PER_PAGE = 9;
|
|
29
|
-
const DEFAULT_YEAR_PICKER_COLUMNS = 3;
|
|
30
|
-
|
|
31
|
-
const defaultProps = {
|
|
32
|
-
monthPickerColumns: DEFAULT_MONTH_PICKER_COLUMNS,
|
|
33
|
-
yearPickerYearsPerPage: DEFAULT_YEAR_PICKER_YEARS_PER_PAGE,
|
|
34
|
-
yearPickerColumns: DEFAULT_YEAR_PICKER_COLUMNS,
|
|
35
|
-
disabled: false,
|
|
36
|
-
size: "md",
|
|
37
|
-
radius: "default"
|
|
38
|
-
} satisfies Partial<DatePickerProps>;
|
|
39
|
-
|
|
40
|
-
type DateBounds = {
|
|
41
|
-
minDate: Date | undefined;
|
|
42
|
-
maxDate: Date | undefined;
|
|
43
|
-
minTimestamp: number;
|
|
44
|
-
maxTimestamp: number;
|
|
45
|
-
hasMin: boolean;
|
|
46
|
-
hasMax: boolean;
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
type DatePickerView = "day" | "month";
|
|
50
|
-
|
|
51
|
-
const isValidDate = (value: unknown): value is Date =>
|
|
52
|
-
value instanceof Date && !Number.isNaN(value.getTime());
|
|
53
|
-
|
|
54
|
-
const toSafeInteger = (value: number | undefined, fallback: number) => {
|
|
55
|
-
if (!Number.isFinite(value)) {
|
|
56
|
-
return fallback;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return Math.trunc(value as number);
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
const clamp = (value: number, min: number, max: number) =>
|
|
63
|
-
Math.min(max, Math.max(min, value));
|
|
64
|
-
|
|
65
|
-
const startOfDay = (value: Date) => {
|
|
66
|
-
const next = new Date(value);
|
|
67
|
-
next.setHours(0, 0, 0, 0);
|
|
68
|
-
return next;
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
const startOfMonth = (value: Date) => {
|
|
72
|
-
const next = startOfDay(value);
|
|
73
|
-
next.setDate(1);
|
|
74
|
-
return next;
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
const isSameDay = (first: Date, second: Date) =>
|
|
78
|
-
first.getFullYear() === second.getFullYear() &&
|
|
79
|
-
first.getMonth() === second.getMonth() &&
|
|
80
|
-
first.getDate() === second.getDate();
|
|
81
|
-
|
|
82
|
-
const isSameMonth = (first: Date, second: Date) =>
|
|
83
|
-
first.getFullYear() === second.getFullYear() &&
|
|
84
|
-
first.getMonth() === second.getMonth();
|
|
85
|
-
|
|
86
|
-
const addDays = (value: Date, amount: number) => {
|
|
87
|
-
const next = startOfDay(value);
|
|
88
|
-
next.setDate(next.getDate() + amount);
|
|
89
|
-
return next;
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
const addMonthsPreservingDay = (value: Date, amount: number) => {
|
|
93
|
-
const originalDay = value.getDate();
|
|
94
|
-
const next = startOfDay(value);
|
|
95
|
-
next.setDate(1);
|
|
96
|
-
next.setMonth(next.getMonth() + amount);
|
|
97
|
-
|
|
98
|
-
const lastDayInTargetMonth = new Date(
|
|
99
|
-
next.getFullYear(),
|
|
100
|
-
next.getMonth() + 1,
|
|
101
|
-
0
|
|
102
|
-
).getDate();
|
|
103
|
-
|
|
104
|
-
next.setDate(Math.min(originalDay, lastDayInTargetMonth));
|
|
105
|
-
return next;
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
const getDateBounds = (minDate?: Date, maxDate?: Date): DateBounds => {
|
|
109
|
-
const normalizedMin = isValidDate(minDate) ? startOfDay(minDate) : undefined;
|
|
110
|
-
const normalizedMax = isValidDate(maxDate) ? startOfDay(maxDate) : undefined;
|
|
111
|
-
|
|
112
|
-
if (normalizedMin && normalizedMax && normalizedMin > normalizedMax) {
|
|
113
|
-
return {
|
|
114
|
-
minDate: normalizedMax,
|
|
115
|
-
maxDate: normalizedMin,
|
|
116
|
-
minTimestamp: normalizedMax.getTime(),
|
|
117
|
-
maxTimestamp: normalizedMin.getTime(),
|
|
118
|
-
hasMin: true,
|
|
119
|
-
hasMax: true
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
return {
|
|
124
|
-
minDate: normalizedMin,
|
|
125
|
-
maxDate: normalizedMax,
|
|
126
|
-
minTimestamp: normalizedMin?.getTime() ?? Number.NEGATIVE_INFINITY,
|
|
127
|
-
maxTimestamp: normalizedMax?.getTime() ?? Number.POSITIVE_INFINITY,
|
|
128
|
-
hasMin: normalizedMin !== undefined,
|
|
129
|
-
hasMax: normalizedMax !== undefined
|
|
130
|
-
};
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
const clampDate = (value: Date, bounds: DateBounds) => {
|
|
134
|
-
const normalizedValue = startOfDay(value);
|
|
135
|
-
const timestamp = normalizedValue.getTime();
|
|
136
|
-
|
|
137
|
-
if (timestamp < bounds.minTimestamp && bounds.minDate) {
|
|
138
|
-
return bounds.minDate;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
if (timestamp > bounds.maxTimestamp && bounds.maxDate) {
|
|
142
|
-
return bounds.maxDate;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
return normalizedValue;
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
const isDateDisabled = (value: Date, disabled: boolean, bounds: DateBounds) =>
|
|
149
|
-
disabled ||
|
|
150
|
-
value.getTime() < bounds.minTimestamp ||
|
|
151
|
-
value.getTime() > bounds.maxTimestamp;
|
|
152
|
-
|
|
153
|
-
const toMonthIndex = (value: Date) => value.getFullYear() * 12 + value.getMonth();
|
|
154
|
-
|
|
155
|
-
const resolveMonthStart = (value: Date, bounds: DateBounds) =>
|
|
156
|
-
startOfMonth(clampDate(value, bounds));
|
|
157
|
-
|
|
158
|
-
const getVisibleDays = (
|
|
159
|
-
displayedMonth: Date,
|
|
160
|
-
firstDayOfWeek: number,
|
|
161
|
-
consistentWeeks: boolean
|
|
162
|
-
) => {
|
|
163
|
-
const monthStart = startOfMonth(displayedMonth);
|
|
164
|
-
const leadingDays =
|
|
165
|
-
(monthStart.getDay() - firstDayOfWeek + DAYS_IN_WEEK) % DAYS_IN_WEEK;
|
|
166
|
-
const daysInMonth = new Date(
|
|
167
|
-
monthStart.getFullYear(),
|
|
168
|
-
monthStart.getMonth() + 1,
|
|
169
|
-
0
|
|
170
|
-
).getDate();
|
|
171
|
-
|
|
172
|
-
const visibleWithoutTrailing = leadingDays + daysInMonth;
|
|
173
|
-
const trailingDays = consistentWeeks
|
|
174
|
-
? DAYS_IN_WEEK * CONSISTENT_WEEK_COUNT - visibleWithoutTrailing
|
|
175
|
-
: (DAYS_IN_WEEK - (visibleWithoutTrailing % DAYS_IN_WEEK)) % DAYS_IN_WEEK;
|
|
176
|
-
|
|
177
|
-
const totalDays = visibleWithoutTrailing + trailingDays;
|
|
178
|
-
const firstVisibleDate = addDays(monthStart, -leadingDays);
|
|
179
|
-
|
|
180
|
-
return Array.from({ length: totalDays }, (_, index) =>
|
|
181
|
-
addDays(firstVisibleDate, index)
|
|
182
|
-
);
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
const DatePicker = factory<DatePickerFactoryPayload>((_props, ref) => {
|
|
186
|
-
const { cx, getRadius } = useTheme();
|
|
187
|
-
const { createDate, firstDayOfWeek, consistentWeeks, weekdays } = useDates();
|
|
188
|
-
const {
|
|
189
|
-
id,
|
|
190
|
-
value,
|
|
191
|
-
defaultValue,
|
|
192
|
-
onChange,
|
|
193
|
-
minDate,
|
|
194
|
-
maxDate,
|
|
195
|
-
monthPickerColumns,
|
|
196
|
-
yearPickerYearsPerPage,
|
|
197
|
-
yearPickerColumns,
|
|
198
|
-
disabled,
|
|
199
|
-
size,
|
|
200
|
-
radius,
|
|
201
|
-
getWeekdayLabel,
|
|
202
|
-
getDayLabel,
|
|
203
|
-
getDayAriaLabel,
|
|
204
|
-
getHeaderLabel,
|
|
205
|
-
getNavigationAriaLabel,
|
|
206
|
-
getMonthLabel,
|
|
207
|
-
getMonthAriaLabel,
|
|
208
|
-
getMonthHeaderLabel,
|
|
209
|
-
getMonthNavigationAriaLabel,
|
|
210
|
-
className,
|
|
211
|
-
classNames,
|
|
212
|
-
...props
|
|
213
|
-
} = useProps("DatePicker", defaultProps, _props);
|
|
214
|
-
const classes = useClassNames("DatePicker", classNames);
|
|
215
|
-
|
|
216
|
-
const _id = useId(id);
|
|
217
|
-
const today = useMemo(() => startOfDay(new Date()), []);
|
|
218
|
-
const bounds = useMemo(() => getDateBounds(minDate, maxDate), [minDate, maxDate]);
|
|
219
|
-
|
|
220
|
-
const safeMonthPickerColumns = clamp(
|
|
221
|
-
toSafeInteger(monthPickerColumns, DEFAULT_MONTH_PICKER_COLUMNS),
|
|
222
|
-
1,
|
|
223
|
-
6
|
|
224
|
-
);
|
|
225
|
-
const safeYearPickerYearsPerPage = Math.max(
|
|
226
|
-
1,
|
|
227
|
-
toSafeInteger(
|
|
228
|
-
yearPickerYearsPerPage,
|
|
229
|
-
DEFAULT_YEAR_PICKER_YEARS_PER_PAGE
|
|
230
|
-
)
|
|
231
|
-
);
|
|
232
|
-
const safeYearPickerColumns = clamp(
|
|
233
|
-
toSafeInteger(yearPickerColumns, DEFAULT_YEAR_PICKER_COLUMNS),
|
|
234
|
-
1,
|
|
235
|
-
Math.min(6, safeYearPickerYearsPerPage)
|
|
236
|
-
);
|
|
237
|
-
|
|
238
|
-
const [selectedDateState, setSelectedDate] = useUncontrolled<
|
|
239
|
-
Date | undefined
|
|
240
|
-
>({
|
|
241
|
-
value,
|
|
242
|
-
defaultValue,
|
|
243
|
-
finalValue: undefined,
|
|
244
|
-
onChange: (nextDate) => {
|
|
245
|
-
if (nextDate !== undefined) {
|
|
246
|
-
onChange?.(nextDate);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
const selectedDateTimestamp = isValidDate(selectedDateState)
|
|
252
|
-
? startOfDay(selectedDateState).getTime()
|
|
253
|
-
: undefined;
|
|
254
|
-
const selectedDate =
|
|
255
|
-
selectedDateTimestamp !== undefined
|
|
256
|
-
? new Date(selectedDateTimestamp)
|
|
257
|
-
: undefined;
|
|
258
|
-
|
|
259
|
-
const [displayedMonth, setDisplayedMonth] = useState(() =>
|
|
260
|
-
resolveMonthStart(selectedDate ?? today, bounds)
|
|
261
|
-
);
|
|
262
|
-
const [view, setView] = useState<DatePickerView>("day");
|
|
263
|
-
|
|
264
|
-
useEffect(() => {
|
|
265
|
-
setDisplayedMonth((previousMonth) =>
|
|
266
|
-
resolveMonthStart(previousMonth, bounds)
|
|
267
|
-
);
|
|
268
|
-
}, [bounds]);
|
|
269
|
-
|
|
270
|
-
useEffect(() => {
|
|
271
|
-
if (selectedDate === undefined) {
|
|
272
|
-
return;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
const normalizedMonth = resolveMonthStart(selectedDate, bounds);
|
|
276
|
-
|
|
277
|
-
setDisplayedMonth((previousMonth) => {
|
|
278
|
-
if (isSameMonth(previousMonth, normalizedMonth)) {
|
|
279
|
-
return previousMonth;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
return normalizedMonth;
|
|
283
|
-
});
|
|
284
|
-
}, [bounds, selectedDateTimestamp]);
|
|
285
|
-
|
|
286
|
-
const visibleDays = useMemo(
|
|
287
|
-
() => getVisibleDays(displayedMonth, firstDayOfWeek, consistentWeeks),
|
|
288
|
-
[consistentWeeks, displayedMonth, firstDayOfWeek]
|
|
289
|
-
);
|
|
290
|
-
|
|
291
|
-
const hasVisibleSelection =
|
|
292
|
-
selectedDate !== undefined &&
|
|
293
|
-
visibleDays.some((day) => isSameDay(day, selectedDate));
|
|
294
|
-
|
|
295
|
-
const firstAvailableDay = useMemo(
|
|
296
|
-
() =>
|
|
297
|
-
visibleDays.find(
|
|
298
|
-
(day) => !isDateDisabled(day, disabled ?? false, bounds)
|
|
299
|
-
),
|
|
300
|
-
[bounds, disabled, visibleDays]
|
|
301
|
-
);
|
|
302
|
-
|
|
303
|
-
const canGoPrevious =
|
|
304
|
-
!disabled &&
|
|
305
|
-
(!bounds.hasMin ||
|
|
306
|
-
(bounds.minDate !== undefined &&
|
|
307
|
-
toMonthIndex(displayedMonth) > toMonthIndex(bounds.minDate)));
|
|
308
|
-
const canGoNext =
|
|
309
|
-
!disabled &&
|
|
310
|
-
(!bounds.hasMax ||
|
|
311
|
-
(bounds.maxDate !== undefined &&
|
|
312
|
-
toMonthIndex(displayedMonth) < toMonthIndex(bounds.maxDate)));
|
|
313
|
-
|
|
314
|
-
const sizeStyles = getPickerSizeStyles(size);
|
|
315
|
-
|
|
316
|
-
const resolveNavigationLabel = (
|
|
317
|
-
direction: DatePickerNavigationDirection
|
|
318
|
-
) => {
|
|
319
|
-
if (getNavigationAriaLabel) {
|
|
320
|
-
return getNavigationAriaLabel(direction, displayedMonth);
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
const nextVisibleMonth = addMonthsPreservingDay(
|
|
324
|
-
displayedMonth,
|
|
325
|
-
direction === "previous" ? -1 : 1
|
|
326
|
-
);
|
|
327
|
-
|
|
328
|
-
return direction === "previous"
|
|
329
|
-
? `Show previous month (${createDate(nextVisibleMonth).format("MMMM YYYY")})`
|
|
330
|
-
: `Show next month (${createDate(nextVisibleMonth).format("MMMM YYYY")})`;
|
|
331
|
-
};
|
|
332
|
-
|
|
333
|
-
const resolveDayAriaLabel = (
|
|
334
|
-
day: Date,
|
|
335
|
-
isSelected: boolean,
|
|
336
|
-
isOutside: boolean,
|
|
337
|
-
isDisabled: boolean
|
|
338
|
-
) => {
|
|
339
|
-
if (getDayAriaLabel) {
|
|
340
|
-
return getDayAriaLabel(day, isSelected);
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
const dayLabel = createDate(day).format("dddd, MMMM D, YYYY");
|
|
344
|
-
|
|
345
|
-
if (isSelected) {
|
|
346
|
-
return `${dayLabel}, selected`;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
const suffix = [
|
|
350
|
-
isOutside ? "outside current month" : undefined,
|
|
351
|
-
isDisabled ? "unavailable" : undefined
|
|
352
|
-
]
|
|
353
|
-
.filter(Boolean)
|
|
354
|
-
.join(", ");
|
|
355
|
-
|
|
356
|
-
return suffix ? `Choose ${dayLabel}, ${suffix}` : `Choose ${dayLabel}`;
|
|
357
|
-
};
|
|
358
|
-
|
|
359
|
-
const setDay = (day: Date) => {
|
|
360
|
-
const normalizedDay = startOfDay(day);
|
|
361
|
-
|
|
362
|
-
if (isDateDisabled(normalizedDay, disabled ?? false, bounds)) {
|
|
363
|
-
return;
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
if (selectedDate !== undefined && isSameDay(selectedDate, normalizedDay)) {
|
|
367
|
-
return;
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
setDisplayedMonth(startOfMonth(normalizedDay));
|
|
371
|
-
setSelectedDate(normalizedDay);
|
|
372
|
-
};
|
|
373
|
-
|
|
374
|
-
const shiftDisplayedMonth = (direction: -1 | 1) => {
|
|
375
|
-
setDisplayedMonth((previousMonth) =>
|
|
376
|
-
resolveMonthStart(
|
|
377
|
-
addMonthsPreservingDay(previousMonth, direction),
|
|
378
|
-
bounds
|
|
379
|
-
)
|
|
380
|
-
);
|
|
381
|
-
};
|
|
382
|
-
|
|
383
|
-
const handlePrevious = () => {
|
|
384
|
-
if (!canGoPrevious) {
|
|
385
|
-
return;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
shiftDisplayedMonth(-1);
|
|
389
|
-
};
|
|
390
|
-
|
|
391
|
-
const handleNext = () => {
|
|
392
|
-
if (!canGoNext) {
|
|
393
|
-
return;
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
shiftDisplayedMonth(1);
|
|
397
|
-
};
|
|
398
|
-
|
|
399
|
-
const handleGridKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
|
|
400
|
-
if (disabled) {
|
|
401
|
-
return;
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
const keyboardBaseDay =
|
|
405
|
-
selectedDate && visibleDays.some((day) => isSameDay(day, selectedDate))
|
|
406
|
-
? selectedDate
|
|
407
|
-
: firstAvailableDay ?? clampDate(displayedMonth, bounds);
|
|
408
|
-
|
|
409
|
-
const keyboardSteps: Record<string, number> = {
|
|
410
|
-
ArrowLeft: -1,
|
|
411
|
-
ArrowRight: 1,
|
|
412
|
-
ArrowUp: -DAYS_IN_WEEK,
|
|
413
|
-
ArrowDown: DAYS_IN_WEEK
|
|
414
|
-
};
|
|
415
|
-
|
|
416
|
-
const step = keyboardSteps[event.key];
|
|
417
|
-
|
|
418
|
-
if (step !== undefined) {
|
|
419
|
-
event.preventDefault();
|
|
420
|
-
setDay(addDays(keyboardBaseDay, step));
|
|
421
|
-
return;
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
if (event.key === "Home") {
|
|
425
|
-
event.preventDefault();
|
|
426
|
-
setDay(startOfMonth(displayedMonth));
|
|
427
|
-
return;
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
if (event.key === "End") {
|
|
431
|
-
event.preventDefault();
|
|
432
|
-
setDay(
|
|
433
|
-
new Date(
|
|
434
|
-
displayedMonth.getFullYear(),
|
|
435
|
-
displayedMonth.getMonth() + 1,
|
|
436
|
-
0
|
|
437
|
-
)
|
|
438
|
-
);
|
|
439
|
-
return;
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
if (event.key === "PageUp") {
|
|
443
|
-
event.preventDefault();
|
|
444
|
-
setDay(addMonthsPreservingDay(keyboardBaseDay, -1));
|
|
445
|
-
return;
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
if (event.key === "PageDown") {
|
|
449
|
-
event.preventDefault();
|
|
450
|
-
setDay(addMonthsPreservingDay(keyboardBaseDay, 1));
|
|
451
|
-
}
|
|
452
|
-
};
|
|
453
|
-
|
|
454
|
-
const handleHeaderLabelClick = () => {
|
|
455
|
-
if (disabled) {
|
|
456
|
-
return;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
setView("month");
|
|
460
|
-
};
|
|
461
|
-
|
|
462
|
-
const handleMonthChange = (nextMonth: Date) => {
|
|
463
|
-
if (disabled) {
|
|
464
|
-
return;
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
setDisplayedMonth(resolveMonthStart(nextMonth, bounds));
|
|
468
|
-
setView("day");
|
|
469
|
-
};
|
|
470
|
-
|
|
471
|
-
const monthViewMinYear = bounds.minDate?.getFullYear();
|
|
472
|
-
const monthViewMaxYear = bounds.maxDate?.getFullYear();
|
|
473
|
-
|
|
474
|
-
return (
|
|
475
|
-
<div
|
|
476
|
-
ref={ref}
|
|
477
|
-
id={_id}
|
|
478
|
-
className={cx(
|
|
479
|
-
"inline-flex w-full flex-col gap-2 bg-[var(--refraktor-bg)] p-2",
|
|
480
|
-
getRadius(radius),
|
|
481
|
-
classes.root,
|
|
482
|
-
className
|
|
483
|
-
)}
|
|
484
|
-
{...props}
|
|
485
|
-
>
|
|
486
|
-
{view === "month" ? (
|
|
487
|
-
<MonthPicker
|
|
488
|
-
value={displayedMonth}
|
|
489
|
-
onChange={handleMonthChange}
|
|
490
|
-
minYear={monthViewMinYear}
|
|
491
|
-
maxYear={monthViewMaxYear}
|
|
492
|
-
columns={safeMonthPickerColumns}
|
|
493
|
-
yearPickerYearsPerPage={safeYearPickerYearsPerPage}
|
|
494
|
-
yearPickerColumns={safeYearPickerColumns}
|
|
495
|
-
disabled={disabled}
|
|
496
|
-
size={size}
|
|
497
|
-
radius={radius}
|
|
498
|
-
getMonthLabel={getMonthLabel}
|
|
499
|
-
getMonthAriaLabel={getMonthAriaLabel}
|
|
500
|
-
getHeaderLabel={getMonthHeaderLabel}
|
|
501
|
-
getNavigationAriaLabel={getMonthNavigationAriaLabel}
|
|
502
|
-
className={cx("bg-transparent p-0", classes.monthPicker)}
|
|
503
|
-
/>
|
|
504
|
-
) : (
|
|
505
|
-
<>
|
|
506
|
-
<PickerHeader
|
|
507
|
-
label={
|
|
508
|
-
getHeaderLabel
|
|
509
|
-
? getHeaderLabel(
|
|
510
|
-
displayedMonth.getMonth(),
|
|
511
|
-
displayedMonth.getFullYear(),
|
|
512
|
-
displayedMonth
|
|
513
|
-
)
|
|
514
|
-
: createDate(displayedMonth).format("MMMM YYYY")
|
|
515
|
-
}
|
|
516
|
-
onPrevious={handlePrevious}
|
|
517
|
-
onNext={handleNext}
|
|
518
|
-
onLabelClick={
|
|
519
|
-
disabled ? undefined : handleHeaderLabelClick
|
|
520
|
-
}
|
|
521
|
-
previousDisabled={!canGoPrevious}
|
|
522
|
-
nextDisabled={!canGoNext}
|
|
523
|
-
previousLabel={resolveNavigationLabel("previous")}
|
|
524
|
-
nextLabel={resolveNavigationLabel("next")}
|
|
525
|
-
size={size}
|
|
526
|
-
radius={radius}
|
|
527
|
-
classNames={{
|
|
528
|
-
root: classes.header,
|
|
529
|
-
controls: classes.headerControls,
|
|
530
|
-
control: classes.headerControl,
|
|
531
|
-
previousControl: classes.headerPreviousControl,
|
|
532
|
-
nextControl: classes.headerNextControl,
|
|
533
|
-
label: classes.headerLabel
|
|
534
|
-
}}
|
|
535
|
-
/>
|
|
536
|
-
|
|
537
|
-
<div
|
|
538
|
-
role="row"
|
|
539
|
-
className={cx(
|
|
540
|
-
"grid grid-cols-7 text-center text-[var(--refraktor-text-secondary)]",
|
|
541
|
-
sizeStyles.label,
|
|
542
|
-
sizeStyles.gridGap,
|
|
543
|
-
classes.weekdays
|
|
544
|
-
)}
|
|
545
|
-
>
|
|
546
|
-
{weekdays.map((weekday, index) => {
|
|
547
|
-
const dayOfWeek =
|
|
548
|
-
(firstDayOfWeek + index) % DAYS_IN_WEEK;
|
|
549
|
-
|
|
550
|
-
return (
|
|
551
|
-
<span
|
|
552
|
-
key={`${weekday}-${index}`}
|
|
553
|
-
className={cx("truncate", classes.weekday)}
|
|
554
|
-
>
|
|
555
|
-
{getWeekdayLabel
|
|
556
|
-
? getWeekdayLabel(dayOfWeek, weekday)
|
|
557
|
-
: weekday}
|
|
558
|
-
</span>
|
|
559
|
-
);
|
|
560
|
-
})}
|
|
561
|
-
</div>
|
|
562
|
-
|
|
563
|
-
<div
|
|
564
|
-
role="grid"
|
|
565
|
-
aria-label={`Date picker, ${createDate(displayedMonth).format("MMMM YYYY")}`}
|
|
566
|
-
className={cx(
|
|
567
|
-
"grid",
|
|
568
|
-
getGridColumns(DAYS_IN_WEEK),
|
|
569
|
-
sizeStyles.gridGap,
|
|
570
|
-
classes.grid
|
|
571
|
-
)}
|
|
572
|
-
onKeyDown={handleGridKeyDown}
|
|
573
|
-
>
|
|
574
|
-
{visibleDays.map((day) => {
|
|
575
|
-
const isOutside = !isSameMonth(day, displayedMonth);
|
|
576
|
-
const isSelected =
|
|
577
|
-
selectedDate !== undefined &&
|
|
578
|
-
isSameDay(day, selectedDate);
|
|
579
|
-
const isToday = isSameDay(day, today);
|
|
580
|
-
const isDayDisabled = isDateDisabled(
|
|
581
|
-
day,
|
|
582
|
-
disabled ?? false,
|
|
583
|
-
bounds
|
|
584
|
-
);
|
|
585
|
-
const tabIndex =
|
|
586
|
-
isSelected ||
|
|
587
|
-
(!hasVisibleSelection &&
|
|
588
|
-
firstAvailableDay !== undefined &&
|
|
589
|
-
isSameDay(day, firstAvailableDay))
|
|
590
|
-
? 0
|
|
591
|
-
: -1;
|
|
592
|
-
|
|
593
|
-
return (
|
|
594
|
-
<button
|
|
595
|
-
key={day.toISOString()}
|
|
596
|
-
type="button"
|
|
597
|
-
role="gridcell"
|
|
598
|
-
aria-selected={isSelected}
|
|
599
|
-
aria-current={isToday ? "date" : undefined}
|
|
600
|
-
aria-label={resolveDayAriaLabel(
|
|
601
|
-
day,
|
|
602
|
-
isSelected,
|
|
603
|
-
isOutside,
|
|
604
|
-
isDayDisabled
|
|
605
|
-
)}
|
|
606
|
-
tabIndex={tabIndex}
|
|
607
|
-
data-active={isSelected}
|
|
608
|
-
data-outside={isOutside}
|
|
609
|
-
data-today={isToday}
|
|
610
|
-
data-disabled={isDayDisabled}
|
|
611
|
-
disabled={isDayDisabled}
|
|
612
|
-
className={cx(
|
|
613
|
-
"inline-flex items-center justify-center font-medium text-[var(--refraktor-text)] transition-colors",
|
|
614
|
-
"hover:bg-[var(--refraktor-bg-hover)]",
|
|
615
|
-
isSelected &&
|
|
616
|
-
"bg-[var(--refraktor-primary)] text-[var(--refraktor-primary-text)] hover:bg-[var(--refraktor-primary)]",
|
|
617
|
-
isOutside &&
|
|
618
|
-
"text-[var(--refraktor-text-secondary)]",
|
|
619
|
-
isToday &&
|
|
620
|
-
!isSelected &&
|
|
621
|
-
"ring-1 ring-[var(--refraktor-primary)]",
|
|
622
|
-
isDayDisabled &&
|
|
623
|
-
"pointer-events-none cursor-not-allowed opacity-50",
|
|
624
|
-
sizeStyles.cell,
|
|
625
|
-
getRadius(radius),
|
|
626
|
-
classes.day,
|
|
627
|
-
isOutside && classes.dayOutside,
|
|
628
|
-
isToday && classes.dayToday,
|
|
629
|
-
isSelected && classes.daySelected,
|
|
630
|
-
isDayDisabled && classes.dayDisabled
|
|
631
|
-
)}
|
|
632
|
-
onClick={() => setDay(day)}
|
|
633
|
-
>
|
|
634
|
-
{getDayLabel ? getDayLabel(day) : day.getDate()}
|
|
635
|
-
</button>
|
|
636
|
-
);
|
|
637
|
-
})}
|
|
638
|
-
</div>
|
|
639
|
-
</>
|
|
640
|
-
)}
|
|
641
|
-
</div>
|
|
642
|
-
);
|
|
643
|
-
});
|
|
644
|
-
|
|
645
|
-
DatePicker.displayName = "@refraktor/dates/DatePicker";
|
|
646
|
-
DatePicker.configure = createComponentConfig<DatePickerProps>();
|
|
647
|
-
DatePicker.classNames = createClassNamesConfig<DatePickerClassNames>();
|
|
648
|
-
|
|
649
|
-
export default DatePicker;
|