@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/api-reference.json +1 -1
- package/dist/index.cjs +127 -52
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +138 -63
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/api-reference.json
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -6793,7 +6793,40 @@ var variantStyles5 = {
|
|
|
6793
6793
|
inactiveTab: "text-muted-foreground hover:text-foreground"
|
|
6794
6794
|
}
|
|
6795
6795
|
};
|
|
6796
|
+
function normalizeTabsId(value) {
|
|
6797
|
+
const normalized = value.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
6798
|
+
return normalized || "default";
|
|
6799
|
+
}
|
|
6800
|
+
function getTabsBaseId(tabs, id, fallbackId) {
|
|
6801
|
+
if (id) return `tabs-${normalizeTabsId(id)}`;
|
|
6802
|
+
if (fallbackId) return `tabs-${normalizeTabsId(fallbackId)}`;
|
|
6803
|
+
const key = tabs.map((t) => t.value).join("-");
|
|
6804
|
+
return `tabs-${normalizeTabsId(key || "default")}`;
|
|
6805
|
+
}
|
|
6806
|
+
function getTabTriggerId(baseId, index) {
|
|
6807
|
+
return `${baseId}-tab-${index}`;
|
|
6808
|
+
}
|
|
6809
|
+
function getTabPanelId(baseId, index) {
|
|
6810
|
+
return `${baseId}-panel-${index}`;
|
|
6811
|
+
}
|
|
6812
|
+
function getTabHref(tab, panelId) {
|
|
6813
|
+
return tab.href ?? `#${panelId}`;
|
|
6814
|
+
}
|
|
6815
|
+
function resolveTabValueFromHash(hash, tabs, baseId) {
|
|
6816
|
+
const normalizedHash = hash.replace(/^#/, "");
|
|
6817
|
+
if (!normalizedHash) return null;
|
|
6818
|
+
const matchIndex = tabs.findIndex((tab, index) => {
|
|
6819
|
+
const tabId = getTabTriggerId(baseId, index);
|
|
6820
|
+
const panelId = getTabPanelId(baseId, index);
|
|
6821
|
+
return normalizedHash === tabId || normalizedHash === panelId;
|
|
6822
|
+
});
|
|
6823
|
+
return matchIndex >= 0 ? tabs[matchIndex]?.value ?? null : null;
|
|
6824
|
+
}
|
|
6825
|
+
function shouldHandleTabClickLocally(event, target) {
|
|
6826
|
+
return !(event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || target === "_blank");
|
|
6827
|
+
}
|
|
6796
6828
|
var Tabs = ({
|
|
6829
|
+
id,
|
|
6797
6830
|
tabs,
|
|
6798
6831
|
defaultValue,
|
|
6799
6832
|
className,
|
|
@@ -6810,14 +6843,34 @@ var Tabs = ({
|
|
|
6810
6843
|
const [active, setActive] = React22.useState(defaultValue || tabs[0]?.value);
|
|
6811
6844
|
const [underlineStyle, setUnderlineStyle] = React22.useState({});
|
|
6812
6845
|
const tabRefs = React22.useRef([]);
|
|
6813
|
-
const
|
|
6814
|
-
|
|
6815
|
-
return `tabs-${key || "default"}`;
|
|
6816
|
-
}, [tabs]);
|
|
6846
|
+
const autoId = React22.useId();
|
|
6847
|
+
const baseId = React22.useMemo(() => getTabsBaseId(tabs, id, autoId), [autoId, id, tabs]);
|
|
6817
6848
|
const handleTabChange = (value) => {
|
|
6818
6849
|
setActive(value);
|
|
6819
6850
|
onTabChange?.(value);
|
|
6820
6851
|
};
|
|
6852
|
+
const handleTabKeyDown = (event) => {
|
|
6853
|
+
const count = tabs.length;
|
|
6854
|
+
const idx = tabs.findIndex((t) => t.value === active);
|
|
6855
|
+
let next = idx;
|
|
6856
|
+
if (orientation === "horizontal") {
|
|
6857
|
+
if (event.key === "ArrowRight") next = (idx + 1) % count;
|
|
6858
|
+
if (event.key === "ArrowLeft") next = (idx - 1 + count) % count;
|
|
6859
|
+
} else {
|
|
6860
|
+
if (event.key === "ArrowDown") next = (idx + 1) % count;
|
|
6861
|
+
if (event.key === "ArrowUp") next = (idx - 1 + count) % count;
|
|
6862
|
+
}
|
|
6863
|
+
if (event.key === "Home") next = 0;
|
|
6864
|
+
if (event.key === "End") next = count - 1;
|
|
6865
|
+
if (next !== idx) {
|
|
6866
|
+
event.preventDefault();
|
|
6867
|
+
const nextTab = tabs[next];
|
|
6868
|
+
if (!nextTab?.disabled) {
|
|
6869
|
+
handleTabChange(nextTab.value);
|
|
6870
|
+
}
|
|
6871
|
+
tabRefs.current[next]?.focus();
|
|
6872
|
+
}
|
|
6873
|
+
};
|
|
6821
6874
|
React22.useEffect(() => {
|
|
6822
6875
|
if (variant === "underline" && orientation === "horizontal") {
|
|
6823
6876
|
const activeIndex2 = tabs.findIndex((tab) => tab.value === active);
|
|
@@ -6831,6 +6884,18 @@ var Tabs = ({
|
|
|
6831
6884
|
}
|
|
6832
6885
|
}
|
|
6833
6886
|
}, [active, variant, orientation, tabs]);
|
|
6887
|
+
React22.useEffect(() => {
|
|
6888
|
+
if (typeof window === "undefined") return;
|
|
6889
|
+
const syncFromHash = () => {
|
|
6890
|
+
const nextValue = resolveTabValueFromHash(window.location.hash, tabs, baseId);
|
|
6891
|
+
if (nextValue) {
|
|
6892
|
+
setActive(nextValue);
|
|
6893
|
+
}
|
|
6894
|
+
};
|
|
6895
|
+
syncFromHash();
|
|
6896
|
+
window.addEventListener("hashchange", syncFromHash);
|
|
6897
|
+
return () => window.removeEventListener("hashchange", syncFromHash);
|
|
6898
|
+
}, [baseId, tabs]);
|
|
6834
6899
|
const containerClasses = cn(
|
|
6835
6900
|
"relative",
|
|
6836
6901
|
orientation === "horizontal" ? "w-full flex space-x-1 overflow-x-auto" : "flex flex-col space-y-1 shrink-0",
|
|
@@ -6853,59 +6918,69 @@ var Tabs = ({
|
|
|
6853
6918
|
tabs.map((tab, index) => {
|
|
6854
6919
|
const isActive = active === tab.value;
|
|
6855
6920
|
const Icon = tab.icon;
|
|
6856
|
-
const tabId2 =
|
|
6857
|
-
const panelId2 =
|
|
6921
|
+
const tabId2 = getTabTriggerId(baseId, index);
|
|
6922
|
+
const panelId2 = getTabPanelId(baseId, index);
|
|
6923
|
+
const tabHref = getTabHref(tab, panelId2);
|
|
6924
|
+
const sharedClassName = cn(
|
|
6925
|
+
"font-medium transition-all duration-200 cursor-pointer flex items-center gap-2",
|
|
6926
|
+
"focus:outline-none focus-visible:outline-none",
|
|
6927
|
+
"disabled:opacity-50 disabled:cursor-not-allowed",
|
|
6928
|
+
sizeStyles6[size].tab,
|
|
6929
|
+
variantStyles5[variant].tab,
|
|
6930
|
+
isActive ? variantStyles5[variant].activeTab : variantStyles5[variant].inactiveTab,
|
|
6931
|
+
orientation === "vertical" && "justify-start w-full",
|
|
6932
|
+
stretch && orientation === "horizontal" && "flex-1 justify-center",
|
|
6933
|
+
tab.href && tab.disabled && "pointer-events-none cursor-not-allowed opacity-50"
|
|
6934
|
+
);
|
|
6935
|
+
const sharedStyle = {
|
|
6936
|
+
boxShadow: "none",
|
|
6937
|
+
transform: "none",
|
|
6938
|
+
outline: "none",
|
|
6939
|
+
border: "none"
|
|
6940
|
+
};
|
|
6941
|
+
const sharedProps = {
|
|
6942
|
+
ref: (el) => {
|
|
6943
|
+
tabRefs.current[index] = el;
|
|
6944
|
+
},
|
|
6945
|
+
id: tabId2,
|
|
6946
|
+
role: "tab",
|
|
6947
|
+
"aria-selected": isActive,
|
|
6948
|
+
"aria-controls": panelId2,
|
|
6949
|
+
tabIndex: isActive ? 0 : -1,
|
|
6950
|
+
className: sharedClassName,
|
|
6951
|
+
style: sharedStyle,
|
|
6952
|
+
onKeyDown: handleTabKeyDown
|
|
6953
|
+
};
|
|
6954
|
+
if (!tab.disabled) {
|
|
6955
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
|
|
6956
|
+
"a",
|
|
6957
|
+
{
|
|
6958
|
+
...sharedProps,
|
|
6959
|
+
href: tabHref,
|
|
6960
|
+
target: tab.target,
|
|
6961
|
+
rel: tab.rel,
|
|
6962
|
+
onClick: (event) => {
|
|
6963
|
+
if (shouldHandleTabClickLocally(event, tab.target)) {
|
|
6964
|
+
event.preventDefault();
|
|
6965
|
+
handleTabChange(tab.value);
|
|
6966
|
+
}
|
|
6967
|
+
},
|
|
6968
|
+
className: cn(sharedClassName, "no-underline"),
|
|
6969
|
+
children: [
|
|
6970
|
+
Icon && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(Icon, { className: "h-4 w-4" }),
|
|
6971
|
+
tab.label
|
|
6972
|
+
]
|
|
6973
|
+
},
|
|
6974
|
+
tab.value
|
|
6975
|
+
);
|
|
6976
|
+
}
|
|
6858
6977
|
return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
|
|
6859
6978
|
"button",
|
|
6860
6979
|
{
|
|
6861
|
-
|
|
6862
|
-
tabRefs.current[index] = el;
|
|
6863
|
-
},
|
|
6980
|
+
...sharedProps,
|
|
6864
6981
|
onClick: () => !tab.disabled && handleTabChange(tab.value),
|
|
6865
6982
|
disabled: tab.disabled,
|
|
6866
|
-
|
|
6867
|
-
boxShadow: "none",
|
|
6868
|
-
transform: "none",
|
|
6869
|
-
outline: "none",
|
|
6870
|
-
border: "none"
|
|
6871
|
-
},
|
|
6872
|
-
className: cn(
|
|
6873
|
-
"font-medium transition-all duration-200 cursor-pointer flex items-center gap-2",
|
|
6874
|
-
"focus:outline-none focus-visible:outline-none",
|
|
6875
|
-
"disabled:opacity-50 disabled:cursor-not-allowed",
|
|
6876
|
-
"border-0 bg-transparent outline-none",
|
|
6877
|
-
// Reset button default styles
|
|
6878
|
-
sizeStyles6[size].tab,
|
|
6879
|
-
variantStyles5[variant].tab,
|
|
6880
|
-
isActive ? variantStyles5[variant].activeTab : variantStyles5[variant].inactiveTab,
|
|
6881
|
-
orientation === "vertical" && "justify-start w-full",
|
|
6882
|
-
stretch && orientation === "horizontal" && "flex-1 justify-center"
|
|
6883
|
-
),
|
|
6884
|
-
role: "tab",
|
|
6885
|
-
id: tabId2,
|
|
6886
|
-
"aria-selected": isActive,
|
|
6887
|
-
"aria-controls": panelId2,
|
|
6888
|
-
tabIndex: isActive ? 0 : -1,
|
|
6889
|
-
onKeyDown: (e) => {
|
|
6890
|
-
const count = tabs.length;
|
|
6891
|
-
const idx = tabs.findIndex((t) => t.value === active);
|
|
6892
|
-
let next = idx;
|
|
6893
|
-
if (orientation === "horizontal") {
|
|
6894
|
-
if (e.key === "ArrowRight") next = (idx + 1) % count;
|
|
6895
|
-
if (e.key === "ArrowLeft") next = (idx - 1 + count) % count;
|
|
6896
|
-
} else {
|
|
6897
|
-
if (e.key === "ArrowDown") next = (idx + 1) % count;
|
|
6898
|
-
if (e.key === "ArrowUp") next = (idx - 1 + count) % count;
|
|
6899
|
-
}
|
|
6900
|
-
if (e.key === "Home") next = 0;
|
|
6901
|
-
if (e.key === "End") next = count - 1;
|
|
6902
|
-
if (next !== idx) {
|
|
6903
|
-
e.preventDefault();
|
|
6904
|
-
const nextVal = tabs[next].value;
|
|
6905
|
-
handleTabChange(nextVal);
|
|
6906
|
-
tabRefs.current[next]?.focus();
|
|
6907
|
-
}
|
|
6908
|
-
},
|
|
6983
|
+
className: cn(sharedClassName, "border-0 bg-transparent outline-none"),
|
|
6909
6984
|
children: [
|
|
6910
6985
|
Icon && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(Icon, { className: "h-4 w-4" }),
|
|
6911
6986
|
tab.label
|