@astherix/ui 0.40.0 → 0.41.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/README.md +118 -1
- package/dist/index.d.ts +158 -1
- package/dist/index.js +909 -26
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/theme.css +27 -0
package/dist/index.js
CHANGED
|
@@ -19,7 +19,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
19
19
|
var ModalContext = React.createContext(null);
|
|
20
20
|
function useModalContext(component) {
|
|
21
21
|
const ctx = React.useContext(ModalContext);
|
|
22
|
-
if (!ctx) throw new Error(`<${component}> must be used inside <Modal>.`);
|
|
22
|
+
if (!ctx) throw new Error(`<${component}> must be used inside <Modal> or <Drawer>.`);
|
|
23
23
|
return ctx;
|
|
24
24
|
}
|
|
25
25
|
function Modal({ open: openProp, defaultOpen = false, onOpenChange, children }) {
|
|
@@ -153,10 +153,11 @@ var modalPanelVariants = cva(
|
|
|
153
153
|
}
|
|
154
154
|
);
|
|
155
155
|
var XIcon = () => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", strokeWidth: 1.7, strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M5.5 5.5l9 9M14.5 5.5l-9 9" }) });
|
|
156
|
-
var
|
|
156
|
+
var DialogSurface = React.forwardRef(
|
|
157
157
|
({
|
|
158
|
-
|
|
159
|
-
|
|
158
|
+
component,
|
|
159
|
+
dialogClassName,
|
|
160
|
+
panelClassName,
|
|
160
161
|
backdrop = "default",
|
|
161
162
|
dismissible = true,
|
|
162
163
|
showCloseButton = true,
|
|
@@ -165,7 +166,7 @@ var ModalContent = React.forwardRef(
|
|
|
165
166
|
"aria-label": ariaLabel,
|
|
166
167
|
...props
|
|
167
168
|
}, ref) => {
|
|
168
|
-
const { open, setOpen, titleId, descriptionId, hasDescription } = useModalContext(
|
|
169
|
+
const { open, setOpen, titleId, descriptionId, hasDescription } = useModalContext(component);
|
|
169
170
|
const dialogRef = React.useRef(null);
|
|
170
171
|
const returnFocusRef = React.useRef(null);
|
|
171
172
|
const pressedBackdrop = React.useRef(false);
|
|
@@ -228,9 +229,8 @@ var ModalContent = React.forwardRef(
|
|
|
228
229
|
"data-state": state,
|
|
229
230
|
className: cn(
|
|
230
231
|
// The dialog fills the viewport so clicks outside the panel land on it (= backdrop).
|
|
231
|
-
"fixed inset-0 m-0 h-dvh max-h-none w-full max-w-none
|
|
232
|
-
|
|
233
|
-
size === "full" && "p-0 sm:p-0",
|
|
232
|
+
"fixed inset-0 m-0 h-dvh max-h-none w-full max-w-none overscroll-contain bg-transparent text-fg",
|
|
233
|
+
dialogClassName,
|
|
234
234
|
backdropClasses[backdrop],
|
|
235
235
|
"data-[state=open]:backdrop:animate-[ui-fade-in_200ms_ease-out]",
|
|
236
236
|
"data-[state=closing]:backdrop:animate-[ui-fade-out_140ms_ease-in_forwards]"
|
|
@@ -253,24 +253,30 @@ var ModalContent = React.forwardRef(
|
|
|
253
253
|
if (dismissible && pressedBackdrop.current && event.target === event.currentTarget) setOpen(false);
|
|
254
254
|
pressedBackdrop.current = false;
|
|
255
255
|
},
|
|
256
|
-
children: (state === "open" || state === "closing") && /* @__PURE__ */ jsxs(
|
|
257
|
-
|
|
258
|
-
{
|
|
259
|
-
|
|
260
|
-
tabIndex: -1,
|
|
261
|
-
"data-state": state,
|
|
262
|
-
className: cn(modalPanelVariants({ size }), className),
|
|
263
|
-
...props,
|
|
264
|
-
children: [
|
|
265
|
-
children,
|
|
266
|
-
showCloseButton && dismissible && /* @__PURE__ */ jsx("div", { className: "absolute right-3 top-3", children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", iconOnly: true, "aria-label": closeLabel, onClick: () => setOpen(false), children: /* @__PURE__ */ jsx(XIcon, {}) }) })
|
|
267
|
-
]
|
|
268
|
-
}
|
|
269
|
-
)
|
|
256
|
+
children: (state === "open" || state === "closing") && /* @__PURE__ */ jsxs("div", { ref: setPanelRef, tabIndex: -1, "data-state": state, className: panelClassName, ...props, children: [
|
|
257
|
+
children,
|
|
258
|
+
showCloseButton && dismissible && /* @__PURE__ */ jsx("div", { className: "absolute right-3 top-3", children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", iconOnly: true, "aria-label": closeLabel, onClick: () => setOpen(false), children: /* @__PURE__ */ jsx(XIcon, {}) }) })
|
|
259
|
+
] })
|
|
270
260
|
}
|
|
271
261
|
);
|
|
272
262
|
}
|
|
273
263
|
);
|
|
264
|
+
DialogSurface.displayName = "DialogSurface";
|
|
265
|
+
var ModalContent = React.forwardRef(
|
|
266
|
+
({ className, size, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
267
|
+
DialogSurface,
|
|
268
|
+
{
|
|
269
|
+
ref,
|
|
270
|
+
component: "ModalContent",
|
|
271
|
+
dialogClassName: cn(
|
|
272
|
+
"overflow-y-auto p-4 sm:p-6 open:flex open:flex-col open:items-center",
|
|
273
|
+
size === "full" && "p-0 sm:p-0"
|
|
274
|
+
),
|
|
275
|
+
panelClassName: cn(modalPanelVariants({ size }), className),
|
|
276
|
+
...props
|
|
277
|
+
}
|
|
278
|
+
)
|
|
279
|
+
);
|
|
274
280
|
ModalContent.displayName = "ModalContent";
|
|
275
281
|
var ModalHeader = React.forwardRef(
|
|
276
282
|
({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("grid gap-1.5 px-6 pb-2 pr-14 pt-6", className), ...props })
|
|
@@ -3937,7 +3943,7 @@ function DataTable({
|
|
|
3937
3943
|
onQueryChange,
|
|
3938
3944
|
className
|
|
3939
3945
|
}) {
|
|
3940
|
-
const
|
|
3946
|
+
const keyOf3 = React12.useCallback(
|
|
3941
3947
|
(row) => typeof rowKey === "function" ? rowKey(row) : row[rowKey],
|
|
3942
3948
|
[rowKey]
|
|
3943
3949
|
);
|
|
@@ -3996,10 +4002,10 @@ function DataTable({
|
|
|
3996
4002
|
setSort((s) => !s || s.key !== key ? { key, direction: "asc" } : s.direction === "asc" ? { key, direction: "desc" } : null);
|
|
3997
4003
|
setPage(1);
|
|
3998
4004
|
};
|
|
3999
|
-
const pageKeys = paged.map(
|
|
4005
|
+
const pageKeys = paged.map(keyOf3);
|
|
4000
4006
|
const selectedOnPage = pageKeys.filter((k) => selected.includes(k));
|
|
4001
4007
|
const allOnPage = pageKeys.length > 0 && selectedOnPage.length === pageKeys.length;
|
|
4002
|
-
const selectedRows = data.filter((r) => selected.includes(
|
|
4008
|
+
const selectedRows = data.filter((r) => selected.includes(keyOf3(r)));
|
|
4003
4009
|
const refreshing = loading && paged.length > 0 || refreshingByButton;
|
|
4004
4010
|
const [justRefreshed, setJustRefreshed] = React12.useState(false);
|
|
4005
4011
|
const wasRefreshing = React12.useRef(false);
|
|
@@ -4225,7 +4231,7 @@ function DataTable({
|
|
|
4225
4231
|
] }) }),
|
|
4226
4232
|
/* @__PURE__ */ jsxs12("tbody", { role: "rowgroup", className: cn(cardBody), children: [
|
|
4227
4233
|
loading && paged.length === 0 ? Array.from({ length: Math.min(pageSize || 5, 5) }, (_, i) => /* @__PURE__ */ jsx12("tr", { role: "row", className: cn(cardRow), children: Array.from({ length: colCount }, (_2, j) => /* @__PURE__ */ jsx12("td", { role: "cell", className: cn("border-b border-border", pad, cards && "@max-[40rem]:border-0 @max-[40rem]:p-0"), children: /* @__PURE__ */ jsx12(Skeleton, { className: "h-3.5 w-full max-w-32 rounded-full" }) }, j)) }, `sk-${i}`)) : paged.map((row, ri) => {
|
|
4228
|
-
const k =
|
|
4234
|
+
const k = keyOf3(row);
|
|
4229
4235
|
const isSel = selected.includes(k);
|
|
4230
4236
|
return /* @__PURE__ */ jsxs12(
|
|
4231
4237
|
"tr",
|
|
@@ -10409,6 +10415,868 @@ function HeroMedia({ frame = false, url, float = false, tilt = false, className,
|
|
|
10409
10415
|
}
|
|
10410
10416
|
);
|
|
10411
10417
|
}
|
|
10418
|
+
|
|
10419
|
+
// src/components/drawer/Drawer.tsx
|
|
10420
|
+
import * as React35 from "react";
|
|
10421
|
+
import { jsx as jsx34 } from "react/jsx-runtime";
|
|
10422
|
+
var Drawer = Modal;
|
|
10423
|
+
var DrawerTrigger = ModalTrigger;
|
|
10424
|
+
var DrawerClose = ModalClose;
|
|
10425
|
+
var DrawerTitle = ModalTitle;
|
|
10426
|
+
var DrawerDescription = ModalDescription;
|
|
10427
|
+
function useDrawer(initialOpen = false) {
|
|
10428
|
+
const { modalProps, ...rest } = useModal(initialOpen);
|
|
10429
|
+
return { ...rest, drawerProps: modalProps };
|
|
10430
|
+
}
|
|
10431
|
+
var placement = {
|
|
10432
|
+
left: "open:justify-start",
|
|
10433
|
+
right: "open:justify-end",
|
|
10434
|
+
top: "open:flex-col open:justify-start",
|
|
10435
|
+
bottom: "open:flex-col open:justify-end"
|
|
10436
|
+
};
|
|
10437
|
+
var panelSide = {
|
|
10438
|
+
left: "h-full border-r [--ui-drawer-from:translateX(-100%)]",
|
|
10439
|
+
right: "h-full border-l [--ui-drawer-from:translateX(100%)]",
|
|
10440
|
+
top: "w-full border-b [--ui-drawer-from:translateY(-100%)]",
|
|
10441
|
+
bottom: "w-full border-t rounded-t-modal [--ui-drawer-from:translateY(100%)]"
|
|
10442
|
+
};
|
|
10443
|
+
var reach = {
|
|
10444
|
+
x: { sm: "w-[min(18rem,calc(100%-3rem))]", md: "w-[min(24rem,calc(100%-3rem))]", lg: "w-[min(36rem,calc(100%-3rem))]" },
|
|
10445
|
+
y: { sm: "max-h-[40dvh]", md: "max-h-[65dvh]", lg: "max-h-[85dvh]" }
|
|
10446
|
+
};
|
|
10447
|
+
var DrawerContent = React35.forwardRef(
|
|
10448
|
+
({ side = "right", size = "md", className, ...props }, ref) => /* @__PURE__ */ jsx34(
|
|
10449
|
+
DialogSurface,
|
|
10450
|
+
{
|
|
10451
|
+
ref,
|
|
10452
|
+
component: "DrawerContent",
|
|
10453
|
+
dialogClassName: cn("overflow-hidden p-0 open:flex", placement[side]),
|
|
10454
|
+
panelClassName: cn(
|
|
10455
|
+
"relative flex flex-col overflow-y-auto overscroll-contain border-border bg-surface text-fg outline-none",
|
|
10456
|
+
"shadow-[var(--ui-shadow-modal)]",
|
|
10457
|
+
panelSide[side],
|
|
10458
|
+
reach[side === "left" || side === "right" ? "x" : "y"][size],
|
|
10459
|
+
"data-[state=open]:animate-[ui-drawer-in_260ms_cubic-bezier(0.2,0.9,0.3,1)]",
|
|
10460
|
+
"data-[state=closing]:animate-[ui-drawer-out_140ms_ease-in_forwards]",
|
|
10461
|
+
"motion-reduce:data-[state=open]:animate-[ui-fade-in_140ms_ease-out]",
|
|
10462
|
+
"motion-reduce:data-[state=closing]:animate-[ui-fade-out_140ms_ease-in_forwards]",
|
|
10463
|
+
// Keep content clear of notches and the home indicator.
|
|
10464
|
+
"pb-[env(safe-area-inset-bottom)]",
|
|
10465
|
+
className
|
|
10466
|
+
),
|
|
10467
|
+
...props
|
|
10468
|
+
}
|
|
10469
|
+
)
|
|
10470
|
+
);
|
|
10471
|
+
DrawerContent.displayName = "DrawerContent";
|
|
10472
|
+
var DrawerHeader = React35.forwardRef(
|
|
10473
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsx34("div", { ref, className: cn("grid gap-1.5 px-5 pb-2 pr-14 pt-5", className), ...props })
|
|
10474
|
+
);
|
|
10475
|
+
DrawerHeader.displayName = "DrawerHeader";
|
|
10476
|
+
var DrawerBody = React35.forwardRef(
|
|
10477
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsx34("div", { ref, className: cn("flex-1 px-5 py-3 text-[0.9375rem] leading-relaxed", className), ...props })
|
|
10478
|
+
);
|
|
10479
|
+
DrawerBody.displayName = "DrawerBody";
|
|
10480
|
+
var DrawerFooter = React35.forwardRef(
|
|
10481
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsx34(
|
|
10482
|
+
"div",
|
|
10483
|
+
{
|
|
10484
|
+
ref,
|
|
10485
|
+
className: cn("mt-auto flex flex-col-reverse gap-2 border-t border-border px-5 py-4 sm:flex-row sm:justify-end", className),
|
|
10486
|
+
...props
|
|
10487
|
+
}
|
|
10488
|
+
)
|
|
10489
|
+
);
|
|
10490
|
+
DrawerFooter.displayName = "DrawerFooter";
|
|
10491
|
+
|
|
10492
|
+
// src/components/tooltip/Tooltip.tsx
|
|
10493
|
+
import * as React36 from "react";
|
|
10494
|
+
import { Slot as Slot6 } from "@radix-ui/react-slot";
|
|
10495
|
+
import { Fragment as Fragment20, jsx as jsx35, jsxs as jsxs34 } from "react/jsx-runtime";
|
|
10496
|
+
var openCount = 0;
|
|
10497
|
+
var lastHiddenAt = 0;
|
|
10498
|
+
var closeCurrent = null;
|
|
10499
|
+
var WARM_MS = 500;
|
|
10500
|
+
var GAP2 = 6;
|
|
10501
|
+
var supportsPopover = () => typeof HTMLElement !== "undefined" && "showPopover" in HTMLElement.prototype;
|
|
10502
|
+
function Tooltip2({
|
|
10503
|
+
content,
|
|
10504
|
+
children,
|
|
10505
|
+
side = "top",
|
|
10506
|
+
align = "center",
|
|
10507
|
+
delay = 400,
|
|
10508
|
+
disabled = false,
|
|
10509
|
+
as = "description",
|
|
10510
|
+
className
|
|
10511
|
+
}) {
|
|
10512
|
+
const id = React36.useId();
|
|
10513
|
+
const triggerRef = React36.useRef(null);
|
|
10514
|
+
const tipRef = React36.useRef(null);
|
|
10515
|
+
const timers = React36.useRef({ show: 0, hide: 0 });
|
|
10516
|
+
const [open, setOpen] = React36.useState(false);
|
|
10517
|
+
const [placed, setPlaced] = React36.useState({
|
|
10518
|
+
side,
|
|
10519
|
+
style: { visibility: "hidden" }
|
|
10520
|
+
});
|
|
10521
|
+
const clear = () => {
|
|
10522
|
+
window.clearTimeout(timers.current.show);
|
|
10523
|
+
window.clearTimeout(timers.current.hide);
|
|
10524
|
+
};
|
|
10525
|
+
const show = (wait) => {
|
|
10526
|
+
if (disabled) return;
|
|
10527
|
+
clear();
|
|
10528
|
+
const instant = wait === 0 || openCount > 0 || Date.now() - lastHiddenAt < WARM_MS;
|
|
10529
|
+
if (instant) setOpen(true);
|
|
10530
|
+
else timers.current.show = window.setTimeout(() => setOpen(true), wait);
|
|
10531
|
+
};
|
|
10532
|
+
const hide = (wait = 0) => {
|
|
10533
|
+
clear();
|
|
10534
|
+
const done = () => setOpen(false);
|
|
10535
|
+
if (wait) timers.current.hide = window.setTimeout(done, wait);
|
|
10536
|
+
else done();
|
|
10537
|
+
};
|
|
10538
|
+
React36.useEffect(() => () => clear(), []);
|
|
10539
|
+
React36.useEffect(() => {
|
|
10540
|
+
if (!open) return;
|
|
10541
|
+
const close = () => hide();
|
|
10542
|
+
if (closeCurrent && closeCurrent !== close) closeCurrent();
|
|
10543
|
+
closeCurrent = close;
|
|
10544
|
+
openCount++;
|
|
10545
|
+
return () => {
|
|
10546
|
+
openCount--;
|
|
10547
|
+
lastHiddenAt = Date.now();
|
|
10548
|
+
if (closeCurrent === close) closeCurrent = null;
|
|
10549
|
+
};
|
|
10550
|
+
}, [open]);
|
|
10551
|
+
React36.useEffect(() => {
|
|
10552
|
+
if (disabled) hide();
|
|
10553
|
+
}, [disabled]);
|
|
10554
|
+
const place = React36.useCallback(() => {
|
|
10555
|
+
const t = triggerRef.current;
|
|
10556
|
+
const tip = tipRef.current;
|
|
10557
|
+
if (!t || !tip) return;
|
|
10558
|
+
const r = t.getBoundingClientRect();
|
|
10559
|
+
const w = tip.offsetWidth;
|
|
10560
|
+
const h = tip.offsetHeight;
|
|
10561
|
+
const vw = window.innerWidth;
|
|
10562
|
+
const vh = window.innerHeight;
|
|
10563
|
+
const room = { top: r.top, bottom: vh - r.bottom, left: r.left, right: vw - r.right };
|
|
10564
|
+
const need = side === "top" || side === "bottom" ? h + GAP2 : w + GAP2;
|
|
10565
|
+
const opposite = { top: "bottom", bottom: "top", left: "right", right: "left" };
|
|
10566
|
+
const s = room[side] >= need || room[side] >= room[opposite[side]] ? side : opposite[side];
|
|
10567
|
+
let left;
|
|
10568
|
+
let top;
|
|
10569
|
+
if (s === "top" || s === "bottom") {
|
|
10570
|
+
top = s === "top" ? r.top - h - GAP2 : r.bottom + GAP2;
|
|
10571
|
+
left = align === "start" ? r.left : align === "end" ? r.right - w : r.left + r.width / 2 - w / 2;
|
|
10572
|
+
} else {
|
|
10573
|
+
left = s === "left" ? r.left - w - GAP2 : r.right + GAP2;
|
|
10574
|
+
top = align === "start" ? r.top : align === "end" ? r.bottom - h : r.top + r.height / 2 - h / 2;
|
|
10575
|
+
}
|
|
10576
|
+
left = Math.max(8, Math.min(left, vw - w - 8));
|
|
10577
|
+
top = Math.max(8, Math.min(top, vh - h - 8));
|
|
10578
|
+
setPlaced({ side: s, style: { left, top } });
|
|
10579
|
+
}, [side, align]);
|
|
10580
|
+
React36.useLayoutEffect(() => {
|
|
10581
|
+
const tip = tipRef.current;
|
|
10582
|
+
if (!tip) return;
|
|
10583
|
+
if (!open) {
|
|
10584
|
+
if (supportsPopover() && tip.matches(":popover-open")) tip.hidePopover();
|
|
10585
|
+
setPlaced((p) => ({ ...p, style: { visibility: "hidden" } }));
|
|
10586
|
+
return;
|
|
10587
|
+
}
|
|
10588
|
+
if (supportsPopover() && !tip.matches(":popover-open")) tip.showPopover();
|
|
10589
|
+
place();
|
|
10590
|
+
const onKey = (e) => {
|
|
10591
|
+
if (e.key === "Escape") hide();
|
|
10592
|
+
};
|
|
10593
|
+
window.addEventListener("resize", place);
|
|
10594
|
+
window.addEventListener("scroll", place, true);
|
|
10595
|
+
document.addEventListener("keydown", onKey, true);
|
|
10596
|
+
return () => {
|
|
10597
|
+
window.removeEventListener("resize", place);
|
|
10598
|
+
window.removeEventListener("scroll", place, true);
|
|
10599
|
+
document.removeEventListener("keydown", onKey, true);
|
|
10600
|
+
};
|
|
10601
|
+
}, [open, place]);
|
|
10602
|
+
const childProps = children.props;
|
|
10603
|
+
const ariaKey = as === "label" ? "aria-labelledby" : "aria-describedby";
|
|
10604
|
+
const ariaValue = [childProps[ariaKey], `${id}-tip`].filter(Boolean).join(" ");
|
|
10605
|
+
const fromBySide = {
|
|
10606
|
+
top: "translateY(3px)",
|
|
10607
|
+
bottom: "translateY(-3px)",
|
|
10608
|
+
left: "translateX(3px)",
|
|
10609
|
+
right: "translateX(-3px)"
|
|
10610
|
+
};
|
|
10611
|
+
return /* @__PURE__ */ jsxs34(Fragment20, { children: [
|
|
10612
|
+
/* @__PURE__ */ jsx35(
|
|
10613
|
+
Slot6,
|
|
10614
|
+
{
|
|
10615
|
+
ref: triggerRef,
|
|
10616
|
+
...{ [ariaKey]: disabled && as === "description" ? childProps[ariaKey] : ariaValue },
|
|
10617
|
+
onPointerEnter: (e) => {
|
|
10618
|
+
if (e.pointerType !== "touch") show(delay);
|
|
10619
|
+
},
|
|
10620
|
+
onPointerLeave: (e) => {
|
|
10621
|
+
if (e.pointerType !== "touch") hide(100);
|
|
10622
|
+
},
|
|
10623
|
+
onPointerDown: () => hide(),
|
|
10624
|
+
onFocus: (e) => {
|
|
10625
|
+
if (e.currentTarget.matches(":focus-visible")) show(0);
|
|
10626
|
+
},
|
|
10627
|
+
onBlur: () => hide(),
|
|
10628
|
+
children
|
|
10629
|
+
}
|
|
10630
|
+
),
|
|
10631
|
+
/* @__PURE__ */ jsx35(
|
|
10632
|
+
"div",
|
|
10633
|
+
{
|
|
10634
|
+
ref: tipRef,
|
|
10635
|
+
id: `${id}-tip`,
|
|
10636
|
+
role: "tooltip",
|
|
10637
|
+
popover: supportsPopover() ? "manual" : void 0,
|
|
10638
|
+
hidden: !supportsPopover() && !open ? true : void 0,
|
|
10639
|
+
"data-side": placed.side,
|
|
10640
|
+
"data-state": open ? "open" : "closed",
|
|
10641
|
+
onPointerEnter: () => open && clear(),
|
|
10642
|
+
onPointerLeave: () => hide(100),
|
|
10643
|
+
style: { ...placed.style, ["--ui-tooltip-from"]: fromBySide[placed.side] },
|
|
10644
|
+
className: cn(
|
|
10645
|
+
// Undo the browser's popover box (centered, bordered) and place it ourselves.
|
|
10646
|
+
"fixed inset-auto m-0 overflow-visible border-0 p-0",
|
|
10647
|
+
"z-[70] max-w-[min(18rem,calc(100vw-1rem))] rounded-control bg-ash-900 px-2.5 py-1.5 text-[0.8125rem] font-medium leading-snug text-white",
|
|
10648
|
+
"shadow-[var(--ui-shadow-md)] dark:bg-ash-50 dark:text-ash-950",
|
|
10649
|
+
"data-[state=open]:animate-[ui-tooltip-in_120ms_ease-out] motion-reduce:data-[state=open]:animate-[ui-fade-in_120ms_ease-out]",
|
|
10650
|
+
className
|
|
10651
|
+
),
|
|
10652
|
+
children: content
|
|
10653
|
+
}
|
|
10654
|
+
)
|
|
10655
|
+
] });
|
|
10656
|
+
}
|
|
10657
|
+
|
|
10658
|
+
// src/components/input/OtpInput.tsx
|
|
10659
|
+
import * as React37 from "react";
|
|
10660
|
+
import { jsx as jsx36, jsxs as jsxs35 } from "react/jsx-runtime";
|
|
10661
|
+
var boxSize = {
|
|
10662
|
+
sm: "h-8 w-7 text-sm",
|
|
10663
|
+
md: "h-10 w-9 text-base",
|
|
10664
|
+
lg: "h-12 w-11 text-lg"
|
|
10665
|
+
};
|
|
10666
|
+
var allowPattern = (allow) => allow instanceof RegExp ? allow : allow === "alphanumeric" ? /[0-9a-zA-Z]/ : /[0-9]/;
|
|
10667
|
+
var OtpInput = React37.forwardRef(function OtpInput2({
|
|
10668
|
+
length = 6,
|
|
10669
|
+
value: valueProp,
|
|
10670
|
+
defaultValue = "",
|
|
10671
|
+
onValueChange,
|
|
10672
|
+
onComplete,
|
|
10673
|
+
allow = "digits",
|
|
10674
|
+
groupSize,
|
|
10675
|
+
mask = false,
|
|
10676
|
+
size = "md",
|
|
10677
|
+
invalid: invalidProp,
|
|
10678
|
+
id: idProp,
|
|
10679
|
+
required: requiredProp,
|
|
10680
|
+
disabled: disabledProp,
|
|
10681
|
+
"aria-describedby": describedBy,
|
|
10682
|
+
"aria-label": ariaLabel,
|
|
10683
|
+
autoComplete = "one-time-code",
|
|
10684
|
+
frameClassName,
|
|
10685
|
+
className,
|
|
10686
|
+
onFocus,
|
|
10687
|
+
onBlur,
|
|
10688
|
+
onKeyDown,
|
|
10689
|
+
...props
|
|
10690
|
+
}, ref) {
|
|
10691
|
+
const control = useFieldControlProps({
|
|
10692
|
+
id: idProp,
|
|
10693
|
+
invalid: invalidProp,
|
|
10694
|
+
required: requiredProp,
|
|
10695
|
+
disabled: disabledProp,
|
|
10696
|
+
"aria-describedby": describedBy
|
|
10697
|
+
});
|
|
10698
|
+
const inputRef = React37.useRef(null);
|
|
10699
|
+
const [inner, setInner] = React37.useState(() => defaultValue.slice(0, length));
|
|
10700
|
+
const value = (valueProp ?? inner).slice(0, length);
|
|
10701
|
+
const [focused, setFocused] = React37.useState(false);
|
|
10702
|
+
const pattern = allowPattern(allow);
|
|
10703
|
+
const commit = (next) => {
|
|
10704
|
+
if (valueProp === void 0) setInner(next);
|
|
10705
|
+
onValueChange?.(next);
|
|
10706
|
+
if (next.length === length && value.length !== length) onComplete?.(next);
|
|
10707
|
+
};
|
|
10708
|
+
React37.useEffect(() => {
|
|
10709
|
+
const form = inputRef.current?.form;
|
|
10710
|
+
if (!form) return;
|
|
10711
|
+
const onReset = () => {
|
|
10712
|
+
if (valueProp === void 0) setInner(defaultValue.slice(0, length));
|
|
10713
|
+
onValueChange?.(defaultValue.slice(0, length));
|
|
10714
|
+
};
|
|
10715
|
+
form.addEventListener("reset", onReset);
|
|
10716
|
+
return () => form.removeEventListener("reset", onReset);
|
|
10717
|
+
});
|
|
10718
|
+
const caretToEnd = () => {
|
|
10719
|
+
const el = inputRef.current;
|
|
10720
|
+
if (el) requestAnimationFrame(() => el.setSelectionRange(el.value.length, el.value.length));
|
|
10721
|
+
};
|
|
10722
|
+
const active = focused ? Math.min(value.length, length - 1) : -1;
|
|
10723
|
+
const full = value.length === length;
|
|
10724
|
+
return /* @__PURE__ */ jsxs35(
|
|
10725
|
+
"div",
|
|
10726
|
+
{
|
|
10727
|
+
className: cn("relative inline-flex items-center gap-1.5", control.disabled && "opacity-70", frameClassName),
|
|
10728
|
+
onPointerDown: (e) => {
|
|
10729
|
+
if (e.target !== inputRef.current) {
|
|
10730
|
+
e.preventDefault();
|
|
10731
|
+
inputRef.current?.focus();
|
|
10732
|
+
}
|
|
10733
|
+
},
|
|
10734
|
+
children: [
|
|
10735
|
+
Array.from({ length }, (_, i) => {
|
|
10736
|
+
const char = value[i];
|
|
10737
|
+
const isActive2 = i === active;
|
|
10738
|
+
return /* @__PURE__ */ jsxs35(React37.Fragment, { children: [
|
|
10739
|
+
groupSize && i > 0 && i % groupSize === 0 && /* @__PURE__ */ jsx36("span", { "aria-hidden": "true", className: "mx-0.5 h-px w-2.5 bg-fg-muted/70" }),
|
|
10740
|
+
/* @__PURE__ */ jsx36(
|
|
10741
|
+
"div",
|
|
10742
|
+
{
|
|
10743
|
+
"aria-hidden": "true",
|
|
10744
|
+
"data-active": isActive2 || void 0,
|
|
10745
|
+
"data-filled": char !== void 0 || void 0,
|
|
10746
|
+
className: cn(
|
|
10747
|
+
"relative grid shrink-0 place-items-center rounded-control border border-border-strong bg-surface font-medium tabular-nums text-fg",
|
|
10748
|
+
"shadow-[inset_0_1px_2px_rgb(var(--ui-shadow-color)/0.07)] transition-[border-color,box-shadow] duration-100",
|
|
10749
|
+
"data-[active]:border-ring data-[active]:ring-3 data-[active]:ring-ring/25",
|
|
10750
|
+
control.invalid && "border-danger data-[active]:border-danger data-[active]:ring-danger/25",
|
|
10751
|
+
control.disabled && "cursor-not-allowed bg-secondary-hover",
|
|
10752
|
+
"motion-reduce:transition-none",
|
|
10753
|
+
boxSize[size]
|
|
10754
|
+
),
|
|
10755
|
+
children: char !== void 0 ? mask ? /* @__PURE__ */ jsx36("span", { className: "size-2 rounded-full bg-fg" }) : char : isActive2 && !full ? /* @__PURE__ */ jsx36("span", { className: "h-[1.1em] w-px animate-[ui-caret_1s_steps(1)_infinite] bg-fg motion-reduce:animate-none" }) : null
|
|
10756
|
+
}
|
|
10757
|
+
)
|
|
10758
|
+
] }, i);
|
|
10759
|
+
}),
|
|
10760
|
+
/* @__PURE__ */ jsx36(
|
|
10761
|
+
"input",
|
|
10762
|
+
{
|
|
10763
|
+
ref: (node) => {
|
|
10764
|
+
inputRef.current = node;
|
|
10765
|
+
if (typeof ref === "function") ref(node);
|
|
10766
|
+
else if (ref) ref.current = node;
|
|
10767
|
+
},
|
|
10768
|
+
type: "text",
|
|
10769
|
+
inputMode: allow === "digits" ? "numeric" : "text",
|
|
10770
|
+
autoComplete,
|
|
10771
|
+
autoCapitalize: "off",
|
|
10772
|
+
autoCorrect: "off",
|
|
10773
|
+
spellCheck: false,
|
|
10774
|
+
maxLength: length,
|
|
10775
|
+
value,
|
|
10776
|
+
"aria-label": ariaLabel ?? (control.id ? void 0 : `Verification code, ${length} characters`),
|
|
10777
|
+
id: control.id,
|
|
10778
|
+
required: control.required,
|
|
10779
|
+
disabled: control.disabled,
|
|
10780
|
+
"aria-invalid": control["aria-invalid"],
|
|
10781
|
+
"aria-describedby": control["aria-describedby"],
|
|
10782
|
+
className: cn(
|
|
10783
|
+
// Invisible but real: it takes the taps, typing, pastes and autofill.
|
|
10784
|
+
"absolute inset-0 h-full w-full cursor-text bg-transparent text-transparent caret-transparent outline-none",
|
|
10785
|
+
"selection:bg-transparent [-webkit-text-fill-color:transparent] disabled:cursor-not-allowed",
|
|
10786
|
+
className
|
|
10787
|
+
),
|
|
10788
|
+
onChange: (e) => {
|
|
10789
|
+
const next = Array.from(e.target.value).filter((c) => pattern.test(c)).join("").slice(0, length);
|
|
10790
|
+
commit(allow === "alphanumeric" ? next.toUpperCase() : next);
|
|
10791
|
+
},
|
|
10792
|
+
onFocus: (e) => {
|
|
10793
|
+
setFocused(true);
|
|
10794
|
+
caretToEnd();
|
|
10795
|
+
onFocus?.(e);
|
|
10796
|
+
},
|
|
10797
|
+
onBlur: (e) => {
|
|
10798
|
+
setFocused(false);
|
|
10799
|
+
onBlur?.(e);
|
|
10800
|
+
},
|
|
10801
|
+
onKeyDown: (e) => {
|
|
10802
|
+
onKeyDown?.(e);
|
|
10803
|
+
if (["ArrowLeft", "ArrowRight", "Home", "End", "ArrowUp", "ArrowDown"].includes(e.key)) e.preventDefault();
|
|
10804
|
+
},
|
|
10805
|
+
onSelect: caretToEnd,
|
|
10806
|
+
...props
|
|
10807
|
+
}
|
|
10808
|
+
)
|
|
10809
|
+
]
|
|
10810
|
+
}
|
|
10811
|
+
);
|
|
10812
|
+
});
|
|
10813
|
+
|
|
10814
|
+
// src/components/breadcrumbs/Breadcrumbs.tsx
|
|
10815
|
+
import * as React38 from "react";
|
|
10816
|
+
import { Fragment as Fragment23, jsx as jsx37, jsxs as jsxs36 } from "react/jsx-runtime";
|
|
10817
|
+
var Chevron4 = () => /* @__PURE__ */ jsx37("svg", { viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: 1.6, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", className: "size-3.5 rtl:-scale-x-100", children: /* @__PURE__ */ jsx37("path", { d: "M6 3.5l4.5 4.5L6 12.5" }) });
|
|
10818
|
+
function Breadcrumbs({
|
|
10819
|
+
items,
|
|
10820
|
+
maxItems = 4,
|
|
10821
|
+
separator = "chevron",
|
|
10822
|
+
linkComponent: LinkC = "a",
|
|
10823
|
+
size = "sm",
|
|
10824
|
+
className,
|
|
10825
|
+
"aria-label": ariaLabel = "Breadcrumb",
|
|
10826
|
+
...props
|
|
10827
|
+
}) {
|
|
10828
|
+
const [expanded, setExpanded] = React38.useState(false);
|
|
10829
|
+
const firstHiddenRef = React38.useRef(null);
|
|
10830
|
+
const fold = !expanded && maxItems > 0 && items.length > maxItems;
|
|
10831
|
+
const tail = Math.max(1, maxItems - 2);
|
|
10832
|
+
const hiddenFrom = 1;
|
|
10833
|
+
const hiddenTo = items.length - tail;
|
|
10834
|
+
React38.useEffect(() => {
|
|
10835
|
+
if (expanded) firstHiddenRef.current?.focus();
|
|
10836
|
+
}, [expanded]);
|
|
10837
|
+
const sep = /* @__PURE__ */ jsx37("li", { role: "presentation", "aria-hidden": "true", className: "flex shrink-0 items-center text-fg-muted/70", children: separator === "chevron" ? /* @__PURE__ */ jsx37(Chevron4, {}) : separator === "slash" ? /* @__PURE__ */ jsx37("span", { className: "px-0.5", children: "/" }) : separator });
|
|
10838
|
+
const crumb = (item, i) => {
|
|
10839
|
+
const current = i === items.length - 1;
|
|
10840
|
+
const content = /* @__PURE__ */ jsxs36(Fragment23, { children: [
|
|
10841
|
+
renderIcon(item.icon),
|
|
10842
|
+
/* @__PURE__ */ jsx37("span", { className: "truncate", children: item.label })
|
|
10843
|
+
] });
|
|
10844
|
+
const base = "inline-flex min-w-0 max-w-[16rem] items-center gap-1.5 rounded-control-sm [&_svg]:size-[1.1em] [&_svg]:shrink-0";
|
|
10845
|
+
return /* @__PURE__ */ jsx37("li", { className: "flex min-w-0 items-center", children: current || !item.href ? /* @__PURE__ */ jsx37("span", { "aria-current": current ? "page" : void 0, title: item.label, className: cn(base, current ? "font-medium text-fg" : "text-fg-muted"), children: content }) : /* @__PURE__ */ jsx37(
|
|
10846
|
+
LinkC,
|
|
10847
|
+
{
|
|
10848
|
+
ref: i === hiddenFrom && expanded ? firstHiddenRef : void 0,
|
|
10849
|
+
href: item.href,
|
|
10850
|
+
title: item.label,
|
|
10851
|
+
className: cn(
|
|
10852
|
+
base,
|
|
10853
|
+
"text-fg-muted underline-offset-4 transition-colors hover:text-fg hover:underline",
|
|
10854
|
+
"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring"
|
|
10855
|
+
),
|
|
10856
|
+
children: content
|
|
10857
|
+
}
|
|
10858
|
+
) }, `${item.label}-${i}`);
|
|
10859
|
+
};
|
|
10860
|
+
const shown = [];
|
|
10861
|
+
items.forEach((item, i) => {
|
|
10862
|
+
if (fold && i >= hiddenFrom && i < hiddenTo) {
|
|
10863
|
+
if (i === hiddenFrom) {
|
|
10864
|
+
shown.push(sep);
|
|
10865
|
+
shown.push(
|
|
10866
|
+
/* @__PURE__ */ jsx37("li", { className: "flex items-center", children: /* @__PURE__ */ jsx37(
|
|
10867
|
+
"button",
|
|
10868
|
+
{
|
|
10869
|
+
type: "button",
|
|
10870
|
+
onClick: () => setExpanded(true),
|
|
10871
|
+
"aria-label": `Show ${hiddenTo - hiddenFrom} more`,
|
|
10872
|
+
className: "grid h-6 min-w-6 cursor-pointer place-items-center rounded-control-sm px-1 text-fg-muted hover:bg-secondary-hover hover:text-fg focus-visible:outline-2 focus-visible:outline-ring",
|
|
10873
|
+
children: "\u2026"
|
|
10874
|
+
}
|
|
10875
|
+
) }, "more")
|
|
10876
|
+
);
|
|
10877
|
+
}
|
|
10878
|
+
return;
|
|
10879
|
+
}
|
|
10880
|
+
if (i > 0) shown.push(sep);
|
|
10881
|
+
shown.push(crumb(item, i));
|
|
10882
|
+
});
|
|
10883
|
+
return /* @__PURE__ */ jsx37("nav", { "aria-label": ariaLabel, className: cn("min-w-0", className), ...props, children: /* @__PURE__ */ jsx37(
|
|
10884
|
+
"ol",
|
|
10885
|
+
{
|
|
10886
|
+
className: cn(
|
|
10887
|
+
"flex min-w-0 flex-wrap items-center gap-x-1.5 gap-y-1",
|
|
10888
|
+
size === "sm" ? "text-sm" : "text-[0.9375rem]"
|
|
10889
|
+
),
|
|
10890
|
+
children: shown.map((node, i) => /* @__PURE__ */ jsx37(React38.Fragment, { children: node }, i))
|
|
10891
|
+
}
|
|
10892
|
+
) });
|
|
10893
|
+
}
|
|
10894
|
+
|
|
10895
|
+
// src/components/navigation/NavigationMenu.tsx
|
|
10896
|
+
import * as React39 from "react";
|
|
10897
|
+
import { jsx as jsx38, jsxs as jsxs37 } from "react/jsx-runtime";
|
|
10898
|
+
var keyOf2 = (item, i) => item.id ?? `${item.label}-${i}`;
|
|
10899
|
+
function matches2(href, active, nested) {
|
|
10900
|
+
if (!href || !active) return false;
|
|
10901
|
+
if (href === active) return true;
|
|
10902
|
+
return nested && href !== "/" && active.startsWith(href.endsWith("/") ? href : `${href}/`);
|
|
10903
|
+
}
|
|
10904
|
+
function bestMatch(items, active, nested) {
|
|
10905
|
+
let found;
|
|
10906
|
+
const walk = (list) => list.forEach((it) => {
|
|
10907
|
+
if (matches2(it.href, active, nested) && (!found || (it.href?.length ?? 0) > found.length)) found = it.href;
|
|
10908
|
+
if (it.children) walk(it.children);
|
|
10909
|
+
});
|
|
10910
|
+
walk(items);
|
|
10911
|
+
return found;
|
|
10912
|
+
}
|
|
10913
|
+
var ExternalIcon = () => /* @__PURE__ */ jsx38("svg", { viewBox: "0 0 12 12", fill: "none", stroke: "currentColor", strokeWidth: 1.4, strokeLinecap: "round", "aria-hidden": "true", className: "size-3 shrink-0 opacity-60", children: /* @__PURE__ */ jsx38("path", { d: "M4 2.5h5.5V8M9.5 2.5L3 9" }) });
|
|
10914
|
+
var Chevron5 = ({ open, vertical }) => /* @__PURE__ */ jsx38(
|
|
10915
|
+
"svg",
|
|
10916
|
+
{
|
|
10917
|
+
viewBox: "0 0 16 16",
|
|
10918
|
+
fill: "none",
|
|
10919
|
+
stroke: "currentColor",
|
|
10920
|
+
strokeWidth: 1.7,
|
|
10921
|
+
strokeLinecap: "round",
|
|
10922
|
+
strokeLinejoin: "round",
|
|
10923
|
+
"aria-hidden": "true",
|
|
10924
|
+
className: cn(
|
|
10925
|
+
"size-3.5 shrink-0 opacity-70 transition-transform duration-200 motion-reduce:transition-none",
|
|
10926
|
+
open && (vertical ? "rotate-90" : "rotate-180"),
|
|
10927
|
+
vertical && !open && "rtl:-scale-x-100"
|
|
10928
|
+
),
|
|
10929
|
+
children: /* @__PURE__ */ jsx38("path", { d: vertical ? "M6 3.5l4.5 4.5L6 12.5" : "M3.5 6l4.5 4.5L12.5 6" })
|
|
10930
|
+
}
|
|
10931
|
+
);
|
|
10932
|
+
function ItemLink2({
|
|
10933
|
+
item,
|
|
10934
|
+
ctx,
|
|
10935
|
+
className,
|
|
10936
|
+
children,
|
|
10937
|
+
...rest
|
|
10938
|
+
}) {
|
|
10939
|
+
const Comp = item.external ? "a" : ctx.linkComponent;
|
|
10940
|
+
const active = !!item.href && item.href === ctx.best;
|
|
10941
|
+
return /* @__PURE__ */ jsx38(
|
|
10942
|
+
Comp,
|
|
10943
|
+
{
|
|
10944
|
+
href: item.href,
|
|
10945
|
+
"aria-current": active ? "page" : void 0,
|
|
10946
|
+
"data-active": active || void 0,
|
|
10947
|
+
...item.external ? { target: "_blank", rel: "noopener noreferrer" } : {},
|
|
10948
|
+
className,
|
|
10949
|
+
onClick: () => {
|
|
10950
|
+
ctx.close();
|
|
10951
|
+
if (item.href) ctx.onNavigate?.(item.href);
|
|
10952
|
+
},
|
|
10953
|
+
...rest,
|
|
10954
|
+
children
|
|
10955
|
+
}
|
|
10956
|
+
);
|
|
10957
|
+
}
|
|
10958
|
+
var focusRing = "outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring";
|
|
10959
|
+
function NavigationMenu({
|
|
10960
|
+
items,
|
|
10961
|
+
activeHref,
|
|
10962
|
+
matchNested = true,
|
|
10963
|
+
orientation = "horizontal",
|
|
10964
|
+
indicator = "pill",
|
|
10965
|
+
linkComponent = "a",
|
|
10966
|
+
onNavigate,
|
|
10967
|
+
"aria-label": ariaLabel = "Main",
|
|
10968
|
+
className
|
|
10969
|
+
}) {
|
|
10970
|
+
const vertical = orientation === "vertical";
|
|
10971
|
+
const best = React39.useMemo(() => bestMatch(items, activeHref, matchNested), [items, activeHref, matchNested]);
|
|
10972
|
+
const [openKey, setOpenKey] = React39.useState(null);
|
|
10973
|
+
const navRef = React39.useRef(null);
|
|
10974
|
+
const hoverTimer = React39.useRef(0);
|
|
10975
|
+
const focusFirstLink = React39.useRef(false);
|
|
10976
|
+
const id = React39.useId();
|
|
10977
|
+
const ctx = { linkComponent, best, onNavigate, close: () => !vertical && setOpenKey(null) };
|
|
10978
|
+
React39.useEffect(() => {
|
|
10979
|
+
if (vertical || openKey === null) return;
|
|
10980
|
+
const onDown = (e) => {
|
|
10981
|
+
if (!navRef.current?.contains(e.target)) setOpenKey(null);
|
|
10982
|
+
};
|
|
10983
|
+
document.addEventListener("pointerdown", onDown);
|
|
10984
|
+
return () => document.removeEventListener("pointerdown", onDown);
|
|
10985
|
+
}, [vertical, openKey]);
|
|
10986
|
+
React39.useEffect(() => () => window.clearTimeout(hoverTimer.current), []);
|
|
10987
|
+
React39.useEffect(() => {
|
|
10988
|
+
if (!focusFirstLink.current || openKey === null) return;
|
|
10989
|
+
focusFirstLink.current = false;
|
|
10990
|
+
document.getElementById(`${id}-${openKey}`)?.querySelector("a,button")?.focus();
|
|
10991
|
+
}, [openKey, id]);
|
|
10992
|
+
React39.useLayoutEffect(() => {
|
|
10993
|
+
if (vertical || openKey === null) return;
|
|
10994
|
+
const panel = document.getElementById(`${id}-${openKey}`);
|
|
10995
|
+
if (!panel) return;
|
|
10996
|
+
const fit = () => {
|
|
10997
|
+
panel.style.translate = "";
|
|
10998
|
+
const r = panel.getBoundingClientRect();
|
|
10999
|
+
const vw = document.documentElement.clientWidth;
|
|
11000
|
+
let shift = 0;
|
|
11001
|
+
if (r.right > vw - 8) shift = vw - 8 - r.right;
|
|
11002
|
+
if (r.left + shift < 8) shift = 8 - r.left;
|
|
11003
|
+
if (shift) panel.style.translate = `${shift}px 0`;
|
|
11004
|
+
};
|
|
11005
|
+
fit();
|
|
11006
|
+
window.addEventListener("resize", fit);
|
|
11007
|
+
return () => window.removeEventListener("resize", fit);
|
|
11008
|
+
}, [vertical, openKey, id]);
|
|
11009
|
+
const triggers = () => Array.from(navRef.current?.querySelectorAll("[data-nav-top]") ?? []);
|
|
11010
|
+
const onTopKeyDown = (e, key, hasPanel) => {
|
|
11011
|
+
if (vertical) return;
|
|
11012
|
+
const list = triggers();
|
|
11013
|
+
const i = list.indexOf(e.currentTarget);
|
|
11014
|
+
const rtl = getComputedStyle(e.currentTarget).direction === "rtl";
|
|
11015
|
+
const next = rtl ? "ArrowLeft" : "ArrowRight";
|
|
11016
|
+
const prev = rtl ? "ArrowRight" : "ArrowLeft";
|
|
11017
|
+
if (e.key === "Escape" && openKey !== null) {
|
|
11018
|
+
e.preventDefault();
|
|
11019
|
+
setOpenKey(null);
|
|
11020
|
+
} else if (e.key === next || e.key === prev) {
|
|
11021
|
+
e.preventDefault();
|
|
11022
|
+
const target = list[(i + (e.key === next ? 1 : -1) + list.length) % list.length];
|
|
11023
|
+
target?.focus();
|
|
11024
|
+
if (openKey !== null) setOpenKey(null);
|
|
11025
|
+
} else if (e.key === "Home" || e.key === "End") {
|
|
11026
|
+
e.preventDefault();
|
|
11027
|
+
list[e.key === "Home" ? 0 : list.length - 1]?.focus();
|
|
11028
|
+
} else if (e.key === "ArrowDown" && hasPanel) {
|
|
11029
|
+
e.preventDefault();
|
|
11030
|
+
if (openKey === key) {
|
|
11031
|
+
document.getElementById(`${id}-${key}`)?.querySelector("a,button")?.focus();
|
|
11032
|
+
} else {
|
|
11033
|
+
focusFirstLink.current = true;
|
|
11034
|
+
setOpenKey(key);
|
|
11035
|
+
}
|
|
11036
|
+
}
|
|
11037
|
+
};
|
|
11038
|
+
const onPanelKeyDown = (e, key) => {
|
|
11039
|
+
const links = Array.from(e.currentTarget.querySelectorAll("a,button"));
|
|
11040
|
+
const i = links.indexOf(document.activeElement);
|
|
11041
|
+
if (e.key === "Escape") {
|
|
11042
|
+
e.preventDefault();
|
|
11043
|
+
e.stopPropagation();
|
|
11044
|
+
setOpenKey(null);
|
|
11045
|
+
navRef.current?.querySelector(`[data-nav-key="${CSS.escape(key)}"]`)?.focus();
|
|
11046
|
+
} else if (e.key === "ArrowDown" || e.key === "ArrowUp") {
|
|
11047
|
+
e.preventDefault();
|
|
11048
|
+
links[(i + (e.key === "ArrowDown" ? 1 : -1) + links.length) % links.length]?.focus();
|
|
11049
|
+
}
|
|
11050
|
+
};
|
|
11051
|
+
const hoverOpen = (key, e) => {
|
|
11052
|
+
if (vertical || e.pointerType !== "mouse") return;
|
|
11053
|
+
window.clearTimeout(hoverTimer.current);
|
|
11054
|
+
hoverTimer.current = window.setTimeout(() => setOpenKey(key), key === null ? 220 : openKey === null ? 120 : 0);
|
|
11055
|
+
};
|
|
11056
|
+
const topItemClass = cn(
|
|
11057
|
+
"relative inline-flex h-9 shrink-0 cursor-pointer items-center gap-2 whitespace-nowrap rounded-control px-3 text-sm font-medium text-fg-muted transition-colors duration-100",
|
|
11058
|
+
"hover:text-fg [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0",
|
|
11059
|
+
focusRing,
|
|
11060
|
+
indicator === "pill" ? "hover:bg-secondary-hover data-[active]:bg-secondary-hover data-[active]:text-fg" : "data-[active]:text-fg"
|
|
11061
|
+
);
|
|
11062
|
+
const underline = (on) => indicator === "underline" && on ? /* @__PURE__ */ jsx38("span", { "aria-hidden": "true", className: "pointer-events-none absolute inset-x-3 bottom-0 h-0.5 rounded-full bg-fg" }) : null;
|
|
11063
|
+
const rowClass = cn(
|
|
11064
|
+
"flex min-h-9 w-full min-w-0 cursor-pointer items-center gap-2.5 rounded-control px-3 py-1.5 text-start text-sm text-fg-muted transition-colors duration-100",
|
|
11065
|
+
"hover:bg-secondary-hover hover:text-fg data-[active]:bg-[color:color-mix(in_srgb,var(--color-primary)_10%,transparent)] data-[active]:font-medium data-[active]:text-primary",
|
|
11066
|
+
"[&_svg:not([class*='size-'])]:size-[1.125rem] [&_svg]:shrink-0",
|
|
11067
|
+
focusRing
|
|
11068
|
+
);
|
|
11069
|
+
const panelLink = (child, j) => vertical ? (
|
|
11070
|
+
// In a list, sub-links are quieter rows under their section, like SidebarNav.
|
|
11071
|
+
/* @__PURE__ */ jsx38("li", { children: /* @__PURE__ */ jsxs37(ItemLink2, { item: child, ctx, className: cn(rowClass, "min-h-8 py-1"), children: [
|
|
11072
|
+
renderIcon(child.icon),
|
|
11073
|
+
/* @__PURE__ */ jsx38("span", { className: "min-w-0 flex-1 truncate", children: child.label }),
|
|
11074
|
+
child.badge,
|
|
11075
|
+
child.external && /* @__PURE__ */ jsx38(ExternalIcon, {})
|
|
11076
|
+
] }) }, keyOf2(child, j))
|
|
11077
|
+
) : /* @__PURE__ */ jsx38("li", { children: /* @__PURE__ */ jsxs37(
|
|
11078
|
+
ItemLink2,
|
|
11079
|
+
{
|
|
11080
|
+
item: child,
|
|
11081
|
+
ctx,
|
|
11082
|
+
className: cn(
|
|
11083
|
+
"group/link flex min-w-0 gap-3 rounded-control p-2.5 text-sm transition-colors duration-100 hover:bg-secondary-hover",
|
|
11084
|
+
"data-[active]:bg-[color:color-mix(in_srgb,var(--color-primary)_10%,transparent)]",
|
|
11085
|
+
"[&_svg]:mt-0.5 [&_svg]:size-[1.125rem] [&_svg]:shrink-0 [&_svg]:text-fg-muted data-[active]:[&_svg]:text-primary",
|
|
11086
|
+
focusRing
|
|
11087
|
+
),
|
|
11088
|
+
children: [
|
|
11089
|
+
renderIcon(child.icon),
|
|
11090
|
+
/* @__PURE__ */ jsxs37("span", { className: "grid min-w-0 gap-0.5", children: [
|
|
11091
|
+
/* @__PURE__ */ jsxs37("span", { className: "flex items-center gap-1.5 font-medium text-fg group-data-[active]/link:text-primary", children: [
|
|
11092
|
+
/* @__PURE__ */ jsx38("span", { className: "truncate", children: child.label }),
|
|
11093
|
+
child.badge,
|
|
11094
|
+
child.external && /* @__PURE__ */ jsx38(ExternalIcon, {})
|
|
11095
|
+
] }),
|
|
11096
|
+
child.description && /* @__PURE__ */ jsx38("span", { className: "text-[0.8125rem] leading-snug text-fg-muted", children: child.description })
|
|
11097
|
+
] })
|
|
11098
|
+
]
|
|
11099
|
+
}
|
|
11100
|
+
) }, keyOf2(child, j));
|
|
11101
|
+
return /* @__PURE__ */ jsx38(
|
|
11102
|
+
"nav",
|
|
11103
|
+
{
|
|
11104
|
+
ref: navRef,
|
|
11105
|
+
"aria-label": ariaLabel,
|
|
11106
|
+
className: cn(vertical ? "grid" : "flex items-stretch", className),
|
|
11107
|
+
onBlur: (e) => {
|
|
11108
|
+
if (!vertical && !e.currentTarget.contains(e.relatedTarget)) setOpenKey(null);
|
|
11109
|
+
},
|
|
11110
|
+
children: /* @__PURE__ */ jsx38("ul", { role: "list", className: cn(vertical ? "grid gap-0.5" : "flex items-stretch gap-1"), children: items.map((item, i) => {
|
|
11111
|
+
const key = keyOf2(item, i);
|
|
11112
|
+
const hasPanel = !!item.children?.length;
|
|
11113
|
+
const holdsActive = hasPanel && item.children.some((c) => !!c.href && c.href === best);
|
|
11114
|
+
const active = !!item.href && item.href === best;
|
|
11115
|
+
if (vertical) {
|
|
11116
|
+
return /* @__PURE__ */ jsx38(VerticalItem, { item, ctx, rowClass, holdsActive, panelLink }, key);
|
|
11117
|
+
}
|
|
11118
|
+
const open = openKey === key;
|
|
11119
|
+
return /* @__PURE__ */ jsxs37(
|
|
11120
|
+
"li",
|
|
11121
|
+
{
|
|
11122
|
+
className: "relative flex items-center",
|
|
11123
|
+
onPointerEnter: (e) => hasPanel && hoverOpen(key, e),
|
|
11124
|
+
onPointerLeave: (e) => hasPanel && hoverOpen(null, e),
|
|
11125
|
+
children: [
|
|
11126
|
+
hasPanel ? /* @__PURE__ */ jsxs37(
|
|
11127
|
+
"button",
|
|
11128
|
+
{
|
|
11129
|
+
type: "button",
|
|
11130
|
+
"data-nav-top": "",
|
|
11131
|
+
"data-nav-key": key,
|
|
11132
|
+
"data-active": holdsActive || void 0,
|
|
11133
|
+
"aria-expanded": open,
|
|
11134
|
+
"aria-controls": `${id}-${key}`,
|
|
11135
|
+
onClick: () => setOpenKey(open ? null : key),
|
|
11136
|
+
onKeyDown: (e) => onTopKeyDown(e, key, true),
|
|
11137
|
+
className: topItemClass,
|
|
11138
|
+
children: [
|
|
11139
|
+
renderIcon(item.icon),
|
|
11140
|
+
item.label,
|
|
11141
|
+
item.badge,
|
|
11142
|
+
/* @__PURE__ */ jsx38(Chevron5, { open, vertical: false })
|
|
11143
|
+
]
|
|
11144
|
+
}
|
|
11145
|
+
) : /* @__PURE__ */ jsxs37(ItemLink2, { item, ctx, "data-nav-top": "", onKeyDown: (e) => onTopKeyDown(e, key, false), className: topItemClass, children: [
|
|
11146
|
+
renderIcon(item.icon),
|
|
11147
|
+
item.label,
|
|
11148
|
+
item.badge,
|
|
11149
|
+
item.external && /* @__PURE__ */ jsx38(ExternalIcon, {})
|
|
11150
|
+
] }),
|
|
11151
|
+
underline(active || holdsActive),
|
|
11152
|
+
hasPanel && /* @__PURE__ */ jsx38(
|
|
11153
|
+
"div",
|
|
11154
|
+
{
|
|
11155
|
+
id: `${id}-${key}`,
|
|
11156
|
+
hidden: !open,
|
|
11157
|
+
onKeyDown: (e) => onPanelKeyDown(e, key),
|
|
11158
|
+
className: cn(
|
|
11159
|
+
"absolute left-0 top-full z-50 pt-2 rtl:left-auto rtl:right-0",
|
|
11160
|
+
"animate-[ui-menu-down_140ms_cubic-bezier(0.2,0.9,0.3,1)] motion-reduce:animate-none"
|
|
11161
|
+
),
|
|
11162
|
+
children: /* @__PURE__ */ jsx38(
|
|
11163
|
+
"ul",
|
|
11164
|
+
{
|
|
11165
|
+
role: "list",
|
|
11166
|
+
className: cn(
|
|
11167
|
+
"grid w-max max-w-[min(36rem,calc(100vw-2rem))] gap-0.5 rounded-control-lg border border-border bg-surface p-1.5 shadow-[var(--ui-shadow-lg)]",
|
|
11168
|
+
item.children.length > 4 && item.children.some((c) => c.description) ? "sm:grid-cols-2" : "min-w-56"
|
|
11169
|
+
),
|
|
11170
|
+
children: item.children.map(panelLink)
|
|
11171
|
+
}
|
|
11172
|
+
)
|
|
11173
|
+
}
|
|
11174
|
+
)
|
|
11175
|
+
]
|
|
11176
|
+
},
|
|
11177
|
+
key
|
|
11178
|
+
);
|
|
11179
|
+
}) })
|
|
11180
|
+
}
|
|
11181
|
+
);
|
|
11182
|
+
}
|
|
11183
|
+
function VerticalItem({
|
|
11184
|
+
item,
|
|
11185
|
+
ctx,
|
|
11186
|
+
rowClass,
|
|
11187
|
+
holdsActive,
|
|
11188
|
+
panelLink
|
|
11189
|
+
}) {
|
|
11190
|
+
const [open, setOpen] = React39.useState(holdsActive);
|
|
11191
|
+
const subId = React39.useId();
|
|
11192
|
+
const subRef = React39.useRef(null);
|
|
11193
|
+
React39.useEffect(() => {
|
|
11194
|
+
if (holdsActive) setOpen(true);
|
|
11195
|
+
}, [holdsActive]);
|
|
11196
|
+
React39.useEffect(() => {
|
|
11197
|
+
if (!subRef.current) return;
|
|
11198
|
+
if (open) subRef.current.removeAttribute("inert");
|
|
11199
|
+
else subRef.current.setAttribute("inert", "");
|
|
11200
|
+
}, [open]);
|
|
11201
|
+
if (!item.children?.length) {
|
|
11202
|
+
return /* @__PURE__ */ jsx38("li", { children: /* @__PURE__ */ jsxs37(ItemLink2, { item, ctx, className: rowClass, children: [
|
|
11203
|
+
renderIcon(item.icon),
|
|
11204
|
+
/* @__PURE__ */ jsx38("span", { className: "min-w-0 flex-1 truncate", children: item.label }),
|
|
11205
|
+
item.badge,
|
|
11206
|
+
item.external && /* @__PURE__ */ jsx38(ExternalIcon, {})
|
|
11207
|
+
] }) });
|
|
11208
|
+
}
|
|
11209
|
+
return /* @__PURE__ */ jsxs37("li", { className: "grid", children: [
|
|
11210
|
+
/* @__PURE__ */ jsxs37(
|
|
11211
|
+
"button",
|
|
11212
|
+
{
|
|
11213
|
+
type: "button",
|
|
11214
|
+
"aria-expanded": open,
|
|
11215
|
+
"aria-controls": subId,
|
|
11216
|
+
onClick: () => setOpen((o) => !o),
|
|
11217
|
+
className: cn(rowClass, holdsActive && !open && "text-fg"),
|
|
11218
|
+
children: [
|
|
11219
|
+
renderIcon(item.icon),
|
|
11220
|
+
/* @__PURE__ */ jsx38("span", { className: "min-w-0 flex-1 truncate", children: item.label }),
|
|
11221
|
+
item.badge,
|
|
11222
|
+
/* @__PURE__ */ jsx38(Chevron5, { open, vertical: true })
|
|
11223
|
+
]
|
|
11224
|
+
}
|
|
11225
|
+
),
|
|
11226
|
+
/* @__PURE__ */ jsx38(
|
|
11227
|
+
"div",
|
|
11228
|
+
{
|
|
11229
|
+
ref: subRef,
|
|
11230
|
+
id: subId,
|
|
11231
|
+
className: cn(
|
|
11232
|
+
"grid transition-[grid-template-rows,opacity] duration-200 ease-out motion-reduce:transition-none",
|
|
11233
|
+
open ? "grid-rows-[1fr] opacity-100" : "grid-rows-[0fr] opacity-0"
|
|
11234
|
+
),
|
|
11235
|
+
children: /* @__PURE__ */ jsx38("div", { className: "min-h-0 overflow-hidden", children: /* @__PURE__ */ jsx38("ul", { role: "list", className: "ms-4 grid gap-0.5 border-s border-border py-1 ps-2", children: item.children.map(panelLink) }) })
|
|
11236
|
+
}
|
|
11237
|
+
)
|
|
11238
|
+
] });
|
|
11239
|
+
}
|
|
11240
|
+
|
|
11241
|
+
// src/components/placeholder/Placeholder.tsx
|
|
11242
|
+
import * as React40 from "react";
|
|
11243
|
+
import { jsx as jsx39, jsxs as jsxs38 } from "react/jsx-runtime";
|
|
11244
|
+
var patterns = {
|
|
11245
|
+
stripes: {
|
|
11246
|
+
backgroundImage: "repeating-linear-gradient(-45deg, color-mix(in srgb, currentColor 14%, transparent) 0 1px, transparent 1px 9px)"
|
|
11247
|
+
},
|
|
11248
|
+
dots: {
|
|
11249
|
+
backgroundImage: "radial-gradient(color-mix(in srgb, currentColor 22%, transparent) 1px, transparent 1.5px)",
|
|
11250
|
+
backgroundSize: "12px 12px"
|
|
11251
|
+
},
|
|
11252
|
+
grid: {
|
|
11253
|
+
backgroundImage: "linear-gradient(color-mix(in srgb, currentColor 11%, transparent) 1px, transparent 1px), linear-gradient(90deg, color-mix(in srgb, currentColor 11%, transparent) 1px, transparent 1px)",
|
|
11254
|
+
backgroundSize: "16px 16px",
|
|
11255
|
+
backgroundPosition: "-1px -1px"
|
|
11256
|
+
},
|
|
11257
|
+
none: {}
|
|
11258
|
+
};
|
|
11259
|
+
var Placeholder = React40.forwardRef(function Placeholder2({ pattern = "stripes", label, ratio, border = "dashed", className, style, children, ...props }, ref) {
|
|
11260
|
+
return /* @__PURE__ */ jsxs38(
|
|
11261
|
+
"div",
|
|
11262
|
+
{
|
|
11263
|
+
ref,
|
|
11264
|
+
"aria-hidden": label || children ? void 0 : true,
|
|
11265
|
+
className: cn(
|
|
11266
|
+
"relative grid min-h-24 place-items-center overflow-hidden rounded-card text-fg-muted",
|
|
11267
|
+
border === "dashed" && "border border-dashed border-border-strong",
|
|
11268
|
+
border === "solid" && "border border-border",
|
|
11269
|
+
className
|
|
11270
|
+
),
|
|
11271
|
+
style: { ...patterns[pattern], aspectRatio: ratio, ...style },
|
|
11272
|
+
...props,
|
|
11273
|
+
children: [
|
|
11274
|
+
label && /* @__PURE__ */ jsx39("span", { className: "rounded-control bg-surface px-2.5 py-1 text-[0.8125rem] font-medium text-fg-muted shadow-[0_0_0_6px_var(--color-surface)]", children: label }),
|
|
11275
|
+
children
|
|
11276
|
+
]
|
|
11277
|
+
}
|
|
11278
|
+
);
|
|
11279
|
+
});
|
|
10412
11280
|
export {
|
|
10413
11281
|
Accordion,
|
|
10414
11282
|
AccordionItem,
|
|
@@ -10426,6 +11294,7 @@ export {
|
|
|
10426
11294
|
BarChart,
|
|
10427
11295
|
BarList,
|
|
10428
11296
|
Blockquote,
|
|
11297
|
+
Breadcrumbs,
|
|
10429
11298
|
Button,
|
|
10430
11299
|
CalendarHeatmap,
|
|
10431
11300
|
Card,
|
|
@@ -10450,6 +11319,15 @@ export {
|
|
|
10450
11319
|
DataTable,
|
|
10451
11320
|
Divider,
|
|
10452
11321
|
DonutChart,
|
|
11322
|
+
Drawer,
|
|
11323
|
+
DrawerBody,
|
|
11324
|
+
DrawerClose,
|
|
11325
|
+
DrawerContent,
|
|
11326
|
+
DrawerDescription,
|
|
11327
|
+
DrawerFooter,
|
|
11328
|
+
DrawerHeader,
|
|
11329
|
+
DrawerTitle,
|
|
11330
|
+
DrawerTrigger,
|
|
10453
11331
|
DropdownMenu,
|
|
10454
11332
|
DropdownMenuCheckboxItem,
|
|
10455
11333
|
DropdownMenuContent,
|
|
@@ -10499,10 +11377,13 @@ export {
|
|
|
10499
11377
|
ModalHeader,
|
|
10500
11378
|
ModalTitle,
|
|
10501
11379
|
ModalTrigger,
|
|
11380
|
+
NavigationMenu,
|
|
11381
|
+
OtpInput,
|
|
10502
11382
|
Pagination,
|
|
10503
11383
|
Pill,
|
|
10504
11384
|
PillGroup,
|
|
10505
11385
|
PillOption,
|
|
11386
|
+
Placeholder,
|
|
10506
11387
|
ProgressBar,
|
|
10507
11388
|
ProgressRing,
|
|
10508
11389
|
Prose,
|
|
@@ -10551,6 +11432,7 @@ export {
|
|
|
10551
11432
|
TimelineGroup,
|
|
10552
11433
|
TimelineItem,
|
|
10553
11434
|
Toaster,
|
|
11435
|
+
Tooltip2 as Tooltip,
|
|
10554
11436
|
WidgetCard,
|
|
10555
11437
|
buttonVariants,
|
|
10556
11438
|
cardVariants,
|
|
@@ -10572,6 +11454,7 @@ export {
|
|
|
10572
11454
|
toast,
|
|
10573
11455
|
useAccordion,
|
|
10574
11456
|
useDelayedLoading,
|
|
11457
|
+
useDrawer,
|
|
10575
11458
|
useField,
|
|
10576
11459
|
useFileUploads,
|
|
10577
11460
|
useModal,
|