@underverse-ui/underverse 1.0.92 → 1.0.93
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/api-reference.json +1 -1
- package/dist/index.cjs +145 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +151 -37
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/api-reference.json
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -9754,7 +9754,7 @@ function WheelColumn({
|
|
|
9754
9754
|
ref: scrollRef,
|
|
9755
9755
|
className: cn(
|
|
9756
9756
|
"h-full overflow-y-auto overscroll-contain snap-y snap-mandatory",
|
|
9757
|
-
"select-none
|
|
9757
|
+
"select-none",
|
|
9758
9758
|
"focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background rounded-xl",
|
|
9759
9759
|
"[scrollbar-width:none] [-ms-overflow-style:none]",
|
|
9760
9760
|
"[&::-webkit-scrollbar]:hidden"
|
|
@@ -9765,15 +9765,7 @@ function WheelColumn({
|
|
|
9765
9765
|
tabIndex: focused ? 0 : -1,
|
|
9766
9766
|
onKeyDown: (e) => onKeyDown(e, column),
|
|
9767
9767
|
onFocus: () => setFocusedColumn(column),
|
|
9768
|
-
onScroll:
|
|
9769
|
-
if (draggingRef.current) return;
|
|
9770
|
-
if (inertialRef.current) return;
|
|
9771
|
-
handleScroll();
|
|
9772
|
-
},
|
|
9773
|
-
onPointerDown,
|
|
9774
|
-
onPointerMove,
|
|
9775
|
-
onPointerUp: (e) => endDrag(e.pointerId),
|
|
9776
|
-
onPointerCancel: (e) => endDrag(e.pointerId),
|
|
9768
|
+
onScroll: handleScroll,
|
|
9777
9769
|
children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { children: extendedItems.map((n, index) => {
|
|
9778
9770
|
const dist = Math.abs(index - currentVirtual);
|
|
9779
9771
|
const distForVisual = Math.min(dist, 2);
|
|
@@ -11236,7 +11228,7 @@ function WheelColumn2({
|
|
|
11236
11228
|
ref: scrollRef,
|
|
11237
11229
|
className: cn(
|
|
11238
11230
|
"h-full overflow-y-auto overscroll-contain snap-y snap-mandatory",
|
|
11239
|
-
"select-none
|
|
11231
|
+
"select-none",
|
|
11240
11232
|
"focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background rounded-xl",
|
|
11241
11233
|
"[scrollbar-width:none] [-ms-overflow-style:none]",
|
|
11242
11234
|
"[&::-webkit-scrollbar]:hidden"
|
|
@@ -11247,15 +11239,7 @@ function WheelColumn2({
|
|
|
11247
11239
|
tabIndex: focused ? 0 : -1,
|
|
11248
11240
|
onKeyDown: (e) => onKeyDown(e, column),
|
|
11249
11241
|
onFocus: () => setFocusedColumn(column),
|
|
11250
|
-
onScroll:
|
|
11251
|
-
if (draggingRef.current) return;
|
|
11252
|
-
if (inertialRef.current) return;
|
|
11253
|
-
handleScroll();
|
|
11254
|
-
},
|
|
11255
|
-
onPointerDown,
|
|
11256
|
-
onPointerMove,
|
|
11257
|
-
onPointerUp: (e) => endDrag(e.pointerId),
|
|
11258
|
-
onPointerCancel: (e) => endDrag(e.pointerId),
|
|
11242
|
+
onScroll: handleScroll,
|
|
11259
11243
|
children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { children: extendedItems.map((n, index) => {
|
|
11260
11244
|
const dist = Math.abs(index - currentVirtual);
|
|
11261
11245
|
const distForVisual = Math.min(dist, 2);
|
|
@@ -11330,6 +11314,14 @@ function formatTime2({ h, m, s, p }, fmt, includeSeconds) {
|
|
|
11330
11314
|
const base2 = `${pad(h)}:${pad(m)}`;
|
|
11331
11315
|
return includeSeconds ? `${base2}:${pad(s)}` : base2;
|
|
11332
11316
|
}
|
|
11317
|
+
function isCompleteTimeInput(input, fmt, includeSeconds) {
|
|
11318
|
+
const trimmed = input.trim().toUpperCase();
|
|
11319
|
+
if (!trimmed) return false;
|
|
11320
|
+
if (fmt === "12") {
|
|
11321
|
+
return includeSeconds ? /^\d{1,2}:\d{2}:\d{2}\s?(AM|PM)$/.test(trimmed) : /^\d{1,2}:\d{2}\s?(AM|PM)$/.test(trimmed);
|
|
11322
|
+
}
|
|
11323
|
+
return includeSeconds ? /^\d{1,2}:\d{2}:\d{2}$/.test(trimmed) : /^\d{1,2}:\d{2}$/.test(trimmed);
|
|
11324
|
+
}
|
|
11333
11325
|
var PRESETS = {
|
|
11334
11326
|
morning: { h: 9, m: 0, s: 0, icon: import_lucide_react18.Coffee, label: "Morning", color: "from-amber-400 to-orange-400" },
|
|
11335
11327
|
afternoon: { h: 14, m: 0, s: 0, icon: import_lucide_react18.Sun, label: "Afternoon", color: "from-yellow-400 to-amber-400" },
|
|
@@ -11371,24 +11363,38 @@ function TimePicker({
|
|
|
11371
11363
|
...rest
|
|
11372
11364
|
}) {
|
|
11373
11365
|
const tv = useSmartTranslations("ValidationInput");
|
|
11366
|
+
const autoId = React31.useId();
|
|
11374
11367
|
const isControlled = value !== void 0;
|
|
11375
11368
|
const now = /* @__PURE__ */ new Date();
|
|
11376
11369
|
const initial = parseTime(isControlled ? value : defaultValue, format, includeSeconds) || (format === "12" ? { h: now.getHours() % 12 || 12, m: now.getMinutes(), s: now.getSeconds(), p: now.getHours() >= 12 ? "PM" : "AM" } : { h: now.getHours(), m: now.getMinutes(), s: now.getSeconds() });
|
|
11377
11370
|
const [open, setOpen] = React31.useState(false);
|
|
11378
11371
|
const [parts, setParts] = React31.useState(initial);
|
|
11379
|
-
const [manualInput, setManualInput] = React31.useState(
|
|
11372
|
+
const [manualInput, setManualInput] = React31.useState(formatTime2(initial, format, includeSeconds));
|
|
11373
|
+
const [isDirectEditing, setIsDirectEditing] = React31.useState(false);
|
|
11380
11374
|
const [focusedColumn, setFocusedColumn] = React31.useState(null);
|
|
11381
11375
|
const [localRequiredError, setLocalRequiredError] = React31.useState();
|
|
11382
11376
|
const [hasCommittedValue, setHasCommittedValue] = React31.useState(Boolean(isControlled ? value : defaultValue));
|
|
11383
11377
|
const hourScrollRef = React31.useRef(null);
|
|
11384
11378
|
const minuteScrollRef = React31.useRef(null);
|
|
11385
11379
|
const secondScrollRef = React31.useRef(null);
|
|
11380
|
+
const periodRef = React31.useRef(null);
|
|
11381
|
+
const directEditInputRef = React31.useRef(null);
|
|
11382
|
+
const triggerId = `time-picker-trigger-${autoId}`;
|
|
11383
|
+
const labelId = label ? `time-picker-label-${autoId}` : void 0;
|
|
11386
11384
|
React31.useEffect(() => {
|
|
11387
11385
|
if (isControlled) {
|
|
11388
11386
|
const parsed = parseTime(value, format, includeSeconds);
|
|
11389
11387
|
if (parsed) setParts(parsed);
|
|
11390
11388
|
}
|
|
11391
11389
|
}, [value, isControlled, format, includeSeconds]);
|
|
11390
|
+
React31.useEffect(() => {
|
|
11391
|
+
setManualInput(formatTime2(parts, format, includeSeconds));
|
|
11392
|
+
}, [format, includeSeconds, parts]);
|
|
11393
|
+
React31.useEffect(() => {
|
|
11394
|
+
if (!isDirectEditing) return;
|
|
11395
|
+
directEditInputRef.current?.focus();
|
|
11396
|
+
directEditInputRef.current?.select();
|
|
11397
|
+
}, [isDirectEditing]);
|
|
11392
11398
|
React31.useEffect(() => {
|
|
11393
11399
|
if (isControlled) {
|
|
11394
11400
|
setHasCommittedValue(Boolean(value));
|
|
@@ -11479,6 +11485,15 @@ function TimePicker({
|
|
|
11479
11485
|
setLocalRequiredError(void 0);
|
|
11480
11486
|
}
|
|
11481
11487
|
}, [disabled, hasCommittedValue, required]);
|
|
11488
|
+
const focusColumn = React31.useCallback((column) => {
|
|
11489
|
+
if (!column) return;
|
|
11490
|
+
const target = column === "hour" ? hourScrollRef.current : column === "minute" ? minuteScrollRef.current : column === "second" ? secondScrollRef.current : periodRef.current;
|
|
11491
|
+
target?.focus({ preventScroll: true });
|
|
11492
|
+
}, []);
|
|
11493
|
+
React31.useEffect(() => {
|
|
11494
|
+
if (variant !== "inline" && !open) return;
|
|
11495
|
+
focusColumn(focusedColumn);
|
|
11496
|
+
}, [focusColumn, focusedColumn, open, variant]);
|
|
11482
11497
|
const handleKeyDown2 = (e, column) => {
|
|
11483
11498
|
if (!["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Home", "End", "PageUp", "PageDown"].includes(e.key)) return;
|
|
11484
11499
|
e.preventDefault();
|
|
@@ -11522,6 +11537,7 @@ function TimePicker({
|
|
|
11522
11537
|
}
|
|
11523
11538
|
tryUpdate(newParts);
|
|
11524
11539
|
};
|
|
11540
|
+
const display = formatTime2(parts, format, includeSeconds);
|
|
11525
11541
|
const setNow = () => {
|
|
11526
11542
|
const now2 = /* @__PURE__ */ new Date();
|
|
11527
11543
|
const h = now2.getHours();
|
|
@@ -11547,6 +11563,7 @@ function TimePicker({
|
|
|
11547
11563
|
};
|
|
11548
11564
|
const handleManualInput = (input) => {
|
|
11549
11565
|
setManualInput(input);
|
|
11566
|
+
if (!isCompleteTimeInput(input, format, includeSeconds)) return;
|
|
11550
11567
|
const parsed = parseTime(input, format, includeSeconds);
|
|
11551
11568
|
if (parsed) {
|
|
11552
11569
|
const timeStr = formatTime2(parsed, format, includeSeconds);
|
|
@@ -11555,6 +11572,53 @@ function TimePicker({
|
|
|
11555
11572
|
}
|
|
11556
11573
|
}
|
|
11557
11574
|
};
|
|
11575
|
+
const commitManualInput = React31.useCallback(
|
|
11576
|
+
(input) => {
|
|
11577
|
+
const trimmed = input.trim();
|
|
11578
|
+
if (!trimmed) {
|
|
11579
|
+
setManualInput(display);
|
|
11580
|
+
return false;
|
|
11581
|
+
}
|
|
11582
|
+
if (!isCompleteTimeInput(trimmed, format, includeSeconds)) {
|
|
11583
|
+
setManualInput(display);
|
|
11584
|
+
return false;
|
|
11585
|
+
}
|
|
11586
|
+
const parsed = parseTime(trimmed, format, includeSeconds);
|
|
11587
|
+
if (!parsed) {
|
|
11588
|
+
setManualInput(display);
|
|
11589
|
+
return false;
|
|
11590
|
+
}
|
|
11591
|
+
const timeStr = formatTime2(parsed, format, includeSeconds);
|
|
11592
|
+
if (!isTimeInRange(timeStr) || isTimeDisabled(timeStr)) {
|
|
11593
|
+
setManualInput(display);
|
|
11594
|
+
return false;
|
|
11595
|
+
}
|
|
11596
|
+
const updated = tryUpdate(parsed);
|
|
11597
|
+
if (!updated) {
|
|
11598
|
+
setManualInput(display);
|
|
11599
|
+
return false;
|
|
11600
|
+
}
|
|
11601
|
+
setManualInput(timeStr);
|
|
11602
|
+
return true;
|
|
11603
|
+
},
|
|
11604
|
+
[display, format, includeSeconds, isTimeDisabled, isTimeInRange, tryUpdate]
|
|
11605
|
+
);
|
|
11606
|
+
const startDirectEdit = React31.useCallback(() => {
|
|
11607
|
+
if (disabled) return;
|
|
11608
|
+
setManualInput(display);
|
|
11609
|
+
setIsDirectEditing(true);
|
|
11610
|
+
}, [disabled, display]);
|
|
11611
|
+
const stopDirectEdit = React31.useCallback(
|
|
11612
|
+
(mode) => {
|
|
11613
|
+
if (mode === "commit") {
|
|
11614
|
+
commitManualInput(manualInput);
|
|
11615
|
+
} else {
|
|
11616
|
+
setManualInput(display);
|
|
11617
|
+
}
|
|
11618
|
+
setIsDirectEditing(false);
|
|
11619
|
+
},
|
|
11620
|
+
[commitManualInput, display, manualInput]
|
|
11621
|
+
);
|
|
11558
11622
|
const handleCustomPreset = (time) => {
|
|
11559
11623
|
const parsed = parseTime(time, format, includeSeconds);
|
|
11560
11624
|
if (parsed) {
|
|
@@ -11622,13 +11686,14 @@ function TimePicker({
|
|
|
11622
11686
|
const shouldMatchTriggerWidth = matchTriggerWidth ?? variant !== "compact";
|
|
11623
11687
|
const compactPanel = variant === "compact";
|
|
11624
11688
|
const effectiveError = error ?? localRequiredError;
|
|
11625
|
-
const display = formatTime2(parts, format, includeSeconds);
|
|
11626
11689
|
const trigger = variant === "inline" ? null : /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
|
|
11627
11690
|
"button",
|
|
11628
11691
|
{
|
|
11692
|
+
id: triggerId,
|
|
11629
11693
|
type: "button",
|
|
11630
11694
|
disabled,
|
|
11631
11695
|
"aria-label": "Select time",
|
|
11696
|
+
"aria-labelledby": labelId,
|
|
11632
11697
|
"aria-haspopup": "dialog",
|
|
11633
11698
|
"aria-expanded": open,
|
|
11634
11699
|
"aria-required": required,
|
|
@@ -11665,10 +11730,10 @@ function TimePicker({
|
|
|
11665
11730
|
{
|
|
11666
11731
|
className: cn(
|
|
11667
11732
|
"truncate font-medium transition-colors duration-200",
|
|
11668
|
-
!
|
|
11669
|
-
|
|
11733
|
+
!hasCommittedValue && "text-muted-foreground",
|
|
11734
|
+
hasCommittedValue ? "text-foreground" : ""
|
|
11670
11735
|
),
|
|
11671
|
-
children:
|
|
11736
|
+
children: hasCommittedValue ? display : placeholder
|
|
11672
11737
|
}
|
|
11673
11738
|
)
|
|
11674
11739
|
] }),
|
|
@@ -11688,13 +11753,51 @@ function TimePicker({
|
|
|
11688
11753
|
tryUpdate(next);
|
|
11689
11754
|
};
|
|
11690
11755
|
const timePickerContent = /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: cn(panelSz.stackGap, compactPanel && "space-y-2.5"), children: [
|
|
11691
|
-
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: cn("flex items-center justify-center py-1", compactPanel && "py-0.5"), children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
11756
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: cn("flex items-center justify-center py-1", compactPanel && "py-0.5"), children: isDirectEditing ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
11757
|
+
"input",
|
|
11758
|
+
{
|
|
11759
|
+
ref: directEditInputRef,
|
|
11760
|
+
type: "text",
|
|
11761
|
+
value: manualInput,
|
|
11762
|
+
onChange: (e) => setManualInput(e.target.value),
|
|
11763
|
+
onBlur: () => stopDirectEdit("commit"),
|
|
11764
|
+
onKeyDown: (e) => {
|
|
11765
|
+
if (e.key === "Enter") {
|
|
11766
|
+
e.preventDefault();
|
|
11767
|
+
stopDirectEdit("commit");
|
|
11768
|
+
}
|
|
11769
|
+
if (e.key === "Escape") {
|
|
11770
|
+
e.preventDefault();
|
|
11771
|
+
stopDirectEdit("cancel");
|
|
11772
|
+
}
|
|
11773
|
+
},
|
|
11774
|
+
"aria-label": "Edit time value",
|
|
11775
|
+
className: cn(
|
|
11776
|
+
panelSz.timeText,
|
|
11777
|
+
"min-w-0 w-36 bg-transparent border-b border-primary/60 text-center font-bold tabular-nums tracking-wide text-foreground",
|
|
11778
|
+
"focus:outline-none focus:border-primary"
|
|
11779
|
+
)
|
|
11780
|
+
}
|
|
11781
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
11782
|
+
"button",
|
|
11783
|
+
{
|
|
11784
|
+
type: "button",
|
|
11785
|
+
onClick: startDirectEdit,
|
|
11786
|
+
className: cn(
|
|
11787
|
+
panelSz.timeText,
|
|
11788
|
+
"font-bold tabular-nums tracking-wide text-foreground underline underline-offset-8 decoration-primary/60",
|
|
11789
|
+
"rounded-md px-2 transition-colors hover:text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/50"
|
|
11790
|
+
),
|
|
11791
|
+
"aria-label": "Edit selected time",
|
|
11792
|
+
children: display
|
|
11793
|
+
}
|
|
11794
|
+
) }),
|
|
11692
11795
|
allowManualInput && /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "relative", children: [
|
|
11693
11796
|
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
11694
11797
|
Input_default,
|
|
11695
11798
|
{
|
|
11696
11799
|
placeholder: format === "12" ? "02:30 PM" : "14:30",
|
|
11697
|
-
value: manualInput
|
|
11800
|
+
value: manualInput,
|
|
11698
11801
|
onChange: (e) => handleManualInput(e.target.value),
|
|
11699
11802
|
size: panelSz.inputSize,
|
|
11700
11803
|
variant: "outlined",
|
|
@@ -11844,9 +11947,11 @@ function TimePicker({
|
|
|
11844
11947
|
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
11845
11948
|
"div",
|
|
11846
11949
|
{
|
|
11950
|
+
ref: periodRef,
|
|
11847
11951
|
className: cn("flex flex-col p-1 rounded-xl bg-muted/30", panelSz.periodGap),
|
|
11848
11952
|
role: "radiogroup",
|
|
11849
11953
|
"aria-label": "Select AM or PM",
|
|
11954
|
+
"aria-labelledby": labelId,
|
|
11850
11955
|
tabIndex: focusedColumn === "period" ? 0 : -1,
|
|
11851
11956
|
onKeyDown: (e) => handleKeyDown2(e, "period"),
|
|
11852
11957
|
onFocus: () => setFocusedColumn("period"),
|
|
@@ -11942,10 +12047,17 @@ function TimePicker({
|
|
|
11942
12047
|
] });
|
|
11943
12048
|
if (variant === "inline") {
|
|
11944
12049
|
return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "w-fit max-w-full", ...rest, children: [
|
|
11945
|
-
label && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "flex items-center justify-between mb-3", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
|
|
11946
|
-
label,
|
|
11947
|
-
|
|
11948
|
-
|
|
12050
|
+
label && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "flex items-center justify-between mb-3", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
|
|
12051
|
+
"label",
|
|
12052
|
+
{
|
|
12053
|
+
id: labelId,
|
|
12054
|
+
className: cn(sz.label, "font-semibold", disabled ? "text-muted-foreground" : "text-foreground", effectiveError && "text-destructive"),
|
|
12055
|
+
children: [
|
|
12056
|
+
label,
|
|
12057
|
+
required && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "text-destructive ml-1", children: "*" })
|
|
12058
|
+
]
|
|
12059
|
+
}
|
|
12060
|
+
) }),
|
|
11949
12061
|
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
11950
12062
|
"input",
|
|
11951
12063
|
{
|
|
@@ -11982,6 +12094,8 @@ function TimePicker({
|
|
|
11982
12094
|
label && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "flex items-center justify-between mb-2", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
|
|
11983
12095
|
"label",
|
|
11984
12096
|
{
|
|
12097
|
+
id: labelId,
|
|
12098
|
+
htmlFor: triggerId,
|
|
11985
12099
|
className: cn(
|
|
11986
12100
|
sz.label,
|
|
11987
12101
|
"font-semibold",
|