@refraktor/dates 0.0.2 → 0.0.4

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 (49) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/build/components/date-input/date-input.d.ts.map +1 -1
  3. package/build/components/date-input/date-input.js +5 -3
  4. package/build/components/date-range-picker/date-range-picker.d.ts +4 -0
  5. package/build/components/date-range-picker/date-range-picker.d.ts.map +1 -0
  6. package/build/components/date-range-picker/date-range-picker.js +379 -0
  7. package/build/components/date-range-picker/date-range-picker.types.d.ts +100 -0
  8. package/build/components/date-range-picker/date-range-picker.types.d.ts.map +1 -0
  9. package/build/components/date-range-picker/date-range-picker.types.js +1 -0
  10. package/build/components/date-range-picker/index.d.ts +3 -0
  11. package/build/components/date-range-picker/index.d.ts.map +1 -0
  12. package/build/components/date-range-picker/index.js +1 -0
  13. package/build/components/index.d.ts +2 -0
  14. package/build/components/index.d.ts.map +1 -1
  15. package/build/components/index.js +2 -0
  16. package/build/components/month-input/month-input.d.ts.map +1 -1
  17. package/build/components/month-input/month-input.js +5 -3
  18. package/build/components/time-input/index.d.ts +3 -0
  19. package/build/components/time-input/index.d.ts.map +1 -0
  20. package/build/components/time-input/index.js +1 -0
  21. package/build/components/time-input/time-input.d.ts +4 -0
  22. package/build/components/time-input/time-input.d.ts.map +1 -0
  23. package/build/components/time-input/time-input.js +18 -0
  24. package/build/components/time-input/time-input.types.d.ts +21 -0
  25. package/build/components/time-input/time-input.types.d.ts.map +1 -0
  26. package/build/components/time-input/time-input.types.js +1 -0
  27. package/build/components/time-picker/index.d.ts +3 -0
  28. package/build/components/time-picker/index.d.ts.map +1 -0
  29. package/build/components/time-picker/index.js +1 -0
  30. package/build/components/time-picker/time-picker.d.ts +4 -0
  31. package/build/components/time-picker/time-picker.d.ts.map +1 -0
  32. package/build/components/time-picker/time-picker.js +553 -0
  33. package/build/components/time-picker/time-picker.types.d.ts +114 -0
  34. package/build/components/time-picker/time-picker.types.d.ts.map +1 -0
  35. package/build/components/time-picker/time-picker.types.js +1 -0
  36. package/build/components/year-input/year-input.d.ts.map +1 -1
  37. package/build/components/year-input/year-input.js +5 -3
  38. package/build/style.css +1 -1
  39. package/package.json +3 -3
  40. package/src/components/date-input/date-input.tsx +5 -2
  41. package/src/components/index.ts +2 -0
  42. package/src/components/month-input/month-input.tsx +5 -2
  43. package/src/components/time-input/index.ts +6 -0
  44. package/src/components/time-input/time-input.tsx +48 -0
  45. package/src/components/time-input/time-input.types.ts +30 -0
  46. package/src/components/time-picker/index.ts +10 -0
  47. package/src/components/time-picker/time-picker.tsx +1088 -0
  48. package/src/components/time-picker/time-picker.types.ts +166 -0
  49. package/src/components/year-input/year-input.tsx +5 -2
@@ -0,0 +1,21 @@
1
+ import { createClassNamesConfig, createComponentConfig, FactoryPayload, InputProps } from "@refraktor/core";
2
+ export type TimeInputClassNames = {
3
+ input?: string;
4
+ };
5
+ interface _TimeInputProps {
6
+ /** Show seconds in the native time input @default `false` */
7
+ withSeconds?: boolean;
8
+ /** Used for styling TimeInput parts */
9
+ classNames?: TimeInputClassNames;
10
+ }
11
+ export type TimeInputProps = _TimeInputProps & Omit<InputProps, "type" | "classNames">;
12
+ export interface TimeInputFactoryPayload extends FactoryPayload {
13
+ props: TimeInputProps;
14
+ ref: HTMLInputElement;
15
+ compound: {
16
+ configure: ReturnType<typeof createComponentConfig<TimeInputProps>>;
17
+ classNames: ReturnType<typeof createClassNamesConfig<TimeInputClassNames>>;
18
+ };
19
+ }
20
+ export {};
21
+ //# sourceMappingURL=time-input.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time-input.types.d.ts","sourceRoot":"","sources":["../../../src/components/time-input/time-input.types.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,sBAAsB,EACtB,qBAAqB,EACrB,cAAc,EACd,UAAU,EACb,MAAM,iBAAiB,CAAC;AAEzB,MAAM,MAAM,mBAAmB,GAAG;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,UAAU,eAAe;IACrB,6DAA6D;IAC7D,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB,uCAAuC;IACvC,UAAU,CAAC,EAAE,mBAAmB,CAAC;CACpC;AAED,MAAM,MAAM,cAAc,GAAG,eAAe,GACxC,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,YAAY,CAAC,CAAC;AAE5C,MAAM,WAAW,uBAAwB,SAAQ,cAAc;IAC3D,KAAK,EAAE,cAAc,CAAC;IACtB,GAAG,EAAE,gBAAgB,CAAC;IACtB,QAAQ,EAAE;QACN,SAAS,EAAE,UAAU,CAAC,OAAO,qBAAqB,CAAC,cAAc,CAAC,CAAC,CAAC;QACpE,UAAU,EAAE,UAAU,CAAC,OAAO,sBAAsB,CAAC,mBAAmB,CAAC,CAAC,CAAC;KAC9E,CAAC;CACL"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ export { default as TimePicker } from "./time-picker";
2
+ export type { TimePickerAmPmLabels, TimePickerClassNames, TimePickerFactoryPayload, TimePickerFormat, TimePickerPopoverProps, TimePickerProps, TimePickerValue } from "./time-picker.types";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/time-picker/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,eAAe,CAAC;AACtD,YAAY,EACR,oBAAoB,EACpB,oBAAoB,EACpB,wBAAwB,EACxB,gBAAgB,EAChB,sBAAsB,EACtB,eAAe,EACf,eAAe,EAClB,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1 @@
1
+ export { default as TimePicker } from "./time-picker";
@@ -0,0 +1,4 @@
1
+ import { TimePickerFactoryPayload } from "./time-picker.types";
2
+ declare const TimePicker: import("@refraktor/core").RefraktorComponent<TimePickerFactoryPayload>;
3
+ export default TimePicker;
4
+ //# sourceMappingURL=time-picker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time-picker.d.ts","sourceRoot":"","sources":["../../../src/components/time-picker/time-picker.tsx"],"names":[],"mappings":"AAkCA,OAAO,EAEH,wBAAwB,EAI3B,MAAM,qBAAqB,CAAC;AA8K7B,QAAA,MAAM,UAAU,wEAm2Bd,CAAC;AAMH,eAAe,UAAU,CAAC"}
@@ -0,0 +1,553 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useId, useUncontrolled } from "@refraktor/utils";
3
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
4
+ import { createClassNamesConfig, createComponentConfig, factory, Input, useClassNames, useProps, useTheme } from "@refraktor/core";
5
+ import { autoUpdate, flip, FloatingFocusManager, FloatingPortal, offset, shift, useClick, useDismiss, useFloating, useInteractions, useRole } from "@floating-ui/react";
6
+ import { getPickerSizeStyles } from "../picker-shared";
7
+ const HOURS_IN_DAY = 24;
8
+ const MINUTES_IN_HOUR = 60;
9
+ const SECONDS_IN_MINUTE = 60;
10
+ const PLACEHOLDER = "--";
11
+ const defaultProps = {
12
+ format: "24h",
13
+ withSeconds: false,
14
+ withDropdown: false,
15
+ clearable: false,
16
+ disabled: false,
17
+ readOnly: false,
18
+ variant: "default",
19
+ size: "md",
20
+ radius: "default",
21
+ hoursStep: 1,
22
+ minutesStep: 1,
23
+ secondsStep: 1,
24
+ amPmLabels: { am: "AM", pm: "PM" }
25
+ };
26
+ const pad = (value) => String(value).padStart(2, "0");
27
+ const parseValue = (value, withSeconds) => {
28
+ if (!value || value === "") {
29
+ return { hours: null, minutes: null, seconds: null, amPm: null };
30
+ }
31
+ const segments = value.trim().split(":");
32
+ if (segments.length < 2) {
33
+ return { hours: null, minutes: null, seconds: null, amPm: null };
34
+ }
35
+ const hours = Number.parseInt(segments[0], 10);
36
+ const minutes = Number.parseInt(segments[1], 10);
37
+ const seconds = segments.length >= 3 ? Number.parseInt(segments[2], 10) : 0;
38
+ if (Number.isNaN(hours) ||
39
+ Number.isNaN(minutes) ||
40
+ Number.isNaN(seconds) ||
41
+ hours < 0 ||
42
+ hours >= HOURS_IN_DAY ||
43
+ minutes < 0 ||
44
+ minutes >= MINUTES_IN_HOUR ||
45
+ seconds < 0 ||
46
+ seconds >= SECONDS_IN_MINUTE) {
47
+ return { hours: null, minutes: null, seconds: null, amPm: null };
48
+ }
49
+ return {
50
+ hours,
51
+ minutes,
52
+ seconds: withSeconds ? seconds : null,
53
+ amPm: hours >= 12 ? "PM" : "AM"
54
+ };
55
+ };
56
+ const formatValue = (parts, withSeconds) => {
57
+ if (parts.hours === null || parts.minutes === null) {
58
+ return "";
59
+ }
60
+ const base = `${pad(parts.hours)}:${pad(parts.minutes)}`;
61
+ if (withSeconds) {
62
+ return `${base}:${pad(parts.seconds ?? 0)}`;
63
+ }
64
+ return base;
65
+ };
66
+ const to12Hour = (hours24) => {
67
+ if (hours24 === 0 || hours24 === 12)
68
+ return 12;
69
+ return hours24 % 12;
70
+ };
71
+ const to24Hour = (hour12, amPm) => {
72
+ if (amPm === "AM") {
73
+ return hour12 === 12 ? 0 : hour12;
74
+ }
75
+ return hour12 === 12 ? 12 : hour12 + 12;
76
+ };
77
+ const clampValue = (value, min, max, step) => {
78
+ const clamped = Math.max(min, Math.min(max, value));
79
+ return Math.round(clamped / step) * step;
80
+ };
81
+ const parseMinMax = (timeStr) => {
82
+ if (!timeStr)
83
+ return null;
84
+ const segments = timeStr.trim().split(":");
85
+ if (segments.length < 2)
86
+ return null;
87
+ const hours = Number.parseInt(segments[0], 10);
88
+ const minutes = Number.parseInt(segments[1], 10);
89
+ const seconds = segments.length >= 3 ? Number.parseInt(segments[2], 10) : 0;
90
+ if (Number.isNaN(hours) || Number.isNaN(minutes) || Number.isNaN(seconds))
91
+ return null;
92
+ return { hours, minutes, seconds };
93
+ };
94
+ const setRef = (ref, node) => {
95
+ if (typeof ref === "function") {
96
+ ref(node);
97
+ }
98
+ else if (ref && "current" in ref) {
99
+ ref.current = node;
100
+ }
101
+ };
102
+ const inputSizes = {
103
+ xs: "h-5 px-2 text-[8px]",
104
+ sm: "h-6 px-2.5 text-[10px]",
105
+ md: "h-8 px-3 text-xs",
106
+ lg: "h-10 px-3.5 text-sm",
107
+ xl: "h-12 px-4 text-base"
108
+ };
109
+ const inputVariants = {
110
+ default: "bg-[var(--refraktor-bg)] text-[var(--refraktor-text)] border border-[var(--refraktor-border)]",
111
+ filled: "bg-[var(--refraktor-bg)] text-[var(--refraktor-text)]",
112
+ outline: "bg-transparent text-[var(--refraktor-text)] border border-[var(--refraktor-border)]"
113
+ };
114
+ const segmentWidths = {
115
+ xs: "w-[1.25rem]",
116
+ sm: "w-[1.5rem]",
117
+ md: "w-[1.75rem]",
118
+ lg: "w-[2rem]",
119
+ xl: "w-[2.5rem]"
120
+ };
121
+ const amPmWidths = {
122
+ xs: "w-[1.75rem]",
123
+ sm: "w-[2rem]",
124
+ md: "w-[2.25rem]",
125
+ lg: "w-[2.75rem]",
126
+ xl: "w-[3.25rem]"
127
+ };
128
+ const separatorSizes = {
129
+ xs: "text-[8px]",
130
+ sm: "text-[10px]",
131
+ md: "text-xs",
132
+ lg: "text-sm",
133
+ xl: "text-base"
134
+ };
135
+ const TimePicker = factory((_props, ref) => {
136
+ const { cx, getRadius } = useTheme();
137
+ const { id, value, defaultValue, onChange, format, withSeconds, withDropdown, clearable, min, max, hoursStep, minutesStep, secondsStep, amPmLabels, disabled, readOnly, variant, size, radius, label, description, error, required, withAsterisk, leftSection, rightSection, hoursRef: hoursRefProp, minutesRef: minutesRefProp, secondsRef: secondsRefProp, amPmRef: amPmRefProp, hoursInputLabel, minutesInputLabel, secondsInputLabel, amPmInputLabel, popoverProps, onFocus, onBlur, className, classNames, ...props } = useProps("TimePicker", defaultProps, _props);
138
+ const classes = useClassNames("TimePicker", classNames);
139
+ const _id = useId(id);
140
+ const is12h = format === "12h";
141
+ const hoursRef = useRef(null);
142
+ const minutesRef = useRef(null);
143
+ const secondsRef = useRef(null);
144
+ const amPmRef = useRef(null);
145
+ const wrapperRef = useRef(null);
146
+ const blurTimeoutRef = useRef(null);
147
+ const minParsed = useMemo(() => parseMinMax(min), [min]);
148
+ const maxParsed = useMemo(() => parseMinMax(max), [max]);
149
+ const [internalValue, setInternalValue] = useUncontrolled({
150
+ value,
151
+ defaultValue,
152
+ finalValue: "",
153
+ onChange
154
+ });
155
+ const [parts, setParts] = useState(() => parseValue(internalValue, withSeconds));
156
+ useEffect(() => {
157
+ const parsed = parseValue(internalValue, withSeconds);
158
+ setParts(parsed);
159
+ }, [internalValue, withSeconds]);
160
+ const [dropdownOpen, setDropdownOpen] = useState(false);
161
+ const isDropdownVisible = withDropdown && dropdownOpen && !disabled;
162
+ const floating = useFloating({
163
+ placement: popoverProps?.placement ?? "bottom-start",
164
+ open: isDropdownVisible,
165
+ onOpenChange: (open) => {
166
+ if (!disabled && !readOnly)
167
+ setDropdownOpen(open);
168
+ },
169
+ middleware: [
170
+ offset(popoverProps?.offset ?? 4),
171
+ flip(),
172
+ shift()
173
+ ],
174
+ whileElementsMounted: autoUpdate,
175
+ strategy: "fixed"
176
+ });
177
+ const click = useClick(floating.context, {
178
+ enabled: withDropdown && !disabled && !readOnly
179
+ });
180
+ const dismiss = useDismiss(floating.context);
181
+ const role = useRole(floating.context, { role: "dialog" });
182
+ const { getReferenceProps, getFloatingProps } = useInteractions([
183
+ click,
184
+ dismiss,
185
+ role
186
+ ]);
187
+ const emitValue = useCallback((nextParts) => {
188
+ const allFilled = nextParts.hours !== null &&
189
+ nextParts.minutes !== null &&
190
+ (!withSeconds || nextParts.seconds !== null) &&
191
+ (!is12h || nextParts.amPm !== null);
192
+ const allEmpty = nextParts.hours === null &&
193
+ nextParts.minutes === null &&
194
+ (nextParts.seconds === null || !withSeconds);
195
+ if (allFilled) {
196
+ let hours = nextParts.hours;
197
+ if (is12h && nextParts.amPm !== null) {
198
+ hours = to24Hour(hours, nextParts.amPm);
199
+ }
200
+ const formatted = formatValue({ ...nextParts, hours }, withSeconds);
201
+ setInternalValue(formatted);
202
+ }
203
+ else if (allEmpty) {
204
+ setInternalValue("");
205
+ }
206
+ }, [is12h, setInternalValue, withSeconds]);
207
+ const updateParts = useCallback((updater) => {
208
+ setParts((prev) => {
209
+ const next = updater(prev);
210
+ emitValue(next);
211
+ return next;
212
+ });
213
+ }, [emitValue]);
214
+ const getDisplayHours = useCallback((hours24) => {
215
+ if (hours24 === null)
216
+ return PLACEHOLDER;
217
+ if (is12h)
218
+ return pad(to12Hour(hours24));
219
+ return pad(hours24);
220
+ }, [is12h]);
221
+ const handleClear = useCallback(() => {
222
+ if (disabled || readOnly)
223
+ return;
224
+ const empty = {
225
+ hours: null,
226
+ minutes: null,
227
+ seconds: null,
228
+ amPm: null
229
+ };
230
+ setParts(empty);
231
+ setInternalValue("");
232
+ hoursRef.current?.focus();
233
+ }, [disabled, readOnly, setInternalValue]);
234
+ const createSegmentKeyHandler = useCallback((segment, prevRef, nextRef) => {
235
+ return (event) => {
236
+ if (disabled || readOnly)
237
+ return;
238
+ const step = segment === "hours"
239
+ ? hoursStep
240
+ : segment === "minutes"
241
+ ? minutesStep
242
+ : secondsStep;
243
+ const maxVal = segment === "hours"
244
+ ? is12h
245
+ ? 12
246
+ : HOURS_IN_DAY - 1
247
+ : segment === "minutes"
248
+ ? MINUTES_IN_HOUR - 1
249
+ : SECONDS_IN_MINUTE - 1;
250
+ const minVal = segment === "hours" && is12h ? 1 : 0;
251
+ if (event.key === "ArrowUp") {
252
+ event.preventDefault();
253
+ updateParts((prev) => {
254
+ const current = prev[segment] ?? minVal;
255
+ let next = current + step;
256
+ if (next > maxVal)
257
+ next = minVal;
258
+ return { ...prev, [segment]: next };
259
+ });
260
+ return;
261
+ }
262
+ if (event.key === "ArrowDown") {
263
+ event.preventDefault();
264
+ updateParts((prev) => {
265
+ const current = prev[segment] ?? maxVal;
266
+ let next = current - step;
267
+ if (next < minVal)
268
+ next = maxVal;
269
+ return { ...prev, [segment]: next };
270
+ });
271
+ return;
272
+ }
273
+ if (event.key === "ArrowRight") {
274
+ event.preventDefault();
275
+ nextRef.current?.focus();
276
+ nextRef.current?.select();
277
+ return;
278
+ }
279
+ if (event.key === "ArrowLeft") {
280
+ event.preventDefault();
281
+ prevRef.current?.focus();
282
+ prevRef.current?.select();
283
+ return;
284
+ }
285
+ if (event.key === "Home") {
286
+ event.preventDefault();
287
+ updateParts((prev) => ({ ...prev, [segment]: minVal }));
288
+ return;
289
+ }
290
+ if (event.key === "End") {
291
+ event.preventDefault();
292
+ updateParts((prev) => ({ ...prev, [segment]: maxVal }));
293
+ return;
294
+ }
295
+ if (event.key === "Backspace") {
296
+ event.preventDefault();
297
+ updateParts((prev) => {
298
+ if (prev[segment] === null) {
299
+ prevRef.current?.focus();
300
+ prevRef.current?.select();
301
+ return prev;
302
+ }
303
+ return { ...prev, [segment]: null };
304
+ });
305
+ return;
306
+ }
307
+ if (event.key === "Tab") {
308
+ return;
309
+ }
310
+ if (/^\d$/.test(event.key)) {
311
+ event.preventDefault();
312
+ const digit = Number.parseInt(event.key, 10);
313
+ updateParts((prev) => {
314
+ const current = prev[segment];
315
+ let next;
316
+ if (current === null || current >= 10) {
317
+ next = digit;
318
+ }
319
+ else {
320
+ next = current * 10 + digit;
321
+ }
322
+ if (next > maxVal) {
323
+ next = digit;
324
+ }
325
+ const result = { ...prev, [segment]: next };
326
+ if (next >= (maxVal + 1) / 10 || (current !== null && current < 10)) {
327
+ requestAnimationFrame(() => {
328
+ nextRef.current?.focus();
329
+ nextRef.current?.select();
330
+ });
331
+ }
332
+ return result;
333
+ });
334
+ return;
335
+ }
336
+ if (!event.ctrlKey && !event.metaKey && !event.altKey) {
337
+ event.preventDefault();
338
+ }
339
+ };
340
+ }, [disabled, hoursStep, is12h, minutesStep, readOnly, secondsStep, updateParts]);
341
+ const handleAmPmKeyDown = useCallback((event) => {
342
+ if (disabled || readOnly)
343
+ return;
344
+ if (event.key === "ArrowUp" || event.key === "ArrowDown") {
345
+ event.preventDefault();
346
+ updateParts((prev) => ({
347
+ ...prev,
348
+ amPm: prev.amPm === "AM" ? "PM" : "AM"
349
+ }));
350
+ return;
351
+ }
352
+ if (event.key === "ArrowLeft") {
353
+ event.preventDefault();
354
+ const prev = withSeconds ? secondsRef : minutesRef;
355
+ prev.current?.focus();
356
+ prev.current?.select();
357
+ return;
358
+ }
359
+ if (event.key.toLowerCase() === "a" ||
360
+ event.key.toLowerCase() === "p") {
361
+ event.preventDefault();
362
+ const nextAmPm = event.key.toLowerCase() === "a" ? "AM" : "PM";
363
+ updateParts((prev) => ({ ...prev, amPm: nextAmPm }));
364
+ return;
365
+ }
366
+ if (event.key === "Backspace") {
367
+ event.preventDefault();
368
+ updateParts((prev) => {
369
+ if (prev.amPm === null) {
370
+ const prev2 = withSeconds ? secondsRef : minutesRef;
371
+ prev2.current?.focus();
372
+ prev2.current?.select();
373
+ return prev;
374
+ }
375
+ return { ...prev, amPm: null };
376
+ });
377
+ return;
378
+ }
379
+ if (event.key === "Tab") {
380
+ return;
381
+ }
382
+ if (!event.ctrlKey && !event.metaKey && !event.altKey) {
383
+ event.preventDefault();
384
+ }
385
+ }, [disabled, readOnly, updateParts, withSeconds]);
386
+ const handleWrapperFocus = useCallback((event) => {
387
+ if (blurTimeoutRef.current !== null) {
388
+ clearTimeout(blurTimeoutRef.current);
389
+ blurTimeoutRef.current = null;
390
+ }
391
+ if (!wrapperRef.current?.contains(event.relatedTarget)) {
392
+ onFocus?.(event);
393
+ if (withDropdown && !disabled && !readOnly) {
394
+ setDropdownOpen(true);
395
+ }
396
+ }
397
+ }, [disabled, onFocus, readOnly, withDropdown]);
398
+ const handleWrapperBlur = useCallback((event) => {
399
+ blurTimeoutRef.current = setTimeout(() => {
400
+ if (!wrapperRef.current?.contains(document.activeElement) &&
401
+ !floating.refs.floating.current?.contains(document.activeElement)) {
402
+ onBlur?.(event);
403
+ setDropdownOpen(false);
404
+ }
405
+ }, 0);
406
+ }, [floating.refs.floating, onBlur]);
407
+ const displayHours = is12h
408
+ ? parts.hours !== null
409
+ ? pad(to12Hour(parts.hours))
410
+ : PLACEHOLDER
411
+ : parts.hours !== null
412
+ ? pad(parts.hours)
413
+ : PLACEHOLDER;
414
+ const displayMinutes = parts.minutes !== null ? pad(parts.minutes) : PLACEHOLDER;
415
+ const displaySeconds = parts.seconds !== null ? pad(parts.seconds) : PLACEHOLDER;
416
+ const displayAmPm = parts.amPm ?? PLACEHOLDER;
417
+ const hasValue = parts.hours !== null ||
418
+ parts.minutes !== null ||
419
+ (withSeconds && parts.seconds !== null);
420
+ const showClearButton = clearable && hasValue && !disabled && !readOnly;
421
+ const effectiveRightSection = showClearButton ? (_jsx("button", { type: "button", tabIndex: -1, "aria-label": "Clear time", className: cx("inline-flex items-center justify-center text-[var(--refraktor-text-secondary)] hover:text-[var(--refraktor-text)] transition-colors cursor-pointer"), onClick: handleClear, onMouseDown: (e) => e.preventDefault(), children: _jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 16 16", fill: "currentColor", className: "size-3.5", children: _jsx("path", { d: "M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.75.75 0 1 1 1.06 1.06L9.06 8l3.22 3.22a.75.75 0 1 1-1.06 1.06L8 9.06l-3.22 3.22a.75.75 0 0 1-1.06-1.06L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z" }) }) })) : (rightSection);
422
+ const sizeClass = inputSizes[size] ?? inputSizes.md;
423
+ const variantClass = inputVariants[variant] ?? inputVariants.default;
424
+ const segmentWidth = segmentWidths[size] ?? segmentWidths.md;
425
+ const amPmWidth = amPmWidths[size] ?? amPmWidths.md;
426
+ const sepSize = separatorSizes[size] ?? separatorSizes.md;
427
+ const hoursKeyHandler = createSegmentKeyHandler("hours", { current: null }, minutesRef);
428
+ const minutesKeyHandler = createSegmentKeyHandler("minutes", hoursRef, withSeconds ? secondsRef : is12h ? amPmRef : { current: null });
429
+ const secondsKeyHandler = createSegmentKeyHandler("seconds", minutesRef, is12h ? amPmRef : { current: null });
430
+ const renderDropdown = () => {
431
+ if (!withDropdown)
432
+ return null;
433
+ const sizeStyles = getPickerSizeStyles(size);
434
+ const hoursCount = is12h ? 12 : HOURS_IN_DAY;
435
+ const hoursStart = is12h ? 1 : 0;
436
+ const hourOptions = [];
437
+ for (let i = hoursStart; i < hoursStart + hoursCount; i += hoursStep) {
438
+ hourOptions.push(i);
439
+ }
440
+ const minuteOptions = [];
441
+ for (let i = 0; i < MINUTES_IN_HOUR; i += minutesStep) {
442
+ minuteOptions.push(i);
443
+ }
444
+ const secondOptions = [];
445
+ for (let i = 0; i < SECONDS_IN_MINUTE; i += secondsStep) {
446
+ secondOptions.push(i);
447
+ }
448
+ const currentHourDisplay = is12h
449
+ ? parts.hours !== null
450
+ ? to12Hour(parts.hours)
451
+ : null
452
+ : parts.hours;
453
+ const renderColumn = (columnLabel, options, selectedValue, onSelect, extraClassName) => (_jsxs("div", { className: cx("flex flex-col min-w-0", classes.dropdownColumn, extraClassName), children: [_jsx("div", { className: cx("py-1.5 text-center font-medium text-[var(--refraktor-text-secondary)] border-b border-[var(--refraktor-border)] bg-[var(--refraktor-bg-subtle)]", sizeStyles.label, classes.dropdownColumnLabel), children: columnLabel }), _jsx("div", { className: cx("refraktor-scrollbar flex max-h-52 flex-col gap-0.5 p-1 overflow-y-auto"), children: options.map((opt) => {
454
+ const isSelected = opt === selectedValue;
455
+ const displayLabel = typeof opt === "number" ? pad(opt) : opt;
456
+ return (_jsx("button", { type: "button", tabIndex: -1, className: cx("inline-flex w-full items-center justify-center font-medium transition-colors", isSelected
457
+ ? "bg-[var(--refraktor-primary)] text-[var(--refraktor-primary-text)]"
458
+ : "hover:bg-[var(--refraktor-bg-hover)] text-[var(--refraktor-text)]", sizeStyles.cell, getRadius(radius), classes.dropdownOption, isSelected && classes.dropdownOptionActive), onMouseDown: (e) => e.preventDefault(), onClick: () => onSelect(opt), children: displayLabel }, String(opt)));
459
+ }) })] }));
460
+ const handleDropdownHourSelect = (val) => {
461
+ const hourVal = typeof val === "number" ? val : Number(val);
462
+ updateParts((prev) => {
463
+ const next = { ...prev, hours: is12h ? to24Hour(hourVal, prev.amPm ?? "AM") : hourVal };
464
+ if (next.minutes === null) {
465
+ requestAnimationFrame(() => {
466
+ minutesRef.current?.focus();
467
+ minutesRef.current?.select();
468
+ });
469
+ }
470
+ return next;
471
+ });
472
+ };
473
+ const handleDropdownMinuteSelect = (val) => {
474
+ const minVal = typeof val === "number" ? val : Number(val);
475
+ updateParts((prev) => {
476
+ const next = { ...prev, minutes: minVal };
477
+ if (withSeconds && next.seconds === null) {
478
+ requestAnimationFrame(() => {
479
+ secondsRef.current?.focus();
480
+ secondsRef.current?.select();
481
+ });
482
+ }
483
+ return next;
484
+ });
485
+ };
486
+ const handleDropdownSecondSelect = (val) => {
487
+ const secVal = typeof val === "number" ? val : Number(val);
488
+ updateParts((prev) => ({ ...prev, seconds: secVal }));
489
+ };
490
+ const handleDropdownAmPmSelect = (val) => {
491
+ const amPmVal = val;
492
+ updateParts((prev) => {
493
+ if (prev.hours === null)
494
+ return { ...prev, amPm: amPmVal };
495
+ const hour12 = to12Hour(prev.hours);
496
+ const newHours24 = to24Hour(hour12, amPmVal);
497
+ return { ...prev, hours: newHours24, amPm: amPmVal };
498
+ });
499
+ };
500
+ const columnCount = (withSeconds ? 4 : 3) + (is12h ? 1 : 0) - (withSeconds ? 0 : 1);
501
+ const gridColsClass = columnCount === 2
502
+ ? "grid-cols-2"
503
+ : columnCount === 3
504
+ ? "grid-cols-3"
505
+ : "grid-cols-4";
506
+ return (_jsx(FloatingPortal, { children: isDropdownVisible && (_jsx(FloatingFocusManager, { context: floating.context, modal: false, initialFocus: -1, returnFocus: false, children: _jsx("div", { ref: floating.refs.setFloating, style: {
507
+ ...floating.floatingStyles,
508
+ zIndex: 1000
509
+ }, className: cx("border border-[var(--refraktor-border)] bg-[var(--refraktor-bg)] shadow-md overflow-hidden", getRadius(radius), classes.dropdown), ...getFloatingProps(), children: _jsxs("div", { className: cx("grid divide-x divide-[var(--refraktor-border)]", gridColsClass), children: [renderColumn("Hour", hourOptions, currentHourDisplay, handleDropdownHourSelect), renderColumn("Min", minuteOptions, parts.minutes, handleDropdownMinuteSelect), withSeconds &&
510
+ renderColumn("Sec", secondOptions, parts.seconds, handleDropdownSecondSelect), is12h &&
511
+ renderColumn(amPmLabels.am + "/" + amPmLabels.pm, [amPmLabels.am, amPmLabels.pm], parts.amPm ===
512
+ "AM"
513
+ ? amPmLabels.am
514
+ : parts.amPm === "PM"
515
+ ? amPmLabels.pm
516
+ : null, (val) => {
517
+ const normalized = val === amPmLabels.am
518
+ ? "AM"
519
+ : "PM";
520
+ handleDropdownAmPmSelect(normalized);
521
+ })] }) }) })) }));
522
+ };
523
+ const hasWrapper = label || description || error;
524
+ const inputContent = (_jsxs("div", { ref: (node) => {
525
+ if (wrapperRef)
526
+ wrapperRef.current = node;
527
+ floating.refs.setReference(node);
528
+ }, className: cx("relative w-full inline-flex items-center transition-all", sizeClass, variantClass, getRadius(radius), "focus-within:border-[var(--refraktor-primary)]", error && typeof error !== "boolean" && "border-[var(--refraktor-colors-red-6)]", disabled && "opacity-50 cursor-not-allowed", classes.fieldsWrapper, !hasWrapper && className), onFocus: handleWrapperFocus, onBlur: handleWrapperBlur, ...(!hasWrapper ? getReferenceProps(props) : getReferenceProps()), children: [leftSection && (_jsx("div", { className: "flex h-full items-center justify-center text-[var(--refraktor-text-secondary)] shrink-0 select-none", children: leftSection })), _jsxs("div", { className: cx("flex items-center flex-1 min-w-0 gap-0.5"), children: [_jsx("input", { ref: (node) => {
529
+ hoursRef.current = node;
530
+ setRef(hoursRefProp, node);
531
+ }, id: `${_id}-hours`, type: "text", inputMode: "numeric", autoComplete: "off", placeholder: PLACEHOLDER, value: parts.hours !== null ? (is12h ? pad(to12Hour(parts.hours)) : pad(parts.hours)) : "", "aria-label": hoursInputLabel ?? "Hours", readOnly: true, tabIndex: disabled ? -1 : 0, disabled: disabled, className: cx("bg-transparent border-none outline-none text-center text-[var(--refraktor-text)] placeholder:text-[var(--refraktor-text-tertiary)] cursor-default select-all p-0", segmentWidth, classes.field), onKeyDown: hoursKeyHandler, onFocus: (e) => e.target.select() }), _jsx("span", { className: cx("text-[var(--refraktor-text-secondary)] select-none leading-none", sepSize, classes.separator), children: ":" }), _jsx("input", { ref: (node) => {
532
+ minutesRef.current = node;
533
+ setRef(minutesRefProp, node);
534
+ }, id: `${_id}-minutes`, type: "text", inputMode: "numeric", autoComplete: "off", placeholder: PLACEHOLDER, value: parts.minutes !== null ? pad(parts.minutes) : "", "aria-label": minutesInputLabel ?? "Minutes", readOnly: true, tabIndex: disabled ? -1 : 0, disabled: disabled, className: cx("bg-transparent border-none outline-none text-center text-[var(--refraktor-text)] placeholder:text-[var(--refraktor-text-tertiary)] cursor-default select-all p-0", segmentWidth, classes.field), onKeyDown: minutesKeyHandler, onFocus: (e) => e.target.select() }), withSeconds && (_jsxs(_Fragment, { children: [_jsx("span", { className: cx("text-[var(--refraktor-text-secondary)] select-none leading-none", sepSize, classes.separator), children: ":" }), _jsx("input", { ref: (node) => {
535
+ secondsRef.current = node;
536
+ setRef(secondsRefProp, node);
537
+ }, id: `${_id}-seconds`, type: "text", inputMode: "numeric", autoComplete: "off", placeholder: PLACEHOLDER, value: parts.seconds !== null
538
+ ? pad(parts.seconds)
539
+ : "", "aria-label": secondsInputLabel ?? "Seconds", readOnly: true, tabIndex: disabled ? -1 : 0, disabled: disabled, className: cx("bg-transparent border-none outline-none text-center text-[var(--refraktor-text)] placeholder:text-[var(--refraktor-text-tertiary)] cursor-default select-all p-0", segmentWidth, classes.field), onKeyDown: secondsKeyHandler, onFocus: (e) => e.target.select() })] })), is12h && (_jsx("input", { ref: (node) => {
540
+ amPmRef.current = node;
541
+ setRef(amPmRefProp, node);
542
+ }, id: `${_id}-ampm`, type: "text", autoComplete: "off", placeholder: PLACEHOLDER, value: parts.amPm !== null
543
+ ? parts.amPm === "AM"
544
+ ? amPmLabels.am
545
+ : amPmLabels.pm
546
+ : "", "aria-label": amPmInputLabel ?? "AM/PM", readOnly: true, tabIndex: disabled ? -1 : 0, disabled: disabled, className: cx("bg-transparent border-none outline-none text-center text-[var(--refraktor-text)] placeholder:text-[var(--refraktor-text-tertiary)] cursor-default select-all p-0 ml-1", amPmWidth, classes.amPmInput), onKeyDown: handleAmPmKeyDown, onFocus: (e) => e.target.select() }))] }), effectiveRightSection && (_jsx("div", { className: "flex h-full items-center justify-center text-[var(--refraktor-text-secondary)] shrink-0 select-none", children: effectiveRightSection }))] }));
547
+ const content = hasWrapper ? (_jsxs(Input.Wrapper, { ref: ref, label: label, description: description, error: error, required: required, withAsterisk: withAsterisk, inputId: `${_id}-hours`, className: cx(classes.root, className), children: [inputContent, renderDropdown()] })) : (_jsxs("div", { ref: ref, className: cx(classes.root, className), children: [inputContent, renderDropdown()] }));
548
+ return content;
549
+ });
550
+ TimePicker.displayName = "@refraktor/dates/TimePicker";
551
+ TimePicker.configure = createComponentConfig();
552
+ TimePicker.classNames = createClassNamesConfig();
553
+ export default TimePicker;