@underverse-ui/underverse 1.0.95 → 1.0.97

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.d.cts CHANGED
@@ -611,8 +611,12 @@ interface Tab {
611
611
  className?: string;
612
612
  }>;
613
613
  disabled?: boolean;
614
+ href?: string;
615
+ target?: React$1.HTMLAttributeAnchorTarget;
616
+ rel?: string;
614
617
  }
615
618
  interface TabsProps {
619
+ id?: string;
616
620
  tabs: Tab[];
617
621
  defaultValue?: string;
618
622
  className?: string;
package/dist/index.d.ts CHANGED
@@ -611,8 +611,12 @@ interface Tab {
611
611
  className?: string;
612
612
  }>;
613
613
  disabled?: boolean;
614
+ href?: string;
615
+ target?: React$1.HTMLAttributeAnchorTarget;
616
+ rel?: string;
614
617
  }
615
618
  interface TabsProps {
619
+ id?: string;
616
620
  tabs: Tab[];
617
621
  defaultValue?: string;
618
622
  className?: string;
package/dist/index.js CHANGED
@@ -6608,7 +6608,40 @@ var variantStyles5 = {
6608
6608
  inactiveTab: "text-muted-foreground hover:text-foreground"
6609
6609
  }
6610
6610
  };
6611
+ function normalizeTabsId(value) {
6612
+ const normalized = value.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
6613
+ return normalized || "default";
6614
+ }
6615
+ function getTabsBaseId(tabs, id, fallbackId) {
6616
+ if (id) return `tabs-${normalizeTabsId(id)}`;
6617
+ if (fallbackId) return `tabs-${normalizeTabsId(fallbackId)}`;
6618
+ const key = tabs.map((t) => t.value).join("-");
6619
+ return `tabs-${normalizeTabsId(key || "default")}`;
6620
+ }
6621
+ function getTabTriggerId(baseId, index) {
6622
+ return `${baseId}-tab-${index}`;
6623
+ }
6624
+ function getTabPanelId(baseId, index) {
6625
+ return `${baseId}-panel-${index}`;
6626
+ }
6627
+ function getTabHref(tab, panelId) {
6628
+ return tab.href ?? `#${panelId}`;
6629
+ }
6630
+ function resolveTabValueFromHash(hash, tabs, baseId) {
6631
+ const normalizedHash = hash.replace(/^#/, "");
6632
+ if (!normalizedHash) return null;
6633
+ const matchIndex = tabs.findIndex((tab, index) => {
6634
+ const tabId = getTabTriggerId(baseId, index);
6635
+ const panelId = getTabPanelId(baseId, index);
6636
+ return normalizedHash === tabId || normalizedHash === panelId;
6637
+ });
6638
+ return matchIndex >= 0 ? tabs[matchIndex]?.value ?? null : null;
6639
+ }
6640
+ function shouldHandleTabClickLocally(event, target) {
6641
+ return !(event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || target === "_blank");
6642
+ }
6611
6643
  var Tabs = ({
6644
+ id,
6612
6645
  tabs,
6613
6646
  defaultValue,
6614
6647
  className,
@@ -6625,14 +6658,34 @@ var Tabs = ({
6625
6658
  const [active, setActive] = React22.useState(defaultValue || tabs[0]?.value);
6626
6659
  const [underlineStyle, setUnderlineStyle] = React22.useState({});
6627
6660
  const tabRefs = React22.useRef([]);
6628
- const baseId = React22.useMemo(() => {
6629
- const key = tabs.map((t) => t.value).join("-");
6630
- return `tabs-${key || "default"}`;
6631
- }, [tabs]);
6661
+ const autoId = React22.useId();
6662
+ const baseId = React22.useMemo(() => getTabsBaseId(tabs, id, autoId), [autoId, id, tabs]);
6632
6663
  const handleTabChange = (value) => {
6633
6664
  setActive(value);
6634
6665
  onTabChange?.(value);
6635
6666
  };
6667
+ const handleTabKeyDown = (event) => {
6668
+ const count = tabs.length;
6669
+ const idx = tabs.findIndex((t) => t.value === active);
6670
+ let next = idx;
6671
+ if (orientation === "horizontal") {
6672
+ if (event.key === "ArrowRight") next = (idx + 1) % count;
6673
+ if (event.key === "ArrowLeft") next = (idx - 1 + count) % count;
6674
+ } else {
6675
+ if (event.key === "ArrowDown") next = (idx + 1) % count;
6676
+ if (event.key === "ArrowUp") next = (idx - 1 + count) % count;
6677
+ }
6678
+ if (event.key === "Home") next = 0;
6679
+ if (event.key === "End") next = count - 1;
6680
+ if (next !== idx) {
6681
+ event.preventDefault();
6682
+ const nextTab = tabs[next];
6683
+ if (!nextTab?.disabled) {
6684
+ handleTabChange(nextTab.value);
6685
+ }
6686
+ tabRefs.current[next]?.focus();
6687
+ }
6688
+ };
6636
6689
  React22.useEffect(() => {
6637
6690
  if (variant === "underline" && orientation === "horizontal") {
6638
6691
  const activeIndex2 = tabs.findIndex((tab) => tab.value === active);
@@ -6646,6 +6699,18 @@ var Tabs = ({
6646
6699
  }
6647
6700
  }
6648
6701
  }, [active, variant, orientation, tabs]);
6702
+ React22.useEffect(() => {
6703
+ if (typeof window === "undefined") return;
6704
+ const syncFromHash = () => {
6705
+ const nextValue = resolveTabValueFromHash(window.location.hash, tabs, baseId);
6706
+ if (nextValue) {
6707
+ setActive(nextValue);
6708
+ }
6709
+ };
6710
+ syncFromHash();
6711
+ window.addEventListener("hashchange", syncFromHash);
6712
+ return () => window.removeEventListener("hashchange", syncFromHash);
6713
+ }, [baseId, tabs]);
6649
6714
  const containerClasses = cn(
6650
6715
  "relative",
6651
6716
  orientation === "horizontal" ? "w-full flex space-x-1 overflow-x-auto" : "flex flex-col space-y-1 shrink-0",
@@ -6668,59 +6733,69 @@ var Tabs = ({
6668
6733
  tabs.map((tab, index) => {
6669
6734
  const isActive = active === tab.value;
6670
6735
  const Icon = tab.icon;
6671
- const tabId2 = `${baseId}-tab-${index}`;
6672
- const panelId2 = `${baseId}-panel-${index}`;
6736
+ const tabId2 = getTabTriggerId(baseId, index);
6737
+ const panelId2 = getTabPanelId(baseId, index);
6738
+ const tabHref = getTabHref(tab, panelId2);
6739
+ const sharedClassName = cn(
6740
+ "font-medium transition-all duration-200 cursor-pointer flex items-center gap-2",
6741
+ "focus:outline-none focus-visible:outline-none",
6742
+ "disabled:opacity-50 disabled:cursor-not-allowed",
6743
+ sizeStyles6[size].tab,
6744
+ variantStyles5[variant].tab,
6745
+ isActive ? variantStyles5[variant].activeTab : variantStyles5[variant].inactiveTab,
6746
+ orientation === "vertical" && "justify-start w-full",
6747
+ stretch && orientation === "horizontal" && "flex-1 justify-center",
6748
+ tab.href && tab.disabled && "pointer-events-none cursor-not-allowed opacity-50"
6749
+ );
6750
+ const sharedStyle = {
6751
+ boxShadow: "none",
6752
+ transform: "none",
6753
+ outline: "none",
6754
+ border: "none"
6755
+ };
6756
+ const sharedProps = {
6757
+ ref: (el) => {
6758
+ tabRefs.current[index] = el;
6759
+ },
6760
+ id: tabId2,
6761
+ role: "tab",
6762
+ "aria-selected": isActive,
6763
+ "aria-controls": panelId2,
6764
+ tabIndex: isActive ? 0 : -1,
6765
+ className: sharedClassName,
6766
+ style: sharedStyle,
6767
+ onKeyDown: handleTabKeyDown
6768
+ };
6769
+ if (!tab.disabled) {
6770
+ return /* @__PURE__ */ jsxs20(
6771
+ "a",
6772
+ {
6773
+ ...sharedProps,
6774
+ href: tabHref,
6775
+ target: tab.target,
6776
+ rel: tab.rel,
6777
+ onClick: (event) => {
6778
+ if (shouldHandleTabClickLocally(event, tab.target)) {
6779
+ event.preventDefault();
6780
+ handleTabChange(tab.value);
6781
+ }
6782
+ },
6783
+ className: cn(sharedClassName, "no-underline"),
6784
+ children: [
6785
+ Icon && /* @__PURE__ */ jsx27(Icon, { className: "h-4 w-4" }),
6786
+ tab.label
6787
+ ]
6788
+ },
6789
+ tab.value
6790
+ );
6791
+ }
6673
6792
  return /* @__PURE__ */ jsxs20(
6674
6793
  "button",
6675
6794
  {
6676
- ref: (el) => {
6677
- tabRefs.current[index] = el;
6678
- },
6795
+ ...sharedProps,
6679
6796
  onClick: () => !tab.disabled && handleTabChange(tab.value),
6680
6797
  disabled: tab.disabled,
6681
- style: {
6682
- boxShadow: "none",
6683
- transform: "none",
6684
- outline: "none",
6685
- border: "none"
6686
- },
6687
- className: cn(
6688
- "font-medium transition-all duration-200 cursor-pointer flex items-center gap-2",
6689
- "focus:outline-none focus-visible:outline-none",
6690
- "disabled:opacity-50 disabled:cursor-not-allowed",
6691
- "border-0 bg-transparent outline-none",
6692
- // Reset button default styles
6693
- sizeStyles6[size].tab,
6694
- variantStyles5[variant].tab,
6695
- isActive ? variantStyles5[variant].activeTab : variantStyles5[variant].inactiveTab,
6696
- orientation === "vertical" && "justify-start w-full",
6697
- stretch && orientation === "horizontal" && "flex-1 justify-center"
6698
- ),
6699
- role: "tab",
6700
- id: tabId2,
6701
- "aria-selected": isActive,
6702
- "aria-controls": panelId2,
6703
- tabIndex: isActive ? 0 : -1,
6704
- onKeyDown: (e) => {
6705
- const count = tabs.length;
6706
- const idx = tabs.findIndex((t) => t.value === active);
6707
- let next = idx;
6708
- if (orientation === "horizontal") {
6709
- if (e.key === "ArrowRight") next = (idx + 1) % count;
6710
- if (e.key === "ArrowLeft") next = (idx - 1 + count) % count;
6711
- } else {
6712
- if (e.key === "ArrowDown") next = (idx + 1) % count;
6713
- if (e.key === "ArrowUp") next = (idx - 1 + count) % count;
6714
- }
6715
- if (e.key === "Home") next = 0;
6716
- if (e.key === "End") next = count - 1;
6717
- if (next !== idx) {
6718
- e.preventDefault();
6719
- const nextVal = tabs[next].value;
6720
- handleTabChange(nextVal);
6721
- tabRefs.current[next]?.focus();
6722
- }
6723
- },
6798
+ className: cn(sharedClassName, "border-0 bg-transparent outline-none"),
6724
6799
  children: [
6725
6800
  Icon && /* @__PURE__ */ jsx27(Icon, { className: "h-4 w-4" }),
6726
6801
  tab.label
@@ -6981,7 +7056,7 @@ import { ChevronLeft, ChevronRight as ChevronRight2, ChevronsLeft, ChevronsRight
6981
7056
 
6982
7057
  // src/components/Combobox.tsx
6983
7058
  import * as React24 from "react";
6984
- import { useId as useId5 } from "react";
7059
+ import { useId as useId6 } from "react";
6985
7060
  import { ChevronDown, Search as Search4, SearchX, Check as Check3, X as X9 } from "lucide-react";
6986
7061
  import { Fragment as Fragment5, jsx as jsx29, jsxs as jsxs22 } from "react/jsx-runtime";
6987
7062
  var getOptionLabel = (option) => {
@@ -7040,7 +7115,7 @@ var Combobox = ({
7040
7115
  const inputRef = React24.useRef(null);
7041
7116
  const optionsViewportRef = React24.useRef(null);
7042
7117
  useOverlayScrollbarTarget(optionsViewportRef, { enabled: useOverlayScrollbar });
7043
- const autoId = useId5();
7118
+ const autoId = useId6();
7044
7119
  const resolvedId = id ? String(id) : `combobox-${autoId}`;
7045
7120
  const labelId = label ? `${resolvedId}-label` : void 0;
7046
7121
  const enableSearch = options.length > 10;
@@ -8102,7 +8177,7 @@ function formatDateSmart(date, locale = "en") {
8102
8177
  // src/components/DatePicker.tsx
8103
8178
  import { Calendar, ChevronLeft as ChevronLeft2, ChevronRight as ChevronRight3, Sparkles as Sparkles2, X as XIcon } from "lucide-react";
8104
8179
  import * as React28 from "react";
8105
- import { useId as useId6 } from "react";
8180
+ import { useId as useId7 } from "react";
8106
8181
  import { Fragment as Fragment6, jsx as jsx34, jsxs as jsxs24 } from "react/jsx-runtime";
8107
8182
  var DatePicker = ({
8108
8183
  id,
@@ -8509,7 +8584,7 @@ var DatePicker = ({
8509
8584
  )
8510
8585
  ] })
8511
8586
  ] });
8512
- const autoId = useId6();
8587
+ const autoId = useId7();
8513
8588
  const resolvedId = id ? String(id) : `datepicker-${autoId}`;
8514
8589
  const labelId = label ? `${resolvedId}-label` : void 0;
8515
8590
  const labelSize = sizeStyles8[size].label;
@@ -9088,7 +9163,7 @@ var DateRangePicker = ({
9088
9163
  const displayFormat = (date) => formatDateShort(date);
9089
9164
  const displayLabel = tempStart && tempEnd ? `${displayFormat(tempStart)} - ${displayFormat(tempEnd)}` : tempStart ? `${displayFormat(tempStart)} - ...` : placeholder;
9090
9165
  const effectiveError = localRequiredError;
9091
- const autoId = useId6();
9166
+ const autoId = useId7();
9092
9167
  const resolvedId = id ? String(id) : `daterangepicker-${autoId}`;
9093
9168
  const labelId = label ? `${resolvedId}-label` : void 0;
9094
9169
  return /* @__PURE__ */ jsxs24("div", { className: cn("space-y-1.5", className), children: [
@@ -15203,7 +15278,7 @@ function CalendarTimeline({
15203
15278
 
15204
15279
  // src/components/MultiCombobox.tsx
15205
15280
  import * as React39 from "react";
15206
- import { useId as useId8 } from "react";
15281
+ import { useId as useId9 } from "react";
15207
15282
  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";
15208
15283
  import { Fragment as Fragment13, jsx as jsx45, jsxs as jsxs35 } from "react/jsx-runtime";
15209
15284
  var MultiCombobox = ({
@@ -15341,7 +15416,7 @@ var MultiCombobox = ({
15341
15416
  outline: "border-2 border-input bg-transparent hover:border-primary",
15342
15417
  ghost: "border border-transparent bg-muted/50 hover:bg-muted"
15343
15418
  };
15344
- const autoId = useId8();
15419
+ const autoId = useId9();
15345
15420
  const resolvedId = id ? String(id) : `multicombobox-${autoId}`;
15346
15421
  const labelId = label ? `${resolvedId}-label` : void 0;
15347
15422
  const labelSize = size === "sm" ? "text-xs" : size === "lg" ? "text-base" : "text-sm";
@@ -16845,7 +16920,7 @@ function OverlayControls({
16845
16920
  }
16846
16921
 
16847
16922
  // src/components/CategoryTreeSelect.tsx
16848
- import { useEffect as useEffect25, useId as useId10, useMemo as useMemo19, useRef as useRef19, useState as useState31 } from "react";
16923
+ import { useEffect as useEffect25, useId as useId11, useMemo as useMemo19, useRef as useRef19, useState as useState31 } from "react";
16849
16924
  import { ChevronRight as ChevronRight6, ChevronDown as ChevronDown5, FolderTree, Layers, Search as Search6, SearchX as SearchX3, X as X14 } from "lucide-react";
16850
16925
  import { jsx as jsx49, jsxs as jsxs39 } from "react/jsx-runtime";
16851
16926
  var defaultLabels = {
@@ -16902,7 +16977,7 @@ function CategoryTreeSelect(props) {
16902
16977
  const searchInputRef = useRef19(null);
16903
16978
  const dropdownViewportRef = useRef19(null);
16904
16979
  useOverlayScrollbarTarget(dropdownViewportRef, { enabled: useOverlayScrollbar });
16905
- const autoId = useId10();
16980
+ const autoId = useId11();
16906
16981
  const resolvedId = id ? String(id) : `category-tree-select-${autoId}`;
16907
16982
  const labelId = label ? `${resolvedId}-label` : void 0;
16908
16983
  const effectiveError = error ?? localRequiredError;
@@ -20664,7 +20739,7 @@ var MusicPlayer = ({
20664
20739
  var MusicPlayer_default = MusicPlayer;
20665
20740
 
20666
20741
  // src/components/Grid.tsx
20667
- import React51, { useId as useId11 } from "react";
20742
+ import React51, { useId as useId12 } from "react";
20668
20743
  import { Fragment as Fragment20, jsx as jsx59, jsxs as jsxs49 } from "react/jsx-runtime";
20669
20744
  var BP_MIN = {
20670
20745
  sm: 640,
@@ -20729,7 +20804,7 @@ var GridRoot = React51.forwardRef(
20729
20804
  children,
20730
20805
  ...rest
20731
20806
  }, ref) => {
20732
- const id = useId11().replace(/[:]/g, "");
20807
+ const id = useId12().replace(/[:]/g, "");
20733
20808
  const baseClass = `uv-grid-${id}`;
20734
20809
  const baseCols = toTemplateCols(columns, minColumnWidth);
20735
20810
  const baseRows = toTemplateRows(rows);