@refraktor/dates 0.0.3 → 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.
Files changed (63) hide show
  1. package/build/components/date-range-picker/date-range-picker.d.ts +4 -0
  2. package/build/components/date-range-picker/date-range-picker.d.ts.map +1 -0
  3. package/build/components/date-range-picker/date-range-picker.js +379 -0
  4. package/build/components/date-range-picker/date-range-picker.types.d.ts +100 -0
  5. package/build/components/date-range-picker/date-range-picker.types.d.ts.map +1 -0
  6. package/build/components/date-range-picker/date-range-picker.types.js +1 -0
  7. package/build/components/date-range-picker/index.d.ts +3 -0
  8. package/build/components/date-range-picker/index.d.ts.map +1 -0
  9. package/build/components/date-range-picker/index.js +1 -0
  10. package/build/components/time-input/index.d.ts +1 -1
  11. package/build/components/time-input/index.d.ts.map +1 -1
  12. package/build/components/time-input/time-input.d.ts.map +1 -1
  13. package/build/components/time-input/time-input.js +7 -196
  14. package/build/components/time-input/time-input.types.d.ts +5 -83
  15. package/build/components/time-input/time-input.types.d.ts.map +1 -1
  16. package/build/components/time-picker/index.d.ts +1 -1
  17. package/build/components/time-picker/index.d.ts.map +1 -1
  18. package/build/components/time-picker/time-picker.d.ts.map +1 -1
  19. package/build/components/time-picker/time-picker.js +498 -350
  20. package/build/components/time-picker/time-picker.types.d.ts +96 -61
  21. package/build/components/time-picker/time-picker.types.d.ts.map +1 -1
  22. package/build/style.css +2 -2
  23. package/package.json +33 -4
  24. package/.turbo/turbo-build.log +0 -4
  25. package/refraktor-dates-0.0.1-alpha.0.tgz +0 -0
  26. package/src/components/date-input/date-input.tsx +0 -379
  27. package/src/components/date-input/date-input.types.ts +0 -161
  28. package/src/components/date-input/index.ts +0 -13
  29. package/src/components/date-picker/date-picker.tsx +0 -649
  30. package/src/components/date-picker/date-picker.types.ts +0 -145
  31. package/src/components/date-picker/index.ts +0 -15
  32. package/src/components/dates-provider/context.ts +0 -18
  33. package/src/components/dates-provider/dates-provider.tsx +0 -136
  34. package/src/components/dates-provider/index.ts +0 -10
  35. package/src/components/dates-provider/types.ts +0 -33
  36. package/src/components/dates-provider/use-dates.ts +0 -5
  37. package/src/components/index.ts +0 -9
  38. package/src/components/month-input/index.ts +0 -13
  39. package/src/components/month-input/month-input.tsx +0 -366
  40. package/src/components/month-input/month-input.types.ts +0 -139
  41. package/src/components/month-picker/index.ts +0 -14
  42. package/src/components/month-picker/month-picker.tsx +0 -458
  43. package/src/components/month-picker/month-picker.types.ts +0 -117
  44. package/src/components/picker-shared/index.ts +0 -7
  45. package/src/components/picker-shared/picker-header.tsx +0 -178
  46. package/src/components/picker-shared/picker-header.types.ts +0 -49
  47. package/src/components/picker-shared/picker.styles.ts +0 -69
  48. package/src/components/picker-shared/picker.types.ts +0 -4
  49. package/src/components/time-input/index.ts +0 -23
  50. package/src/components/time-input/time-input.tsx +0 -453
  51. package/src/components/time-input/time-input.types.ts +0 -163
  52. package/src/components/time-picker/index.ts +0 -19
  53. package/src/components/time-picker/time-picker.tsx +0 -737
  54. package/src/components/time-picker/time-picker.types.ts +0 -135
  55. package/src/components/year-input/index.ts +0 -13
  56. package/src/components/year-input/year-input.tsx +0 -350
  57. package/src/components/year-input/year-input.types.ts +0 -118
  58. package/src/components/year-picker/index.ts +0 -15
  59. package/src/components/year-picker/year-picker.tsx +0 -504
  60. package/src/components/year-picker/year-picker.types.ts +0 -108
  61. package/src/index.ts +0 -3
  62. package/src/style.css +0 -1
  63. package/tsconfig.json +0 -13
@@ -1,737 +0,0 @@
1
- import { useId, useUncontrolled } from "@refraktor/utils";
2
- import { KeyboardEvent, ReactNode, useMemo } from "react";
3
- import {
4
- createClassNamesConfig,
5
- createComponentConfig,
6
- factory,
7
- useClassNames,
8
- useProps,
9
- useTheme
10
- } from "@refraktor/core";
11
- import { getGridColumns, getPickerSizeStyles } from "../picker-shared";
12
- import {
13
- TimePickerClassNames,
14
- TimePickerFactoryPayload,
15
- TimePickerMode,
16
- TimePickerPeriod,
17
- TimePickerProps,
18
- TimePickerValue
19
- } from "./time-picker.types";
20
-
21
- const HOURS_IN_DAY = 24;
22
- const MINUTES_IN_HOUR = 60;
23
- const SECONDS_IN_MINUTE = 60;
24
- const SECONDS_IN_DAY = HOURS_IN_DAY * MINUTES_IN_HOUR * SECONDS_IN_MINUTE;
25
- const DEFAULT_MODE: TimePickerMode = "24h";
26
- const TIME_SEGMENT_PATTERN = /^\d{1,2}$/;
27
-
28
- const defaultProps = {
29
- mode: DEFAULT_MODE,
30
- disabled: false,
31
- size: "md",
32
- radius: "default"
33
- } satisfies Partial<TimePickerProps>;
34
-
35
- type TimeBounds = {
36
- minSeconds: number;
37
- maxSeconds: number;
38
- hasMin: boolean;
39
- hasMax: boolean;
40
- };
41
-
42
- type TimeParts = {
43
- hours24: number;
44
- hour12: number;
45
- minutes: number;
46
- seconds: number;
47
- period: TimePickerPeriod;
48
- };
49
-
50
- type PickerOptionValue = number | TimePickerPeriod;
51
-
52
- type PickerOption<TValue extends PickerOptionValue = PickerOptionValue> = {
53
- value: TValue;
54
- label: ReactNode;
55
- ariaLabel: string;
56
- selected: boolean;
57
- disabled: boolean;
58
- };
59
-
60
- const pad = (value: number) => String(value).padStart(2, "0");
61
-
62
- const to12Hour = (hours24: number) => {
63
- const normalized = hours24 % 12;
64
- return normalized === 0 ? 12 : normalized;
65
- };
66
-
67
- const to24Hour = (hour12: number, period: TimePickerPeriod) => {
68
- const normalizedHour = hour12 % 12;
69
- return period === "pm" ? normalizedHour + 12 : normalizedHour;
70
- };
71
-
72
- const toSecondsOfDay = (hours24: number, minutes: number, seconds: number) =>
73
- hours24 * MINUTES_IN_HOUR * SECONDS_IN_MINUTE + minutes * SECONDS_IN_MINUTE + seconds;
74
-
75
- const createTimeParts = (hours24: number, minutes: number, seconds: number): TimeParts => {
76
- const normalizedHours24 = hours24 % HOURS_IN_DAY;
77
-
78
- return {
79
- hours24: normalizedHours24,
80
- hour12: to12Hour(normalizedHours24),
81
- minutes,
82
- seconds,
83
- period: normalizedHours24 >= 12 ? "pm" : "am"
84
- };
85
- };
86
-
87
- const parseTimeValue = (value: unknown): TimeParts | undefined => {
88
- if (typeof value !== "string") {
89
- return undefined;
90
- }
91
-
92
- const segments = value.trim().split(":");
93
-
94
- if (
95
- segments.length !== 3 ||
96
- !segments.every((segment) => TIME_SEGMENT_PATTERN.test(segment))
97
- ) {
98
- return undefined;
99
- }
100
-
101
- const [hoursText, minutesText, secondsText] = segments;
102
- const hours24 = Number.parseInt(hoursText, 10);
103
- const minutes = Number.parseInt(minutesText, 10);
104
- const seconds = Number.parseInt(secondsText, 10);
105
-
106
- if (
107
- hours24 < 0 ||
108
- hours24 >= HOURS_IN_DAY ||
109
- minutes < 0 ||
110
- minutes >= MINUTES_IN_HOUR ||
111
- seconds < 0 ||
112
- seconds >= SECONDS_IN_MINUTE
113
- ) {
114
- return undefined;
115
- }
116
-
117
- return createTimeParts(hours24, minutes, seconds);
118
- };
119
-
120
- const formatTimeValue = (hours24: number, minutes: number, seconds: number) =>
121
- `${pad(hours24)}:${pad(minutes)}:${pad(seconds)}`;
122
-
123
- const toTimePartsFromSeconds = (seconds: number) => {
124
- const normalizedSeconds =
125
- ((seconds % SECONDS_IN_DAY) + SECONDS_IN_DAY) % SECONDS_IN_DAY;
126
- const hours24 = Math.floor(
127
- normalizedSeconds / (MINUTES_IN_HOUR * SECONDS_IN_MINUTE)
128
- );
129
- const minutes = Math.floor(
130
- (normalizedSeconds % (MINUTES_IN_HOUR * SECONDS_IN_MINUTE)) /
131
- SECONDS_IN_MINUTE
132
- );
133
- const remainingSeconds = normalizedSeconds % SECONDS_IN_MINUTE;
134
-
135
- return createTimeParts(hours24, minutes, remainingSeconds);
136
- };
137
-
138
- const clampTimeParts = (parts: TimeParts, bounds: TimeBounds) => {
139
- const seconds = toSecondsOfDay(parts.hours24, parts.minutes, parts.seconds);
140
-
141
- if (bounds.hasMin && seconds < bounds.minSeconds) {
142
- return toTimePartsFromSeconds(bounds.minSeconds);
143
- }
144
-
145
- if (bounds.hasMax && seconds > bounds.maxSeconds) {
146
- return toTimePartsFromSeconds(bounds.maxSeconds);
147
- }
148
-
149
- return parts;
150
- };
151
-
152
- const getCurrentTimeParts = () => {
153
- const current = new Date();
154
- return createTimeParts(
155
- current.getHours(),
156
- current.getMinutes(),
157
- current.getSeconds()
158
- );
159
- };
160
-
161
- const getTimeBounds = (
162
- minTime?: TimePickerValue,
163
- maxTime?: TimePickerValue
164
- ): TimeBounds => {
165
- const minParts = parseTimeValue(minTime);
166
- const maxParts = parseTimeValue(maxTime);
167
- const hasMin = minParts !== undefined;
168
- const hasMax = maxParts !== undefined;
169
-
170
- const minSeconds = hasMin
171
- ? toSecondsOfDay(minParts.hours24, minParts.minutes, minParts.seconds)
172
- : Number.NEGATIVE_INFINITY;
173
- const maxSeconds = hasMax
174
- ? toSecondsOfDay(maxParts.hours24, maxParts.minutes, maxParts.seconds)
175
- : Number.POSITIVE_INFINITY;
176
-
177
- if (hasMin && hasMax && minSeconds > maxSeconds) {
178
- return {
179
- minSeconds: maxSeconds,
180
- maxSeconds: minSeconds,
181
- hasMin: true,
182
- hasMax: true
183
- };
184
- }
185
-
186
- return {
187
- minSeconds,
188
- maxSeconds,
189
- hasMin,
190
- hasMax
191
- };
192
- };
193
-
194
- const isTimeDisabled = (seconds: number, disabled: boolean, bounds: TimeBounds) =>
195
- disabled ||
196
- (bounds.hasMin && seconds < bounds.minSeconds) ||
197
- (bounds.hasMax && seconds > bounds.maxSeconds);
198
-
199
- const getSelectablePeriodRange = (
200
- period: TimePickerPeriod,
201
- bounds: TimeBounds
202
- ) => {
203
- const periodStart = period === "am" ? 0 : SECONDS_IN_DAY / 2;
204
- const periodEnd = period === "am" ? SECONDS_IN_DAY / 2 - 1 : SECONDS_IN_DAY - 1;
205
-
206
- const start = Math.max(periodStart, bounds.minSeconds);
207
- const end = Math.min(periodEnd, bounds.maxSeconds);
208
-
209
- if (start > end) {
210
- return undefined;
211
- }
212
-
213
- return { start, end };
214
- };
215
-
216
- const getFirstEnabledIndex = <TValue extends PickerOptionValue>(
217
- options: PickerOption<TValue>[]
218
- ) => options.findIndex((option) => !option.disabled);
219
-
220
- const getLastEnabledIndex = <TValue extends PickerOptionValue>(
221
- options: PickerOption<TValue>[]
222
- ) => {
223
- for (let index = options.length - 1; index >= 0; index -= 1) {
224
- if (!options[index].disabled) {
225
- return index;
226
- }
227
- }
228
-
229
- return -1;
230
- };
231
-
232
- const findNextEnabledIndex = <TValue extends PickerOptionValue>(
233
- options: PickerOption<TValue>[],
234
- startIndex: number,
235
- direction: 1 | -1
236
- ) => {
237
- let index = startIndex + direction;
238
-
239
- while (index >= 0 && index < options.length) {
240
- if (!options[index].disabled) {
241
- return index;
242
- }
243
-
244
- index += direction;
245
- }
246
-
247
- return startIndex;
248
- };
249
-
250
- const handleListKeyDown = <TValue extends PickerOptionValue>(
251
- event: KeyboardEvent<HTMLDivElement>,
252
- options: PickerOption<TValue>[],
253
- onSelect: (value: TValue) => void
254
- ) => {
255
- const firstEnabledIndex = getFirstEnabledIndex(options);
256
- const lastEnabledIndex = getLastEnabledIndex(options);
257
-
258
- if (firstEnabledIndex === -1 || lastEnabledIndex === -1) {
259
- return;
260
- }
261
-
262
- const selectedIndex = options.findIndex((option) => option.selected);
263
-
264
- if (event.key === "Home") {
265
- event.preventDefault();
266
- onSelect(options[firstEnabledIndex].value);
267
- return;
268
- }
269
-
270
- if (event.key === "End") {
271
- event.preventDefault();
272
- onSelect(options[lastEnabledIndex].value);
273
- return;
274
- }
275
-
276
- const applyStep = (direction: 1 | -1, repeat = 1) => {
277
- event.preventDefault();
278
-
279
- let index =
280
- selectedIndex === -1
281
- ? direction === 1
282
- ? firstEnabledIndex
283
- : lastEnabledIndex
284
- : selectedIndex;
285
-
286
- for (let step = 0; step < repeat; step += 1) {
287
- const nextIndex = findNextEnabledIndex(options, index, direction);
288
-
289
- if (nextIndex === index) {
290
- break;
291
- }
292
-
293
- index = nextIndex;
294
- }
295
-
296
- onSelect(options[index].value);
297
- };
298
-
299
- if (event.key === "ArrowDown") {
300
- applyStep(1);
301
- return;
302
- }
303
-
304
- if (event.key === "ArrowUp") {
305
- applyStep(-1);
306
- return;
307
- }
308
-
309
- if (event.key === "PageDown") {
310
- applyStep(1, 5);
311
- return;
312
- }
313
-
314
- if (event.key === "PageUp") {
315
- applyStep(-1, 5);
316
- }
317
- };
318
-
319
- const TimePicker = factory<TimePickerFactoryPayload>((_props, ref) => {
320
- const { cx, getRadius } = useTheme();
321
- const {
322
- id,
323
- value,
324
- defaultValue,
325
- onChange,
326
- minTime,
327
- maxTime,
328
- mode,
329
- disabled,
330
- size,
331
- radius,
332
- getHourLabel,
333
- getMinuteLabel,
334
- getSecondLabel,
335
- getPeriodLabel,
336
- getHourAriaLabel,
337
- getMinuteAriaLabel,
338
- getSecondAriaLabel,
339
- getPeriodAriaLabel,
340
- className,
341
- classNames,
342
- ...props
343
- } = useProps("TimePicker", defaultProps, _props);
344
- const classes = useClassNames("TimePicker", classNames);
345
-
346
- const _id = useId(id);
347
- const sizeStyles = getPickerSizeStyles(size);
348
-
349
- const bounds = useMemo(() => getTimeBounds(minTime, maxTime), [minTime, maxTime]);
350
-
351
- const [selectedTimeState, setSelectedTime] = useUncontrolled<
352
- TimePickerValue | undefined
353
- >({
354
- value,
355
- defaultValue,
356
- finalValue: undefined,
357
- onChange: (nextTime) => {
358
- if (nextTime !== undefined) {
359
- onChange?.(nextTime);
360
- }
361
- }
362
- });
363
-
364
- const selectedParts = useMemo(() => {
365
- const parsed = parseTimeValue(selectedTimeState);
366
-
367
- if (!parsed) {
368
- return undefined;
369
- }
370
-
371
- return clampTimeParts(parsed, bounds);
372
- }, [bounds, selectedTimeState]);
373
- const selectedTime = useMemo(
374
- () =>
375
- selectedParts
376
- ? formatTimeValue(
377
- selectedParts.hours24,
378
- selectedParts.minutes,
379
- selectedParts.seconds
380
- )
381
- : undefined,
382
- [selectedParts]
383
- );
384
- const fallbackParts = useMemo(
385
- () => clampTimeParts(getCurrentTimeParts(), bounds),
386
- [bounds]
387
- );
388
- const activeParts = selectedParts ?? fallbackParts;
389
-
390
- const applyTime = (hours24: number, minutes: number, seconds: number) => {
391
- if (disabled) {
392
- return;
393
- }
394
-
395
- const nextSeconds = toSecondsOfDay(hours24, minutes, seconds);
396
-
397
- if (isTimeDisabled(nextSeconds, false, bounds)) {
398
- return;
399
- }
400
-
401
- const nextTime = formatTimeValue(hours24, minutes, seconds);
402
-
403
- if (selectedTime === nextTime) {
404
- return;
405
- }
406
-
407
- setSelectedTime(nextTime);
408
- };
409
-
410
- const setHour = (hour: number) => {
411
- const nextHour = mode === "12h" ? to24Hour(hour, activeParts.period) : hour;
412
- applyTime(nextHour, activeParts.minutes, activeParts.seconds);
413
- };
414
-
415
- const setMinute = (minute: number) => {
416
- applyTime(activeParts.hours24, minute, activeParts.seconds);
417
- };
418
-
419
- const setSecond = (second: number) => {
420
- applyTime(activeParts.hours24, activeParts.minutes, second);
421
- };
422
-
423
- const setPeriod = (period: TimePickerPeriod) => {
424
- const selectableRange = getSelectablePeriodRange(period, bounds);
425
-
426
- if (!selectableRange) {
427
- return;
428
- }
429
-
430
- const nextHour24 = to24Hour(activeParts.hour12, period);
431
- const candidateSeconds = toSecondsOfDay(
432
- nextHour24,
433
- activeParts.minutes,
434
- activeParts.seconds
435
- );
436
- const nextSeconds = Math.min(
437
- selectableRange.end,
438
- Math.max(selectableRange.start, candidateSeconds)
439
- );
440
- const nextParts = toTimePartsFromSeconds(nextSeconds);
441
-
442
- applyTime(nextParts.hours24, nextParts.minutes, nextParts.seconds);
443
- };
444
-
445
- const hourOptions = useMemo<PickerOption<number>[]>(
446
- () => {
447
- const totalHours = mode === "12h" ? 12 : HOURS_IN_DAY;
448
-
449
- return Array.from({ length: totalHours }, (_, index) => {
450
- const hour = mode === "12h" ? index + 1 : index;
451
- const hours24 =
452
- mode === "12h" ? to24Hour(hour, activeParts.period) : hour;
453
- const nextSeconds = toSecondsOfDay(
454
- hours24,
455
- activeParts.minutes,
456
- activeParts.seconds
457
- );
458
- const selected =
459
- selectedParts !== undefined &&
460
- (mode === "12h"
461
- ? selectedParts.hour12 === hour
462
- : selectedParts.hours24 === hour);
463
-
464
- return {
465
- value: hour,
466
- label: getHourLabel ? getHourLabel(hour, mode) : pad(hour),
467
- ariaLabel: getHourAriaLabel
468
- ? getHourAriaLabel(hour, mode, selected)
469
- : selected
470
- ? `Hour ${pad(hour)}, selected`
471
- : `Choose hour ${pad(hour)}`,
472
- selected,
473
- disabled: isTimeDisabled(nextSeconds, disabled ?? false, bounds)
474
- };
475
- });
476
- },
477
- [
478
- activeParts.minutes,
479
- activeParts.period,
480
- activeParts.seconds,
481
- bounds,
482
- disabled,
483
- getHourAriaLabel,
484
- getHourLabel,
485
- mode,
486
- selectedParts
487
- ]
488
- );
489
-
490
- const minuteOptions = useMemo<PickerOption<number>[]>(
491
- () =>
492
- Array.from({ length: MINUTES_IN_HOUR }, (_, minute) => {
493
- const nextSeconds = toSecondsOfDay(
494
- activeParts.hours24,
495
- minute,
496
- activeParts.seconds
497
- );
498
- const selected = selectedParts?.minutes === minute;
499
-
500
- return {
501
- value: minute,
502
- label: getMinuteLabel ? getMinuteLabel(minute) : pad(minute),
503
- ariaLabel: getMinuteAriaLabel
504
- ? getMinuteAriaLabel(minute, selected)
505
- : selected
506
- ? `Minute ${pad(minute)}, selected`
507
- : `Choose minute ${pad(minute)}`,
508
- selected,
509
- disabled: isTimeDisabled(nextSeconds, disabled ?? false, bounds)
510
- };
511
- }),
512
- [
513
- activeParts.hours24,
514
- activeParts.seconds,
515
- bounds,
516
- disabled,
517
- getMinuteAriaLabel,
518
- getMinuteLabel,
519
- selectedParts
520
- ]
521
- );
522
-
523
- const secondOptions = useMemo<PickerOption<number>[]>(
524
- () =>
525
- Array.from({ length: SECONDS_IN_MINUTE }, (_, second) => {
526
- const nextSeconds = toSecondsOfDay(
527
- activeParts.hours24,
528
- activeParts.minutes,
529
- second
530
- );
531
- const selected = selectedParts?.seconds === second;
532
-
533
- return {
534
- value: second,
535
- label: getSecondLabel ? getSecondLabel(second) : pad(second),
536
- ariaLabel: getSecondAriaLabel
537
- ? getSecondAriaLabel(second, selected)
538
- : selected
539
- ? `Second ${pad(second)}, selected`
540
- : `Choose second ${pad(second)}`,
541
- selected,
542
- disabled: isTimeDisabled(nextSeconds, disabled ?? false, bounds)
543
- };
544
- }),
545
- [
546
- activeParts.hours24,
547
- activeParts.minutes,
548
- bounds,
549
- disabled,
550
- getSecondAriaLabel,
551
- getSecondLabel,
552
- selectedParts
553
- ]
554
- );
555
-
556
- const periodOptions = useMemo<PickerOption<TimePickerPeriod>[]>(
557
- () =>
558
- (["am", "pm"] as const).map((period) => {
559
- const selected = selectedParts?.period === period;
560
-
561
- return {
562
- value: period,
563
- label: getPeriodLabel
564
- ? getPeriodLabel(period)
565
- : period.toUpperCase(),
566
- ariaLabel: getPeriodAriaLabel
567
- ? getPeriodAriaLabel(period, selected)
568
- : selected
569
- ? `${period.toUpperCase()}, selected`
570
- : `Choose ${period.toUpperCase()}`,
571
- selected,
572
- disabled:
573
- (disabled ?? false) ||
574
- !getSelectablePeriodRange(period, bounds)
575
- };
576
- }),
577
- [
578
- bounds,
579
- disabled,
580
- getPeriodAriaLabel,
581
- getPeriodLabel,
582
- selectedParts
583
- ]
584
- );
585
-
586
- const renderSection = <TValue extends PickerOptionValue>({
587
- label,
588
- labelId,
589
- listLabel,
590
- options,
591
- onSelect,
592
- className
593
- }: {
594
- label: ReactNode;
595
- labelId: string;
596
- listLabel: string;
597
- options: PickerOption<TValue>[];
598
- onSelect: (value: TValue) => void;
599
- className?: string;
600
- }) => {
601
- const hasVisibleSelection = options.some((option) => option.selected);
602
- const firstEnabledIndex = getFirstEnabledIndex(options);
603
-
604
- return (
605
- <div
606
- role="group"
607
- aria-labelledby={labelId}
608
- className={cx("flex flex-col min-w-0", classes.section, className)}
609
- >
610
- <div
611
- id={labelId}
612
- className={cx(
613
- "py-2 text-center font-medium text-[var(--refraktor-text-secondary)] border-b border-[var(--refraktor-border)] bg-[var(--refraktor-bg-subtle)]",
614
- sizeStyles.label,
615
- classes.sectionLabel
616
- )}
617
- >
618
- {label}
619
- </div>
620
-
621
- <div
622
- role="listbox"
623
- aria-label={listLabel}
624
- className={cx(
625
- "refraktor-scrollbar flex max-h-64 flex-col p-1 overflow-y-auto",
626
- sizeStyles.gridGap,
627
- classes.list
628
- )}
629
- onKeyDown={(event) => handleListKeyDown(event, options, onSelect)}
630
- >
631
- {options.map((option, index) => {
632
- const tabIndex =
633
- option.selected ||
634
- (!hasVisibleSelection && index === firstEnabledIndex)
635
- ? 0
636
- : -1;
637
-
638
- return (
639
- <button
640
- key={`${labelId}-${String(option.value)}`}
641
- type="button"
642
- role="option"
643
- aria-selected={option.selected}
644
- aria-label={option.ariaLabel}
645
- data-active={option.selected}
646
- data-disabled={option.disabled}
647
- disabled={option.disabled}
648
- tabIndex={tabIndex}
649
- className={cx(
650
- "inline-flex w-full items-center justify-center font-medium text-[var(--refraktor-text)] transition-colors",
651
- option.selected
652
- ? "bg-[var(--refraktor-primary)] text-[var(--refraktor-primary-text)]"
653
- : "hover:bg-[var(--refraktor-bg-hover)]",
654
- option.disabled &&
655
- "pointer-events-none cursor-not-allowed opacity-50",
656
- sizeStyles.cell,
657
- getRadius(radius),
658
- classes.option,
659
- option.selected && classes.optionActive,
660
- option.disabled && classes.optionDisabled
661
- )}
662
- onClick={() => onSelect(option.value)}
663
- >
664
- {option.label}
665
- </button>
666
- );
667
- })}
668
- </div>
669
- </div>
670
- );
671
- };
672
-
673
- return (
674
- <div
675
- ref={ref}
676
- id={_id}
677
- className={cx(
678
- "inline-flex w-full flex-col bg-[var(--refraktor-bg)] overflow-hidden border border-[var(--refraktor-border)]",
679
- getRadius(radius),
680
- classes.root,
681
- className
682
- )}
683
- {...props}
684
- >
685
- <div
686
- className={cx(
687
- "grid divide-x divide-[var(--refraktor-border)]",
688
- getGridColumns(mode === "12h" ? 4 : 3),
689
- classes.grid
690
- )}
691
- >
692
- {renderSection({
693
- label: "Hour",
694
- labelId: `${_id}-hour-label`,
695
- listLabel: "Hour options",
696
- options: hourOptions,
697
- onSelect: setHour,
698
- className: classes.hourSection
699
- })}
700
-
701
- {renderSection({
702
- label: "Minute",
703
- labelId: `${_id}-minute-label`,
704
- listLabel: "Minute options",
705
- options: minuteOptions,
706
- onSelect: setMinute,
707
- className: classes.minuteSection
708
- })}
709
-
710
- {renderSection({
711
- label: "Second",
712
- labelId: `${_id}-second-label`,
713
- listLabel: "Second options",
714
- options: secondOptions,
715
- onSelect: setSecond,
716
- className: classes.secondSection
717
- })}
718
-
719
- {mode === "12h" &&
720
- renderSection({
721
- label: "Period",
722
- labelId: `${_id}-period-label`,
723
- listLabel: "AM or PM options",
724
- options: periodOptions,
725
- onSelect: setPeriod,
726
- className: classes.periodSection
727
- })}
728
- </div>
729
- </div>
730
- );
731
- });
732
-
733
- TimePicker.displayName = "@refraktor/dates/TimePicker";
734
- TimePicker.configure = createComponentConfig<TimePickerProps>();
735
- TimePicker.classNames = createClassNamesConfig<TimePickerClassNames>();
736
-
737
- export default TimePicker;