@geomak/ui 6.0.1 → 6.1.0
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.cjs +128 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +129 -14
- package/dist/index.js.map +1 -1
- package/dist/styles.css +23 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { colors_default } from './chunk-GKXP6OJJ.js';
|
|
2
2
|
export { colors_default as COLORS, PALETTE as palette, semanticTokens, vars } from './chunk-GKXP6OJJ.js';
|
|
3
3
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
4
|
-
import React8, { createContext, useState, useEffect, useMemo, useId, useCallback,
|
|
4
|
+
import React8, { createContext, useState, useEffect, useMemo, useId, useCallback, useRef, useContext, useLayoutEffect, useSyncExternalStore } from 'react';
|
|
5
5
|
import { createPortal } from 'react-dom';
|
|
6
6
|
import * as AvatarPrimitive from '@radix-ui/react-avatar';
|
|
7
7
|
import * as Dialog from '@radix-ui/react-dialog';
|
|
@@ -951,15 +951,27 @@ function Tabs({
|
|
|
951
951
|
const current = isControlled ? value : internal;
|
|
952
952
|
const reduced = !!useReducedMotion();
|
|
953
953
|
const indicatorId = useId();
|
|
954
|
-
const
|
|
954
|
+
const select = useCallback((next) => {
|
|
955
955
|
if (!isControlled) setInternal(next);
|
|
956
956
|
onValueChange?.(next);
|
|
957
|
-
};
|
|
958
|
-
|
|
957
|
+
}, [isControlled, onValueChange]);
|
|
958
|
+
const registry = useRef(/* @__PURE__ */ new Map());
|
|
959
|
+
const orderRef = useRef(0);
|
|
960
|
+
const [, bump] = useState(0);
|
|
961
|
+
const registerTab = useCallback((val, meta) => {
|
|
962
|
+
const existing = registry.current.get(val);
|
|
963
|
+
registry.current.set(val, { ...meta, order: existing?.order ?? orderRef.current++ });
|
|
964
|
+
if (!existing) bump((v) => v + 1);
|
|
965
|
+
}, []);
|
|
966
|
+
const unregisterTab = useCallback((val) => {
|
|
967
|
+
if (registry.current.delete(val)) bump((v) => v + 1);
|
|
968
|
+
}, []);
|
|
969
|
+
const getTabs = useCallback(() => [...registry.current.entries()].sort((a, b) => a[1].order - b[1].order).map(([val, m]) => ({ value: val, label: m.label, icon: m.icon, disabled: m.disabled })), []);
|
|
970
|
+
return /* @__PURE__ */ jsx(TabsContext.Provider, { value: { value: current, variant, size, orientation, indicatorId, reduced, select, registerTab, unregisterTab, getTabs }, children: /* @__PURE__ */ jsx(
|
|
959
971
|
TabsPrimitive.Root,
|
|
960
972
|
{
|
|
961
973
|
value: current,
|
|
962
|
-
onValueChange:
|
|
974
|
+
onValueChange: select,
|
|
963
975
|
orientation,
|
|
964
976
|
className: [
|
|
965
977
|
"flex min-w-0",
|
|
@@ -972,7 +984,7 @@ function Tabs({
|
|
|
972
984
|
) });
|
|
973
985
|
}
|
|
974
986
|
function TabsList({ children, "aria-label": ariaLabel, className = "" }) {
|
|
975
|
-
const { variant, orientation, reduced } = useTabsContext();
|
|
987
|
+
const { variant, orientation, reduced, value } = useTabsContext();
|
|
976
988
|
const horizontal = orientation === "horizontal";
|
|
977
989
|
const scrollRef = useRef(null);
|
|
978
990
|
const [edges, setEdges] = useState({ start: false, end: false });
|
|
@@ -1008,6 +1020,14 @@ function TabsList({ children, "aria-label": ariaLabel, className = "" }) {
|
|
|
1008
1020
|
const amount = (horizontal ? el.clientWidth : el.clientHeight) * 0.7 * dir;
|
|
1009
1021
|
el.scrollBy({ [horizontal ? "left" : "top"]: amount, behavior: reduced ? "auto" : "smooth" });
|
|
1010
1022
|
}, [horizontal, reduced]);
|
|
1023
|
+
useLayoutEffect(() => {
|
|
1024
|
+
const el = scrollRef.current;
|
|
1025
|
+
if (!el || !scrollable) return;
|
|
1026
|
+
const active = el.querySelector("[role=tab][data-state=active]");
|
|
1027
|
+
if (active && typeof active.scrollIntoView === "function") {
|
|
1028
|
+
active.scrollIntoView({ block: "nearest", inline: "nearest", behavior: reduced ? "auto" : "smooth" });
|
|
1029
|
+
}
|
|
1030
|
+
}, [value, scrollable, reduced]);
|
|
1011
1031
|
const maskStyle = scrollable && (edges.start || edges.end) ? (() => {
|
|
1012
1032
|
const dir = horizontal ? "to right" : "to bottom";
|
|
1013
1033
|
const a = edges.start ? "transparent, black 36px" : "black";
|
|
@@ -1024,7 +1044,8 @@ function TabsList({ children, "aria-label": ariaLabel, className = "" }) {
|
|
|
1024
1044
|
return `flex ${horizontal ? "flex-row" : "flex-col"} ${align} gap-1 ${hairline}`;
|
|
1025
1045
|
})();
|
|
1026
1046
|
const scrollClass = scrollable ? horizontal ? "overflow-x-auto overflow-y-hidden hidden-scrollbar" : "overflow-y-auto overflow-x-hidden hidden-scrollbar" : "";
|
|
1027
|
-
|
|
1047
|
+
const overflowing = scrollable && (edges.start || edges.end);
|
|
1048
|
+
return /* @__PURE__ */ jsxs("div", { className: ["relative flex min-w-0 gap-1", horizontal ? "flex-row items-stretch" : "flex-col items-stretch", className].filter(Boolean).join(" "), children: [
|
|
1028
1049
|
scrollable && edges.start && /* @__PURE__ */ jsx(Chevron, { side: "start", orientation, onClick: () => nudge(-1) }),
|
|
1029
1050
|
/* @__PURE__ */ jsx(
|
|
1030
1051
|
TabsPrimitive.List,
|
|
@@ -1036,37 +1057,131 @@ function TabsList({ children, "aria-label": ariaLabel, className = "" }) {
|
|
|
1036
1057
|
children
|
|
1037
1058
|
}
|
|
1038
1059
|
),
|
|
1039
|
-
scrollable && edges.end && /* @__PURE__ */ jsx(Chevron, { side: "end", orientation, onClick: () => nudge(1) })
|
|
1060
|
+
scrollable && edges.end && /* @__PURE__ */ jsx(Chevron, { side: "end", orientation, onClick: () => nudge(1) }),
|
|
1061
|
+
overflowing && /* @__PURE__ */ jsx(OverflowMenu, {})
|
|
1040
1062
|
] });
|
|
1041
1063
|
}
|
|
1042
1064
|
function Chevron({ side, orientation, onClick }) {
|
|
1043
1065
|
const horizontal = orientation === "horizontal";
|
|
1044
1066
|
const rotate = horizontal ? side === "start" ? "rotate-180" : "" : side === "start" ? "-rotate-90" : "rotate-90";
|
|
1045
|
-
const pos = horizontal ? side === "start" ? "left-0 top-1/2 -translate-y-1/2" : "right-0 top-1/2 -translate-y-1/2" : side === "start" ? "top-0 left-1/2 -translate-x-1/2" : "bottom-0 left-1/2 -translate-x-1/2";
|
|
1046
1067
|
return /* @__PURE__ */ jsx(
|
|
1047
1068
|
"button",
|
|
1048
1069
|
{
|
|
1049
1070
|
type: "button",
|
|
1050
1071
|
"aria-label": side === "start" ? "Scroll tabs backward" : "Scroll tabs forward",
|
|
1051
1072
|
onClick,
|
|
1052
|
-
className:
|
|
1073
|
+
className: "flex-shrink-0 self-center flex h-7 w-7 items-center justify-center rounded-full border border-border bg-surface text-foreground-secondary shadow-sm hover:text-foreground hover:bg-surface-raised transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-accent",
|
|
1053
1074
|
children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: `h-4 w-4 ${rotate}`, "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 5l7 7-7 7" }) })
|
|
1054
1075
|
}
|
|
1055
1076
|
);
|
|
1056
1077
|
}
|
|
1078
|
+
function OverflowMenu() {
|
|
1079
|
+
const { getTabs, value, select, orientation } = useTabsContext();
|
|
1080
|
+
const horizontal = orientation === "horizontal";
|
|
1081
|
+
const [open, setOpen] = useState(false);
|
|
1082
|
+
const wrapRef = useRef(null);
|
|
1083
|
+
const timer = useRef(null);
|
|
1084
|
+
const openNow = () => {
|
|
1085
|
+
if (timer.current) clearTimeout(timer.current);
|
|
1086
|
+
setOpen(true);
|
|
1087
|
+
};
|
|
1088
|
+
const closeSoon = () => {
|
|
1089
|
+
timer.current = setTimeout(() => setOpen(false), 160);
|
|
1090
|
+
};
|
|
1091
|
+
useLayoutEffect(() => {
|
|
1092
|
+
if (!open) return;
|
|
1093
|
+
const onDoc = (e) => {
|
|
1094
|
+
if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false);
|
|
1095
|
+
};
|
|
1096
|
+
const onKey = (e) => {
|
|
1097
|
+
if (e.key === "Escape") setOpen(false);
|
|
1098
|
+
};
|
|
1099
|
+
document.addEventListener("mousedown", onDoc);
|
|
1100
|
+
document.addEventListener("keydown", onKey);
|
|
1101
|
+
return () => {
|
|
1102
|
+
document.removeEventListener("mousedown", onDoc);
|
|
1103
|
+
document.removeEventListener("keydown", onKey);
|
|
1104
|
+
};
|
|
1105
|
+
}, [open]);
|
|
1106
|
+
const tabs = getTabs();
|
|
1107
|
+
return /* @__PURE__ */ jsxs(
|
|
1108
|
+
"div",
|
|
1109
|
+
{
|
|
1110
|
+
ref: wrapRef,
|
|
1111
|
+
className: "relative flex-shrink-0 self-center",
|
|
1112
|
+
onMouseEnter: openNow,
|
|
1113
|
+
onMouseLeave: closeSoon,
|
|
1114
|
+
children: [
|
|
1115
|
+
/* @__PURE__ */ jsx(
|
|
1116
|
+
"button",
|
|
1117
|
+
{
|
|
1118
|
+
type: "button",
|
|
1119
|
+
"aria-haspopup": "menu",
|
|
1120
|
+
"aria-expanded": open,
|
|
1121
|
+
"aria-label": "Show all tabs",
|
|
1122
|
+
onClick: () => setOpen((o) => !o),
|
|
1123
|
+
className: "flex h-7 w-7 items-center justify-center rounded-full border border-border bg-surface text-foreground-secondary shadow-sm hover:text-foreground hover:bg-surface-raised data-[expanded=true]:text-foreground transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-accent",
|
|
1124
|
+
"data-expanded": open,
|
|
1125
|
+
children: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "currentColor", className: "h-4 w-4", "aria-hidden": "true", children: [
|
|
1126
|
+
/* @__PURE__ */ jsx("circle", { cx: "5", cy: "12", r: "1.6" }),
|
|
1127
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "1.6" }),
|
|
1128
|
+
/* @__PURE__ */ jsx("circle", { cx: "19", cy: "12", r: "1.6" })
|
|
1129
|
+
] })
|
|
1130
|
+
}
|
|
1131
|
+
),
|
|
1132
|
+
open && /* @__PURE__ */ jsx(
|
|
1133
|
+
"div",
|
|
1134
|
+
{
|
|
1135
|
+
role: "menu",
|
|
1136
|
+
onMouseEnter: openNow,
|
|
1137
|
+
onMouseLeave: closeSoon,
|
|
1138
|
+
className: `absolute z-30 ${horizontal ? "right-0 top-full mt-1.5" : "left-full top-0 ml-1.5"} min-w-[184px] max-h-72 overflow-y-auto hidden-scrollbar rounded-lg border border-border bg-surface p-1 shadow-md animate-in fade-in-0 zoom-in-95`,
|
|
1139
|
+
children: tabs.map((t) => {
|
|
1140
|
+
const isActive = t.value === value;
|
|
1141
|
+
return /* @__PURE__ */ jsxs(
|
|
1142
|
+
"button",
|
|
1143
|
+
{
|
|
1144
|
+
type: "button",
|
|
1145
|
+
role: "menuitem",
|
|
1146
|
+
disabled: t.disabled,
|
|
1147
|
+
onClick: () => {
|
|
1148
|
+
select(t.value);
|
|
1149
|
+
setOpen(false);
|
|
1150
|
+
},
|
|
1151
|
+
className: `flex w-full items-center gap-2 rounded-md px-2.5 py-1.5 text-left text-sm transition-colors disabled:opacity-40 disabled:cursor-not-allowed focus:outline-none focus-visible:ring-2 focus-visible:ring-accent ${isActive ? "bg-surface-raised text-accent" : "text-foreground-secondary hover:bg-surface-raised hover:text-foreground"}`,
|
|
1152
|
+
children: [
|
|
1153
|
+
t.icon && /* @__PURE__ */ jsx("span", { className: "flex-shrink-0 inline-flex h-4 w-4 items-center justify-center", children: t.icon }),
|
|
1154
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: t.label }),
|
|
1155
|
+
isActive && /* @__PURE__ */ jsx("svg", { viewBox: "0 0 20 20", fill: "none", className: "h-4 w-4 flex-shrink-0 text-accent", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M4 10l4.5 4.5L16 6", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) })
|
|
1156
|
+
]
|
|
1157
|
+
},
|
|
1158
|
+
t.value
|
|
1159
|
+
);
|
|
1160
|
+
})
|
|
1161
|
+
}
|
|
1162
|
+
)
|
|
1163
|
+
]
|
|
1164
|
+
}
|
|
1165
|
+
);
|
|
1166
|
+
}
|
|
1057
1167
|
function TabsTrigger({ value, icon, badge, closeable, onClose, disabled, className = "", children }) {
|
|
1058
|
-
const { value: active, variant, size, orientation, indicatorId, reduced } = useTabsContext();
|
|
1168
|
+
const { value: active, variant, size, orientation, indicatorId, reduced, registerTab, unregisterTab } = useTabsContext();
|
|
1059
1169
|
const isActive = active === value;
|
|
1060
1170
|
const horizontal = orientation === "horizontal";
|
|
1061
1171
|
const sz = SIZE[size];
|
|
1062
|
-
|
|
1172
|
+
useLayoutEffect(() => {
|
|
1173
|
+
registerTab(value, { label: children, icon, disabled });
|
|
1174
|
+
return () => unregisterTab(value);
|
|
1175
|
+
}, [value, children, icon, disabled, registerTab, unregisterTab]);
|
|
1176
|
+
const layoutCls = horizontal ? "justify-center flex-shrink-0" : "justify-start w-full";
|
|
1177
|
+
const base = "group/trigger relative inline-flex items-center whitespace-nowrap font-medium select-none transition-colors duration-150 focus:outline-none disabled:opacity-40 disabled:cursor-not-allowed";
|
|
1063
1178
|
const variantCls = variant === "segmented" ? `rounded-md ${isActive ? "text-accent" : "text-foreground-secondary hover:text-foreground"} focus-visible:text-accent` : variant === "enclosed" ? `${horizontal ? "rounded-t-md border border-b-0 -mb-px" : "rounded-l-md border border-r-0 -mr-px"} ${isActive ? "bg-surface border-border text-foreground" : "border-transparent text-foreground-secondary hover:text-foreground hover:bg-surface-raised"} focus-visible:text-accent` : `${isActive ? "text-accent" : "text-foreground-secondary hover:text-foreground"} focus-visible:text-accent`;
|
|
1064
1179
|
const trigger = /* @__PURE__ */ jsxs(
|
|
1065
1180
|
TabsPrimitive.Trigger,
|
|
1066
1181
|
{
|
|
1067
1182
|
value,
|
|
1068
1183
|
disabled,
|
|
1069
|
-
className: [base, sz.trigger, closeable ? "pr-8" : "", variantCls, className].filter(Boolean).join(" "),
|
|
1184
|
+
className: [base, sz.trigger, layoutCls, closeable ? "pr-8" : "", variantCls, className].filter(Boolean).join(" "),
|
|
1070
1185
|
children: [
|
|
1071
1186
|
variant === "segmented" && isActive && /* @__PURE__ */ jsx(
|
|
1072
1187
|
motion.span,
|
|
@@ -1099,7 +1214,7 @@ function TabsTrigger({ value, icon, badge, closeable, onClose, disabled, classNa
|
|
|
1099
1214
|
}
|
|
1100
1215
|
);
|
|
1101
1216
|
if (!closeable) return trigger;
|
|
1102
|
-
return /* @__PURE__ */ jsxs("span", { className:
|
|
1217
|
+
return /* @__PURE__ */ jsxs("span", { className: `relative inline-flex items-center ${horizontal ? "flex-shrink-0" : "w-full"}`, children: [
|
|
1103
1218
|
trigger,
|
|
1104
1219
|
/* @__PURE__ */ jsx(
|
|
1105
1220
|
"button",
|