@neuronection/assistant-ui 0.15.0 → 0.16.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/CHANGELOG.md +14 -0
- package/README.md +1 -1
- package/dist/about.d.ts +4 -0
- package/dist/about.js +1 -1
- package/dist/chunk-3IXMZ6KG.js +439 -0
- package/dist/chunk-3IXMZ6KG.js.map +1 -0
- package/dist/{chunk-7RTSSBBR.js → chunk-INMAUGBK.js} +39 -63
- package/dist/chunk-INMAUGBK.js.map +1 -0
- package/dist/index.js +2 -2
- package/dist/sidebar-nav.d.ts +5 -1
- package/dist/sidebar-nav.js +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/dist/chunk-7RTSSBBR.js.map +0 -1
- package/dist/chunk-EZTQMJ5I.js +0 -428
- package/dist/chunk-EZTQMJ5I.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @neuronection/assistant-ui
|
|
2
2
|
|
|
3
|
+
## 0.16.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`d0d648b`](https://github.com/neuronection/assistant-ui/commit/d0d648b3a5142badd32de81fdd992fdff803127c) Thanks [@constLiakos](https://github.com/constLiakos)! - SidebarNav: add `secondaryItems` — flat nav items pinned below the scroll area, above `footer` (the Settings/About pattern). Pinned items share the main list's rendering, active state, collapsed-rail behavior and keyboard traversal order (arrows and Home/End cross between regions).
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- [`d0d648b`](https://github.com/neuronection/assistant-ui/commit/d0d648b3a5142badd32de81fdd992fdff803127c) Thanks [@constLiakos](https://github.com/constLiakos)! - SponsorCard: compact redesign — the decorative primary glow blob, large icon chip and tinted highlight block are gone. The title is bolder with a small inline heart, the footnote renders as a plain second line under the description, and the highlighted channel is emphasized with a primary border and primary name instead of a filled background. The channel list is a responsive grid — one column below the `sm` viewport breakpoint, two above — with a new `columns` prop (`'auto'` default, `1`, `2`) for narrow surfaces like modals.
|
|
12
|
+
|
|
13
|
+
FamilyBadge: the current app's card is now clickable to its own hub page (it was rendered as a non-interactive div; the ring and "Current app" badge still mark it).
|
|
14
|
+
|
|
15
|
+
- [`d0d648b`](https://github.com/neuronection/assistant-ui/commit/d0d648b3a5142badd32de81fdd992fdff803127c) Thanks [@constLiakos](https://github.com/constLiakos)! - SponsorCard: the highlighted channel renders as a soft primary tint (8–12% color-mix fill, primary-mix border, primary text/icon) instead of a solid primary block, with slightly tighter row padding — calmer, more modern hierarchy.
|
|
16
|
+
|
|
3
17
|
## 0.15.0
|
|
4
18
|
|
|
5
19
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -86,7 +86,7 @@ Visual reference: [gallery](https://neuronection.github.io/assistant-ui/).
|
|
|
86
86
|
| [`info-button`](./docs/components/info-button.md) / [`field-label`](./docs/components/field-label.md) / [`copy-button`](./docs/components/copy-button.md) / [`breadcrumbs`](./docs/components/breadcrumbs.md) | `InfoButton` · `FieldLabel` · `CopyButton` (clipboard + copied state) · `Breadcrumbs` (SPA links via `linkComponent`) | [stories](https://neuronection.github.io/assistant-ui/?story=phase2--copy-buttons) |
|
|
87
87
|
| [`ai-button`](./docs/components/ai-button.md) / [`ai-actions-dropdown`](./docs/components/ai-actions-dropdown.md) / [`ai-magic-fill`](./docs/components/ai-magic-fill.md) | AI affordances: ask-with-suggestions (controlled open, icon-only mode) · action menu + custom prompt · describe-in-words fill modal — API calls stay app-side | [stories](https://neuronection.github.io/assistant-ui/?story=ai-patterns--ai-buttons) |
|
|
88
88
|
| [`settings-shell`](./docs/components/settings-shell.md) / [`provider-form`](./docs/components/provider-form.md) / [`connection-test-row`](./docs/components/connection-test-row.md) | `SettingsShell` (controlled two-pane nav) · `ProviderForm` (write-only API-key field) · `ConnectionTestRow` (idle/testing/ok/fail + latency, `inline` variant + meta slot) | [stories](https://neuronection.github.io/assistant-ui/?story=settings-trio--provider-form-story) |
|
|
89
|
-
| [`sidebar-nav`](./docs/components/sidebar-nav.md) | `SidebarNav` (family-standard vertical nav: groups + sections, badges, controlled active id, optional collapsed icon rail with hover flyouts, header/footer slots) | [stories](https://neuronection.github.io/assistant-ui/?story=sidebar-nav--groups-story) |
|
|
89
|
+
| [`sidebar-nav`](./docs/components/sidebar-nav.md) | `SidebarNav` (family-standard vertical nav: groups + sections, badges, controlled active id, pinned secondary items, optional collapsed icon rail with hover flyouts, header/footer slots) | [stories](https://neuronection.github.io/assistant-ui/?story=sidebar-nav--groups-story) |
|
|
90
90
|
| [`user-menu`](./docs/components/user-menu.md) | `UserMenu` (avatar/identity trigger + action menu incl. checkable items; composed over `Menu`) | [stories](https://neuronection.github.io/assistant-ui/?story=user-menu--checkable-items-story) |
|
|
91
91
|
| [`model-picker`](./docs/components/model-picker.md) / [`task-assignment-picker`](./docs/components/task-assignment-picker.md) / [`capability-chips`](./docs/components/capability-chips.md) | `ModelPicker` (grouped provider→model combobox, capability chips) · `TaskAssignmentPicker` (task → model mapping rows, capability-filtered, sections + fallback assignment, meta slot) · `CapabilityChips` (cap toggle group / badges, min-selected) | [stories](https://neuronection.github.io/assistant-ui/?story=model-assignment--task-assignment-v2-story) |
|
|
92
92
|
| [`model-registry`](./docs/components/model-registry.md) | `ModelRegistry` (provider cards → registered model rows; add/edit in one catalog modal fed by app-fetched remote models; manual id entry; add-all of pending ids) | [stories](https://neuronection.github.io/assistant-ui/?story=model-registry--model-registry-story) |
|
package/dist/about.d.ts
CHANGED
|
@@ -95,6 +95,10 @@ interface SponsorCardProps extends Omit<React.ComponentProps<'section'>, 'title'
|
|
|
95
95
|
description?: React.ReactNode;
|
|
96
96
|
footnote?: React.ReactNode;
|
|
97
97
|
channelsLabel?: string;
|
|
98
|
+
/** Channel list columns. `auto` = one column below the `sm` viewport
|
|
99
|
+
* breakpoint, two above; `1`/`2` force a count (use `1` inside narrow
|
|
100
|
+
* surfaces like modals — breakpoints track the viewport, not the card). */
|
|
101
|
+
columns?: 'auto' | 1 | 2;
|
|
98
102
|
icon?: React.ReactNode;
|
|
99
103
|
heading?: 'h2' | 'h3' | 'h4';
|
|
100
104
|
}
|
package/dist/about.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { AboutCard, AboutFooterLine, AboutLinkList, AboutNote, AboutPanel, FamilyBadge, SponsorCard, TechChips } from './chunk-
|
|
1
|
+
export { AboutCard, AboutFooterLine, AboutLinkList, AboutNote, AboutPanel, FamilyBadge, SponsorCard, TechChips } from './chunk-INMAUGBK.js';
|
|
2
2
|
//# sourceMappingURL=about.js.map
|
|
3
3
|
//# sourceMappingURL=about.js.map
|
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
import { cn } from './chunk-BLJWR4ZV.js';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import * as PopoverPrimitive from '@radix-ui/react-popover';
|
|
4
|
+
import { ChevronRight, ChevronLeft, ChevronDown } from 'lucide-react';
|
|
5
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
var FLYOUT_DELAY_MS = 150;
|
|
8
|
+
var itemButtonClass = (active) => cn(
|
|
9
|
+
"flex w-full cursor-pointer items-center gap-3 rounded-[var(--as-radius)] px-3 py-2.5 text-left text-sm transition-colors focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)] disabled:pointer-events-none disabled:opacity-50",
|
|
10
|
+
active ? "bg-[color-mix(in_srgb,var(--as-primary)_12%,transparent)] font-bold text-[var(--as-primary)]" : "font-medium text-[var(--as-fg)] hover:bg-[var(--as-muted)]"
|
|
11
|
+
);
|
|
12
|
+
var flyoutContentClass = "as-anim-pop z-[var(--as-z-popover)] min-w-44 rounded-[var(--as-radius-lg)] border border-[var(--as-border)] bg-[var(--as-surface-raised)] p-1 text-[var(--as-fg)] shadow-[var(--as-shadow-3)] focus:outline-none";
|
|
13
|
+
function Badge({ value, active }) {
|
|
14
|
+
return /* @__PURE__ */ jsx(
|
|
15
|
+
"span",
|
|
16
|
+
{
|
|
17
|
+
className: cn(
|
|
18
|
+
"inline-flex min-w-5 shrink-0 items-center justify-center rounded-full px-1.5 py-0.5 text-[11px] font-bold leading-none",
|
|
19
|
+
active ? "bg-[var(--as-primary)] text-[var(--as-primary-fg)]" : "bg-[var(--as-muted)] text-[var(--as-muted-fg)]"
|
|
20
|
+
),
|
|
21
|
+
children: value
|
|
22
|
+
}
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
function ItemBadge({ badge, active }) {
|
|
26
|
+
if (badge === void 0) return null;
|
|
27
|
+
return /* @__PURE__ */ jsx("span", { className: "ml-auto", children: /* @__PURE__ */ jsx(Badge, { value: badge, active }) });
|
|
28
|
+
}
|
|
29
|
+
var SidebarNav = React.forwardRef(
|
|
30
|
+
function SidebarNav2({
|
|
31
|
+
items,
|
|
32
|
+
secondaryItems,
|
|
33
|
+
activeId,
|
|
34
|
+
onNavigate,
|
|
35
|
+
collapsed = false,
|
|
36
|
+
onCollapsedChange,
|
|
37
|
+
collapsible = false,
|
|
38
|
+
expandedIds,
|
|
39
|
+
onExpandedIdsChange,
|
|
40
|
+
header,
|
|
41
|
+
footer,
|
|
42
|
+
labels,
|
|
43
|
+
className,
|
|
44
|
+
navClassName
|
|
45
|
+
}, ref) {
|
|
46
|
+
const baseId = React.useId();
|
|
47
|
+
const listRef = React.useRef(null);
|
|
48
|
+
const secondaryRef = React.useRef(null);
|
|
49
|
+
const flyoutTimer = React.useRef(null);
|
|
50
|
+
const [openFlyout, setOpenFlyout] = React.useState(null);
|
|
51
|
+
const [internalExpanded, setInternalExpanded] = React.useState([]);
|
|
52
|
+
const controlledExpanded = expandedIds !== void 0 && onExpandedIdsChange !== void 0;
|
|
53
|
+
const expanded = controlledExpanded ? expandedIds : internalExpanded;
|
|
54
|
+
const setExpandedList = React.useCallback(
|
|
55
|
+
(next) => {
|
|
56
|
+
if (controlledExpanded) onExpandedIdsChange?.(next);
|
|
57
|
+
else setInternalExpanded(next);
|
|
58
|
+
},
|
|
59
|
+
[controlledExpanded, onExpandedIdsChange]
|
|
60
|
+
);
|
|
61
|
+
const toggleGroup = React.useCallback(
|
|
62
|
+
(id) => {
|
|
63
|
+
setExpandedList(
|
|
64
|
+
expanded.includes(id) ? expanded.filter((g) => g !== id) : [...expanded, id]
|
|
65
|
+
);
|
|
66
|
+
},
|
|
67
|
+
[expanded, setExpandedList]
|
|
68
|
+
);
|
|
69
|
+
React.useEffect(() => {
|
|
70
|
+
if (controlledExpanded || activeId == null) return;
|
|
71
|
+
const owner = items.find((it) => it.children?.some((c) => c.id === activeId)) ?? secondaryItems?.find((it) => it.children?.some((c) => c.id === activeId));
|
|
72
|
+
if (owner && !expanded.includes(owner.id)) {
|
|
73
|
+
setExpandedList([...expanded, owner.id]);
|
|
74
|
+
}
|
|
75
|
+
}, [activeId, items, secondaryItems, controlledExpanded]);
|
|
76
|
+
React.useEffect(() => {
|
|
77
|
+
return () => {
|
|
78
|
+
if (flyoutTimer.current) clearTimeout(flyoutTimer.current);
|
|
79
|
+
};
|
|
80
|
+
}, []);
|
|
81
|
+
const flyoutEnter = (id) => {
|
|
82
|
+
if (flyoutTimer.current) clearTimeout(flyoutTimer.current);
|
|
83
|
+
flyoutTimer.current = setTimeout(() => setOpenFlyout(id), FLYOUT_DELAY_MS);
|
|
84
|
+
};
|
|
85
|
+
const flyoutLeave = () => {
|
|
86
|
+
if (flyoutTimer.current) clearTimeout(flyoutTimer.current);
|
|
87
|
+
flyoutTimer.current = setTimeout(() => setOpenFlyout(null), FLYOUT_DELAY_MS);
|
|
88
|
+
};
|
|
89
|
+
const flyoutEnterCancel = () => {
|
|
90
|
+
if (flyoutTimer.current) clearTimeout(flyoutTimer.current);
|
|
91
|
+
};
|
|
92
|
+
const navigate = (id) => {
|
|
93
|
+
setOpenFlyout(null);
|
|
94
|
+
onNavigate?.(id);
|
|
95
|
+
};
|
|
96
|
+
const navRegions = () => [listRef.current, secondaryRef.current].filter(
|
|
97
|
+
(r) => r !== null
|
|
98
|
+
);
|
|
99
|
+
const visibleItems = () => {
|
|
100
|
+
return navRegions().flatMap(
|
|
101
|
+
(region) => Array.from(
|
|
102
|
+
region.querySelectorAll("[data-as-nav-item]")
|
|
103
|
+
).filter((el) => el.closest("[hidden]") === null)
|
|
104
|
+
);
|
|
105
|
+
};
|
|
106
|
+
const focusSibling = (current, offset) => {
|
|
107
|
+
const all = visibleItems();
|
|
108
|
+
const index = all.indexOf(current);
|
|
109
|
+
if (index === -1) return;
|
|
110
|
+
const next = all[Math.min(Math.max(index + offset, 0), all.length - 1)];
|
|
111
|
+
next?.focus();
|
|
112
|
+
};
|
|
113
|
+
const handleFlyoutArrowKeys = (e) => {
|
|
114
|
+
if (e.key !== "ArrowDown" && e.key !== "ArrowUp") return;
|
|
115
|
+
e.preventDefault();
|
|
116
|
+
const buttons = Array.from(
|
|
117
|
+
e.currentTarget.querySelectorAll(
|
|
118
|
+
"button[data-as-flyout-item]:not([disabled])"
|
|
119
|
+
)
|
|
120
|
+
);
|
|
121
|
+
const index = buttons.indexOf(document.activeElement);
|
|
122
|
+
const step = e.key === "ArrowDown" ? 1 : buttons.length - 1;
|
|
123
|
+
buttons[(index + step) % buttons.length]?.focus();
|
|
124
|
+
};
|
|
125
|
+
const groupTriggerFor = (id) => navRegions().flatMap(
|
|
126
|
+
(r) => Array.from(r.querySelectorAll("[data-as-nav-group-id]"))
|
|
127
|
+
).find((b) => b.dataset.asNavGroupId === id);
|
|
128
|
+
const firstChildFor = (id) => navRegions().flatMap(
|
|
129
|
+
(r) => Array.from(r.querySelectorAll("[data-as-nav-parent-id]"))
|
|
130
|
+
).find((b) => b.dataset.asNavParentId === id && !b.disabled);
|
|
131
|
+
const handleListKeyDown = (e) => {
|
|
132
|
+
const button = e.target.closest(
|
|
133
|
+
"[data-as-nav-item]"
|
|
134
|
+
);
|
|
135
|
+
if (!button) return;
|
|
136
|
+
const groupId = button.dataset.asNavGroupId;
|
|
137
|
+
const parentId = button.dataset.asNavParentId;
|
|
138
|
+
switch (e.key) {
|
|
139
|
+
case "ArrowDown":
|
|
140
|
+
e.preventDefault();
|
|
141
|
+
focusSibling(button, 1);
|
|
142
|
+
break;
|
|
143
|
+
case "ArrowUp":
|
|
144
|
+
e.preventDefault();
|
|
145
|
+
focusSibling(button, -1);
|
|
146
|
+
break;
|
|
147
|
+
case "Home": {
|
|
148
|
+
e.preventDefault();
|
|
149
|
+
visibleItems()[0]?.focus();
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
case "End": {
|
|
153
|
+
e.preventDefault();
|
|
154
|
+
const all = visibleItems();
|
|
155
|
+
all[all.length - 1]?.focus();
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
case "ArrowRight":
|
|
159
|
+
if (groupId) {
|
|
160
|
+
e.preventDefault();
|
|
161
|
+
if (!expanded.includes(groupId)) {
|
|
162
|
+
setExpandedList([...expanded, groupId]);
|
|
163
|
+
} else {
|
|
164
|
+
firstChildFor(groupId)?.focus();
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
break;
|
|
168
|
+
case "ArrowLeft":
|
|
169
|
+
if (parentId) {
|
|
170
|
+
e.preventDefault();
|
|
171
|
+
groupTriggerFor(parentId)?.focus();
|
|
172
|
+
} else if (groupId && expanded.includes(groupId)) {
|
|
173
|
+
e.preventDefault();
|
|
174
|
+
toggleGroup(groupId);
|
|
175
|
+
}
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
const navAria = labels?.navAria ?? "Main navigation";
|
|
180
|
+
const rail = collapsed;
|
|
181
|
+
const flyoutHeader = (text) => /* @__PURE__ */ jsx("div", { className: "border-b border-[var(--as-border)] px-2 py-1.5 text-xs font-bold text-[var(--as-muted-fg)]", children: text });
|
|
182
|
+
const renderItem = (item, index, list) => {
|
|
183
|
+
if (item.children !== void 0 && item.children.length === 0) {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
const sectionSeen = !rail && item.section !== void 0 && list.findIndex((i) => i.section === item.section) === index;
|
|
187
|
+
const sectionDivider = sectionSeen ? /* @__PURE__ */ jsx(
|
|
188
|
+
"div",
|
|
189
|
+
{
|
|
190
|
+
role: "presentation",
|
|
191
|
+
className: "mt-2 border-t border-[var(--as-border)] px-3 pb-1 pt-3 text-[11px] font-bold uppercase tracking-wide text-[var(--as-muted-fg)]",
|
|
192
|
+
children: item.section
|
|
193
|
+
}
|
|
194
|
+
) : null;
|
|
195
|
+
const active = item.id === activeId;
|
|
196
|
+
const childActive = item.children?.some((c) => c.id === activeId) ?? false;
|
|
197
|
+
const isExpanded = expanded.includes(item.id);
|
|
198
|
+
const Icon = item.icon;
|
|
199
|
+
const groupId = `${baseId}-group-${item.id}`;
|
|
200
|
+
if (item.children && item.children.length > 0 && rail) {
|
|
201
|
+
return /* @__PURE__ */ jsxs("li", { children: [
|
|
202
|
+
sectionDivider,
|
|
203
|
+
/* @__PURE__ */ jsxs(
|
|
204
|
+
PopoverPrimitive.Root,
|
|
205
|
+
{
|
|
206
|
+
open: openFlyout === item.id,
|
|
207
|
+
onOpenChange: (open) => {
|
|
208
|
+
if (!open) setOpenFlyout(null);
|
|
209
|
+
},
|
|
210
|
+
children: [
|
|
211
|
+
/* @__PURE__ */ jsx(PopoverPrimitive.Anchor, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
212
|
+
"button",
|
|
213
|
+
{
|
|
214
|
+
type: "button",
|
|
215
|
+
"data-as-nav-item": true,
|
|
216
|
+
"data-as-nav-group-id": item.id,
|
|
217
|
+
disabled: item.disabled,
|
|
218
|
+
"aria-label": labels?.openGroup ?? `Open ${item.label} menu`,
|
|
219
|
+
"aria-haspopup": "true",
|
|
220
|
+
"aria-expanded": openFlyout === item.id,
|
|
221
|
+
onClick: () => setOpenFlyout(item.id),
|
|
222
|
+
onKeyDown: (e) => {
|
|
223
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
224
|
+
e.preventDefault();
|
|
225
|
+
setOpenFlyout(item.id);
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
onMouseEnter: () => flyoutEnter(item.id),
|
|
229
|
+
onMouseLeave: flyoutLeave,
|
|
230
|
+
className: cn(itemButtonClass(active || childActive), "justify-center px-0"),
|
|
231
|
+
children: Icon ? /* @__PURE__ */ jsx(
|
|
232
|
+
Icon,
|
|
233
|
+
{
|
|
234
|
+
className: cn(
|
|
235
|
+
"size-5 shrink-0",
|
|
236
|
+
active || childActive ? "text-[var(--as-primary)]" : "text-[var(--as-muted-fg)]"
|
|
237
|
+
),
|
|
238
|
+
"aria-hidden": true
|
|
239
|
+
}
|
|
240
|
+
) : null
|
|
241
|
+
}
|
|
242
|
+
) }),
|
|
243
|
+
/* @__PURE__ */ jsx(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
|
|
244
|
+
PopoverPrimitive.Content,
|
|
245
|
+
{
|
|
246
|
+
side: "right",
|
|
247
|
+
sideOffset: 8,
|
|
248
|
+
"aria-label": item.label,
|
|
249
|
+
onCloseAutoFocus: (e) => {
|
|
250
|
+
e.preventDefault();
|
|
251
|
+
groupTriggerFor(item.id)?.focus();
|
|
252
|
+
},
|
|
253
|
+
onMouseEnter: flyoutEnterCancel,
|
|
254
|
+
onMouseLeave: flyoutLeave,
|
|
255
|
+
onKeyDown: handleFlyoutArrowKeys,
|
|
256
|
+
className: flyoutContentClass,
|
|
257
|
+
children: [
|
|
258
|
+
flyoutHeader(item.label),
|
|
259
|
+
/* @__PURE__ */ jsx("ul", { className: "mt-1 space-y-0.5", children: item.children.map((child) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
|
|
260
|
+
"button",
|
|
261
|
+
{
|
|
262
|
+
type: "button",
|
|
263
|
+
"data-as-flyout-item": true,
|
|
264
|
+
disabled: child.disabled,
|
|
265
|
+
onClick: () => navigate(child.id),
|
|
266
|
+
"aria-current": child.id === activeId ? "page" : void 0,
|
|
267
|
+
className: cn(
|
|
268
|
+
"flex w-full cursor-pointer items-center gap-2 rounded-[calc(var(--as-radius-sm)-2px)] px-2 py-1.5 text-left text-sm outline-none transition-colors focus:bg-[var(--as-secondary)] disabled:pointer-events-none disabled:opacity-50",
|
|
269
|
+
child.id === activeId && "font-bold text-[var(--as-primary)]"
|
|
270
|
+
),
|
|
271
|
+
children: [
|
|
272
|
+
child.icon ? /* @__PURE__ */ jsx(child.icon, { "aria-hidden": true }) : null,
|
|
273
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: child.label }),
|
|
274
|
+
/* @__PURE__ */ jsx(ItemBadge, { badge: child.badge, active: child.id === activeId })
|
|
275
|
+
]
|
|
276
|
+
}
|
|
277
|
+
) }, child.id)) })
|
|
278
|
+
]
|
|
279
|
+
}
|
|
280
|
+
) })
|
|
281
|
+
]
|
|
282
|
+
}
|
|
283
|
+
)
|
|
284
|
+
] }, item.id);
|
|
285
|
+
}
|
|
286
|
+
if (item.children && item.children.length > 0) {
|
|
287
|
+
return /* @__PURE__ */ jsxs("li", { children: [
|
|
288
|
+
sectionDivider,
|
|
289
|
+
/* @__PURE__ */ jsxs(
|
|
290
|
+
"button",
|
|
291
|
+
{
|
|
292
|
+
type: "button",
|
|
293
|
+
"data-as-nav-item": true,
|
|
294
|
+
"data-as-nav-group-id": item.id,
|
|
295
|
+
disabled: item.disabled,
|
|
296
|
+
"aria-expanded": isExpanded,
|
|
297
|
+
"aria-controls": groupId,
|
|
298
|
+
onClick: () => toggleGroup(item.id),
|
|
299
|
+
className: itemButtonClass(active || childActive),
|
|
300
|
+
children: [
|
|
301
|
+
Icon ? /* @__PURE__ */ jsx(
|
|
302
|
+
Icon,
|
|
303
|
+
{
|
|
304
|
+
className: cn(
|
|
305
|
+
"size-4 shrink-0",
|
|
306
|
+
active || childActive ? "text-[var(--as-primary)]" : "text-[var(--as-muted-fg)]"
|
|
307
|
+
),
|
|
308
|
+
"aria-hidden": true
|
|
309
|
+
}
|
|
310
|
+
) : null,
|
|
311
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: item.label }),
|
|
312
|
+
/* @__PURE__ */ jsx(ItemBadge, { badge: item.badge, active: active || childActive }),
|
|
313
|
+
isExpanded ? /* @__PURE__ */ jsx(ChevronDown, { className: "size-4 shrink-0 text-[var(--as-muted-fg)]", "aria-hidden": true }) : /* @__PURE__ */ jsx(ChevronRight, { className: "size-4 shrink-0 text-[var(--as-muted-fg)]", "aria-hidden": true })
|
|
314
|
+
]
|
|
315
|
+
}
|
|
316
|
+
),
|
|
317
|
+
/* @__PURE__ */ jsx("ul", { id: groupId, hidden: !isExpanded, className: "mt-1 space-y-0.5 pl-6", children: item.children.map((child) => {
|
|
318
|
+
const childActiveItem = child.id === activeId;
|
|
319
|
+
const ChildIcon = child.icon;
|
|
320
|
+
const sectionSeen2 = child.section !== void 0 && item.children.findIndex(
|
|
321
|
+
(c) => c.section === child.section
|
|
322
|
+
) === item.children.indexOf(child);
|
|
323
|
+
return /* @__PURE__ */ jsxs("li", { children: [
|
|
324
|
+
sectionSeen2 ? /* @__PURE__ */ jsx(
|
|
325
|
+
"div",
|
|
326
|
+
{
|
|
327
|
+
role: "presentation",
|
|
328
|
+
className: "mt-2 border-t border-[var(--as-border)] px-3 pb-1 pt-3 text-[11px] font-bold uppercase tracking-wide text-[var(--as-muted-fg)]",
|
|
329
|
+
children: child.section
|
|
330
|
+
}
|
|
331
|
+
) : null,
|
|
332
|
+
/* @__PURE__ */ jsxs(
|
|
333
|
+
"button",
|
|
334
|
+
{
|
|
335
|
+
type: "button",
|
|
336
|
+
"data-as-nav-item": true,
|
|
337
|
+
"data-as-nav-parent-id": item.id,
|
|
338
|
+
disabled: child.disabled,
|
|
339
|
+
"aria-current": childActiveItem ? "page" : void 0,
|
|
340
|
+
onClick: () => navigate(child.id),
|
|
341
|
+
className: cn(
|
|
342
|
+
itemButtonClass(childActiveItem),
|
|
343
|
+
"py-2 pl-3 pr-2"
|
|
344
|
+
),
|
|
345
|
+
children: [
|
|
346
|
+
ChildIcon ? /* @__PURE__ */ jsx(ChildIcon, { className: "size-4 shrink-0", "aria-hidden": true }) : null,
|
|
347
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: child.label }),
|
|
348
|
+
/* @__PURE__ */ jsx(ItemBadge, { badge: child.badge, active: childActiveItem })
|
|
349
|
+
]
|
|
350
|
+
}
|
|
351
|
+
)
|
|
352
|
+
] }, child.id);
|
|
353
|
+
}) })
|
|
354
|
+
] }, item.id);
|
|
355
|
+
}
|
|
356
|
+
return /* @__PURE__ */ jsxs("li", { children: [
|
|
357
|
+
sectionDivider,
|
|
358
|
+
/* @__PURE__ */ jsxs(
|
|
359
|
+
"button",
|
|
360
|
+
{
|
|
361
|
+
type: "button",
|
|
362
|
+
"data-as-nav-item": true,
|
|
363
|
+
disabled: item.disabled,
|
|
364
|
+
title: rail ? item.label : void 0,
|
|
365
|
+
"aria-label": rail ? item.label : void 0,
|
|
366
|
+
"aria-current": active ? "page" : void 0,
|
|
367
|
+
onClick: () => navigate(item.id),
|
|
368
|
+
className: cn(itemButtonClass(active), rail && "justify-center px-0"),
|
|
369
|
+
children: [
|
|
370
|
+
Icon ? /* @__PURE__ */ jsx(
|
|
371
|
+
Icon,
|
|
372
|
+
{
|
|
373
|
+
className: cn(
|
|
374
|
+
"shrink-0",
|
|
375
|
+
rail ? "size-5" : "size-4",
|
|
376
|
+
active ? "text-[var(--as-primary)]" : "text-[var(--as-muted-fg)]"
|
|
377
|
+
),
|
|
378
|
+
"aria-hidden": true
|
|
379
|
+
}
|
|
380
|
+
) : null,
|
|
381
|
+
!rail ? /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: item.label }) : null,
|
|
382
|
+
!rail ? /* @__PURE__ */ jsx(ItemBadge, { badge: item.badge, active }) : null
|
|
383
|
+
]
|
|
384
|
+
}
|
|
385
|
+
)
|
|
386
|
+
] }, item.id);
|
|
387
|
+
};
|
|
388
|
+
return /* @__PURE__ */ jsxs(
|
|
389
|
+
"nav",
|
|
390
|
+
{
|
|
391
|
+
ref,
|
|
392
|
+
"data-as": "sidebar-nav",
|
|
393
|
+
"aria-label": navAria,
|
|
394
|
+
className: cn(
|
|
395
|
+
"relative flex h-full flex-col border-r border-[var(--as-border)] bg-[var(--as-surface)] text-[var(--as-fg)] transition-[width] duration-200",
|
|
396
|
+
rail ? "w-20" : "w-64",
|
|
397
|
+
className
|
|
398
|
+
),
|
|
399
|
+
children: [
|
|
400
|
+
collapsible ? /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-y-0 -right-3 z-10 hidden flex-col justify-center lg:flex", children: /* @__PURE__ */ jsx(
|
|
401
|
+
"button",
|
|
402
|
+
{
|
|
403
|
+
type: "button",
|
|
404
|
+
onClick: () => onCollapsedChange?.(!collapsed),
|
|
405
|
+
title: collapsed ? labels?.expand ?? "Expand sidebar" : labels?.collapse ?? "Collapse sidebar",
|
|
406
|
+
"aria-label": collapsed ? labels?.expand ?? "Expand sidebar" : labels?.collapse ?? "Collapse sidebar",
|
|
407
|
+
className: "pointer-events-auto flex size-6 items-center justify-center rounded-lg border border-[var(--as-border)] bg-[var(--as-surface-raised)] text-[var(--as-muted-fg)] shadow-[var(--as-shadow-2)] transition-colors hover:text-[var(--as-fg)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]",
|
|
408
|
+
children: collapsed ? /* @__PURE__ */ jsx(ChevronRight, { className: "size-3.5", "aria-hidden": true }) : /* @__PURE__ */ jsx(ChevronLeft, { className: "size-3.5", "aria-hidden": true })
|
|
409
|
+
}
|
|
410
|
+
) }) : null,
|
|
411
|
+
header ? /* @__PURE__ */ jsx("div", { className: "shrink-0", children: header }) : null,
|
|
412
|
+
/* @__PURE__ */ jsx(
|
|
413
|
+
"div",
|
|
414
|
+
{
|
|
415
|
+
ref: listRef,
|
|
416
|
+
onKeyDown: handleListKeyDown,
|
|
417
|
+
className: cn("min-h-0 flex-1 overflow-y-auto px-3 py-3", navClassName),
|
|
418
|
+
children: /* @__PURE__ */ jsx("ul", { className: "space-y-1", children: items.map((item, index) => renderItem(item, index, items)) })
|
|
419
|
+
}
|
|
420
|
+
),
|
|
421
|
+
secondaryItems && secondaryItems.length > 0 ? /* @__PURE__ */ jsx(
|
|
422
|
+
"div",
|
|
423
|
+
{
|
|
424
|
+
ref: secondaryRef,
|
|
425
|
+
onKeyDown: handleListKeyDown,
|
|
426
|
+
className: "shrink-0 border-t border-[var(--as-border)] px-3 py-3",
|
|
427
|
+
children: /* @__PURE__ */ jsx("ul", { className: "space-y-1", children: secondaryItems.map((item, index) => renderItem(item, index, secondaryItems)) })
|
|
428
|
+
}
|
|
429
|
+
) : null,
|
|
430
|
+
footer ? /* @__PURE__ */ jsx("div", { className: "shrink-0 border-t border-[var(--as-border)] p-3", children: footer }) : null
|
|
431
|
+
]
|
|
432
|
+
}
|
|
433
|
+
);
|
|
434
|
+
}
|
|
435
|
+
);
|
|
436
|
+
|
|
437
|
+
export { SidebarNav };
|
|
438
|
+
//# sourceMappingURL=chunk-3IXMZ6KG.js.map
|
|
439
|
+
//# sourceMappingURL=chunk-3IXMZ6KG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/sidebar-nav/SidebarNav.tsx"],"names":["SidebarNav","sectionSeen"],"mappings":"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","file":"chunk-3IXMZ6KG.js","sourcesContent":["import * as React from 'react'\nimport * as PopoverPrimitive from '@radix-ui/react-popover'\nimport {\n ChevronDown,\n ChevronLeft,\n ChevronRight,\n type LucideIcon,\n} from 'lucide-react'\nimport { cn } from '../../lib/utils'\n\nexport interface NavChild {\n id: string\n label: string\n icon?: LucideIcon\n badge?: string | number\n /** Divider label rendered above this child (first occurrence only). */\n section?: string\n disabled?: boolean\n}\n\nexport interface NavItem {\n id: string\n label: string\n icon?: LucideIcon\n badge?: string | number\n /** One nesting level; deeper trees stay app-side. */\n children?: NavChild[]\n disabled?: boolean\n /** Divider label rendered above this item (first occurrence only) —\n * for flat sidebars with visual groups. Ignored in the collapsed rail. */\n section?: string\n}\n\nexport interface SidebarNavLabels {\n /** aria-label on the <nav> element. */\n navAria?: string\n /** Collapsed-rail toggle title when collapsed. */\n expand?: string\n /** Collapsed-rail toggle title when expanded. */\n collapse?: string\n /** aria-label for a collapsed group's flyout trigger. */\n openGroup?: string\n}\n\nexport interface SidebarNavProps {\n /** Pre-filtered by the app (roles, feature flags); order = render order. */\n items: NavItem[]\n /** Flat items pinned below the scroll area, above `footer` (the\n * Settings/About pattern). Same rendering, active state, rail behavior\n * and keyboard traversal order as `items`. */\n secondaryItems?: NavItem[]\n /** The app resolves route → id; matching logic stays app-side. */\n activeId?: string | null\n /** Fires for leaves only; group triggers toggle expansion instead. */\n onNavigate?: (id: string) => void\n /** Controlled icon rail. */\n collapsed?: boolean\n onCollapsedChange?: (collapsed: boolean) => void\n /** Renders the rail collapse/expand toggle handle (desktop only). */\n collapsible?: boolean\n /** Controlled group expansion; uncontrolled defaults to internal\n * state + auto-expand when activeId enters a group. */\n expandedIds?: string[]\n onExpandedIdsChange?: (ids: string[]) => void\n /** Slot above the list (brand, logo). */\n header?: React.ReactNode\n /** Slot below the list and pinned items (version, legal links). */\n footer?: React.ReactNode\n labels?: SidebarNavLabels\n className?: string\n /** Targets the scrollable list region. */\n navClassName?: string\n}\n\nconst FLYOUT_DELAY_MS = 150\n\nconst itemButtonClass = (active: boolean) =>\n cn(\n 'flex w-full cursor-pointer items-center gap-3 rounded-[var(--as-radius)] px-3 py-2.5 text-left text-sm transition-colors focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)] disabled:pointer-events-none disabled:opacity-50',\n active\n ? 'bg-[color-mix(in_srgb,var(--as-primary)_12%,transparent)] font-bold text-[var(--as-primary)]'\n : 'font-medium text-[var(--as-fg)] hover:bg-[var(--as-muted)]',\n )\n\nconst flyoutContentClass =\n 'as-anim-pop z-[var(--as-z-popover)] min-w-44 rounded-[var(--as-radius-lg)] border border-[var(--as-border)] bg-[var(--as-surface-raised)] p-1 text-[var(--as-fg)] shadow-[var(--as-shadow-3)] focus:outline-none'\n\nfunction Badge({ value, active }: { value: string | number; active: boolean }) {\n return (\n <span\n className={cn(\n 'inline-flex min-w-5 shrink-0 items-center justify-center rounded-full px-1.5 py-0.5 text-[11px] font-bold leading-none',\n active\n ? 'bg-[var(--as-primary)] text-[var(--as-primary-fg)]'\n : 'bg-[var(--as-muted)] text-[var(--as-muted-fg)]',\n )}\n >\n {value}\n </span>\n )\n}\n\nfunction ItemBadge({ badge, active }: { badge?: string | number; active: boolean }) {\n if (badge === undefined) return null\n return (\n <span className=\"ml-auto\">\n <Badge value={badge} active={active} />\n </span>\n )\n}\n\nexport const SidebarNav = React.forwardRef<HTMLElement, SidebarNavProps>(\n function SidebarNav(\n {\n items,\n secondaryItems,\n activeId,\n onNavigate,\n collapsed = false,\n onCollapsedChange,\n collapsible = false,\n expandedIds,\n onExpandedIdsChange,\n header,\n footer,\n labels,\n className,\n navClassName,\n },\n ref,\n ) {\n const baseId = React.useId()\n const listRef = React.useRef<HTMLDivElement>(null)\n const secondaryRef = React.useRef<HTMLDivElement>(null)\n const flyoutTimer = React.useRef<ReturnType<typeof setTimeout> | null>(null)\n const [openFlyout, setOpenFlyout] = React.useState<string | null>(null)\n const [internalExpanded, setInternalExpanded] = React.useState<string[]>([])\n\n const controlledExpanded =\n expandedIds !== undefined && onExpandedIdsChange !== undefined\n const expanded = controlledExpanded ? (expandedIds as string[]) : internalExpanded\n\n const setExpandedList = React.useCallback(\n (next: string[]) => {\n if (controlledExpanded) onExpandedIdsChange?.(next)\n else setInternalExpanded(next)\n },\n [controlledExpanded, onExpandedIdsChange],\n )\n\n const toggleGroup = React.useCallback(\n (id: string) => {\n setExpandedList(\n expanded.includes(id)\n ? expanded.filter((g) => g !== id)\n : [...expanded, id],\n )\n },\n [expanded, setExpandedList],\n )\n\n // Auto-expand the group containing the active item so the highlighted\n // entry is always visible (uncontrolled mode only — the app owns\n // expansion when controlled).\n React.useEffect(() => {\n if (controlledExpanded || activeId == null) return\n const owner =\n items.find((it) => it.children?.some((c) => c.id === activeId)) ??\n secondaryItems?.find((it) => it.children?.some((c) => c.id === activeId))\n if (owner && !expanded.includes(owner.id)) {\n setExpandedList([...expanded, owner.id])\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [activeId, items, secondaryItems, controlledExpanded])\n\n React.useEffect(() => {\n return () => {\n if (flyoutTimer.current) clearTimeout(flyoutTimer.current)\n }\n }, [])\n\n const flyoutEnter = (id: string) => {\n if (flyoutTimer.current) clearTimeout(flyoutTimer.current)\n flyoutTimer.current = setTimeout(() => setOpenFlyout(id), FLYOUT_DELAY_MS)\n }\n const flyoutLeave = () => {\n if (flyoutTimer.current) clearTimeout(flyoutTimer.current)\n flyoutTimer.current = setTimeout(() => setOpenFlyout(null), FLYOUT_DELAY_MS)\n }\n const flyoutEnterCancel = () => {\n if (flyoutTimer.current) clearTimeout(flyoutTimer.current)\n }\n\n const navigate = (id: string) => {\n setOpenFlyout(null)\n onNavigate?.(id)\n }\n\n // ---- keyboard navigation over the visible list ----\n const navRegions = () =>\n [listRef.current, secondaryRef.current].filter(\n (r): r is HTMLDivElement => r !== null,\n )\n\n const visibleItems = (): HTMLButtonElement[] => {\n return navRegions().flatMap((region) =>\n Array.from(\n region.querySelectorAll<HTMLButtonElement>('[data-as-nav-item]'),\n ).filter((el) => el.closest('[hidden]') === null),\n )\n }\n\n const focusSibling = (current: HTMLElement, offset: number) => {\n const all = visibleItems()\n const index = all.indexOf(current as HTMLButtonElement)\n if (index === -1) return\n const next = all[Math.min(Math.max(index + offset, 0), all.length - 1)]\n next?.focus()\n }\n\n const handleFlyoutArrowKeys = (e: React.KeyboardEvent) => {\n if (e.key !== 'ArrowDown' && e.key !== 'ArrowUp') return\n e.preventDefault()\n const buttons = Array.from(\n (e.currentTarget as HTMLElement).querySelectorAll<HTMLButtonElement>(\n 'button[data-as-flyout-item]:not([disabled])',\n ),\n )\n const index = buttons.indexOf(document.activeElement as HTMLButtonElement)\n const step = e.key === 'ArrowDown' ? 1 : buttons.length - 1\n buttons[(index + step) % buttons.length]?.focus()\n }\n\n const groupTriggerFor = (id: string) =>\n navRegions()\n .flatMap((r) =>\n Array.from(r.querySelectorAll<HTMLButtonElement>('[data-as-nav-group-id]')),\n )\n .find((b) => b.dataset.asNavGroupId === id)\n\n const firstChildFor = (id: string) =>\n navRegions()\n .flatMap((r) =>\n Array.from(r.querySelectorAll<HTMLButtonElement>('[data-as-nav-parent-id]')),\n )\n .find((b) => b.dataset.asNavParentId === id && !b.disabled)\n\n const handleListKeyDown = (e: React.KeyboardEvent) => {\n const button = (e.target as HTMLElement).closest<HTMLButtonElement>(\n '[data-as-nav-item]',\n )\n if (!button) return\n const groupId = button.dataset.asNavGroupId\n const parentId = button.dataset.asNavParentId\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault()\n focusSibling(button, 1)\n break\n case 'ArrowUp':\n e.preventDefault()\n focusSibling(button, -1)\n break\n case 'Home': {\n e.preventDefault()\n visibleItems()[0]?.focus()\n break\n }\n case 'End': {\n e.preventDefault()\n const all = visibleItems()\n all[all.length - 1]?.focus()\n break\n }\n case 'ArrowRight':\n if (groupId) {\n e.preventDefault()\n if (!expanded.includes(groupId)) {\n setExpandedList([...expanded, groupId])\n } else {\n firstChildFor(groupId)?.focus()\n }\n }\n break\n case 'ArrowLeft':\n if (parentId) {\n e.preventDefault()\n groupTriggerFor(parentId)?.focus()\n } else if (groupId && expanded.includes(groupId)) {\n e.preventDefault()\n toggleGroup(groupId)\n }\n break\n }\n }\n\n const navAria = labels?.navAria ?? 'Main navigation'\n const rail = collapsed\n\n const flyoutHeader = (text: string) => (\n <div className=\"border-b border-[var(--as-border)] px-2 py-1.5 text-xs font-bold text-[var(--as-muted-fg)]\">\n {text}\n </div>\n )\n\n const renderItem = (item: NavItem, index: number, list: NavItem[]) => {\n // Group whose children were all filtered out app-side renders\n // nothing (presentational).\n if (item.children !== undefined && item.children.length === 0) {\n return null\n }\n const sectionSeen =\n !rail &&\n item.section !== undefined &&\n list.findIndex((i) => i.section === item.section) === index\n const sectionDivider = sectionSeen ? (\n <div\n role=\"presentation\"\n className=\"mt-2 border-t border-[var(--as-border)] px-3 pb-1 pt-3 text-[11px] font-bold uppercase tracking-wide text-[var(--as-muted-fg)]\"\n >\n {item.section}\n </div>\n ) : null\n const active = item.id === activeId\n const childActive = item.children?.some((c) => c.id === activeId) ?? false\n const isExpanded = expanded.includes(item.id)\n const Icon = item.icon\n const groupId = `${baseId}-group-${item.id}`\n\n if (item.children && item.children.length > 0 && rail) {\n return (\n <li key={item.id}>\n {sectionDivider}\n <PopoverPrimitive.Root\n open={openFlyout === item.id}\n onOpenChange={(open) => {\n if (!open) setOpenFlyout(null)\n }}\n >\n <PopoverPrimitive.Anchor asChild>\n <button\n type=\"button\"\n data-as-nav-item\n data-as-nav-group-id={item.id}\n disabled={item.disabled}\n aria-label={labels?.openGroup ?? `Open ${item.label} menu`}\n aria-haspopup=\"true\"\n aria-expanded={openFlyout === item.id}\n onClick={() => setOpenFlyout(item.id)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n setOpenFlyout(item.id)\n }\n }}\n onMouseEnter={() => flyoutEnter(item.id)}\n onMouseLeave={flyoutLeave}\n className={cn(itemButtonClass(active || childActive), 'justify-center px-0')}\n >\n {Icon ? (\n <Icon\n className={cn(\n 'size-5 shrink-0',\n active || childActive\n ? 'text-[var(--as-primary)]'\n : 'text-[var(--as-muted-fg)]',\n )}\n aria-hidden\n />\n ) : null}\n </button>\n </PopoverPrimitive.Anchor>\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Content\n side=\"right\"\n sideOffset={8}\n aria-label={item.label}\n onCloseAutoFocus={(e) => {\n // Menu-standard: focus returns to the trigger.\n e.preventDefault()\n groupTriggerFor(item.id)?.focus()\n }}\n onMouseEnter={flyoutEnterCancel}\n onMouseLeave={flyoutLeave}\n onKeyDown={handleFlyoutArrowKeys}\n className={flyoutContentClass}\n >\n {flyoutHeader(item.label)}\n <ul className=\"mt-1 space-y-0.5\">\n {item.children.map((child) => (\n <li key={child.id}>\n <button\n type=\"button\"\n data-as-flyout-item\n disabled={child.disabled}\n onClick={() => navigate(child.id)}\n aria-current={child.id === activeId ? 'page' : undefined}\n className={cn(\n 'flex w-full cursor-pointer items-center gap-2 rounded-[calc(var(--as-radius-sm)-2px)] px-2 py-1.5 text-left text-sm outline-none transition-colors focus:bg-[var(--as-secondary)] disabled:pointer-events-none disabled:opacity-50',\n child.id === activeId && 'font-bold text-[var(--as-primary)]',\n )}\n >\n {child.icon ? <child.icon aria-hidden /> : null}\n <span className=\"min-w-0 flex-1 truncate\">{child.label}</span>\n <ItemBadge badge={child.badge} active={child.id === activeId} />\n </button>\n </li>\n ))}\n </ul>\n </PopoverPrimitive.Content>\n </PopoverPrimitive.Portal>\n </PopoverPrimitive.Root>\n </li>\n )\n }\n\n if (item.children && item.children.length > 0) {\n return (\n <li key={item.id}>\n {sectionDivider}\n <button\n type=\"button\"\n data-as-nav-item\n data-as-nav-group-id={item.id}\n disabled={item.disabled}\n aria-expanded={isExpanded}\n aria-controls={groupId}\n onClick={() => toggleGroup(item.id)}\n className={itemButtonClass(active || childActive)}\n >\n {Icon ? (\n <Icon\n className={cn(\n 'size-4 shrink-0',\n active || childActive\n ? 'text-[var(--as-primary)]'\n : 'text-[var(--as-muted-fg)]',\n )}\n aria-hidden\n />\n ) : null}\n <span className=\"min-w-0 flex-1 truncate\">{item.label}</span>\n <ItemBadge badge={item.badge} active={active || childActive} />\n {isExpanded ? (\n <ChevronDown className=\"size-4 shrink-0 text-[var(--as-muted-fg)]\" aria-hidden />\n ) : (\n <ChevronRight className=\"size-4 shrink-0 text-[var(--as-muted-fg)]\" aria-hidden />\n )}\n </button>\n <ul id={groupId} hidden={!isExpanded} className=\"mt-1 space-y-0.5 pl-6\">\n {item.children.map((child) => {\n const childActiveItem = child.id === activeId\n const ChildIcon = child.icon\n const sectionSeen =\n child.section !== undefined &&\n item.children!.findIndex(\n (c) => c.section === child.section,\n ) === item.children!.indexOf(child)\n return (\n <li key={child.id}>\n {sectionSeen ? (\n <div\n role=\"presentation\"\n className=\"mt-2 border-t border-[var(--as-border)] px-3 pb-1 pt-3 text-[11px] font-bold uppercase tracking-wide text-[var(--as-muted-fg)]\"\n >\n {child.section}\n </div>\n ) : null}\n <button\n type=\"button\"\n data-as-nav-item\n data-as-nav-parent-id={item.id}\n disabled={child.disabled}\n aria-current={childActiveItem ? 'page' : undefined}\n onClick={() => navigate(child.id)}\n className={cn(\n itemButtonClass(childActiveItem),\n 'py-2 pl-3 pr-2',\n )}\n >\n {ChildIcon ? (\n <ChildIcon className=\"size-4 shrink-0\" aria-hidden />\n ) : null}\n <span className=\"min-w-0 flex-1 truncate\">{child.label}</span>\n <ItemBadge badge={child.badge} active={childActiveItem} />\n </button>\n </li>\n )\n })}\n </ul>\n </li>\n )\n }\n\n return (\n <li key={item.id}>\n {sectionDivider}\n <button\n type=\"button\"\n data-as-nav-item\n disabled={item.disabled}\n title={rail ? item.label : undefined}\n aria-label={rail ? item.label : undefined}\n aria-current={active ? 'page' : undefined}\n onClick={() => navigate(item.id)}\n className={cn(itemButtonClass(active), rail && 'justify-center px-0')}\n >\n {Icon ? (\n <Icon\n className={cn(\n 'shrink-0',\n rail ? 'size-5' : 'size-4',\n active ? 'text-[var(--as-primary)]' : 'text-[var(--as-muted-fg)]',\n )}\n aria-hidden\n />\n ) : null}\n {!rail ? <span className=\"min-w-0 flex-1 truncate\">{item.label}</span> : null}\n {!rail ? <ItemBadge badge={item.badge} active={active} /> : null}\n </button>\n </li>\n )\n }\n\n return (\n <nav\n ref={ref}\n data-as=\"sidebar-nav\"\n aria-label={navAria}\n className={cn(\n 'relative flex h-full flex-col border-r border-[var(--as-border)] bg-[var(--as-surface)] text-[var(--as-fg)] transition-[width] duration-200',\n rail ? 'w-20' : 'w-64',\n className,\n )}\n >\n {collapsible ? (\n <div className=\"pointer-events-none absolute inset-y-0 -right-3 z-10 hidden flex-col justify-center lg:flex\">\n <button\n type=\"button\"\n onClick={() => onCollapsedChange?.(!collapsed)}\n title={\n collapsed\n ? (labels?.expand ?? 'Expand sidebar')\n : (labels?.collapse ?? 'Collapse sidebar')\n }\n aria-label={\n collapsed\n ? (labels?.expand ?? 'Expand sidebar')\n : (labels?.collapse ?? 'Collapse sidebar')\n }\n className=\"pointer-events-auto flex size-6 items-center justify-center rounded-lg border border-[var(--as-border)] bg-[var(--as-surface-raised)] text-[var(--as-muted-fg)] shadow-[var(--as-shadow-2)] transition-colors hover:text-[var(--as-fg)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n {collapsed ? (\n <ChevronRight className=\"size-3.5\" aria-hidden />\n ) : (\n <ChevronLeft className=\"size-3.5\" aria-hidden />\n )}\n </button>\n </div>\n ) : null}\n\n {header ? <div className=\"shrink-0\">{header}</div> : null}\n\n <div\n ref={listRef}\n onKeyDown={handleListKeyDown}\n className={cn('min-h-0 flex-1 overflow-y-auto px-3 py-3', navClassName)}\n >\n <ul className=\"space-y-1\">\n {items.map((item, index) => renderItem(item, index, items))}\n </ul>\n </div>\n\n {secondaryItems && secondaryItems.length > 0 ? (\n <div\n ref={secondaryRef}\n onKeyDown={handleListKeyDown}\n className=\"shrink-0 border-t border-[var(--as-border)] px-3 py-3\"\n >\n <ul className=\"space-y-1\">\n {secondaryItems.map((item, index) => renderItem(item, index, secondaryItems))}\n </ul>\n </div>\n ) : null}\n\n {footer ? (\n <div className=\"shrink-0 border-t border-[var(--as-border)] p-3\">{footer}</div>\n ) : null}\n </nav>\n )\n },\n)\n"]}
|