@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/dist/index.js CHANGED
@@ -9569,7 +9569,7 @@ function WheelColumn({
9569
9569
  ref: scrollRef,
9570
9570
  className: cn(
9571
9571
  "h-full overflow-y-auto overscroll-contain snap-y snap-mandatory",
9572
- "select-none cursor-grab active:cursor-grabbing",
9572
+ "select-none",
9573
9573
  "focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background rounded-xl",
9574
9574
  "[scrollbar-width:none] [-ms-overflow-style:none]",
9575
9575
  "[&::-webkit-scrollbar]:hidden"
@@ -9580,15 +9580,7 @@ function WheelColumn({
9580
9580
  tabIndex: focused ? 0 : -1,
9581
9581
  onKeyDown: (e) => onKeyDown(e, column),
9582
9582
  onFocus: () => setFocusedColumn(column),
9583
- onScroll: () => {
9584
- if (draggingRef.current) return;
9585
- if (inertialRef.current) return;
9586
- handleScroll();
9587
- },
9588
- onPointerDown,
9589
- onPointerMove,
9590
- onPointerUp: (e) => endDrag(e.pointerId),
9591
- onPointerCancel: (e) => endDrag(e.pointerId),
9583
+ onScroll: handleScroll,
9592
9584
  children: /* @__PURE__ */ jsx35("div", { children: extendedItems.map((n, index) => {
9593
9585
  const dist = Math.abs(index - currentVirtual);
9594
9586
  const distForVisual = Math.min(dist, 2);
@@ -11051,7 +11043,7 @@ function WheelColumn2({
11051
11043
  ref: scrollRef,
11052
11044
  className: cn(
11053
11045
  "h-full overflow-y-auto overscroll-contain snap-y snap-mandatory",
11054
- "select-none cursor-grab active:cursor-grabbing",
11046
+ "select-none",
11055
11047
  "focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background rounded-xl",
11056
11048
  "[scrollbar-width:none] [-ms-overflow-style:none]",
11057
11049
  "[&::-webkit-scrollbar]:hidden"
@@ -11062,15 +11054,7 @@ function WheelColumn2({
11062
11054
  tabIndex: focused ? 0 : -1,
11063
11055
  onKeyDown: (e) => onKeyDown(e, column),
11064
11056
  onFocus: () => setFocusedColumn(column),
11065
- onScroll: () => {
11066
- if (draggingRef.current) return;
11067
- if (inertialRef.current) return;
11068
- handleScroll();
11069
- },
11070
- onPointerDown,
11071
- onPointerMove,
11072
- onPointerUp: (e) => endDrag(e.pointerId),
11073
- onPointerCancel: (e) => endDrag(e.pointerId),
11057
+ onScroll: handleScroll,
11074
11058
  children: /* @__PURE__ */ jsx37("div", { children: extendedItems.map((n, index) => {
11075
11059
  const dist = Math.abs(index - currentVirtual);
11076
11060
  const distForVisual = Math.min(dist, 2);
@@ -11145,6 +11129,14 @@ function formatTime2({ h, m, s, p }, fmt, includeSeconds) {
11145
11129
  const base2 = `${pad(h)}:${pad(m)}`;
11146
11130
  return includeSeconds ? `${base2}:${pad(s)}` : base2;
11147
11131
  }
11132
+ function isCompleteTimeInput(input, fmt, includeSeconds) {
11133
+ const trimmed = input.trim().toUpperCase();
11134
+ if (!trimmed) return false;
11135
+ if (fmt === "12") {
11136
+ return includeSeconds ? /^\d{1,2}:\d{2}:\d{2}\s?(AM|PM)$/.test(trimmed) : /^\d{1,2}:\d{2}\s?(AM|PM)$/.test(trimmed);
11137
+ }
11138
+ return includeSeconds ? /^\d{1,2}:\d{2}:\d{2}$/.test(trimmed) : /^\d{1,2}:\d{2}$/.test(trimmed);
11139
+ }
11148
11140
  var PRESETS = {
11149
11141
  morning: { h: 9, m: 0, s: 0, icon: Coffee, label: "Morning", color: "from-amber-400 to-orange-400" },
11150
11142
  afternoon: { h: 14, m: 0, s: 0, icon: Sun, label: "Afternoon", color: "from-yellow-400 to-amber-400" },
@@ -11186,24 +11178,38 @@ function TimePicker({
11186
11178
  ...rest
11187
11179
  }) {
11188
11180
  const tv = useSmartTranslations("ValidationInput");
11181
+ const autoId = React31.useId();
11189
11182
  const isControlled = value !== void 0;
11190
11183
  const now = /* @__PURE__ */ new Date();
11191
11184
  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() });
11192
11185
  const [open, setOpen] = React31.useState(false);
11193
11186
  const [parts, setParts] = React31.useState(initial);
11194
- const [manualInput, setManualInput] = React31.useState("");
11187
+ const [manualInput, setManualInput] = React31.useState(formatTime2(initial, format, includeSeconds));
11188
+ const [isDirectEditing, setIsDirectEditing] = React31.useState(false);
11195
11189
  const [focusedColumn, setFocusedColumn] = React31.useState(null);
11196
11190
  const [localRequiredError, setLocalRequiredError] = React31.useState();
11197
11191
  const [hasCommittedValue, setHasCommittedValue] = React31.useState(Boolean(isControlled ? value : defaultValue));
11198
11192
  const hourScrollRef = React31.useRef(null);
11199
11193
  const minuteScrollRef = React31.useRef(null);
11200
11194
  const secondScrollRef = React31.useRef(null);
11195
+ const periodRef = React31.useRef(null);
11196
+ const directEditInputRef = React31.useRef(null);
11197
+ const triggerId = `time-picker-trigger-${autoId}`;
11198
+ const labelId = label ? `time-picker-label-${autoId}` : void 0;
11201
11199
  React31.useEffect(() => {
11202
11200
  if (isControlled) {
11203
11201
  const parsed = parseTime(value, format, includeSeconds);
11204
11202
  if (parsed) setParts(parsed);
11205
11203
  }
11206
11204
  }, [value, isControlled, format, includeSeconds]);
11205
+ React31.useEffect(() => {
11206
+ setManualInput(formatTime2(parts, format, includeSeconds));
11207
+ }, [format, includeSeconds, parts]);
11208
+ React31.useEffect(() => {
11209
+ if (!isDirectEditing) return;
11210
+ directEditInputRef.current?.focus();
11211
+ directEditInputRef.current?.select();
11212
+ }, [isDirectEditing]);
11207
11213
  React31.useEffect(() => {
11208
11214
  if (isControlled) {
11209
11215
  setHasCommittedValue(Boolean(value));
@@ -11294,6 +11300,15 @@ function TimePicker({
11294
11300
  setLocalRequiredError(void 0);
11295
11301
  }
11296
11302
  }, [disabled, hasCommittedValue, required]);
11303
+ const focusColumn = React31.useCallback((column) => {
11304
+ if (!column) return;
11305
+ const target = column === "hour" ? hourScrollRef.current : column === "minute" ? minuteScrollRef.current : column === "second" ? secondScrollRef.current : periodRef.current;
11306
+ target?.focus({ preventScroll: true });
11307
+ }, []);
11308
+ React31.useEffect(() => {
11309
+ if (variant !== "inline" && !open) return;
11310
+ focusColumn(focusedColumn);
11311
+ }, [focusColumn, focusedColumn, open, variant]);
11297
11312
  const handleKeyDown2 = (e, column) => {
11298
11313
  if (!["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Home", "End", "PageUp", "PageDown"].includes(e.key)) return;
11299
11314
  e.preventDefault();
@@ -11337,6 +11352,7 @@ function TimePicker({
11337
11352
  }
11338
11353
  tryUpdate(newParts);
11339
11354
  };
11355
+ const display = formatTime2(parts, format, includeSeconds);
11340
11356
  const setNow = () => {
11341
11357
  const now2 = /* @__PURE__ */ new Date();
11342
11358
  const h = now2.getHours();
@@ -11362,6 +11378,7 @@ function TimePicker({
11362
11378
  };
11363
11379
  const handleManualInput = (input) => {
11364
11380
  setManualInput(input);
11381
+ if (!isCompleteTimeInput(input, format, includeSeconds)) return;
11365
11382
  const parsed = parseTime(input, format, includeSeconds);
11366
11383
  if (parsed) {
11367
11384
  const timeStr = formatTime2(parsed, format, includeSeconds);
@@ -11370,6 +11387,53 @@ function TimePicker({
11370
11387
  }
11371
11388
  }
11372
11389
  };
11390
+ const commitManualInput = React31.useCallback(
11391
+ (input) => {
11392
+ const trimmed = input.trim();
11393
+ if (!trimmed) {
11394
+ setManualInput(display);
11395
+ return false;
11396
+ }
11397
+ if (!isCompleteTimeInput(trimmed, format, includeSeconds)) {
11398
+ setManualInput(display);
11399
+ return false;
11400
+ }
11401
+ const parsed = parseTime(trimmed, format, includeSeconds);
11402
+ if (!parsed) {
11403
+ setManualInput(display);
11404
+ return false;
11405
+ }
11406
+ const timeStr = formatTime2(parsed, format, includeSeconds);
11407
+ if (!isTimeInRange(timeStr) || isTimeDisabled(timeStr)) {
11408
+ setManualInput(display);
11409
+ return false;
11410
+ }
11411
+ const updated = tryUpdate(parsed);
11412
+ if (!updated) {
11413
+ setManualInput(display);
11414
+ return false;
11415
+ }
11416
+ setManualInput(timeStr);
11417
+ return true;
11418
+ },
11419
+ [display, format, includeSeconds, isTimeDisabled, isTimeInRange, tryUpdate]
11420
+ );
11421
+ const startDirectEdit = React31.useCallback(() => {
11422
+ if (disabled) return;
11423
+ setManualInput(display);
11424
+ setIsDirectEditing(true);
11425
+ }, [disabled, display]);
11426
+ const stopDirectEdit = React31.useCallback(
11427
+ (mode) => {
11428
+ if (mode === "commit") {
11429
+ commitManualInput(manualInput);
11430
+ } else {
11431
+ setManualInput(display);
11432
+ }
11433
+ setIsDirectEditing(false);
11434
+ },
11435
+ [commitManualInput, display, manualInput]
11436
+ );
11373
11437
  const handleCustomPreset = (time) => {
11374
11438
  const parsed = parseTime(time, format, includeSeconds);
11375
11439
  if (parsed) {
@@ -11437,13 +11501,14 @@ function TimePicker({
11437
11501
  const shouldMatchTriggerWidth = matchTriggerWidth ?? variant !== "compact";
11438
11502
  const compactPanel = variant === "compact";
11439
11503
  const effectiveError = error ?? localRequiredError;
11440
- const display = formatTime2(parts, format, includeSeconds);
11441
11504
  const trigger = variant === "inline" ? null : /* @__PURE__ */ jsxs27(
11442
11505
  "button",
11443
11506
  {
11507
+ id: triggerId,
11444
11508
  type: "button",
11445
11509
  disabled,
11446
11510
  "aria-label": "Select time",
11511
+ "aria-labelledby": labelId,
11447
11512
  "aria-haspopup": "dialog",
11448
11513
  "aria-expanded": open,
11449
11514
  "aria-required": required,
@@ -11480,10 +11545,10 @@ function TimePicker({
11480
11545
  {
11481
11546
  className: cn(
11482
11547
  "truncate font-medium transition-colors duration-200",
11483
- !value && !defaultValue && "text-muted-foreground",
11484
- value || defaultValue ? "text-foreground" : ""
11548
+ !hasCommittedValue && "text-muted-foreground",
11549
+ hasCommittedValue ? "text-foreground" : ""
11485
11550
  ),
11486
- children: value || defaultValue ? display : placeholder
11551
+ children: hasCommittedValue ? display : placeholder
11487
11552
  }
11488
11553
  )
11489
11554
  ] }),
@@ -11503,13 +11568,51 @@ function TimePicker({
11503
11568
  tryUpdate(next);
11504
11569
  };
11505
11570
  const timePickerContent = /* @__PURE__ */ jsxs27("div", { className: cn(panelSz.stackGap, compactPanel && "space-y-2.5"), children: [
11506
- /* @__PURE__ */ jsx37("div", { className: cn("flex items-center justify-center py-1", compactPanel && "py-0.5"), children: /* @__PURE__ */ jsx37("span", { className: cn(panelSz.timeText, "font-bold tabular-nums tracking-wide text-foreground underline underline-offset-8 decoration-primary/60"), children: display }) }),
11571
+ /* @__PURE__ */ jsx37("div", { className: cn("flex items-center justify-center py-1", compactPanel && "py-0.5"), children: isDirectEditing ? /* @__PURE__ */ jsx37(
11572
+ "input",
11573
+ {
11574
+ ref: directEditInputRef,
11575
+ type: "text",
11576
+ value: manualInput,
11577
+ onChange: (e) => setManualInput(e.target.value),
11578
+ onBlur: () => stopDirectEdit("commit"),
11579
+ onKeyDown: (e) => {
11580
+ if (e.key === "Enter") {
11581
+ e.preventDefault();
11582
+ stopDirectEdit("commit");
11583
+ }
11584
+ if (e.key === "Escape") {
11585
+ e.preventDefault();
11586
+ stopDirectEdit("cancel");
11587
+ }
11588
+ },
11589
+ "aria-label": "Edit time value",
11590
+ className: cn(
11591
+ panelSz.timeText,
11592
+ "min-w-0 w-36 bg-transparent border-b border-primary/60 text-center font-bold tabular-nums tracking-wide text-foreground",
11593
+ "focus:outline-none focus:border-primary"
11594
+ )
11595
+ }
11596
+ ) : /* @__PURE__ */ jsx37(
11597
+ "button",
11598
+ {
11599
+ type: "button",
11600
+ onClick: startDirectEdit,
11601
+ className: cn(
11602
+ panelSz.timeText,
11603
+ "font-bold tabular-nums tracking-wide text-foreground underline underline-offset-8 decoration-primary/60",
11604
+ "rounded-md px-2 transition-colors hover:text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/50"
11605
+ ),
11606
+ "aria-label": "Edit selected time",
11607
+ children: display
11608
+ }
11609
+ ) }),
11507
11610
  allowManualInput && /* @__PURE__ */ jsxs27("div", { className: "relative", children: [
11508
11611
  /* @__PURE__ */ jsx37(
11509
11612
  Input_default,
11510
11613
  {
11511
11614
  placeholder: format === "12" ? "02:30 PM" : "14:30",
11512
- value: manualInput || display,
11615
+ value: manualInput,
11513
11616
  onChange: (e) => handleManualInput(e.target.value),
11514
11617
  size: panelSz.inputSize,
11515
11618
  variant: "outlined",
@@ -11659,9 +11762,11 @@ function TimePicker({
11659
11762
  /* @__PURE__ */ jsx37(
11660
11763
  "div",
11661
11764
  {
11765
+ ref: periodRef,
11662
11766
  className: cn("flex flex-col p-1 rounded-xl bg-muted/30", panelSz.periodGap),
11663
11767
  role: "radiogroup",
11664
11768
  "aria-label": "Select AM or PM",
11769
+ "aria-labelledby": labelId,
11665
11770
  tabIndex: focusedColumn === "period" ? 0 : -1,
11666
11771
  onKeyDown: (e) => handleKeyDown2(e, "period"),
11667
11772
  onFocus: () => setFocusedColumn("period"),
@@ -11757,10 +11862,17 @@ function TimePicker({
11757
11862
  ] });
11758
11863
  if (variant === "inline") {
11759
11864
  return /* @__PURE__ */ jsxs27("div", { className: "w-fit max-w-full", ...rest, children: [
11760
- label && /* @__PURE__ */ jsx37("div", { className: "flex items-center justify-between mb-3", children: /* @__PURE__ */ jsxs27("label", { className: cn(sz.label, "font-semibold", disabled ? "text-muted-foreground" : "text-foreground", effectiveError && "text-destructive"), children: [
11761
- label,
11762
- required && /* @__PURE__ */ jsx37("span", { className: "text-destructive ml-1", children: "*" })
11763
- ] }) }),
11865
+ label && /* @__PURE__ */ jsx37("div", { className: "flex items-center justify-between mb-3", children: /* @__PURE__ */ jsxs27(
11866
+ "label",
11867
+ {
11868
+ id: labelId,
11869
+ className: cn(sz.label, "font-semibold", disabled ? "text-muted-foreground" : "text-foreground", effectiveError && "text-destructive"),
11870
+ children: [
11871
+ label,
11872
+ required && /* @__PURE__ */ jsx37("span", { className: "text-destructive ml-1", children: "*" })
11873
+ ]
11874
+ }
11875
+ ) }),
11764
11876
  /* @__PURE__ */ jsx37(
11765
11877
  "input",
11766
11878
  {
@@ -11797,6 +11909,8 @@ function TimePicker({
11797
11909
  label && /* @__PURE__ */ jsx37("div", { className: "flex items-center justify-between mb-2", children: /* @__PURE__ */ jsxs27(
11798
11910
  "label",
11799
11911
  {
11912
+ id: labelId,
11913
+ htmlFor: triggerId,
11800
11914
  className: cn(
11801
11915
  sz.label,
11802
11916
  "font-semibold",
@@ -15062,7 +15176,7 @@ function CalendarTimeline({
15062
15176
 
15063
15177
  // src/components/MultiCombobox.tsx
15064
15178
  import * as React39 from "react";
15065
- import { useId as useId7 } from "react";
15179
+ import { useId as useId8 } from "react";
15066
15180
  import { ChevronDown as ChevronDown4, Search as Search5, Check as Check6, SearchX as SearchX2, Loader2 as Loader24, X as X13, Sparkles as Sparkles3 } from "lucide-react";
15067
15181
  import { Fragment as Fragment13, jsx as jsx45, jsxs as jsxs35 } from "react/jsx-runtime";
15068
15182
  var MultiCombobox = ({
@@ -15200,7 +15314,7 @@ var MultiCombobox = ({
15200
15314
  outline: "border-2 border-input bg-transparent hover:border-primary",
15201
15315
  ghost: "border border-transparent bg-muted/50 hover:bg-muted"
15202
15316
  };
15203
- const autoId = useId7();
15317
+ const autoId = useId8();
15204
15318
  const resolvedId = id ? String(id) : `multicombobox-${autoId}`;
15205
15319
  const labelId = label ? `${resolvedId}-label` : void 0;
15206
15320
  const labelSize = size === "sm" ? "text-xs" : size === "lg" ? "text-base" : "text-sm";
@@ -16704,7 +16818,7 @@ function OverlayControls({
16704
16818
  }
16705
16819
 
16706
16820
  // src/components/CategoryTreeSelect.tsx
16707
- import { useEffect as useEffect25, useId as useId9, useMemo as useMemo19, useRef as useRef19, useState as useState31 } from "react";
16821
+ import { useEffect as useEffect25, useId as useId10, useMemo as useMemo19, useRef as useRef19, useState as useState31 } from "react";
16708
16822
  import { ChevronRight as ChevronRight6, ChevronDown as ChevronDown5, FolderTree, Layers, Search as Search6, SearchX as SearchX3, X as X14 } from "lucide-react";
16709
16823
  import { jsx as jsx49, jsxs as jsxs39 } from "react/jsx-runtime";
16710
16824
  var defaultLabels = {
@@ -16761,7 +16875,7 @@ function CategoryTreeSelect(props) {
16761
16875
  const searchInputRef = useRef19(null);
16762
16876
  const dropdownViewportRef = useRef19(null);
16763
16877
  useOverlayScrollbarTarget(dropdownViewportRef, { enabled: useOverlayScrollbar });
16764
- const autoId = useId9();
16878
+ const autoId = useId10();
16765
16879
  const resolvedId = id ? String(id) : `category-tree-select-${autoId}`;
16766
16880
  const labelId = label ? `${resolvedId}-label` : void 0;
16767
16881
  const effectiveError = error ?? localRequiredError;
@@ -20523,7 +20637,7 @@ var MusicPlayer = ({
20523
20637
  var MusicPlayer_default = MusicPlayer;
20524
20638
 
20525
20639
  // src/components/Grid.tsx
20526
- import React51, { useId as useId10 } from "react";
20640
+ import React51, { useId as useId11 } from "react";
20527
20641
  import { Fragment as Fragment20, jsx as jsx59, jsxs as jsxs49 } from "react/jsx-runtime";
20528
20642
  var BP_MIN = {
20529
20643
  sm: 640,
@@ -20588,7 +20702,7 @@ var GridRoot = React51.forwardRef(
20588
20702
  children,
20589
20703
  ...rest
20590
20704
  }, ref) => {
20591
- const id = useId10().replace(/[:]/g, "");
20705
+ const id = useId11().replace(/[:]/g, "");
20592
20706
  const baseClass = `uv-grid-${id}`;
20593
20707
  const baseCols = toTemplateCols(columns, minColumnWidth);
20594
20708
  const baseRows = toTemplateRows(rows);