@datum-cloud/datum-ui 2.1.0 → 2.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/dist/app-navigation/index.mjs +2 -2
- package/dist/{app-navigation-CNig4Maj.mjs → app-navigation--tEBCrfl.mjs} +180 -164
- package/dist/components/base/sidebar/context.d.ts +3 -0
- package/dist/components/base/sidebar/shell.d.ts +2 -2
- package/dist/components/features/app-navigation/nav-menu/index.d.ts +1 -1
- package/dist/components/features/app-navigation/nav-menu/nav-menu-context.d.ts +4 -0
- package/dist/components/features/app-navigation/nav-menu/nav-menu-item.d.ts +3 -0
- package/dist/components/features/app-navigation/nav-menu/types.d.ts +18 -0
- package/dist/index.mjs +2 -2
- package/dist/sidebar/index.mjs +1 -1
- package/dist/{sidebar-BRUNOqXi.mjs → sidebar-D6BoAr6C.mjs} +53 -16
- package/dist/styles/shadcn/animations.css +2 -2
- package/package.json +1 -1
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { C as SidebarProvider, S as SidebarGroupLabel, _ as SidebarMenuSubButton, a as SidebarInput, b as SidebarGroupAction, c as SidebarSeparator, d as SidebarMenuAction, f as SidebarMenuBadge, g as SidebarMenuSub, h as SidebarMenuSkeleton, i as SidebarHeader, l as SidebarTrigger, m as SidebarMenuItem, n as SidebarContent, o as SidebarInset, p as SidebarMenuButton, r as SidebarFooter, s as SidebarRail, t as Sidebar, u as SidebarMenu, v as SidebarMenuSubItem, w as useSidebar, x as SidebarGroupContent, y as SidebarGroup } from "../sidebar-
|
|
2
|
-
import { a as hasActiveDescendant, i as findItemByHref, n as NavMenu, o as isNavItemActive, r as NavMenuItem, s as normalizePath, t as AppNavigation } from "../app-navigation
|
|
1
|
+
import { C as SidebarProvider, S as SidebarGroupLabel, _ as SidebarMenuSubButton, a as SidebarInput, b as SidebarGroupAction, c as SidebarSeparator, d as SidebarMenuAction, f as SidebarMenuBadge, g as SidebarMenuSub, h as SidebarMenuSkeleton, i as SidebarHeader, l as SidebarTrigger, m as SidebarMenuItem, n as SidebarContent, o as SidebarInset, p as SidebarMenuButton, r as SidebarFooter, s as SidebarRail, t as Sidebar, u as SidebarMenu, v as SidebarMenuSubItem, w as useSidebar, x as SidebarGroupContent, y as SidebarGroup } from "../sidebar-D6BoAr6C.mjs";
|
|
2
|
+
import { a as hasActiveDescendant, i as findItemByHref, n as NavMenu, o as isNavItemActive, r as NavMenuItem, s as normalizePath, t as AppNavigation } from "../app-navigation--tEBCrfl.mjs";
|
|
3
3
|
export { AppNavigation, NavMenu, NavMenuItem, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, findItemByHref, hasActiveDescendant, isNavItemActive, normalizePath, useSidebar };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as cn } from "./utils-Bu32wN-x.mjs";
|
|
2
2
|
import { t as Icon } from "./icon-wrapper-DKfJlJd0.mjs";
|
|
3
3
|
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "./collapsible/index.mjs";
|
|
4
|
-
import {
|
|
4
|
+
import { c as SidebarSeparator, g as SidebarMenuSub, h as SidebarMenuSkeleton, i as SidebarHeader, l as SidebarTrigger, m as SidebarMenuItem, n as SidebarContent, p as SidebarMenuButton, r as SidebarFooter, t as Sidebar, u as SidebarMenu, w as useSidebar, x as SidebarGroupContent, y as SidebarGroup } from "./sidebar-D6BoAr6C.mjs";
|
|
5
5
|
import { ChevronRight, ExternalLinkIcon } from "lucide-react";
|
|
6
6
|
import { createContext, memo, use, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
7
7
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -132,28 +132,66 @@ function NavSidebarMenuButton({ ref, item, isActive, disableTooltip, className,
|
|
|
132
132
|
NavSidebarMenuButton.displayName = "NavSidebarMenuButton";
|
|
133
133
|
//#endregion
|
|
134
134
|
//#region src/components/features/app-navigation/nav-menu/nav-menu-item.tsx
|
|
135
|
+
const COLLAPSIBLE_CONTAINER_VARIANTS = {
|
|
136
|
+
hidden: { opacity: 0 },
|
|
137
|
+
visible: {
|
|
138
|
+
opacity: 1,
|
|
139
|
+
transition: {
|
|
140
|
+
staggerChildren: .04,
|
|
141
|
+
delayChildren: .06
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
const COLLAPSIBLE_CHILD_VARIANTS = {
|
|
146
|
+
hidden: {
|
|
147
|
+
opacity: 0,
|
|
148
|
+
y: -4
|
|
149
|
+
},
|
|
150
|
+
visible: {
|
|
151
|
+
opacity: 1,
|
|
152
|
+
y: 0,
|
|
153
|
+
transition: {
|
|
154
|
+
duration: .2,
|
|
155
|
+
ease: [
|
|
156
|
+
.32,
|
|
157
|
+
.72,
|
|
158
|
+
0,
|
|
159
|
+
1
|
|
160
|
+
]
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
};
|
|
135
164
|
/** Stable React key for a rendered nav item at a given depth. */
|
|
136
165
|
function itemKeyOf(item, level) {
|
|
137
166
|
return `${item.title}-${item.href || ""}-${level}`;
|
|
138
167
|
}
|
|
139
168
|
/**
|
|
169
|
+
* Cloudflare-style nav status chip: short inline pill with a dashed border,
|
|
170
|
+
* sitting immediately after the title (never overlapping / corner-pinned).
|
|
171
|
+
* Uses `div` (not `span`) so SidebarMenuButton's `[&>span:last-child]:truncate`
|
|
172
|
+
* does not clip the chip.
|
|
173
|
+
*/
|
|
174
|
+
function NavBadge({ badge }) {
|
|
175
|
+
return /* @__PURE__ */ jsx("div", {
|
|
176
|
+
className: cn("text-muted-foreground inline-flex shrink-0 items-center rounded-full border border-dashed px-1.5 py-px", "text-[10px] leading-none font-medium tracking-wide", "border-border/80"),
|
|
177
|
+
children: badge.label
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
140
181
|
* Recursive nav row. Dispatches to the correct presentational part based on the
|
|
141
182
|
* item type and the current sidebar state, and renders itself for children.
|
|
142
183
|
* Memoized so unchanged subtrees skip re-rendering when their props are stable.
|
|
184
|
+
*
|
|
185
|
+
* Items with children use {@link NavCollapsibleItem}. Section headers use
|
|
186
|
+
* {@link NavGroup} (always open, no dropdown).
|
|
143
187
|
*/
|
|
144
188
|
const NavMenuItem = memo(({ item, level = 0 }) => {
|
|
145
|
-
const { state, isMobile } = useNavMenuContext();
|
|
146
189
|
if (item.hidden) return null;
|
|
147
190
|
if (item.type === "group") return /* @__PURE__ */ jsx(NavGroup, {
|
|
148
191
|
item,
|
|
149
192
|
level
|
|
150
193
|
});
|
|
151
|
-
|
|
152
|
-
if (state === "collapsed" && !isMobile && level <= 2 && hasChildren) return /* @__PURE__ */ jsx(NavCollapsedItem, {
|
|
153
|
-
item,
|
|
154
|
-
level
|
|
155
|
-
});
|
|
156
|
-
if (hasChildren) return /* @__PURE__ */ jsx(NavCollapsibleItem, {
|
|
194
|
+
if ((item.children || []).length > 0) return /* @__PURE__ */ jsx(NavCollapsibleItem, {
|
|
157
195
|
item,
|
|
158
196
|
level
|
|
159
197
|
});
|
|
@@ -163,116 +201,85 @@ const NavMenuItem = memo(({ item, level = 0 }) => {
|
|
|
163
201
|
});
|
|
164
202
|
});
|
|
165
203
|
NavMenuItem.displayName = "NavMenuItem";
|
|
166
|
-
/** A labelled group of nav items (
|
|
204
|
+
/** A labelled group of nav items (always open — Vercel-style section headers). */
|
|
167
205
|
function NavGroup({ item, level }) {
|
|
168
|
-
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs(SidebarGroup, {
|
|
169
|
-
className: "mb-2 p-0! px-2",
|
|
170
|
-
children: [item.title && /* @__PURE__ */ jsx(SidebarGroupLabel, {
|
|
171
|
-
className: "lowercase group-data-[state=collapsed]:hidden first-letter:uppercase",
|
|
172
|
-
children: item.title
|
|
173
|
-
}), /* @__PURE__ */ jsx(SidebarGroupContent, {
|
|
174
|
-
className: "flex flex-col gap-1",
|
|
175
|
-
children: (item.children || []).map((child) => /* @__PURE__ */ jsx(NavMenuItem, {
|
|
176
|
-
item: child,
|
|
177
|
-
level: level + 1
|
|
178
|
-
}, itemKeyOf(child, level + 1)))
|
|
179
|
-
})]
|
|
180
|
-
}), /* @__PURE__ */ jsx(SidebarSeparator, { className: "my-2 hidden group-data-[state=collapsed]:block" })] });
|
|
181
|
-
}
|
|
182
|
-
/**
|
|
183
|
-
* Collapsed-sidebar rendering for an item with children: the parent button
|
|
184
|
-
* expands the sidebar, and active descendants are represented as dots.
|
|
185
|
-
*/
|
|
186
|
-
function NavCollapsedItem({ item, level }) {
|
|
187
206
|
const ctx = useNavMenuContext();
|
|
188
|
-
const { pathname,
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
const hasActiveChild = (item.children?.filter((child) => hasActiveDescendant(child, pathname)) || []).length > 0;
|
|
192
|
-
return /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
207
|
+
const { pathname, isIconRail, isMobile } = ctx;
|
|
208
|
+
const hasActiveChild = item.children?.some((child) => hasActiveDescendant(child, pathname)) ?? false;
|
|
209
|
+
if (isIconRail && !isMobile) return /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
193
210
|
item.showSeparatorAbove && /* @__PURE__ */ jsx(SidebarSeparator, { className: "my-1" }),
|
|
194
|
-
/* @__PURE__ */ jsx(SidebarMenu, {
|
|
195
|
-
className: "
|
|
196
|
-
children:
|
|
211
|
+
/* @__PURE__ */ jsx(SidebarMenu, {
|
|
212
|
+
className: "px-2",
|
|
213
|
+
children: /* @__PURE__ */ jsx(NavSidebarMenuButton, {
|
|
197
214
|
item,
|
|
198
|
-
isActive,
|
|
215
|
+
isActive: hasActiveChild,
|
|
199
216
|
disableTooltip: ctx.disableTooltip,
|
|
200
|
-
className: ctx.itemClassName,
|
|
217
|
+
className: cn(ctx.itemClassName, hasActiveChild && "[&>svg:first-of-type]:text-primary"),
|
|
201
218
|
activeClassName: ctx.activeItemClassName,
|
|
202
|
-
onClick: () =>
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
children: item.children?.map((subItem) => {
|
|
224
|
-
const isSubItemActive = isNavItemActive(subItem, pathname);
|
|
225
|
-
return /* @__PURE__ */ jsx(motion.div, {
|
|
226
|
-
variants: {
|
|
227
|
-
hidden: { opacity: 0 },
|
|
228
|
-
visible: {
|
|
229
|
-
opacity: 1,
|
|
230
|
-
transition: {
|
|
231
|
-
duration: .2,
|
|
232
|
-
ease: "easeOut"
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
},
|
|
236
|
-
children: /* @__PURE__ */ jsx(SidebarMenuButton, {
|
|
237
|
-
tooltip: subItem.title,
|
|
238
|
-
isActive: isSubItemActive,
|
|
239
|
-
className: "h-6 p-0 group-data-[collapsible=icon]:h-6! group-data-[collapsible=icon]:p-0!",
|
|
240
|
-
asChild: true,
|
|
241
|
-
children: /* @__PURE__ */ jsx(ctx.linkComponent, {
|
|
242
|
-
className: "flex items-center justify-center",
|
|
243
|
-
...getLinkProps(subItem.href || ""),
|
|
244
|
-
onClick: () => {
|
|
245
|
-
handleNavigation();
|
|
246
|
-
},
|
|
247
|
-
children: /* @__PURE__ */ jsx("span", { className: cn("size-1 rounded-full", isSubItemActive ? "bg-primary" : "bg-sidebar-primary-foreground") })
|
|
248
|
-
})
|
|
249
|
-
})
|
|
250
|
-
}, `collapsed-dot-${subItem.href}-${level}`);
|
|
219
|
+
onClick: () => ctx.setOpen(true)
|
|
220
|
+
})
|
|
221
|
+
}),
|
|
222
|
+
item.showSeparatorBelow && /* @__PURE__ */ jsx(SidebarSeparator, { className: "my-2" })
|
|
223
|
+
] });
|
|
224
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
225
|
+
item.showSeparatorAbove && /* @__PURE__ */ jsx(SidebarSeparator, { className: "my-2" }),
|
|
226
|
+
/* @__PURE__ */ jsxs(SidebarGroup, {
|
|
227
|
+
className: "mb-1 p-0! px-2",
|
|
228
|
+
children: [item.title && /* @__PURE__ */ jsx(SidebarMenu, {
|
|
229
|
+
className: "w-full",
|
|
230
|
+
children: /* @__PURE__ */ jsx(NavSidebarMenuButton, {
|
|
231
|
+
item,
|
|
232
|
+
isActive: hasActiveChild,
|
|
233
|
+
disableTooltip: true,
|
|
234
|
+
className: cn(ctx.itemClassName, hasActiveChild && "[&>svg:first-of-type]:text-primary", "pointer-events-none h-7 text-xs font-medium text-muted-foreground hover:bg-transparent", hasActiveChild && "text-primary"),
|
|
235
|
+
activeClassName: ctx.activeItemClassName,
|
|
236
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
237
|
+
className: "min-w-0 truncate",
|
|
238
|
+
children: item.title
|
|
239
|
+
})
|
|
251
240
|
})
|
|
241
|
+
}), /* @__PURE__ */ jsx(SidebarGroupContent, {
|
|
242
|
+
className: "flex flex-col gap-0.5",
|
|
243
|
+
children: (item.children || []).map((child) => /* @__PURE__ */ jsx(NavMenuItem, {
|
|
244
|
+
item: child,
|
|
245
|
+
level: level + 1
|
|
246
|
+
}, itemKeyOf(child, level + 1)))
|
|
252
247
|
})]
|
|
253
|
-
})
|
|
248
|
+
}),
|
|
254
249
|
item.showSeparatorBelow && /* @__PURE__ */ jsx(SidebarSeparator, { className: "my-2" })
|
|
255
250
|
] });
|
|
256
251
|
}
|
|
257
252
|
/**
|
|
258
|
-
*
|
|
259
|
-
*
|
|
253
|
+
* Collapsible section for items with children. Used in both expanded and
|
|
254
|
+
* icon-collapsed sidebar modes so width animation does not remount the tree.
|
|
255
|
+
* When the sidebar is icon-collapsed, the panel is forced closed; clicking the
|
|
256
|
+
* parent expands the sidebar and opens this section.
|
|
260
257
|
*/
|
|
261
258
|
function NavCollapsibleItem({ item, level }) {
|
|
262
259
|
const ctx = useNavMenuContext();
|
|
263
|
-
const { pathname, state, openItems, isInitialMount, previousOpenItems, previousState, previousPathname } = ctx;
|
|
260
|
+
const { pathname, state, isIconRail, isMobile, openItems, isInitialMount, previousOpenItems, previousState, previousPathname } = ctx;
|
|
264
261
|
const stateKey = getNavItemKey(item, level);
|
|
265
262
|
const isActive = isNavItemActive(item, pathname);
|
|
266
263
|
const hasActiveChild = (item.children?.filter((child) => hasActiveDescendant(child, pathname)) || []).length > 0;
|
|
264
|
+
const sidebarCollapsed = isIconRail && !isMobile;
|
|
267
265
|
const isOpen = openItems[stateKey] !== void 0 ? openItems[stateKey] : hasActiveChild;
|
|
266
|
+
const panelOpen = sidebarCollapsed ? false : isOpen;
|
|
268
267
|
return /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
269
268
|
item.showSeparatorAbove && /* @__PURE__ */ jsx(SidebarSeparator, { className: "my-2" }),
|
|
270
269
|
/* @__PURE__ */ jsx(SidebarMenu, {
|
|
271
270
|
className: "px-2",
|
|
272
271
|
children: /* @__PURE__ */ jsx(Collapsible, {
|
|
273
272
|
asChild: true,
|
|
274
|
-
open:
|
|
273
|
+
open: panelOpen,
|
|
275
274
|
onOpenChange: (open) => {
|
|
275
|
+
if (sidebarCollapsed) {
|
|
276
|
+
ctx.setOpen(true);
|
|
277
|
+
ctx.setOpenItems((prev) => ({
|
|
278
|
+
...prev,
|
|
279
|
+
[stateKey]: true
|
|
280
|
+
}));
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
276
283
|
ctx.setOpenItems((prev) => ({
|
|
277
284
|
...prev,
|
|
278
285
|
[stateKey]: open
|
|
@@ -286,55 +293,47 @@ function NavCollapsibleItem({ item, level }) {
|
|
|
286
293
|
className: "w-full",
|
|
287
294
|
children: /* @__PURE__ */ jsxs(NavSidebarMenuButton, {
|
|
288
295
|
item,
|
|
289
|
-
isActive,
|
|
296
|
+
isActive: isActive || sidebarCollapsed && hasActiveChild,
|
|
290
297
|
disableTooltip: ctx.disableTooltip,
|
|
291
|
-
className: ctx.itemClassName,
|
|
298
|
+
className: cn(ctx.itemClassName, hasActiveChild && "font-semibold [&>svg:first-of-type]:text-primary"),
|
|
292
299
|
activeClassName: ctx.activeItemClassName,
|
|
293
|
-
children: [
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
300
|
+
children: [
|
|
301
|
+
/* @__PURE__ */ jsx("span", {
|
|
302
|
+
className: "min-w-0 truncate group-data-[collapsible=icon]:hidden",
|
|
303
|
+
children: item.title
|
|
304
|
+
}),
|
|
305
|
+
item.badge && /* @__PURE__ */ jsx("span", {
|
|
306
|
+
className: "group-data-[collapsible=icon]:hidden",
|
|
307
|
+
children: /* @__PURE__ */ jsx(NavBadge, { badge: item.badge })
|
|
308
|
+
}),
|
|
309
|
+
/* @__PURE__ */ jsx(Icon, {
|
|
310
|
+
icon: ChevronRight,
|
|
311
|
+
className: "ml-auto shrink-0 transition-transform duration-200 group-data-[collapsible=icon]:hidden group-data-[state=open]/collapsible:rotate-90"
|
|
312
|
+
})
|
|
313
|
+
]
|
|
297
314
|
})
|
|
298
315
|
}), /* @__PURE__ */ jsx(CollapsibleContent, {
|
|
299
|
-
className: "data-[state=open]:animate-collapsible-down data-[state=closed]:animate-collapsible-up overflow-hidden",
|
|
316
|
+
className: "data-[state=open]:animate-collapsible-down data-[state=closed]:animate-collapsible-up overflow-hidden group-data-[collapsible=icon]:hidden",
|
|
300
317
|
children: /* @__PURE__ */ jsx("div", {
|
|
301
318
|
style: {
|
|
302
319
|
minHeight: 0,
|
|
303
320
|
overflow: "hidden"
|
|
304
321
|
},
|
|
305
322
|
children: /* @__PURE__ */ jsx(motion.div, {
|
|
306
|
-
variants:
|
|
307
|
-
hidden: { opacity: 0 },
|
|
308
|
-
visible: {
|
|
309
|
-
opacity: 1,
|
|
310
|
-
transition: {
|
|
311
|
-
staggerChildren: .05,
|
|
312
|
-
delayChildren: .1
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
},
|
|
323
|
+
variants: COLLAPSIBLE_CONTAINER_VARIANTS,
|
|
316
324
|
initial: isInitialMount.current || previousOpenItems.current[stateKey] === openItems[stateKey] && previousState.current === state && previousPathname.current === pathname && !hasActiveChild ? "visible" : "hidden",
|
|
317
|
-
animate:
|
|
325
|
+
animate: panelOpen ? "visible" : "hidden",
|
|
318
326
|
children: /* @__PURE__ */ jsx(SidebarMenuSub, {
|
|
319
327
|
className: cn(level >= 1 ? "mr-0 pr-[.1rem]" : "", level === 2 ? "pl-4" : "", level === 3 ? "pl-6" : "", "mr-0 gap-0.5 pr-0"),
|
|
320
328
|
children: item.children?.map((subItem, index) => /* @__PURE__ */ jsx(motion.div, {
|
|
321
|
-
variants:
|
|
322
|
-
hidden: { opacity: 0 },
|
|
323
|
-
visible: {
|
|
324
|
-
opacity: 1,
|
|
325
|
-
transition: {
|
|
326
|
-
duration: .2,
|
|
327
|
-
ease: "easeOut"
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
},
|
|
329
|
+
variants: COLLAPSIBLE_CHILD_VARIANTS,
|
|
331
330
|
children: /* @__PURE__ */ jsx(NavMenuItem, {
|
|
332
331
|
item: subItem,
|
|
333
332
|
level: level + 1
|
|
334
333
|
})
|
|
335
334
|
}, `${subItem.href}-${level}-${index}`))
|
|
336
335
|
})
|
|
337
|
-
}, `collapsible-${stateKey}-${
|
|
336
|
+
}, `collapsible-${stateKey}-${panelOpen}`)
|
|
338
337
|
})
|
|
339
338
|
})]
|
|
340
339
|
})
|
|
@@ -360,31 +359,49 @@ function NavLeafItem({ item, level }) {
|
|
|
360
359
|
item,
|
|
361
360
|
isActive: isActive && !hasActiveChild,
|
|
362
361
|
disableTooltip: ctx.disableTooltip,
|
|
363
|
-
className: cn(level >= 1 && "h-
|
|
362
|
+
className: cn(level >= 1 && "h-7", item.muted && "[&_svg]:text-muted-foreground", ctx.itemClassName),
|
|
364
363
|
activeClassName: ctx.activeItemClassName,
|
|
365
364
|
children: item.type === "externalLink" ? /* @__PURE__ */ jsxs("a", {
|
|
366
365
|
href: item.href || "",
|
|
367
366
|
target: "_blank",
|
|
368
367
|
rel: "noopener noreferrer",
|
|
369
|
-
className: "flex items-center
|
|
370
|
-
children: [
|
|
371
|
-
|
|
372
|
-
children: [item?.icon && /* @__PURE__ */ jsx(Icon, {
|
|
368
|
+
className: "flex min-w-0 items-center gap-2",
|
|
369
|
+
children: [
|
|
370
|
+
item?.icon && /* @__PURE__ */ jsx(Icon, {
|
|
373
371
|
icon: item.icon,
|
|
374
|
-
className: "size-4 transition-all duration-300"
|
|
375
|
-
}),
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
372
|
+
className: "size-4 shrink-0 transition-all duration-300"
|
|
373
|
+
}),
|
|
374
|
+
/* @__PURE__ */ jsx("span", {
|
|
375
|
+
className: "min-w-0 truncate group-data-[collapsible=icon]:hidden",
|
|
376
|
+
children: item.title
|
|
377
|
+
}),
|
|
378
|
+
item.badge ? /* @__PURE__ */ jsx("span", {
|
|
379
|
+
className: "group-data-[collapsible=icon]:hidden",
|
|
380
|
+
children: /* @__PURE__ */ jsx(NavBadge, { badge: item.badge })
|
|
381
|
+
}) : /* @__PURE__ */ jsx(Icon, {
|
|
382
|
+
icon: ExternalLinkIcon,
|
|
383
|
+
className: "ml-auto size-4 shrink-0 group-data-[collapsible=icon]:hidden"
|
|
384
|
+
})
|
|
385
|
+
]
|
|
380
386
|
}) : /* @__PURE__ */ jsxs(ctx.linkComponent, {
|
|
381
387
|
...getLinkProps(item.href || ""),
|
|
382
388
|
onClick: handleNavigation,
|
|
383
389
|
onMouseEnter: () => item.onPrefetch?.(),
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
390
|
+
className: "flex min-w-0 items-center gap-2",
|
|
391
|
+
children: [
|
|
392
|
+
item?.icon && /* @__PURE__ */ jsx(Icon, {
|
|
393
|
+
icon: item.icon,
|
|
394
|
+
className: "text-sidebar-primary shrink-0 transition-all duration-300"
|
|
395
|
+
}),
|
|
396
|
+
/* @__PURE__ */ jsx("span", {
|
|
397
|
+
className: "min-w-0 truncate group-data-[collapsible=icon]:hidden",
|
|
398
|
+
children: item.title
|
|
399
|
+
}),
|
|
400
|
+
item.badge && /* @__PURE__ */ jsx("span", {
|
|
401
|
+
className: "group-data-[collapsible=icon]:hidden",
|
|
402
|
+
children: /* @__PURE__ */ jsx(NavBadge, { badge: item.badge })
|
|
403
|
+
})
|
|
404
|
+
]
|
|
388
405
|
})
|
|
389
406
|
})
|
|
390
407
|
})
|
|
@@ -396,12 +413,13 @@ function NavLeafItem({ item, level }) {
|
|
|
396
413
|
//#region src/components/features/app-navigation/nav-menu/nav-menu.tsx
|
|
397
414
|
function NavMenu({ ref, className, items, currentPath, linkComponent: LinkComp = "a", overrideState, itemClassName, activeItemClassName, disableTooltip, closeOnNavigation, ...props }) {
|
|
398
415
|
const pathname = currentPath;
|
|
399
|
-
const { state: sidebarState, isMobile, closeForNavigation, setOpen } = useSidebar();
|
|
416
|
+
const { state: sidebarState, isIconRail: sidebarIsIconRail, isMobile, closeForNavigation, setOpen } = useSidebar();
|
|
400
417
|
const [openItems, setOpenItems] = useState({});
|
|
401
418
|
const isInitialMount = useRef(true);
|
|
402
419
|
const previousOpenItems = useRef({});
|
|
403
420
|
const previousPathname = useRef(pathname);
|
|
404
421
|
const state = overrideState ?? sidebarState;
|
|
422
|
+
const isIconRail = overrideState ? overrideState === "collapsed" : sidebarIsIconRail;
|
|
405
423
|
const previousState = useRef(state);
|
|
406
424
|
useEffect(() => {
|
|
407
425
|
previousOpenItems.current = openItems;
|
|
@@ -443,6 +461,7 @@ function NavMenu({ ref, className, items, currentPath, linkComponent: LinkComp =
|
|
|
443
461
|
const contextValue = useMemo(() => ({
|
|
444
462
|
pathname,
|
|
445
463
|
state,
|
|
464
|
+
isIconRail,
|
|
446
465
|
isMobile,
|
|
447
466
|
openItems,
|
|
448
467
|
setOpenItems,
|
|
@@ -460,6 +479,7 @@ function NavMenu({ ref, className, items, currentPath, linkComponent: LinkComp =
|
|
|
460
479
|
}), [
|
|
461
480
|
pathname,
|
|
462
481
|
state,
|
|
482
|
+
isIconRail,
|
|
463
483
|
isMobile,
|
|
464
484
|
openItems,
|
|
465
485
|
setOpen,
|
|
@@ -540,37 +560,33 @@ function AppNavigation({ navItems, title, closeOnNavigation, defaultOpen, curren
|
|
|
540
560
|
useEffect(() => {
|
|
541
561
|
if (defaultOpen === false) setOpen(false);
|
|
542
562
|
}, [defaultOpen, setOpen]);
|
|
543
|
-
return /* @__PURE__ */
|
|
563
|
+
return /* @__PURE__ */ jsxs(Sidebar, {
|
|
544
564
|
collapsible: props.collapsible ?? "offcanvas",
|
|
545
565
|
...props,
|
|
546
|
-
children: /* @__PURE__ */ jsxs(SidebarContent, {
|
|
566
|
+
children: [/* @__PURE__ */ jsxs(SidebarContent, {
|
|
547
567
|
className: "gap-0",
|
|
548
|
-
children: [
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
className: "
|
|
555
|
-
children: /* @__PURE__ */ jsx(
|
|
556
|
-
className: "flex flex-col gap-0 py-0",
|
|
557
|
-
children: /* @__PURE__ */ jsx(NavSkeleton, {})
|
|
558
|
-
})
|
|
559
|
-
}) : navItems.length > 0 && /* @__PURE__ */ jsx(NavMenu, {
|
|
560
|
-
className: "h-fit py-2",
|
|
561
|
-
items: navItems,
|
|
562
|
-
currentPath,
|
|
563
|
-
linkComponent,
|
|
564
|
-
closeOnNavigation,
|
|
565
|
-
itemClassName,
|
|
566
|
-
activeItemClassName
|
|
567
|
-
}),
|
|
568
|
-
props.collapsible !== "none" && /* @__PURE__ */ jsx(SidebarFooter, {
|
|
569
|
-
className: "mt-auto p-2",
|
|
570
|
-
children: /* @__PURE__ */ jsx(SidebarTrigger, {})
|
|
568
|
+
children: [title && /* @__PURE__ */ jsx(SidebarHeader, {
|
|
569
|
+
className: "px-4 pt-4 pb-0",
|
|
570
|
+
children: title
|
|
571
|
+
}), loading ? /* @__PURE__ */ jsx(SidebarGroup, {
|
|
572
|
+
className: "mb-2 p-0! px-0",
|
|
573
|
+
children: /* @__PURE__ */ jsx(SidebarGroupContent, {
|
|
574
|
+
className: "flex flex-col gap-0 py-0",
|
|
575
|
+
children: /* @__PURE__ */ jsx(NavSkeleton, {})
|
|
571
576
|
})
|
|
572
|
-
|
|
573
|
-
|
|
577
|
+
}) : navItems.length > 0 && /* @__PURE__ */ jsx(NavMenu, {
|
|
578
|
+
className: "h-fit py-2",
|
|
579
|
+
items: navItems,
|
|
580
|
+
currentPath,
|
|
581
|
+
linkComponent,
|
|
582
|
+
closeOnNavigation,
|
|
583
|
+
itemClassName,
|
|
584
|
+
activeItemClassName
|
|
585
|
+
})]
|
|
586
|
+
}), props.collapsible !== "none" && /* @__PURE__ */ jsx(SidebarFooter, {
|
|
587
|
+
className: "border-sidebar-border shrink-0 border-t p-2",
|
|
588
|
+
children: /* @__PURE__ */ jsx(SidebarTrigger, {})
|
|
589
|
+
})]
|
|
574
590
|
});
|
|
575
591
|
}
|
|
576
592
|
//#endregion
|
|
@@ -4,6 +4,8 @@ declare const SIDEBAR_WIDTH_MOBILE = "18rem";
|
|
|
4
4
|
declare const SIDEBAR_WIDTH_ICON = "3rem";
|
|
5
5
|
interface SidebarContext {
|
|
6
6
|
state: 'expanded' | 'collapsed';
|
|
7
|
+
/** True when the sidebar is width-collapsed (icon rail). */
|
|
8
|
+
isIconRail: boolean;
|
|
7
9
|
open: boolean;
|
|
8
10
|
setOpen: (open: boolean) => void;
|
|
9
11
|
openMobile: boolean;
|
|
@@ -12,6 +14,7 @@ interface SidebarContext {
|
|
|
12
14
|
toggleSidebar: () => void;
|
|
13
15
|
handleMouseEnter: () => void;
|
|
14
16
|
handleMouseLeave: () => void;
|
|
17
|
+
cancelHoverLeave: () => void;
|
|
15
18
|
forceClose: () => void;
|
|
16
19
|
closeForNavigation: () => void;
|
|
17
20
|
hasSubLayout: boolean;
|
|
@@ -12,7 +12,7 @@ declare function SidebarRail({ className, ...props }: React.ComponentProps<'butt
|
|
|
12
12
|
declare function SidebarInset({ className, ...props }: React.ComponentProps<'main'>): React.JSX.Element;
|
|
13
13
|
declare function SidebarInput({ className, ...props }: React.ComponentProps<typeof Input>): React.JSX.Element;
|
|
14
14
|
declare function SidebarHeader({ className, ...props }: React.ComponentProps<'div'>): React.JSX.Element;
|
|
15
|
-
declare function SidebarFooter({ className, ...props }: React.ComponentProps<'div'>): React.JSX.Element;
|
|
15
|
+
declare function SidebarFooter({ className, onMouseEnter, onMouseLeave, ...props }: React.ComponentProps<'div'>): React.JSX.Element;
|
|
16
16
|
declare function SidebarSeparator({ className, ...props }: React.ComponentProps<typeof Separator>): React.JSX.Element;
|
|
17
|
-
declare function SidebarContent({ className, ...props }: React.ComponentProps<'div'>): React.JSX.Element;
|
|
17
|
+
declare function SidebarContent({ className, onMouseEnter, onMouseLeave, ...props }: React.ComponentProps<'div'>): React.JSX.Element;
|
|
18
18
|
export { Sidebar, SidebarContent, SidebarFooter, SidebarHeader, SidebarInput, SidebarInset, SidebarRail, SidebarSeparator, SidebarTrigger, };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { findItemByHref, hasActiveDescendant, isNavItemActive, normalizePath, } from './active-path';
|
|
2
2
|
export { NavMenu } from './nav-menu';
|
|
3
3
|
export { NavMenuItem } from './nav-menu-item';
|
|
4
|
-
export type { NavItem } from './types';
|
|
4
|
+
export type { NavItem, NavItemBadge } from './types';
|
|
@@ -11,6 +11,10 @@ export interface NavMenuContextValue {
|
|
|
11
11
|
pathname: string;
|
|
12
12
|
/** Resolved sidebar state (override wins over the sidebar provider). */
|
|
13
13
|
state: 'expanded' | 'collapsed';
|
|
14
|
+
/**
|
|
15
|
+
* True when the sidebar is in icon-rail (collapsed) mode.
|
|
16
|
+
*/
|
|
17
|
+
isIconRail: boolean;
|
|
14
18
|
isMobile: boolean;
|
|
15
19
|
openItems: Record<string, boolean>;
|
|
16
20
|
setOpenItems: Dispatch<SetStateAction<Record<string, boolean>>>;
|
|
@@ -5,6 +5,9 @@ declare function itemKeyOf(item: NavItem, level: number): string;
|
|
|
5
5
|
* Recursive nav row. Dispatches to the correct presentational part based on the
|
|
6
6
|
* item type and the current sidebar state, and renders itself for children.
|
|
7
7
|
* Memoized so unchanged subtrees skip re-rendering when their props are stable.
|
|
8
|
+
*
|
|
9
|
+
* Items with children use {@link NavCollapsibleItem}. Section headers use
|
|
10
|
+
* {@link NavGroup} (always open, no dropdown).
|
|
8
11
|
*/
|
|
9
12
|
export declare const NavMenuItem: import("react").MemoExoticComponent<({ item, level }: {
|
|
10
13
|
item: NavItem;
|
|
@@ -1,4 +1,18 @@
|
|
|
1
1
|
import type { LucideIcon } from 'lucide-react';
|
|
2
|
+
import type { BadgeProps } from '../../../base/badge';
|
|
3
|
+
/**
|
|
4
|
+
* Declarative status chip rendered inline after a nav item title
|
|
5
|
+
* (Cloudflare-style dashed pill: "Beta", "New", "Soon").
|
|
6
|
+
* Data-only so hosts/plugins never pass React nodes into the sidebar.
|
|
7
|
+
*
|
|
8
|
+
* `type` / `theme` are retained for API compatibility but unused — nav chips
|
|
9
|
+
* always use the shared dashed-pill treatment.
|
|
10
|
+
*/
|
|
11
|
+
export interface NavItemBadge {
|
|
12
|
+
label: string;
|
|
13
|
+
type?: BadgeProps['type'];
|
|
14
|
+
theme?: BadgeProps['theme'];
|
|
15
|
+
}
|
|
2
16
|
export interface NavItem {
|
|
3
17
|
title: string;
|
|
4
18
|
href: string | null;
|
|
@@ -11,6 +25,10 @@ export interface NavItem {
|
|
|
11
25
|
hidden?: boolean;
|
|
12
26
|
showSeparatorAbove?: boolean;
|
|
13
27
|
showSeparatorBelow?: boolean;
|
|
28
|
+
/** Optional status badge (e.g. "Coming soon", "Beta"). */
|
|
29
|
+
badge?: NavItemBadge;
|
|
30
|
+
/** Soften label/icon color (e.g. planned services) without disabling the link. */
|
|
31
|
+
muted?: boolean;
|
|
14
32
|
excludePaths?: string[];
|
|
15
33
|
tabChildLinks?: string[];
|
|
16
34
|
/** Called when the user hovers over the link (e.g. to prefetch route data). */
|
package/dist/index.mjs
CHANGED
|
@@ -33,7 +33,7 @@ import { t as useBreakpoint } from "./use-breakpoint-CwiUJSmk.mjs";
|
|
|
33
33
|
import { t as ResponsiveDropdown } from "./responsive-dropdown-n0Txkax5.mjs";
|
|
34
34
|
import { t as ResponsivePopover } from "./responsive-popover-C_1nzIvm.mjs";
|
|
35
35
|
import { a as SelectLabel, c as SelectSeparator, i as SelectItem, l as SelectTrigger, n as SelectContent, o as SelectScrollDownButton, r as SelectGroup, s as SelectScrollUpButton, t as Select, u as SelectValue } from "./select-DCU44UO6.mjs";
|
|
36
|
-
import { C as SidebarProvider, S as SidebarGroupLabel, _ as SidebarMenuSubButton, a as SidebarInput, b as SidebarGroupAction, c as SidebarSeparator, d as SidebarMenuAction, f as SidebarMenuBadge, g as SidebarMenuSub, h as SidebarMenuSkeleton, i as SidebarHeader, l as SidebarTrigger, m as SidebarMenuItem, n as SidebarContent, o as SidebarInset, p as SidebarMenuButton, r as SidebarFooter, s as SidebarRail, t as Sidebar, u as SidebarMenu, v as SidebarMenuSubItem, w as useSidebar, x as SidebarGroupContent, y as SidebarGroup } from "./sidebar-
|
|
36
|
+
import { C as SidebarProvider, S as SidebarGroupLabel, _ as SidebarMenuSubButton, a as SidebarInput, b as SidebarGroupAction, c as SidebarSeparator, d as SidebarMenuAction, f as SidebarMenuBadge, g as SidebarMenuSub, h as SidebarMenuSkeleton, i as SidebarHeader, l as SidebarTrigger, m as SidebarMenuItem, n as SidebarContent, o as SidebarInset, p as SidebarMenuButton, r as SidebarFooter, s as SidebarRail, t as Sidebar, u as SidebarMenu, v as SidebarMenuSubItem, w as useSidebar, x as SidebarGroupContent, y as SidebarGroup } from "./sidebar-D6BoAr6C.mjs";
|
|
37
37
|
import { t as Tooltip } from "./tooltip-C_SL1imw.mjs";
|
|
38
38
|
import { Skeleton } from "./skeleton/index.mjs";
|
|
39
39
|
import { t as Switch } from "./switch-DPHr3TcN.mjs";
|
|
@@ -42,7 +42,7 @@ import { Tabs, TabsContent, TabsLinkTrigger, TabsList, TabsTrigger } from "./tab
|
|
|
42
42
|
import { t as Textarea } from "./textarea-B6YhjFVC.mjs";
|
|
43
43
|
import { a as ListItem, c as Title, d as titleVariants, i as List, l as paragraphVariants, n as Code, o as Paragraph, r as Link, s as Text, t as Blockquote, u as textVariants } from "./typography-BXIBvq3-.mjs";
|
|
44
44
|
import { t as VisuallyHidden } from "./visuallyhidden-D7L_qs8b.mjs";
|
|
45
|
-
import { a as hasActiveDescendant, i as findItemByHref, n as NavMenu, o as isNavItemActive, r as NavMenuItem, s as normalizePath, t as AppNavigation } from "./app-navigation
|
|
45
|
+
import { a as hasActiveDescendant, i as findItemByHref, n as NavMenu, o as isNavItemActive, r as NavMenuItem, s as normalizePath, t as AppNavigation } from "./app-navigation--tEBCrfl.mjs";
|
|
46
46
|
import { t as LoaderOverlay } from "./loader-overlay-DD94azZX.mjs";
|
|
47
47
|
import { t as Autocomplete } from "./autocomplete-DZ_HSBak.mjs";
|
|
48
48
|
import { Autosearch, defaultAutosearchValue } from "./autosearch/index.mjs";
|
package/dist/sidebar/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { C as SidebarProvider, S as SidebarGroupLabel, _ as SidebarMenuSubButton, a as SidebarInput, b as SidebarGroupAction, c as SidebarSeparator, d as SidebarMenuAction, f as SidebarMenuBadge, g as SidebarMenuSub, h as SidebarMenuSkeleton, i as SidebarHeader, l as SidebarTrigger, m as SidebarMenuItem, n as SidebarContent, o as SidebarInset, p as SidebarMenuButton, r as SidebarFooter, s as SidebarRail, t as Sidebar, u as SidebarMenu, v as SidebarMenuSubItem, w as useSidebar, x as SidebarGroupContent, y as SidebarGroup } from "../sidebar-
|
|
1
|
+
import { C as SidebarProvider, S as SidebarGroupLabel, _ as SidebarMenuSubButton, a as SidebarInput, b as SidebarGroupAction, c as SidebarSeparator, d as SidebarMenuAction, f as SidebarMenuBadge, g as SidebarMenuSub, h as SidebarMenuSkeleton, i as SidebarHeader, l as SidebarTrigger, m as SidebarMenuItem, n as SidebarContent, o as SidebarInset, p as SidebarMenuButton, r as SidebarFooter, s as SidebarRail, t as Sidebar, u as SidebarMenu, v as SidebarMenuSubItem, w as useSidebar, x as SidebarGroupContent, y as SidebarGroup } from "../sidebar-D6BoAr6C.mjs";
|
|
2
2
|
export { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, useSidebar };
|
|
@@ -78,18 +78,30 @@ function SidebarProvider({ defaultOpen = true, open: openProp, onOpenChange: set
|
|
|
78
78
|
return () => mql.removeEventListener("change", onChange);
|
|
79
79
|
}, [isMobile, setOpen]);
|
|
80
80
|
const effectiveOpen = open || expandOnHover && isHovered;
|
|
81
|
+
const isIconRail = !effectiveOpen;
|
|
81
82
|
const handleMouseEnter = React.useCallback(() => {
|
|
82
83
|
if (!expandOnHover) return;
|
|
83
84
|
if (hoverLockRef.current) return;
|
|
84
|
-
if (hoverTimeoutRef.current)
|
|
85
|
+
if (hoverTimeoutRef.current) {
|
|
86
|
+
clearTimeout(hoverTimeoutRef.current);
|
|
87
|
+
hoverTimeoutRef.current = null;
|
|
88
|
+
}
|
|
85
89
|
setIsHovered(true);
|
|
86
90
|
}, [expandOnHover]);
|
|
87
91
|
const handleMouseLeave = React.useCallback(() => {
|
|
88
92
|
if (!expandOnHover) return;
|
|
89
|
-
hoverTimeoutRef.current
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
+
if (hoverTimeoutRef.current) {
|
|
94
|
+
clearTimeout(hoverTimeoutRef.current);
|
|
95
|
+
hoverTimeoutRef.current = null;
|
|
96
|
+
}
|
|
97
|
+
setIsHovered(false);
|
|
98
|
+
}, [expandOnHover]);
|
|
99
|
+
const cancelHoverLeave = React.useCallback(() => {
|
|
100
|
+
if (hoverTimeoutRef.current) {
|
|
101
|
+
clearTimeout(hoverTimeoutRef.current);
|
|
102
|
+
hoverTimeoutRef.current = null;
|
|
103
|
+
}
|
|
104
|
+
}, []);
|
|
93
105
|
const toggleSidebar = React.useCallback(() => {
|
|
94
106
|
if (isMobile) {
|
|
95
107
|
setOpenMobile((current) => !current);
|
|
@@ -154,6 +166,7 @@ function SidebarProvider({ defaultOpen = true, open: openProp, onOpenChange: set
|
|
|
154
166
|
const state = effectiveOpen ? "expanded" : "collapsed";
|
|
155
167
|
const contextValue = React.useMemo(() => ({
|
|
156
168
|
state,
|
|
169
|
+
isIconRail,
|
|
157
170
|
open: effectiveOpen,
|
|
158
171
|
setOpen,
|
|
159
172
|
isMobile,
|
|
@@ -162,6 +175,7 @@ function SidebarProvider({ defaultOpen = true, open: openProp, onOpenChange: set
|
|
|
162
175
|
toggleSidebar,
|
|
163
176
|
handleMouseEnter,
|
|
164
177
|
handleMouseLeave,
|
|
178
|
+
cancelHoverLeave,
|
|
165
179
|
forceClose,
|
|
166
180
|
closeForNavigation,
|
|
167
181
|
hasSubLayout,
|
|
@@ -170,6 +184,7 @@ function SidebarProvider({ defaultOpen = true, open: openProp, onOpenChange: set
|
|
|
170
184
|
showBackdrop
|
|
171
185
|
}), [
|
|
172
186
|
state,
|
|
187
|
+
isIconRail,
|
|
173
188
|
effectiveOpen,
|
|
174
189
|
setOpen,
|
|
175
190
|
isMobile,
|
|
@@ -178,6 +193,7 @@ function SidebarProvider({ defaultOpen = true, open: openProp, onOpenChange: set
|
|
|
178
193
|
toggleSidebar,
|
|
179
194
|
handleMouseEnter,
|
|
180
195
|
handleMouseLeave,
|
|
196
|
+
cancelHoverLeave,
|
|
181
197
|
forceClose,
|
|
182
198
|
closeForNavigation,
|
|
183
199
|
hasSubLayout,
|
|
@@ -341,7 +357,7 @@ function SidebarMenuSub({ className, ...props }) {
|
|
|
341
357
|
return /* @__PURE__ */ jsx("ul", {
|
|
342
358
|
"data-slot": "sidebar-menu-sub",
|
|
343
359
|
"data-sidebar": "menu-sub",
|
|
344
|
-
className: cn("border-sidebar-border mx-3.5 flex min-w-0 translate-x-px flex-col px-2.5", "group-data-[collapsible=icon]:hidden", className),
|
|
360
|
+
className: cn("border-sidebar-border mx-3.5 flex min-w-0 translate-x-px flex-col gap-0.5 border-l px-2.5 py-0.5", "group-data-[collapsible=icon]:hidden", className),
|
|
345
361
|
...props
|
|
346
362
|
});
|
|
347
363
|
}
|
|
@@ -366,7 +382,7 @@ function SidebarMenuSubButton({ asChild = false, size = "md", isActive = false,
|
|
|
366
382
|
//#endregion
|
|
367
383
|
//#region src/components/base/sidebar/shell.tsx
|
|
368
384
|
function Sidebar({ side = "left", variant = "sidebar", collapsible = "offcanvas", className, children, ...props }) {
|
|
369
|
-
const { isMobile, state, openMobile, setOpenMobile,
|
|
385
|
+
const { isMobile, state, openMobile, setOpenMobile, expandBehavior, showBackdrop, forceClose } = useSidebar();
|
|
370
386
|
const sidebarRef = React.useRef(null);
|
|
371
387
|
const [showBackdropElement, setShowBackdropElement] = React.useState(false);
|
|
372
388
|
const [backdropVisible, setBackdropVisible] = React.useState(false);
|
|
@@ -422,18 +438,19 @@ function Sidebar({ side = "left", variant = "sidebar", collapsible = "offcanvas"
|
|
|
422
438
|
"data-variant": variant,
|
|
423
439
|
"data-side": side,
|
|
424
440
|
"data-slot": "sidebar",
|
|
425
|
-
onMouseEnter: handleMouseEnter,
|
|
426
|
-
onMouseLeave: handleMouseLeave,
|
|
427
441
|
children: [
|
|
428
|
-
/* @__PURE__ */ jsx("div", { className: cn("relative w-(--sidebar-width) bg-transparent transition-[width] duration-
|
|
442
|
+
/* @__PURE__ */ jsx("div", { className: cn("relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear", "group-data-[collapsible=offcanvas]:w-0", "group-data-[side=right]:rotate-180", expandBehavior === "overlay" ? variant === "floating" || variant === "inset" ? "w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]" : "w-(--sidebar-width-icon)" : variant === "floating" || variant === "inset" ? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]" : "group-data-[collapsible=icon]:w-(--sidebar-width-icon)") }),
|
|
429
443
|
/* @__PURE__ */ jsx("div", {
|
|
430
444
|
ref: sidebarRef,
|
|
431
|
-
className: cn("
|
|
445
|
+
className: cn("fixed inset-y-0 hidden w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex", side === "left" ? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]" : "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]", expandBehavior === "overlay" ? "z-50" : "z-20", variant === "floating" || variant === "inset" ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]" : "bg-sidebar group-data-[collapsible=icon]:w-(--sidebar-width-icon)", className),
|
|
432
446
|
...props,
|
|
433
|
-
children: /* @__PURE__ */
|
|
447
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
434
448
|
"data-sidebar": "sidebar",
|
|
435
|
-
className: cn("
|
|
436
|
-
children
|
|
449
|
+
className: cn("relative flex h-full w-full flex-col", variant === "floating" || variant === "inset" ? "bg-sidebar" : null, "group-data-[variant=floating]:border-sidebar-border group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:shadow-sm", expandBehavior === "overlay" && state === "expanded" && "shadow-lg"),
|
|
450
|
+
children: [children, variant !== "floating" && variant !== "inset" && /* @__PURE__ */ jsx("div", {
|
|
451
|
+
"aria-hidden": true,
|
|
452
|
+
className: cn("bg-sidebar-border pointer-events-none absolute inset-y-0 z-20 w-px", side === "left" ? "right-0" : "left-0")
|
|
453
|
+
})]
|
|
437
454
|
})
|
|
438
455
|
}),
|
|
439
456
|
showBackdropElement && /* @__PURE__ */ jsx("div", {
|
|
@@ -512,11 +529,21 @@ function SidebarHeader({ className, ...props }) {
|
|
|
512
529
|
...props
|
|
513
530
|
});
|
|
514
531
|
}
|
|
515
|
-
function SidebarFooter({ className, ...props }) {
|
|
532
|
+
function SidebarFooter({ className, onMouseEnter, onMouseLeave, ...props }) {
|
|
533
|
+
const { cancelHoverLeave, handleMouseLeave } = useSidebar();
|
|
516
534
|
return /* @__PURE__ */ jsx("div", {
|
|
517
535
|
"data-slot": "sidebar-footer",
|
|
518
536
|
"data-sidebar": "footer",
|
|
519
537
|
className: cn("flex flex-col gap-2 p-2", className),
|
|
538
|
+
onMouseEnter: (event) => {
|
|
539
|
+
cancelHoverLeave();
|
|
540
|
+
onMouseEnter?.(event);
|
|
541
|
+
},
|
|
542
|
+
onMouseLeave: (event) => {
|
|
543
|
+
const next = event.relatedTarget;
|
|
544
|
+
if (!(next instanceof Element && next.closest("[data-sidebar=\"content\"]"))) handleMouseLeave();
|
|
545
|
+
onMouseLeave?.(event);
|
|
546
|
+
},
|
|
520
547
|
...props
|
|
521
548
|
});
|
|
522
549
|
}
|
|
@@ -528,11 +555,21 @@ function SidebarSeparator({ className, ...props }) {
|
|
|
528
555
|
...props
|
|
529
556
|
});
|
|
530
557
|
}
|
|
531
|
-
function SidebarContent({ className, ...props }) {
|
|
558
|
+
function SidebarContent({ className, onMouseEnter, onMouseLeave, ...props }) {
|
|
559
|
+
const { handleMouseEnter, handleMouseLeave } = useSidebar();
|
|
532
560
|
return /* @__PURE__ */ jsx("div", {
|
|
533
561
|
"data-slot": "sidebar-content",
|
|
534
562
|
"data-sidebar": "content",
|
|
535
563
|
className: cn("flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden", className),
|
|
564
|
+
onMouseEnter: (event) => {
|
|
565
|
+
handleMouseEnter();
|
|
566
|
+
onMouseEnter?.(event);
|
|
567
|
+
},
|
|
568
|
+
onMouseLeave: (event) => {
|
|
569
|
+
const next = event.relatedTarget;
|
|
570
|
+
if (!(next instanceof Element && next.closest("[data-sidebar=\"footer\"]"))) handleMouseLeave();
|
|
571
|
+
onMouseLeave?.(event);
|
|
572
|
+
},
|
|
536
573
|
...props
|
|
537
574
|
});
|
|
538
575
|
}
|
|
@@ -92,10 +92,10 @@
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
.animate-collapsible-down {
|
|
95
|
-
animation: collapsibleDown 0.
|
|
95
|
+
animation: collapsibleDown 0.28s cubic-bezier(0.32, 0.72, 0, 1);
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
.animate-collapsible-up {
|
|
99
|
-
animation: collapsibleUp 0.
|
|
99
|
+
animation: collapsibleUp 0.22s cubic-bezier(0.32, 0.72, 0, 1);
|
|
100
100
|
}
|
|
101
101
|
}
|