@stll/ui 0.0.1-placeholder.0 → 0.3.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 +84 -0
- package/dist/components/accordion.d.ts +8 -0
- package/dist/components/accordion.js +38 -0
- package/dist/components/alert-dialog.d.ts +32 -0
- package/dist/components/alert-dialog.js +75 -0
- package/dist/components/avatar.d.ts +7 -0
- package/dist/components/avatar.js +22 -0
- package/dist/components/bidi-text.d.ts +24 -0
- package/dist/components/bidi-text.js +38 -0
- package/dist/components/brand-icons.d.ts +7 -0
- package/dist/components/brand-icons.js +18 -0
- package/dist/components/breadcrumb.d.ts +12 -0
- package/dist/components/breadcrumb.js +61 -0
- package/dist/components/button-variants.d.ts +17 -0
- package/dist/components/button-variants.js +43 -0
- package/dist/components/button.d.ts +20 -0
- package/dist/components/button.js +53 -0
- package/dist/components/checkbox.d.ts +5 -0
- package/dist/components/checkbox.js +44 -0
- package/dist/components/color-picker.d.ts +47 -0
- package/dist/components/color-picker.js +249 -0
- package/dist/components/combobox.d.ts +40 -0
- package/dist/components/combobox.js +208 -0
- package/dist/components/command.d.ts +34 -0
- package/dist/components/command.js +100 -0
- package/dist/components/date-picker-popover.d.ts +29 -0
- package/dist/components/date-picker-popover.js +548 -0
- package/dist/components/date-picker-popover.logic.d.ts +14 -0
- package/dist/components/date-picker-popover.logic.js +26 -0
- package/dist/components/destructive-action-confirmation.d.ts +21 -0
- package/dist/components/destructive-action-confirmation.js +60 -0
- package/dist/components/destructive-confirm-dialog.d.ts +17 -0
- package/dist/components/destructive-confirm-dialog.js +49 -0
- package/dist/components/dialog.d.ts +37 -0
- package/dist/components/dialog.js +92 -0
- package/dist/components/directional-icon.d.ts +30 -0
- package/dist/components/directional-icon.js +12 -0
- package/dist/components/field.d.ts +11 -0
- package/dist/components/field.js +34 -0
- package/dist/components/form.d.ts +5 -0
- package/dist/components/form.js +12 -0
- package/dist/components/frame.d.ts +10 -0
- package/dist/components/frame.js +35 -0
- package/dist/components/hex-color-picker.d.ts +11 -0
- package/dist/components/hex-color-picker.js +366 -0
- package/dist/components/input-group.d.ts +15 -0
- package/dist/components/input-group.js +53 -0
- package/dist/components/input-otp.d.ts +13 -0
- package/dist/components/input-otp.js +39 -0
- package/dist/components/input.d.ts +12 -0
- package/dist/components/input.js +46 -0
- package/dist/components/label.d.ts +5 -0
- package/dist/components/label.js +18 -0
- package/dist/components/menu.d.ts +44 -0
- package/dist/components/menu.js +152 -0
- package/dist/components/outline-rail.d.ts +43 -0
- package/dist/components/outline-rail.js +416 -0
- package/dist/components/pagination.d.ts +17 -0
- package/dist/components/pagination.js +78 -0
- package/dist/components/popover.d.ts +34 -0
- package/dist/components/popover.js +67 -0
- package/dist/components/preview-card.d.ts +12 -0
- package/dist/components/preview-card.js +25 -0
- package/dist/components/preview-pane.d.ts +23 -0
- package/dist/components/preview-pane.js +33 -0
- package/dist/components/scroll-area.d.ts +19 -0
- package/dist/components/scroll-area.js +38 -0
- package/dist/components/scroll-to-top.d.ts +12 -0
- package/dist/components/scroll-to-top.js +43 -0
- package/dist/components/secret-input.d.ts +9 -0
- package/dist/components/secret-input.js +38 -0
- package/dist/components/segmented-icon-toggle.d.ts +22 -0
- package/dist/components/segmented-icon-toggle.js +30 -0
- package/dist/components/select.d.ts +25 -0
- package/dist/components/select.js +196 -0
- package/dist/components/separator.d.ts +16 -0
- package/dist/components/separator.js +26 -0
- package/dist/components/sheet.d.ts +29 -0
- package/dist/components/sheet.js +87 -0
- package/dist/components/skeleton.d.ts +4 -0
- package/dist/components/skeleton.js +10 -0
- package/dist/components/stella-mark.d.ts +5 -0
- package/dist/components/stella-mark.js +12 -0
- package/dist/components/stella-wordmark.d.ts +5 -0
- package/dist/components/stella-wordmark.js +21 -0
- package/dist/components/table.d.ts +20 -0
- package/dist/components/table.js +80 -0
- package/dist/components/tabs.d.ts +11 -0
- package/dist/components/tabs.js +46 -0
- package/dist/components/textarea.d.ts +9 -0
- package/dist/components/textarea.js +31 -0
- package/dist/components/toast.d.ts +60 -0
- package/dist/components/toast.js +261 -0
- package/dist/components/tooltip-trigger-helper.d.ts +11 -0
- package/dist/components/tooltip-trigger-helper.js +14 -0
- package/dist/components/tooltip.d.ts +15 -0
- package/dist/components/tooltip.js +32 -0
- package/dist/data-table/index.d.ts +2 -0
- package/dist/data-table/index.js +2 -0
- package/dist/data-table/schema.d.ts +70 -0
- package/dist/data-table/schema.js +40 -0
- package/dist/hooks/use-contained-handler.d.ts +55 -0
- package/dist/hooks/use-contained-handler.js +54 -0
- package/dist/hooks/use-content-dir.d.ts +48 -0
- package/dist/hooks/use-content-dir.js +45 -0
- package/dist/hooks/use-mobile.d.ts +4 -0
- package/dist/hooks/use-mobile.js +16 -0
- package/dist/hooks/use-viewport-width.d.ts +5 -0
- package/dist/hooks/use-viewport-width.js +14 -0
- package/dist/index.d.ts +67 -0
- package/dist/index.js +65 -0
- package/dist/inspector/chrome.d.ts +72 -0
- package/dist/inspector/chrome.js +153 -0
- package/dist/inspector/dock.d.ts +48 -0
- package/dist/inspector/dock.js +36 -0
- package/dist/inspector/index.d.ts +6 -0
- package/dist/inspector/index.js +6 -0
- package/dist/inspector/layout-tokens.d.ts +28 -0
- package/dist/inspector/layout-tokens.js +28 -0
- package/dist/inspector/pane-width.d.ts +64 -0
- package/dist/inspector/pane-width.js +53 -0
- package/dist/inspector/use-pane-width.d.ts +99 -0
- package/dist/inspector/use-pane-width.js +149 -0
- package/dist/kanban/card-properties.d.ts +27 -0
- package/dist/kanban/card-properties.js +19 -0
- package/dist/kanban/card-shell.d.ts +32 -0
- package/dist/kanban/card-shell.js +46 -0
- package/dist/kanban/column-header.d.ts +23 -0
- package/dist/kanban/column-header.js +24 -0
- package/dist/kanban/grouping.d.ts +84 -0
- package/dist/kanban/grouping.js +76 -0
- package/dist/kanban/index.d.ts +5 -0
- package/dist/kanban/index.js +5 -0
- package/dist/lib/button-disposition.d.ts +63 -0
- package/dist/lib/button-disposition.js +52 -0
- package/dist/lib/option-color.d.ts +28 -0
- package/dist/lib/option-color.js +59 -0
- package/dist/lib/overlay-layer.d.ts +22 -0
- package/dist/lib/overlay-layer.js +21 -0
- package/dist/lib/tab-order.d.ts +15 -0
- package/dist/lib/tab-order.js +15 -0
- package/dist/lib/tooltip-content.d.ts +13 -0
- package/dist/lib/tooltip-content.js +12 -0
- package/dist/lib/utils.d.ts +20 -0
- package/dist/lib/utils.js +41 -0
- package/dist/lib/week.d.ts +25 -0
- package/dist/lib/week.js +40 -0
- package/dist/styles/theme.css +1209 -0
- package/package.json +518 -2
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "../lib/utils.js";
|
|
3
|
+
import { OVERLAY_LAYER_CLASS_NAMES } from "../lib/overlay-layer.js";
|
|
4
|
+
import { renderTooltipTrigger } from "./tooltip-trigger-helper.js";
|
|
5
|
+
import { DirectionalIcon } from "./directional-icon.js";
|
|
6
|
+
import { ChevronRightIcon } from "lucide-react";
|
|
7
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
import { Menu as Menu$1 } from "@base-ui/react/menu";
|
|
9
|
+
//#region src/components/menu.tsx
|
|
10
|
+
const MenuCreateHandle = Menu$1.createHandle;
|
|
11
|
+
const Menu = Menu$1.Root;
|
|
12
|
+
const MenuPortal = Menu$1.Portal;
|
|
13
|
+
function MenuTrigger({ tooltip, ...props }) {
|
|
14
|
+
return renderTooltipTrigger({
|
|
15
|
+
tooltip: tooltip ?? props["aria-label"] ?? props.title,
|
|
16
|
+
trigger: /* @__PURE__ */ jsx(Menu$1.Trigger, {
|
|
17
|
+
"data-slot": "menu-trigger",
|
|
18
|
+
nativeButton: true,
|
|
19
|
+
...props
|
|
20
|
+
})
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
const MenuPopup = ({ children, className, sideOffset = 4, align = "center", alignOffset, side = "bottom", anchor, ...props }) => /* @__PURE__ */ jsx(Menu$1.Portal, { children: /* @__PURE__ */ jsx(Menu$1.Positioner, {
|
|
24
|
+
align: anchor ? "start" : align,
|
|
25
|
+
alignOffset,
|
|
26
|
+
anchor,
|
|
27
|
+
className: OVERLAY_LAYER_CLASS_NAMES.popup,
|
|
28
|
+
"data-slot": "menu-positioner",
|
|
29
|
+
side: anchor ? "bottom" : side,
|
|
30
|
+
sideOffset,
|
|
31
|
+
children: /* @__PURE__ */ jsx(Menu$1.Popup, {
|
|
32
|
+
className: cn("bg-popover relative flex origin-(--transform-origin) rounded-lg border shadow-lg/5 outline-none not-dark:bg-clip-padding not-[class*='w-']:min-w-32 before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] before:shadow-[0_1px_--theme(--color-black/4%)] focus:outline-none dark:before:shadow-[0_-1px_--theme(--color-white/6%)]", className),
|
|
33
|
+
"data-slot": "menu-popup",
|
|
34
|
+
...props,
|
|
35
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
36
|
+
className: "max-h-(--available-height) w-full overflow-y-auto p-1",
|
|
37
|
+
children
|
|
38
|
+
})
|
|
39
|
+
})
|
|
40
|
+
}) });
|
|
41
|
+
const MenuGroup = (props) => /* @__PURE__ */ jsx(Menu$1.Group, {
|
|
42
|
+
"data-slot": "menu-group",
|
|
43
|
+
...props
|
|
44
|
+
});
|
|
45
|
+
const MenuItem = ({ className, inset, variant = "default", ...props }) => /* @__PURE__ */ jsx(Menu$1.Item, {
|
|
46
|
+
className: cn("text-foreground data-highlighted:bg-accent data-highlighted:text-accent-foreground data-[variant=destructive]:text-destructive-foreground flex min-h-8 cursor-pointer items-center gap-2 rounded-sm px-2 py-1 text-base outline-none select-none data-disabled:pointer-events-none data-disabled:opacity-64 data-inset:ps-8 sm:min-h-7 sm:text-sm [&>svg]:pointer-events-none [&>svg]:-mx-0.5 [&>svg]:shrink-0 [&>svg:not([class*='opacity-'])]:opacity-80 [&>svg:not([class*='size-'])]:size-4.5 sm:[&>svg:not([class*='size-'])]:size-4", className),
|
|
47
|
+
"data-inset": inset,
|
|
48
|
+
"data-slot": "menu-item",
|
|
49
|
+
"data-variant": variant,
|
|
50
|
+
...props
|
|
51
|
+
});
|
|
52
|
+
const MenuCheckboxItem = ({ className, children, checked, variant = "default", ...props }) => /* @__PURE__ */ jsx(Menu$1.CheckboxItem, {
|
|
53
|
+
checked,
|
|
54
|
+
className: cn("text-foreground data-highlighted:bg-accent data-highlighted:text-accent-foreground grid min-h-8 cursor-pointer items-center gap-2 rounded-sm py-1 ps-2 text-base outline-none in-data-[side=none]:min-w-[calc(var(--anchor-width)+1.25rem)] data-disabled:pointer-events-none data-disabled:opacity-64 sm:min-h-7 sm:text-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4", variant === "switch" ? "grid-cols-[1fr_auto] gap-4 pe-1.5" : "grid-cols-[1rem_1fr] pe-4", className),
|
|
55
|
+
"data-slot": "menu-checkbox-item",
|
|
56
|
+
...props,
|
|
57
|
+
children: variant === "switch" ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
|
|
58
|
+
className: "col-start-1",
|
|
59
|
+
children
|
|
60
|
+
}), /* @__PURE__ */ jsx(Menu$1.CheckboxItemIndicator, {
|
|
61
|
+
className: "focus-visible:ring-ring focus-visible:ring-offset-background data-checked:bg-primary data-unchecked:bg-input inline-flex h-[calc(var(--thumb-size)+2px)] w-[calc(var(--thumb-size)*2-2px)] shrink-0 items-center rounded-full p-px inset-shadow-[0_1px_--theme(--color-black/4%)] transition-[background-color,box-shadow] duration-200 outline-none [--thumb-size:--spacing(4)] focus-visible:ring-2 focus-visible:ring-offset-1 data-disabled:opacity-64 sm:[--thumb-size:--spacing(3)]",
|
|
62
|
+
keepMounted: true,
|
|
63
|
+
children: /* @__PURE__ */ jsx("span", { className: "bg-background pointer-events-none block aspect-square h-full origin-left rounded-(--thumb-size) shadow-sm/5 will-change-transform [transition:translate_.15s,border-radius_.15s,scale_.1s_.1s,transform-origin_.15s] in-[[data-slot=menu-checkbox-item]:active]:rounded-[var(--thumb-size)/calc(var(--thumb-size)*1.10)] in-[[data-slot=menu-checkbox-item]:active]:not-data-disabled:scale-x-110 in-[[data-slot=menu-checkbox-item][data-checked]]:origin-[var(--thumb-size)_50%] in-[[data-slot=menu-checkbox-item][data-checked]]:translate-x-[calc(var(--thumb-size)-4px)]" })
|
|
64
|
+
})] }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Menu$1.CheckboxItemIndicator, {
|
|
65
|
+
className: "col-start-1",
|
|
66
|
+
children: /* @__PURE__ */ jsxs("svg", {
|
|
67
|
+
fill: "none",
|
|
68
|
+
height: "24",
|
|
69
|
+
stroke: "currentColor",
|
|
70
|
+
strokeLinecap: "round",
|
|
71
|
+
strokeLinejoin: "round",
|
|
72
|
+
strokeWidth: "2",
|
|
73
|
+
viewBox: "0 0 24 24",
|
|
74
|
+
width: "24",
|
|
75
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
76
|
+
children: [/* @__PURE__ */ jsx("title", { children: "Selected" }), /* @__PURE__ */ jsx("path", { d: "M5.252 12.7 10.2 18.63 18.748 5.37" })]
|
|
77
|
+
})
|
|
78
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
79
|
+
className: "col-start-2",
|
|
80
|
+
children
|
|
81
|
+
})] })
|
|
82
|
+
});
|
|
83
|
+
const MenuRadioGroup = (props) => /* @__PURE__ */ jsx(Menu$1.RadioGroup, {
|
|
84
|
+
"data-slot": "menu-radio-group",
|
|
85
|
+
...props
|
|
86
|
+
});
|
|
87
|
+
const MenuRadioItem = ({ className, children, indicator = "check", ...props }) => /* @__PURE__ */ jsx(Menu$1.RadioItem, {
|
|
88
|
+
className: cn("text-foreground data-highlighted:bg-accent data-highlighted:text-accent-foreground min-h-8 cursor-pointer items-center gap-2 rounded-sm py-1 text-base outline-none in-data-[side=none]:min-w-[calc(var(--anchor-width)+1.25rem)] data-disabled:pointer-events-none data-disabled:opacity-64 sm:min-h-7 sm:text-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4", indicator === "check" ? "grid grid-cols-[1rem_1fr] ps-2 pe-4" : "flex px-2", className),
|
|
89
|
+
"data-slot": "menu-radio-item",
|
|
90
|
+
...props,
|
|
91
|
+
children: indicator === "check" ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Menu$1.RadioItemIndicator, {
|
|
92
|
+
className: "col-start-1",
|
|
93
|
+
children: /* @__PURE__ */ jsxs("svg", {
|
|
94
|
+
fill: "none",
|
|
95
|
+
height: "24",
|
|
96
|
+
stroke: "currentColor",
|
|
97
|
+
strokeLinecap: "round",
|
|
98
|
+
strokeLinejoin: "round",
|
|
99
|
+
strokeWidth: "2",
|
|
100
|
+
viewBox: "0 0 24 24",
|
|
101
|
+
width: "24",
|
|
102
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
103
|
+
children: [/* @__PURE__ */ jsx("title", { children: "Selected" }), /* @__PURE__ */ jsx("path", { d: "M5.252 12.7 10.2 18.63 18.748 5.37" })]
|
|
104
|
+
})
|
|
105
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
106
|
+
className: "col-start-2",
|
|
107
|
+
children
|
|
108
|
+
})] }) : children
|
|
109
|
+
});
|
|
110
|
+
const MenuGroupLabel = ({ className, inset, ...props }) => /* @__PURE__ */ jsx(Menu$1.GroupLabel, {
|
|
111
|
+
className: cn("text-muted-foreground px-2 py-1.5 text-xs font-medium data-inset:ps-9 sm:data-inset:ps-8", className),
|
|
112
|
+
"data-inset": inset,
|
|
113
|
+
"data-slot": "menu-label",
|
|
114
|
+
...props
|
|
115
|
+
});
|
|
116
|
+
const MenuSeparator = ({ className, ...props }) => /* @__PURE__ */ jsx(Menu$1.Separator, {
|
|
117
|
+
className: cn("bg-border mx-2 my-1 h-px", className),
|
|
118
|
+
"data-slot": "menu-separator",
|
|
119
|
+
...props
|
|
120
|
+
});
|
|
121
|
+
const MenuShortcut = ({ className, ...props }) => /* @__PURE__ */ jsx("kbd", {
|
|
122
|
+
className: cn("text-foreground-label ms-auto font-sans text-xs font-medium tracking-widest", className),
|
|
123
|
+
"data-slot": "menu-shortcut",
|
|
124
|
+
...props
|
|
125
|
+
});
|
|
126
|
+
const MenuSub = (props) => /* @__PURE__ */ jsx(Menu$1.SubmenuRoot, {
|
|
127
|
+
"data-slot": "menu-sub",
|
|
128
|
+
...props
|
|
129
|
+
});
|
|
130
|
+
const MenuSubTrigger = ({ className, inset, children, ...props }) => /* @__PURE__ */ jsxs(Menu$1.SubmenuTrigger, {
|
|
131
|
+
className: cn("text-foreground data-highlighted:bg-accent data-highlighted:text-accent-foreground data-popup-open:bg-accent data-popup-open:text-accent-foreground flex min-h-8 cursor-pointer items-center gap-2 rounded-sm px-2 py-1 text-base outline-none data-disabled:pointer-events-none data-disabled:opacity-64 data-inset:ps-8 sm:min-h-7 sm:text-sm [&_svg]:pointer-events-none [&_svg]:-mx-0.5 [&_svg]:shrink-0 [&_svg:not([class*='opacity-'])]:opacity-80 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4", className),
|
|
132
|
+
"data-inset": inset,
|
|
133
|
+
"data-slot": "menu-sub-trigger",
|
|
134
|
+
...props,
|
|
135
|
+
children: [children, /* @__PURE__ */ jsx(DirectionalIcon, {
|
|
136
|
+
className: "ms-auto -me-0.5 opacity-80",
|
|
137
|
+
icon: ChevronRightIcon
|
|
138
|
+
})]
|
|
139
|
+
});
|
|
140
|
+
const MenuSubPopup = ({ className, sideOffset = 0, alignOffset, align = "start", ...props }) => {
|
|
141
|
+
return /* @__PURE__ */ jsx(MenuPopup, {
|
|
142
|
+
align,
|
|
143
|
+
alignOffset: alignOffset ?? (align !== "center" ? -5 : void 0),
|
|
144
|
+
className,
|
|
145
|
+
"data-slot": "menu-sub-content",
|
|
146
|
+
side: "inline-end",
|
|
147
|
+
sideOffset,
|
|
148
|
+
...props
|
|
149
|
+
});
|
|
150
|
+
};
|
|
151
|
+
//#endregion
|
|
152
|
+
export { Menu as DropdownMenu, Menu, MenuCheckboxItem as DropdownMenuCheckboxItem, MenuCheckboxItem, MenuPopup as DropdownMenuContent, MenuPopup, MenuCreateHandle as DropdownMenuCreateHandle, MenuCreateHandle, MenuGroup as DropdownMenuGroup, MenuGroup, MenuItem as DropdownMenuItem, MenuItem, MenuGroupLabel as DropdownMenuLabel, MenuGroupLabel, MenuPortal as DropdownMenuPortal, MenuPortal, MenuRadioGroup as DropdownMenuRadioGroup, MenuRadioGroup, MenuRadioItem as DropdownMenuRadioItem, MenuRadioItem, MenuSeparator as DropdownMenuSeparator, MenuSeparator, MenuShortcut as DropdownMenuShortcut, MenuShortcut, MenuSub as DropdownMenuSub, MenuSub, MenuSubPopup as DropdownMenuSubContent, MenuSubPopup, MenuSubTrigger as DropdownMenuSubTrigger, MenuSubTrigger, MenuTrigger as DropdownMenuTrigger, MenuTrigger };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { ReactNode, RefObject } from "react";
|
|
2
|
+
//#region src/components/outline-rail.d.ts
|
|
3
|
+
type OutlineItem = {
|
|
4
|
+
id: string;
|
|
5
|
+
label: string;
|
|
6
|
+
/** What the entry contains, after the label that names it. The label
|
|
7
|
+
* stays whole; the title is what truncates when the row is narrow. */
|
|
8
|
+
title?: string;
|
|
9
|
+
/** Nesting depth among included items; drives indent + tick taper. */
|
|
10
|
+
level: number;
|
|
11
|
+
/** Optional trailing annotation in the panel (e.g. a page number or a
|
|
12
|
+
* provision range); never truncated. */
|
|
13
|
+
meta?: string;
|
|
14
|
+
/** Optional CSS custom-property name colouring this entry's tick + chip
|
|
15
|
+
* (e.g. "--option-blue"). Defaults to the neutral foreground. */
|
|
16
|
+
color?: string;
|
|
17
|
+
};
|
|
18
|
+
type OutlineRailProps = {
|
|
19
|
+
items: OutlineItem[];
|
|
20
|
+
scrollContainerRef: RefObject<HTMLElement | null>;
|
|
21
|
+
/** Vertical position (0–100) of an item's tick. Return null to drop it. */
|
|
22
|
+
resolvePct: (id: string, container: HTMLElement) => number | null;
|
|
23
|
+
/** Caller performs the scroll/navigation. */
|
|
24
|
+
onJump: (id: string, container: HTMLElement) => void;
|
|
25
|
+
/** Controlled active id; when omitted, derived from `resolvePct`. */
|
|
26
|
+
activeId?: string | null;
|
|
27
|
+
/** Pinned at the top of the panel, above the tree (e.g. a jump field).
|
|
28
|
+
* Supplying one also keeps the panel mounted when `items` is short, so a
|
|
29
|
+
* filter that narrows the tree to nothing cannot take its own control
|
|
30
|
+
* away; the caller decides whether the document has an outline at all. */
|
|
31
|
+
header?: ReactNode;
|
|
32
|
+
/** Depth from which entries start collapsed. Their ancestors still open on
|
|
33
|
+
* their own while one of their descendants is active, so a deep outline
|
|
34
|
+
* reads as the chain down to where the reader is rather than as every
|
|
35
|
+
* branch at once. Omitted: everything starts expanded. */
|
|
36
|
+
collapsedFromLevel?: number;
|
|
37
|
+
topOffset?: number;
|
|
38
|
+
panelWidth?: number;
|
|
39
|
+
ariaLabel?: string;
|
|
40
|
+
};
|
|
41
|
+
declare const OutlineRail: ({ items, scrollContainerRef, resolvePct, onJump, activeId, header, collapsedFromLevel, topOffset, panelWidth, ariaLabel }: OutlineRailProps) => import("react").JSX.Element | null;
|
|
42
|
+
//#endregion
|
|
43
|
+
export { OutlineItem, OutlineRail, OutlineRailProps };
|
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "../lib/utils.js";
|
|
3
|
+
import { Tooltip, TooltipContent as TooltipPopup, TooltipTrigger } from "./tooltip.js";
|
|
4
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
6
|
+
//#region src/components/outline-rail.tsx
|
|
7
|
+
/**
|
|
8
|
+
* Outline rail — the shared right-edge navigation rail.
|
|
9
|
+
*
|
|
10
|
+
* Always-visible thin column of ticks (width tapering by nesting level).
|
|
11
|
+
* Hovering reveals a single popover panel with the outline as a collapsible
|
|
12
|
+
* tree; the entry currently in view is emphasised. Click a tick or a row to
|
|
13
|
+
* jump.
|
|
14
|
+
*
|
|
15
|
+
* Generic over the position source: callers supply `resolvePct` (vertical % for
|
|
16
|
+
* a tick) and `onJump`. Active tracking is derived from `resolvePct` by default,
|
|
17
|
+
* or driven externally via the controlled `activeId` prop (e.g. a virtualised
|
|
18
|
+
* editor that measures rendered anchors itself).
|
|
19
|
+
*/
|
|
20
|
+
const RAIL_WIDTH = 20;
|
|
21
|
+
const ROW_H = 29;
|
|
22
|
+
const TICK_BASE_WIDTH = 6;
|
|
23
|
+
const TICK_LEVEL_STEP = 2;
|
|
24
|
+
const TICK_MAX_LEVEL = 5;
|
|
25
|
+
const RAIL_MAX_TICKS = 40;
|
|
26
|
+
/** The entry as one line of text: label, then its title when it has one. */
|
|
27
|
+
const entryText = (item) => item.title === void 0 ? item.label : `${item.label} ${item.title}`;
|
|
28
|
+
const tickWidth = (level) => {
|
|
29
|
+
return TICK_BASE_WIDTH + (TICK_MAX_LEVEL - Math.min(Math.max(level, 0), TICK_MAX_LEVEL)) * TICK_LEVEL_STEP;
|
|
30
|
+
};
|
|
31
|
+
const buildTree = (items) => {
|
|
32
|
+
const roots = [];
|
|
33
|
+
const stack = [];
|
|
34
|
+
for (const [index, item] of items.entries()) {
|
|
35
|
+
const node = {
|
|
36
|
+
item,
|
|
37
|
+
index,
|
|
38
|
+
children: []
|
|
39
|
+
};
|
|
40
|
+
let parent = stack.at(-1);
|
|
41
|
+
while (parent && parent.item.level >= item.level) {
|
|
42
|
+
stack.pop();
|
|
43
|
+
parent = stack.at(-1);
|
|
44
|
+
}
|
|
45
|
+
if (parent) parent.children.push(node);
|
|
46
|
+
else roots.push(node);
|
|
47
|
+
stack.push(node);
|
|
48
|
+
}
|
|
49
|
+
return roots;
|
|
50
|
+
};
|
|
51
|
+
const tickHeight = (isHovered, isActive) => {
|
|
52
|
+
if (isHovered) return 4;
|
|
53
|
+
if (isActive) return 3;
|
|
54
|
+
return 2;
|
|
55
|
+
};
|
|
56
|
+
const tickBackground = (color, isHovered) => {
|
|
57
|
+
if (color !== void 0) return `var(${color})`;
|
|
58
|
+
if (isHovered) return "var(--option-blue)";
|
|
59
|
+
return "var(--color-foreground)";
|
|
60
|
+
};
|
|
61
|
+
const rowTextClass = (isActive, hasChildren) => {
|
|
62
|
+
if (isActive) return "text-foreground font-medium";
|
|
63
|
+
if (hasChildren) return "text-foreground-muted";
|
|
64
|
+
return "text-muted-foreground";
|
|
65
|
+
};
|
|
66
|
+
const Chevron = ({ open }) => /* @__PURE__ */ jsx("svg", {
|
|
67
|
+
"aria-hidden": true,
|
|
68
|
+
className: cn("size-3 shrink-0 transition-transform duration-150", open ? "rotate-90" : "rotate-0"),
|
|
69
|
+
fill: "none",
|
|
70
|
+
stroke: "currentColor",
|
|
71
|
+
strokeLinecap: "round",
|
|
72
|
+
strokeLinejoin: "round",
|
|
73
|
+
strokeWidth: "2",
|
|
74
|
+
viewBox: "0 0 24 24",
|
|
75
|
+
children: /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" })
|
|
76
|
+
});
|
|
77
|
+
const OutlineRail = ({ items, scrollContainerRef, resolvePct, onJump, activeId, header, collapsedFromLevel, topOffset = 0, panelWidth = 300, ariaLabel = "Outline" }) => {
|
|
78
|
+
const [pctById, setPctById] = useState({});
|
|
79
|
+
const [derivedActive, setDerivedActive] = useState(null);
|
|
80
|
+
const [hovered, setHovered] = useState(false);
|
|
81
|
+
const [pinned, setPinned] = useState(false);
|
|
82
|
+
const [collapsed, setCollapsed] = useState(/* @__PURE__ */ new Set());
|
|
83
|
+
const [toggled, setToggled] = useState(/* @__PURE__ */ new Set());
|
|
84
|
+
const [hoveredId, setHoveredId] = useState(null);
|
|
85
|
+
const panelId = useId();
|
|
86
|
+
const triggerId = useId();
|
|
87
|
+
const panelOpen = hovered || pinned;
|
|
88
|
+
const closeTimer = useRef(null);
|
|
89
|
+
const manualLockUntil = useRef(0);
|
|
90
|
+
const panelRef = useRef(null);
|
|
91
|
+
const resolvePctRef = useRef(resolvePct);
|
|
92
|
+
const onJumpRef = useRef(onJump);
|
|
93
|
+
const tree = useMemo(() => buildTree(items), [items]);
|
|
94
|
+
const [seededItems, setSeededItems] = useState(null);
|
|
95
|
+
if (seededItems !== items) {
|
|
96
|
+
setSeededItems(items);
|
|
97
|
+
setToggled(/* @__PURE__ */ new Set());
|
|
98
|
+
setCollapsed(collapsedFromLevel === void 0 ? /* @__PURE__ */ new Set() : new Set(items.filter((item) => item.level >= collapsedFromLevel).map((item) => item.id)));
|
|
99
|
+
}
|
|
100
|
+
const maxLevel = useMemo(() => {
|
|
101
|
+
let max = 0;
|
|
102
|
+
for (const item of items) max = Math.max(max, item.level);
|
|
103
|
+
return max;
|
|
104
|
+
}, [items]);
|
|
105
|
+
const railLevelCap = useMemo(() => {
|
|
106
|
+
const counts = [];
|
|
107
|
+
let minLevel = maxLevel;
|
|
108
|
+
for (const item of items) {
|
|
109
|
+
counts[item.level] = (counts[item.level] ?? 0) + 1;
|
|
110
|
+
if (item.level < minLevel) minLevel = item.level;
|
|
111
|
+
}
|
|
112
|
+
let cap = maxLevel;
|
|
113
|
+
let running = items.length;
|
|
114
|
+
while (cap > minLevel && running > RAIL_MAX_TICKS) {
|
|
115
|
+
running -= counts[cap] ?? 0;
|
|
116
|
+
cap -= 1;
|
|
117
|
+
}
|
|
118
|
+
return cap;
|
|
119
|
+
}, [items, maxLevel]);
|
|
120
|
+
const recalc = useCallback(() => {
|
|
121
|
+
const container = scrollContainerRef.current;
|
|
122
|
+
if (!container) return;
|
|
123
|
+
const next = {};
|
|
124
|
+
for (const item of items) {
|
|
125
|
+
const pct = resolvePct(item.id, container);
|
|
126
|
+
if (pct !== null) next[item.id] = pct;
|
|
127
|
+
}
|
|
128
|
+
setPctById(next);
|
|
129
|
+
}, [
|
|
130
|
+
items,
|
|
131
|
+
scrollContainerRef,
|
|
132
|
+
resolvePct
|
|
133
|
+
]);
|
|
134
|
+
const recalcRef = useRef(recalc);
|
|
135
|
+
useLayoutEffect(() => {
|
|
136
|
+
resolvePctRef.current = resolvePct;
|
|
137
|
+
onJumpRef.current = onJump;
|
|
138
|
+
recalcRef.current = recalc;
|
|
139
|
+
});
|
|
140
|
+
useEffect(() => {
|
|
141
|
+
recalc();
|
|
142
|
+
}, [recalc]);
|
|
143
|
+
useEffect(() => {
|
|
144
|
+
const container = scrollContainerRef.current;
|
|
145
|
+
if (!container) return;
|
|
146
|
+
const observer = new ResizeObserver(() => recalcRef.current());
|
|
147
|
+
observer.observe(container);
|
|
148
|
+
return () => observer.disconnect();
|
|
149
|
+
}, [scrollContainerRef]);
|
|
150
|
+
useEffect(() => {
|
|
151
|
+
if (activeId !== void 0) return;
|
|
152
|
+
const container = scrollContainerRef.current;
|
|
153
|
+
if (!container || items.length === 0) return;
|
|
154
|
+
let raf = 0;
|
|
155
|
+
const compute = () => {
|
|
156
|
+
if (Date.now() < manualLockUntil.current || container.scrollHeight <= 0) return;
|
|
157
|
+
const centrePct = (container.scrollTop + container.clientHeight / 2) / container.scrollHeight * 100;
|
|
158
|
+
let next = null;
|
|
159
|
+
for (const item of items) {
|
|
160
|
+
const pct = resolvePctRef.current(item.id, container);
|
|
161
|
+
if (pct !== null && pct <= centrePct) next = item.id;
|
|
162
|
+
}
|
|
163
|
+
setDerivedActive(next);
|
|
164
|
+
};
|
|
165
|
+
const onScroll = () => {
|
|
166
|
+
cancelAnimationFrame(raf);
|
|
167
|
+
raf = requestAnimationFrame(compute);
|
|
168
|
+
};
|
|
169
|
+
container.addEventListener("scroll", onScroll, { passive: true });
|
|
170
|
+
raf = requestAnimationFrame(compute);
|
|
171
|
+
return () => {
|
|
172
|
+
cancelAnimationFrame(raf);
|
|
173
|
+
container.removeEventListener("scroll", onScroll);
|
|
174
|
+
};
|
|
175
|
+
}, [
|
|
176
|
+
activeId,
|
|
177
|
+
items,
|
|
178
|
+
scrollContainerRef
|
|
179
|
+
]);
|
|
180
|
+
const active = activeId === void 0 ? derivedActive : activeId;
|
|
181
|
+
const activeAncestorIds = useMemo(() => {
|
|
182
|
+
const ancestors = /* @__PURE__ */ new Set();
|
|
183
|
+
const activeIndex = items.findIndex((item) => item.id === active);
|
|
184
|
+
let level = items.at(activeIndex)?.level;
|
|
185
|
+
if (activeIndex === -1 || level === void 0) return ancestors;
|
|
186
|
+
for (let index = activeIndex - 1; index >= 0 && level > 0; index -= 1) {
|
|
187
|
+
const candidate = items[index];
|
|
188
|
+
if (candidate !== void 0 && candidate.level < level) {
|
|
189
|
+
ancestors.add(candidate.id);
|
|
190
|
+
level = candidate.level;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return ancestors;
|
|
194
|
+
}, [active, items]);
|
|
195
|
+
const jumpTo = useCallback((id) => {
|
|
196
|
+
const container = scrollContainerRef.current;
|
|
197
|
+
if (!container) return;
|
|
198
|
+
if (activeId === void 0) {
|
|
199
|
+
setDerivedActive(id);
|
|
200
|
+
manualLockUntil.current = Date.now() + 900;
|
|
201
|
+
}
|
|
202
|
+
onJumpRef.current(id, container);
|
|
203
|
+
}, [activeId, scrollContainerRef]);
|
|
204
|
+
const toggleCollapse = useCallback((id, rowEl) => {
|
|
205
|
+
const rowTop = rowEl?.getBoundingClientRect().top;
|
|
206
|
+
setToggled((prev) => new Set(prev).add(id));
|
|
207
|
+
setCollapsed((prev) => {
|
|
208
|
+
const next = new Set(prev);
|
|
209
|
+
if (next.has(id)) next.delete(id);
|
|
210
|
+
else next.add(id);
|
|
211
|
+
return next;
|
|
212
|
+
});
|
|
213
|
+
requestAnimationFrame(() => {
|
|
214
|
+
const panel = panelRef.current;
|
|
215
|
+
if (!panel || !rowEl || rowTop === void 0) return;
|
|
216
|
+
panel.scrollTop += rowEl.getBoundingClientRect().top - rowTop;
|
|
217
|
+
});
|
|
218
|
+
}, []);
|
|
219
|
+
const openPanel = useCallback(() => {
|
|
220
|
+
if (closeTimer.current !== null) {
|
|
221
|
+
clearTimeout(closeTimer.current);
|
|
222
|
+
closeTimer.current = null;
|
|
223
|
+
}
|
|
224
|
+
setHovered(true);
|
|
225
|
+
}, []);
|
|
226
|
+
const scheduleClose = useCallback(() => {
|
|
227
|
+
if (closeTimer.current !== null) clearTimeout(closeTimer.current);
|
|
228
|
+
closeTimer.current = setTimeout(() => setHovered(false), 120);
|
|
229
|
+
}, []);
|
|
230
|
+
useEffect(() => {
|
|
231
|
+
if (pinned) panelRef.current?.focus();
|
|
232
|
+
}, [pinned]);
|
|
233
|
+
const closePanel = useCallback(() => {
|
|
234
|
+
if (closeTimer.current !== null) {
|
|
235
|
+
clearTimeout(closeTimer.current);
|
|
236
|
+
closeTimer.current = null;
|
|
237
|
+
}
|
|
238
|
+
setHovered(false);
|
|
239
|
+
setPinned(false);
|
|
240
|
+
}, []);
|
|
241
|
+
useEffect(() => () => {
|
|
242
|
+
if (closeTimer.current !== null) clearTimeout(closeTimer.current);
|
|
243
|
+
}, []);
|
|
244
|
+
const renderNode = (node) => {
|
|
245
|
+
const hasChildren = node.children.length > 0;
|
|
246
|
+
const isCollapsed = collapsed.has(node.item.id) && !(activeAncestorIds.has(node.item.id) && !toggled.has(node.item.id));
|
|
247
|
+
const isActive = active === node.item.id;
|
|
248
|
+
const isHovered = hoveredId === node.item.id;
|
|
249
|
+
const highlighted = isActive || isHovered;
|
|
250
|
+
const indent = 8 + Math.min(node.item.level, maxLevel) * 12;
|
|
251
|
+
return /* @__PURE__ */ jsxs("li", { children: [/* @__PURE__ */ jsxs("div", {
|
|
252
|
+
className: cn("flex items-center rounded-md pe-2.5 transition-colors", !hasChildren && highlighted && "bg-accent"),
|
|
253
|
+
onMouseEnter: () => setHoveredId(node.item.id),
|
|
254
|
+
onMouseLeave: () => setHoveredId(null),
|
|
255
|
+
style: hasChildren ? {
|
|
256
|
+
position: "sticky",
|
|
257
|
+
top: node.item.level * ROW_H,
|
|
258
|
+
zIndex: 40 - node.item.level,
|
|
259
|
+
background: highlighted ? "linear-gradient(var(--color-accent), var(--color-accent)), var(--color-popover)" : "var(--color-popover)"
|
|
260
|
+
} : void 0,
|
|
261
|
+
children: [
|
|
262
|
+
hasChildren ? /* @__PURE__ */ jsx("button", {
|
|
263
|
+
"aria-expanded": !isCollapsed,
|
|
264
|
+
"aria-label": isCollapsed ? "Expand" : "Collapse",
|
|
265
|
+
className: "text-muted-foreground hover:text-foreground flex size-5 shrink-0 items-center justify-center",
|
|
266
|
+
onClick: (event) => toggleCollapse(node.item.id, event.currentTarget.parentElement),
|
|
267
|
+
style: { marginInlineStart: indent - 4 },
|
|
268
|
+
type: "button",
|
|
269
|
+
children: /* @__PURE__ */ jsx(Chevron, { open: !isCollapsed })
|
|
270
|
+
}) : /* @__PURE__ */ jsx("span", {
|
|
271
|
+
"aria-hidden": true,
|
|
272
|
+
className: "shrink-0",
|
|
273
|
+
style: { width: indent + 4 }
|
|
274
|
+
}),
|
|
275
|
+
node.item.color && /* @__PURE__ */ jsx("span", {
|
|
276
|
+
"aria-hidden": true,
|
|
277
|
+
className: "me-1.5 size-1.5 shrink-0 rounded-full",
|
|
278
|
+
style: { background: `var(${node.item.color})` }
|
|
279
|
+
}),
|
|
280
|
+
/* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
|
|
281
|
+
render: /* @__PURE__ */ jsx("button", {
|
|
282
|
+
className: cn("flex min-w-0 flex-1 items-baseline gap-1.5 py-1.5 text-start text-[13px] leading-snug", rowTextClass(isActive, hasChildren)),
|
|
283
|
+
onClick: () => jumpTo(node.item.id),
|
|
284
|
+
type: "button"
|
|
285
|
+
}),
|
|
286
|
+
children: node.item.title === void 0 ? /* @__PURE__ */ jsx("span", {
|
|
287
|
+
className: "min-w-0 truncate",
|
|
288
|
+
children: node.item.label
|
|
289
|
+
}) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
|
|
290
|
+
className: "shrink-0 font-medium",
|
|
291
|
+
children: node.item.label
|
|
292
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
293
|
+
className: "min-w-0 truncate font-normal",
|
|
294
|
+
children: node.item.title
|
|
295
|
+
})] })
|
|
296
|
+
}), /* @__PURE__ */ jsx(TooltipPopup, { children: entryText(node.item) })] }),
|
|
297
|
+
node.item.meta !== void 0 && /* @__PURE__ */ jsx("span", {
|
|
298
|
+
className: "text-foreground-placeholder shrink-0 ps-2 text-[11px] tabular-nums",
|
|
299
|
+
children: node.item.meta
|
|
300
|
+
})
|
|
301
|
+
]
|
|
302
|
+
}), hasChildren && !isCollapsed && /* @__PURE__ */ jsx("ul", {
|
|
303
|
+
className: "m-0 list-none p-0",
|
|
304
|
+
children: node.children.map(renderNode)
|
|
305
|
+
})] }, `${node.item.id}-${node.index}`);
|
|
306
|
+
};
|
|
307
|
+
const visibleTicks = items.filter((item) => item.id in pctById && item.level <= railLevelCap);
|
|
308
|
+
if (items.length < 2 && header === void 0) return null;
|
|
309
|
+
const visibleTickIds = new Set(visibleTicks.map((item) => item.id));
|
|
310
|
+
const ghostItem = hoveredId !== null && !visibleTickIds.has(hoveredId) && hoveredId in pctById ? items.find((item) => item.id === hoveredId) ?? null : null;
|
|
311
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
312
|
+
"aria-label": ariaLabel,
|
|
313
|
+
className: "absolute end-0 z-20 max-lg:hidden",
|
|
314
|
+
role: "group",
|
|
315
|
+
style: {
|
|
316
|
+
top: topOffset,
|
|
317
|
+
bottom: 0,
|
|
318
|
+
width: RAIL_WIDTH
|
|
319
|
+
},
|
|
320
|
+
children: [
|
|
321
|
+
/* @__PURE__ */ jsx("button", {
|
|
322
|
+
"aria-controls": panelId,
|
|
323
|
+
"aria-expanded": panelOpen,
|
|
324
|
+
className: "focus-visible:ring-ring bg-popover text-popover-foreground sr-only absolute end-0 top-2 z-30 w-max -translate-x-6 rounded-md border px-2 py-1 text-xs focus:not-sr-only focus-visible:ring-2 focus-visible:outline-none",
|
|
325
|
+
id: triggerId,
|
|
326
|
+
onClick: () => {
|
|
327
|
+
if (pinned) {
|
|
328
|
+
closePanel();
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
setPinned(true);
|
|
332
|
+
openPanel();
|
|
333
|
+
},
|
|
334
|
+
type: "button",
|
|
335
|
+
children: ariaLabel
|
|
336
|
+
}),
|
|
337
|
+
/* @__PURE__ */ jsxs("div", {
|
|
338
|
+
className: "relative h-full",
|
|
339
|
+
onMouseEnter: openPanel,
|
|
340
|
+
onMouseLeave: scheduleClose,
|
|
341
|
+
onWheel: (event) => {
|
|
342
|
+
scrollContainerRef.current?.scrollBy(0, event.deltaY);
|
|
343
|
+
},
|
|
344
|
+
children: [visibleTicks.map((item) => {
|
|
345
|
+
const isActive = active === item.id;
|
|
346
|
+
const isHovered = hoveredId === item.id;
|
|
347
|
+
return /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, { render: /* @__PURE__ */ jsx("button", {
|
|
348
|
+
"aria-current": isActive ? "true" : void 0,
|
|
349
|
+
"aria-label": item.label,
|
|
350
|
+
className: cn("absolute end-0 rounded-full transition-[width,height,opacity,background-color] duration-150", isHovered || isActive ? "opacity-100" : "opacity-45 hover:opacity-90"),
|
|
351
|
+
onClick: () => jumpTo(item.id),
|
|
352
|
+
onMouseEnter: () => setHoveredId(item.id),
|
|
353
|
+
onMouseLeave: () => setHoveredId(null),
|
|
354
|
+
style: {
|
|
355
|
+
top: `${pctById[item.id]}%`,
|
|
356
|
+
transform: "translateY(-50%)",
|
|
357
|
+
width: isHovered ? tickWidth(item.level) + 8 : tickWidth(item.level),
|
|
358
|
+
height: tickHeight(isHovered, isActive),
|
|
359
|
+
background: tickBackground(item.color, isHovered)
|
|
360
|
+
},
|
|
361
|
+
type: "button"
|
|
362
|
+
}) }), /* @__PURE__ */ jsx(TooltipPopup, { children: item.label })] }, item.id);
|
|
363
|
+
}), ghostItem && /* @__PURE__ */ jsx("span", {
|
|
364
|
+
"aria-hidden": true,
|
|
365
|
+
className: "absolute end-0 rounded-full opacity-100",
|
|
366
|
+
style: {
|
|
367
|
+
top: `${pctById[ghostItem.id]}%`,
|
|
368
|
+
transform: "translateY(-50%)",
|
|
369
|
+
width: tickWidth(ghostItem.level) + 8,
|
|
370
|
+
height: 4,
|
|
371
|
+
background: ghostItem.color ? `var(${ghostItem.color})` : "var(--option-blue)"
|
|
372
|
+
}
|
|
373
|
+
})]
|
|
374
|
+
}),
|
|
375
|
+
/* @__PURE__ */ jsxs("nav", {
|
|
376
|
+
"aria-hidden": !panelOpen,
|
|
377
|
+
id: panelId,
|
|
378
|
+
inert: panelOpen ? void 0 : true,
|
|
379
|
+
tabIndex: -1,
|
|
380
|
+
className: cn("border-border bg-popover text-popover-foreground absolute overflow-y-auto rounded-xl border pb-2 shadow-lg transition-[opacity,transform] duration-150", panelOpen ? "translate-x-0 opacity-100" : "pointer-events-none translate-x-2 opacity-0"),
|
|
381
|
+
onBlurCapture: (event) => {
|
|
382
|
+
if (pinned && !event.currentTarget.contains(event.relatedTarget)) {
|
|
383
|
+
closePanel();
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
scheduleClose();
|
|
387
|
+
},
|
|
388
|
+
onFocusCapture: openPanel,
|
|
389
|
+
onKeyDown: (event) => {
|
|
390
|
+
if (event.key !== "Escape") return;
|
|
391
|
+
const trigger = event.currentTarget.ownerDocument.querySelector(`#${CSS.escape(triggerId)}`);
|
|
392
|
+
if (trigger instanceof HTMLElement) trigger.focus();
|
|
393
|
+
closePanel();
|
|
394
|
+
},
|
|
395
|
+
onMouseEnter: openPanel,
|
|
396
|
+
onMouseLeave: scheduleClose,
|
|
397
|
+
ref: panelRef,
|
|
398
|
+
style: {
|
|
399
|
+
top: 0,
|
|
400
|
+
insetInlineEnd: 26,
|
|
401
|
+
width: panelWidth,
|
|
402
|
+
maxHeight: "calc(100% - 24px)"
|
|
403
|
+
},
|
|
404
|
+
children: [header !== void 0 && /* @__PURE__ */ jsx("div", {
|
|
405
|
+
className: "bg-popover sticky top-0 z-50 border-b p-2",
|
|
406
|
+
children: header
|
|
407
|
+
}), /* @__PURE__ */ jsx("ul", {
|
|
408
|
+
className: "m-0 list-none p-0",
|
|
409
|
+
children: tree.map(renderNode)
|
|
410
|
+
})]
|
|
411
|
+
})
|
|
412
|
+
]
|
|
413
|
+
});
|
|
414
|
+
};
|
|
415
|
+
//#endregion
|
|
416
|
+
export { OutlineRail };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Button } from "./button.js";
|
|
2
|
+
import { useRender } from "@base-ui/react/use-render";
|
|
3
|
+
import * as React$1 from "react";
|
|
4
|
+
//#region src/components/pagination.d.ts
|
|
5
|
+
declare const Pagination: ({ className, ...props }: React$1.ComponentProps<"nav">) => React$1.JSX.Element;
|
|
6
|
+
declare const PaginationContent: ({ className, ...props }: React$1.ComponentProps<"ul">) => React$1.JSX.Element;
|
|
7
|
+
declare const PaginationItem: ({ ...props }: React$1.ComponentProps<"li">) => React$1.JSX.Element;
|
|
8
|
+
type PaginationLinkProps = {
|
|
9
|
+
isActive?: boolean;
|
|
10
|
+
size?: React$1.ComponentProps<typeof Button>["size"];
|
|
11
|
+
} & useRender.ComponentProps<"a">;
|
|
12
|
+
declare function PaginationLink({ className, isActive, size, render, ...props }: PaginationLinkProps): React$1.ReactElement<unknown, string | React$1.JSXElementConstructor<any>>;
|
|
13
|
+
declare const PaginationPrevious: ({ className, ...props }: React$1.ComponentProps<typeof PaginationLink>) => React$1.JSX.Element;
|
|
14
|
+
declare const PaginationNext: ({ className, ...props }: React$1.ComponentProps<typeof PaginationLink>) => React$1.JSX.Element;
|
|
15
|
+
declare const PaginationEllipsis: ({ className, ...props }: React$1.ComponentProps<"span">) => React$1.JSX.Element;
|
|
16
|
+
//#endregion
|
|
17
|
+
export { Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious };
|