@exegia/corpora-ui 0.9.0 → 0.11.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.
Files changed (67) hide show
  1. package/dist-lib/components/agents/ai-sidebar.d.ts +2 -0
  2. package/dist-lib/components/agents/index.d.ts +12 -0
  3. package/dist-lib/components/agents/marquee-label.d.ts +4 -0
  4. package/dist-lib/components/agents/sidebar-icon.d.ts +2 -0
  5. package/dist-lib/components/agents/sidebar-row.d.ts +2 -0
  6. package/dist-lib/components/agents/type.d.ts +77 -0
  7. package/dist-lib/components/agents/use-ai-sidebar.d.ts +31 -0
  8. package/dist-lib/components/agents/utils.d.ts +16 -0
  9. package/dist-lib/components/beste/piece/browser-frame.d.ts +2 -0
  10. package/dist-lib/components/beste/piece/type.d.ts +9 -0
  11. package/dist-lib/components/beste/piece/utils.d.ts +3 -0
  12. package/dist-lib/components/blocks/auth/linked-accounts-block.d.ts +7 -1
  13. package/dist-lib/components/blocks/auth/signup-block.d.ts +5 -5
  14. package/dist-lib/components/blocks/nav/sidebar-block.d.ts +3 -56
  15. package/dist-lib/components/blocks/nav/sidebar-nav-row.d.ts +3 -0
  16. package/dist-lib/components/blocks/nav/types.d.ts +60 -0
  17. package/dist-lib/components/blocks/nav/utils.d.ts +1 -0
  18. package/dist-lib/components/blocks/profile/profile-card-block.d.ts +6 -1
  19. package/dist-lib/components/blocks/shell/index.d.ts +1 -0
  20. package/dist-lib/components/blocks/shell/shell-layout.d.ts +3 -0
  21. package/dist-lib/components/blocks/shell/type.d.ts +26 -0
  22. package/dist-lib/components/blocks/shell/utils.d.ts +1 -0
  23. package/dist-lib/components/composed/social-providers.d.ts +9 -1
  24. package/dist-lib/components/docs/demo-controls.d.ts +4 -4
  25. package/dist-lib/components/motion/animated-sidebar.d.ts +12 -1
  26. package/dist-lib/components/motion/popover-morph.d.ts +35 -0
  27. package/dist-lib/components/motion/popover-position.d.ts +15 -0
  28. package/dist-lib/components/ui/badge.d.ts +7 -0
  29. package/dist-lib/components/ui/select.d.ts +30 -0
  30. package/dist-lib/index.d.ts +1 -0
  31. package/dist-lib/index.js +669 -549
  32. package/dist-lib/index.js.map +1 -1
  33. package/package.json +1 -1
  34. package/src/components/agents/ai-sidebar.tsx +116 -0
  35. package/src/components/agents/index.ts +14 -0
  36. package/src/components/agents/marquee-label.tsx +59 -0
  37. package/src/components/agents/sidebar-icon.tsx +14 -0
  38. package/src/components/agents/sidebar-row.tsx +271 -0
  39. package/src/components/agents/type.ts +89 -0
  40. package/src/components/agents/use-ai-sidebar.ts +386 -0
  41. package/src/components/agents/utils.ts +142 -0
  42. package/src/components/beste/piece/browser-frame.tsx +55 -0
  43. package/src/components/beste/piece/type.ts +11 -0
  44. package/src/components/beste/piece/utils.ts +21 -0
  45. package/src/components/blocks/auth/__tests__/linked-accounts-block.test.tsx +18 -0
  46. package/src/components/blocks/auth/linked-accounts-block.tsx +63 -57
  47. package/src/components/blocks/auth/passkey-manager-block.tsx +88 -81
  48. package/src/components/blocks/auth/signup-block.tsx +6 -6
  49. package/src/components/blocks/nav/sidebar-block.tsx +25 -155
  50. package/src/components/blocks/nav/sidebar-nav-row.tsx +79 -0
  51. package/src/components/blocks/nav/types.ts +65 -0
  52. package/src/components/blocks/nav/utils.ts +4 -0
  53. package/src/components/blocks/profile/profile-card-block.tsx +17 -6
  54. package/src/components/blocks/shell/index.ts +1 -0
  55. package/src/components/blocks/shell/shell-layout.tsx +115 -0
  56. package/src/components/blocks/shell/type.ts +50 -0
  57. package/src/components/blocks/shell/utils.ts +2 -0
  58. package/src/components/composed/social-providers.tsx +83 -27
  59. package/src/components/docs/component-preview.tsx +13 -12
  60. package/src/components/docs/demo-controls.tsx +37 -21
  61. package/src/components/motion/animated-sidebar.tsx +392 -364
  62. package/src/components/motion/popover-morph.tsx +292 -0
  63. package/src/components/motion/popover-position.ts +86 -0
  64. package/src/components/ui/badge.tsx +52 -0
  65. package/src/components/ui/select.tsx +257 -0
  66. package/src/index.css +18 -0
  67. package/src/index.ts +1 -0
@@ -1,14 +1,14 @@
1
- "use client";
1
+ "use client"
2
2
  // beui.dev/components/motion/animated-sidebar
3
3
 
4
- import { ChevronRight } from "lucide-react";
4
+ import { ChevronRight } from "lucide-react"
5
5
  import {
6
6
  AnimatePresence,
7
7
  type HTMLMotionProps,
8
8
  motion,
9
9
  useReducedMotion,
10
10
  type Variants,
11
- } from "motion/react";
11
+ } from "motion/react"
12
12
  import {
13
13
  type ButtonHTMLAttributes,
14
14
  type CSSProperties,
@@ -23,29 +23,24 @@ import {
23
23
  useRef,
24
24
  useState,
25
25
  useSyncExternalStore,
26
- } from "react";
27
- import { createPortal } from "react-dom";
28
- import { SharedLayoutBg } from "@/components/motion/shared-layout-bg";
29
- import {
30
- EASE_DRAWER,
31
- EASE_OUT,
32
- SPRING_LAYOUT,
33
- SPRING_PRESS,
34
- } from "@/lib/ease";
35
- import { cn } from "@/lib/utils";
26
+ } from "react"
27
+ import { createPortal } from "react-dom"
28
+ import { SharedLayoutBg } from "@/components/motion/shared-layout-bg"
29
+ import { EASE_DRAWER, EASE_OUT, SPRING_LAYOUT, SPRING_PRESS } from "@/lib/ease"
30
+ import { cn } from "@/lib/utils"
36
31
 
37
- type SidebarState = "expanded" | "collapsed";
38
- type SidebarSide = "left" | "right";
39
- type SidebarVariant = "sidebar" | "floating" | "inset";
40
- type SidebarCollapsible = "offcanvas" | "icon" | "none";
32
+ type SidebarState = "expanded" | "collapsed"
33
+ type SidebarSide = "left" | "right"
34
+ type SidebarVariant = "sidebar" | "floating" | "inset"
35
+ type SidebarCollapsible = "offcanvas" | "icon" | "none"
41
36
 
42
- const MOBILE_QUERY = "(max-width: 767px)";
43
- const SIDEBAR_KEYBOARD_SHORTCUT = "b";
37
+ const MOBILE_QUERY = "(max-width: 767px)"
38
+ const SIDEBAR_KEYBOARD_SHORTCUT = "b"
44
39
 
45
40
  const PANEL_TRANSITION = {
46
41
  duration: 0.36,
47
42
  ease: EASE_DRAWER,
48
- } as const;
43
+ } as const
49
44
 
50
45
  // The desktop rail settles at a hard zero-width boundary. Keep the spring
51
46
  // critically damped so it cannot overshoot, pause against that boundary, and
@@ -55,23 +50,23 @@ const SIDEBAR_MORPH_TRANSITION = {
55
50
  stiffness: 380,
56
51
  damping: 35,
57
52
  mass: 0.75,
58
- } as const;
53
+ } as const
59
54
 
60
55
  const LABEL_ENTER_TRANSITION = {
61
56
  duration: 0.2,
62
57
  delay: 0.08,
63
58
  ease: EASE_OUT,
64
- } as const;
59
+ } as const
65
60
 
66
61
  const LABEL_EXIT_TRANSITION = {
67
62
  duration: 0.12,
68
63
  ease: EASE_OUT,
69
- } as const;
64
+ } as const
70
65
 
71
66
  const SUBMENU_TRANSITION = {
72
67
  duration: 0.18,
73
68
  ease: EASE_OUT,
74
- } as const;
69
+ } as const
75
70
 
76
71
  const SUBMENU_VARIANTS: Variants = {
77
72
  closed: {
@@ -94,7 +89,7 @@ const SUBMENU_VARIANTS: Variants = {
94
89
  staggerChildren: 0.045,
95
90
  },
96
91
  },
97
- };
92
+ }
98
93
 
99
94
  const SUBMENU_ITEM_VARIANTS: Variants = {
100
95
  closed: {
@@ -108,12 +103,12 @@ const SUBMENU_ITEM_VARIANTS: Variants = {
108
103
  filter: "blur(0px)",
109
104
  transition: SUBMENU_TRANSITION,
110
105
  },
111
- };
106
+ }
112
107
 
113
108
  const REDUCED_TRANSITION = {
114
109
  duration: 0.16,
115
110
  ease: EASE_OUT,
116
- } as const;
111
+ } as const
117
112
 
118
113
  const FOCUSABLE_SELECTOR = [
119
114
  "a[href]",
@@ -122,90 +117,98 @@ const FOCUSABLE_SELECTOR = [
122
117
  "select:not([disabled])",
123
118
  "textarea:not([disabled])",
124
119
  "[tabindex]:not([tabindex='-1'])",
125
- ].join(",");
120
+ ].join(",")
126
121
 
127
122
  function subscribeToMobileQuery(callback: () => void) {
128
- const query = window.matchMedia(MOBILE_QUERY);
129
- query.addEventListener("change", callback);
130
- return () => query.removeEventListener("change", callback);
123
+ const query = window.matchMedia(MOBILE_QUERY)
124
+ query.addEventListener("change", callback)
125
+ return () => query.removeEventListener("change", callback)
131
126
  }
132
127
 
133
128
  function getMobileSnapshot() {
134
- return window.matchMedia(MOBILE_QUERY).matches;
129
+ return window.matchMedia(MOBILE_QUERY).matches
135
130
  }
136
131
 
137
132
  function getServerMobileSnapshot() {
138
- return false;
133
+ return false
139
134
  }
140
135
 
141
136
  function useIsMobile() {
142
137
  return useSyncExternalStore(
143
138
  subscribeToMobileQuery,
144
139
  getMobileSnapshot,
145
- getServerMobileSnapshot,
146
- );
140
+ getServerMobileSnapshot
141
+ )
147
142
  }
148
143
 
149
144
  interface AnimatedSidebarContextValue {
150
- isMobile: boolean;
151
- layoutId: string;
152
- open: boolean;
153
- openMobile: boolean;
154
- reduce: boolean;
155
- setOpen: (open: boolean) => void;
156
- setOpenMobile: (open: boolean) => void;
157
- state: SidebarState;
158
- toggleSidebar: () => void;
159
- triggerRef: React.RefObject<HTMLButtonElement | null>;
145
+ isMobile: boolean
146
+ layoutId: string
147
+ open: boolean
148
+ openMobile: boolean
149
+ /** The right panel is a single hidden/shown state — no icon rail, no
150
+ * separate mobile state. */
151
+ openRight: boolean
152
+ reduce: boolean
153
+ setOpen: (open: boolean) => void
154
+ setOpenMobile: (open: boolean) => void
155
+ setOpenRight: (open: boolean) => void
156
+ state: SidebarState
157
+ toggleSidebar: () => void
158
+ toggleRightSidebar: () => void
159
+ triggerRef: React.RefObject<HTMLButtonElement | null>
160
+ rightTriggerRef: React.RefObject<HTMLButtonElement | null>
160
161
  }
161
162
 
162
163
  const AnimatedSidebarContext =
163
- createContext<AnimatedSidebarContextValue | null>(null);
164
+ createContext<AnimatedSidebarContextValue | null>(null)
164
165
 
165
166
  interface AnimatedSidebarPanelContextValue {
166
- collapsed: boolean;
167
- collapsible: SidebarCollapsible;
168
- side: SidebarSide;
167
+ collapsed: boolean
168
+ collapsible: SidebarCollapsible
169
+ side: SidebarSide
169
170
  }
170
171
 
171
172
  const AnimatedSidebarPanelContext =
172
- createContext<AnimatedSidebarPanelContextValue | null>(null);
173
+ createContext<AnimatedSidebarPanelContextValue | null>(null)
173
174
 
174
175
  export function useAnimatedSidebar() {
175
- const context = useContext(AnimatedSidebarContext);
176
+ const context = useContext(AnimatedSidebarContext)
176
177
  if (!context) {
177
178
  throw new Error(
178
- "useAnimatedSidebar must be used inside AnimatedSidebarProvider.",
179
- );
179
+ "useAnimatedSidebar must be used inside AnimatedSidebarProvider."
180
+ )
180
181
  }
181
- return context;
182
+ return context
182
183
  }
183
184
 
184
185
  function useAnimatedSidebarPanel() {
185
- const context = useContext(AnimatedSidebarPanelContext);
186
+ const context = useContext(AnimatedSidebarPanelContext)
186
187
  if (!context) {
187
188
  throw new Error(
188
- "Animated Sidebar parts must be used inside AnimatedSidebar.",
189
- );
189
+ "Animated Sidebar parts must be used inside AnimatedSidebar."
190
+ )
190
191
  }
191
- return context;
192
+ return context
192
193
  }
193
194
 
194
195
  type SidebarProviderStyle = CSSProperties & {
195
- "--sidebar-width"?: string;
196
- "--sidebar-width-icon"?: string;
197
- "--sidebar-width-mobile"?: string;
198
- };
199
-
200
- export interface AnimatedSidebarProviderProps
201
- extends HTMLAttributes<HTMLDivElement> {
202
- open?: boolean;
203
- defaultOpen?: boolean;
204
- onOpenChange?: (open: boolean) => void;
205
- openMobile?: boolean;
206
- defaultOpenMobile?: boolean;
207
- onOpenMobileChange?: (open: boolean) => void;
208
- style?: SidebarProviderStyle;
196
+ "--sidebar-width"?: string
197
+ "--sidebar-width-icon"?: string
198
+ "--sidebar-width-mobile"?: string
199
+ }
200
+
201
+ export interface AnimatedSidebarProviderProps extends HTMLAttributes<HTMLDivElement> {
202
+ open?: boolean
203
+ defaultOpen?: boolean
204
+ onOpenChange?: (open: boolean) => void
205
+ openMobile?: boolean
206
+ defaultOpenMobile?: boolean
207
+ onOpenMobileChange?: (open: boolean) => void
208
+ openRight?: boolean
209
+ defaultOpenRight?: boolean
210
+ onOpenRightChange?: (open: boolean) => void
211
+ style?: SidebarProviderStyle
209
212
  }
210
213
 
211
214
  export function AnimatedSidebarProvider({
@@ -216,40 +219,58 @@ export function AnimatedSidebarProvider({
216
219
  openMobile,
217
220
  defaultOpenMobile = false,
218
221
  onOpenMobileChange,
222
+ openRight,
223
+ defaultOpenRight = false,
224
+ onOpenRightChange,
219
225
  className,
220
226
  style,
221
227
  ...props
222
228
  }: AnimatedSidebarProviderProps) {
223
- const [internalOpen, setInternalOpen] = useState(defaultOpen);
229
+ const [internalOpen, setInternalOpen] = useState(defaultOpen)
224
230
  const [internalOpenMobile, setInternalOpenMobile] =
225
- useState(defaultOpenMobile);
226
- const isMobile = useIsMobile();
227
- const reduce = useReducedMotion() ?? false;
228
- const generatedId = useId();
229
- const triggerRef = useRef<HTMLButtonElement>(null);
230
- const desktopOpen = open ?? internalOpen;
231
- const mobileOpen = openMobile ?? internalOpenMobile;
231
+ useState(defaultOpenMobile)
232
+ const [internalOpenRight, setInternalOpenRight] = useState(defaultOpenRight)
233
+ const isMobile = useIsMobile()
234
+ const reduce = useReducedMotion() ?? false
235
+ const generatedId = useId()
236
+ const triggerRef = useRef<HTMLButtonElement>(null)
237
+ const rightTriggerRef = useRef<HTMLButtonElement>(null)
238
+ const desktopOpen = open ?? internalOpen
239
+ const mobileOpen = openMobile ?? internalOpenMobile
240
+ const rightOpen = openRight ?? internalOpenRight
232
241
 
233
242
  const setOpen = useCallback(
234
243
  (nextOpen: boolean) => {
235
- if (open === undefined) setInternalOpen(nextOpen);
236
- onOpenChange?.(nextOpen);
244
+ if (open === undefined) setInternalOpen(nextOpen)
245
+ onOpenChange?.(nextOpen)
237
246
  },
238
- [onOpenChange, open],
239
- );
247
+ [onOpenChange, open]
248
+ )
240
249
 
241
250
  const setOpenMobile = useCallback(
242
251
  (nextOpen: boolean) => {
243
- if (openMobile === undefined) setInternalOpenMobile(nextOpen);
244
- onOpenMobileChange?.(nextOpen);
252
+ if (openMobile === undefined) setInternalOpenMobile(nextOpen)
253
+ onOpenMobileChange?.(nextOpen)
245
254
  },
246
- [onOpenMobileChange, openMobile],
247
- );
255
+ [onOpenMobileChange, openMobile]
256
+ )
257
+
258
+ const setOpenRight = useCallback(
259
+ (nextOpen: boolean) => {
260
+ if (openRight === undefined) setInternalOpenRight(nextOpen)
261
+ onOpenRightChange?.(nextOpen)
262
+ },
263
+ [onOpenRightChange, openRight]
264
+ )
248
265
 
249
266
  const toggleSidebar = useCallback(() => {
250
- if (isMobile) setOpenMobile(!mobileOpen);
251
- else setOpen(!desktopOpen);
252
- }, [desktopOpen, isMobile, mobileOpen, setOpen, setOpenMobile]);
267
+ if (isMobile) setOpenMobile(!mobileOpen)
268
+ else setOpen(!desktopOpen)
269
+ }, [desktopOpen, isMobile, mobileOpen, setOpen, setOpenMobile])
270
+
271
+ const toggleRightSidebar = useCallback(() => {
272
+ setOpenRight(!rightOpen)
273
+ }, [rightOpen, setOpenRight])
253
274
 
254
275
  useEffect(() => {
255
276
  const handleShortcut = (event: KeyboardEvent) => {
@@ -257,14 +278,14 @@ export function AnimatedSidebarProvider({
257
278
  event.key.toLowerCase() === SIDEBAR_KEYBOARD_SHORTCUT &&
258
279
  (event.metaKey || event.ctrlKey)
259
280
  ) {
260
- event.preventDefault();
261
- toggleSidebar();
281
+ event.preventDefault()
282
+ toggleSidebar()
262
283
  }
263
- };
284
+ }
264
285
 
265
- window.addEventListener("keydown", handleShortcut);
266
- return () => window.removeEventListener("keydown", handleShortcut);
267
- }, [toggleSidebar]);
286
+ window.addEventListener("keydown", handleShortcut)
287
+ return () => window.removeEventListener("keydown", handleShortcut)
288
+ }, [toggleSidebar])
268
289
 
269
290
  return (
270
291
  <AnimatedSidebarContext.Provider
@@ -273,33 +294,38 @@ export function AnimatedSidebarProvider({
273
294
  layoutId: `${generatedId}-active`,
274
295
  open: desktopOpen,
275
296
  openMobile: mobileOpen,
297
+ openRight: rightOpen,
276
298
  reduce,
277
299
  setOpen,
278
300
  setOpenMobile,
301
+ setOpenRight,
279
302
  state: desktopOpen ? "expanded" : "collapsed",
280
303
  toggleSidebar,
304
+ toggleRightSidebar,
281
305
  triggerRef,
306
+ rightTriggerRef,
282
307
  }}
283
308
  >
284
309
  <div
285
310
  {...props}
286
311
  data-slot="sidebar-wrapper"
287
312
  data-state={desktopOpen ? "expanded" : "collapsed"}
313
+ data-right-state={rightOpen ? "expanded" : "collapsed"}
288
314
  style={{
289
- "--sidebar-width": "16rem",
315
+ "--sidebar-width": "19rem",
290
316
  "--sidebar-width-icon": "4.25rem",
291
317
  "--sidebar-width-mobile": "18rem",
292
318
  ...style,
293
319
  }}
294
320
  className={cn(
295
321
  "group/sidebar-wrapper flex min-h-svh w-full min-w-0",
296
- className,
322
+ className
297
323
  )}
298
324
  >
299
325
  {children}
300
326
  </div>
301
327
  </AnimatedSidebarContext.Provider>
302
- );
328
+ )
303
329
  }
304
330
 
305
331
  function MobileSidebar({
@@ -308,78 +334,79 @@ function MobileSidebar({
308
334
  className,
309
335
  side,
310
336
  }: {
311
- ariaLabel: string;
312
- children: ReactNode;
313
- className?: string;
314
- side: SidebarSide;
337
+ ariaLabel: string
338
+ children: ReactNode
339
+ className?: string
340
+ side: SidebarSide
315
341
  }) {
316
- const context = useAnimatedSidebar();
317
- const panelRef = useRef<HTMLDivElement>(null);
318
- const [mounted, setMounted] = useState(false);
342
+ const context = useAnimatedSidebar()
343
+ const isRight = side === "right"
344
+ const open = isRight ? context.openRight : context.openMobile
345
+ const setOpen = isRight ? context.setOpenRight : context.setOpenMobile
346
+ const triggerRef = isRight ? context.rightTriggerRef : context.triggerRef
347
+ const panelRef = useRef<HTMLDivElement>(null)
348
+ const [mounted, setMounted] = useState(false)
319
349
 
320
- useEffect(() => setMounted(true), []);
350
+ useEffect(() => setMounted(true), [])
321
351
 
322
352
  useEffect(() => {
323
- if (!context.openMobile) return;
353
+ if (!open) return
324
354
 
325
- const body = document.body;
326
- const scrollY = window.scrollY;
355
+ const body = document.body
356
+ const scrollY = window.scrollY
357
+ const trigger = triggerRef.current
327
358
  const previousBodyStyles = {
328
359
  left: body.style.left,
329
360
  overflow: body.style.overflow,
330
361
  position: body.style.position,
331
362
  right: body.style.right,
332
363
  top: body.style.top,
333
- };
364
+ }
334
365
 
335
- body.style.position = "fixed";
336
- body.style.top = `-${scrollY}px`;
337
- body.style.left = "0";
338
- body.style.right = "0";
339
- body.style.overflow = "hidden";
366
+ body.style.position = "fixed"
367
+ body.style.top = `-${scrollY}px`
368
+ body.style.left = "0"
369
+ body.style.right = "0"
370
+ body.style.overflow = "hidden"
340
371
 
341
372
  const focusFrame = requestAnimationFrame(() => {
342
373
  const firstFocusable =
343
- panelRef.current?.querySelector<HTMLElement>(FOCUSABLE_SELECTOR);
344
- (firstFocusable ?? panelRef.current)?.focus({ preventScroll: true });
345
- });
374
+ panelRef.current?.querySelector<HTMLElement>(FOCUSABLE_SELECTOR)
375
+ ;(firstFocusable ?? panelRef.current)?.focus({ preventScroll: true })
376
+ })
346
377
 
347
378
  return () => {
348
- cancelAnimationFrame(focusFrame);
349
- body.style.position = previousBodyStyles.position;
350
- body.style.top = previousBodyStyles.top;
351
- body.style.left = previousBodyStyles.left;
352
- body.style.right = previousBodyStyles.right;
353
- body.style.overflow = previousBodyStyles.overflow;
354
- window.scrollTo(0, scrollY);
355
- context.triggerRef.current?.focus({ preventScroll: true });
356
- };
357
- }, [context.openMobile, context.triggerRef]);
358
-
359
- if (!mounted) return null;
379
+ cancelAnimationFrame(focusFrame)
380
+ body.style.position = previousBodyStyles.position
381
+ body.style.top = previousBodyStyles.top
382
+ body.style.left = previousBodyStyles.left
383
+ body.style.right = previousBodyStyles.right
384
+ body.style.overflow = previousBodyStyles.overflow
385
+ window.scrollTo(0, scrollY)
386
+ trigger?.focus({ preventScroll: true })
387
+ }
388
+ }, [open, triggerRef])
389
+
390
+ if (!mounted) return null
360
391
 
361
392
  return createPortal(
362
393
  <div
363
394
  className={cn(
364
395
  "pointer-events-none fixed inset-0 z-50 md:hidden",
365
- context.openMobile ? "visible" : "invisible",
396
+ open ? "visible" : "invisible"
366
397
  )}
367
398
  >
368
399
  <motion.button
369
400
  type="button"
370
401
  aria-label="Close sidebar"
371
- tabIndex={context.openMobile ? 0 : -1}
402
+ tabIndex={open ? 0 : -1}
372
403
  initial={false}
373
- animate={{ opacity: context.openMobile ? 1 : 0 }}
374
- transition={
375
- context.reduce ? REDUCED_TRANSITION : PANEL_TRANSITION
376
- }
377
- onClick={() => context.setOpenMobile(false)}
404
+ animate={{ opacity: open ? 1 : 0 }}
405
+ transition={context.reduce ? REDUCED_TRANSITION : PANEL_TRANSITION}
406
+ onClick={() => setOpen(false)}
378
407
  className={cn(
379
- "absolute inset-0 bg-black/40",
380
- context.openMobile
381
- ? "pointer-events-auto"
382
- : "pointer-events-none",
408
+ "absolute inset-0",
409
+ open ? "pointer-events-auto" : "pointer-events-none"
383
410
  )}
384
411
  />
385
412
 
@@ -388,68 +415,62 @@ function MobileSidebar({
388
415
  role="dialog"
389
416
  aria-modal="true"
390
417
  aria-label={ariaLabel}
391
- aria-hidden={!context.openMobile}
392
- inert={!context.openMobile}
418
+ aria-hidden={!open}
419
+ inert={!open}
393
420
  tabIndex={-1}
394
421
  data-mobile="true"
395
- data-state={context.openMobile ? "expanded" : "collapsed"}
422
+ data-state={open ? "expanded" : "collapsed"}
396
423
  data-side={side}
397
424
  initial={false}
398
425
  animate={{
399
- opacity: context.reduce
400
- ? context.openMobile
401
- ? 1
402
- : 0
403
- : 1,
426
+ opacity: context.reduce ? (open ? 1 : 0) : 1,
404
427
  x: context.reduce
405
428
  ? 0
406
- : context.openMobile
429
+ : open
407
430
  ? "0%"
408
431
  : side === "left"
409
432
  ? "-100%"
410
433
  : "100%",
411
434
  }}
412
- transition={
413
- context.reduce ? REDUCED_TRANSITION : PANEL_TRANSITION
414
- }
435
+ transition={context.reduce ? REDUCED_TRANSITION : PANEL_TRANSITION}
415
436
  onKeyDown={(event) => {
416
437
  if (event.key === "Escape") {
417
- event.preventDefault();
418
- context.setOpenMobile(false);
419
- return;
438
+ event.preventDefault()
439
+ setOpen(false)
440
+ return
420
441
  }
421
442
 
422
- if (event.key !== "Tab") return;
443
+ if (event.key !== "Tab") return
423
444
  const focusable = panelRef.current
424
445
  ? Array.from(
425
446
  panelRef.current.querySelectorAll<HTMLElement>(
426
- FOCUSABLE_SELECTOR,
427
- ),
447
+ FOCUSABLE_SELECTOR
448
+ )
428
449
  )
429
- : [];
450
+ : []
430
451
 
431
452
  if (focusable.length === 0) {
432
- event.preventDefault();
433
- panelRef.current?.focus();
434
- return;
453
+ event.preventDefault()
454
+ panelRef.current?.focus()
455
+ return
435
456
  }
436
457
 
437
- const first = focusable[0];
438
- const last = focusable[focusable.length - 1];
458
+ const first = focusable[0]
459
+ const last = focusable[focusable.length - 1]
439
460
  if (event.shiftKey && document.activeElement === first) {
440
- event.preventDefault();
441
- last.focus();
461
+ event.preventDefault()
462
+ last.focus()
442
463
  } else if (!event.shiftKey && document.activeElement === last) {
443
- event.preventDefault();
444
- first.focus();
464
+ event.preventDefault()
465
+ first.focus()
445
466
  }
446
467
  }}
447
468
  className={cn(
448
469
  "pointer-events-auto absolute inset-y-0 flex h-dvh w-(--sidebar-width-mobile) max-w-[88vw] flex-col overflow-hidden",
449
- "border-border bg-background shadow-2xl will-change-transform",
450
- side === "left" ? "left-0 border-r" : "right-0 border-l",
451
- !context.openMobile && "pointer-events-none",
452
- className,
470
+ "will-change-transform",
471
+ side === "left" ? "left-0 border-r" : "right-0",
472
+ !open && "pointer-events-none",
473
+ className
453
474
  )}
454
475
  >
455
476
  <AnimatedSidebarPanelContext.Provider
@@ -459,18 +480,20 @@ function MobileSidebar({
459
480
  </AnimatedSidebarPanelContext.Provider>
460
481
  </motion.div>
461
482
  </div>,
462
- document.body,
463
- );
483
+ document.body
484
+ )
464
485
  }
465
486
 
466
- export interface AnimatedSidebarProps
467
- extends Omit<HTMLMotionProps<"aside">, "children"> {
468
- children?: ReactNode;
469
- side?: SidebarSide;
470
- variant?: SidebarVariant;
471
- collapsible?: SidebarCollapsible;
472
- ariaLabel?: string;
473
- panelClassName?: string;
487
+ export interface AnimatedSidebarProps extends Omit<
488
+ HTMLMotionProps<"aside">,
489
+ "children"
490
+ > {
491
+ children?: ReactNode
492
+ side?: SidebarSide
493
+ variant?: SidebarVariant
494
+ collapsible?: SidebarCollapsible
495
+ ariaLabel?: string
496
+ panelClassName?: string
474
497
  }
475
498
 
476
499
  export const AnimatedSidebar = forwardRef<HTMLElement, AnimatedSidebarProps>(
@@ -486,27 +509,24 @@ export const AnimatedSidebar = forwardRef<HTMLElement, AnimatedSidebarProps>(
486
509
  style,
487
510
  ...props
488
511
  },
489
- forwardedRef,
512
+ forwardedRef
490
513
  ) {
491
- const context = useAnimatedSidebar();
492
- const collapsed = collapsible !== "none" && !context.open;
493
- const offcanvas = collapsed && collapsible === "offcanvas";
514
+ const context = useAnimatedSidebar()
515
+ const sideOpen = side === "right" ? context.openRight : context.open
516
+ const collapsed = collapsible !== "none" && !sideOpen
517
+ const offcanvas = collapsed && collapsible === "offcanvas"
494
518
  const width = offcanvas
495
519
  ? "0px"
496
520
  : collapsed
497
521
  ? "var(--sidebar-width-icon)"
498
- : "var(--sidebar-width)";
522
+ : "var(--sidebar-width)"
499
523
 
500
524
  if (context.isMobile) {
501
525
  return (
502
- <MobileSidebar
503
- ariaLabel={ariaLabel}
504
- className={className}
505
- side={side}
506
- >
526
+ <MobileSidebar ariaLabel={ariaLabel} className={className} side={side}>
507
527
  {children}
508
528
  </MobileSidebar>
509
- );
529
+ )
510
530
  }
511
531
 
512
532
  return (
@@ -521,35 +541,38 @@ export const AnimatedSidebar = forwardRef<HTMLElement, AnimatedSidebarProps>(
521
541
  data-variant={variant}
522
542
  data-side={side}
523
543
  animate={{ width }}
524
- transition={
525
- context.reduce ? { duration: 0 } : SIDEBAR_MORPH_TRANSITION
526
- }
544
+ transition={context.reduce ? { duration: 0 } : SIDEBAR_MORPH_TRANSITION}
527
545
  style={style}
528
546
  className={cn(
529
- "group/sidebar relative hidden h-auto shrink-0 md:block will-change-[width]",
530
- "peer",
547
+ "group/sidebar relative hidden h-auto shrink-0 will-change-[width] md:block",
548
+ "peer flex!",
549
+ // The offcanvas panel keeps its full width and slides out of the
550
+ // zero-width rail, so clip it rather than letting it bleed past the
551
+ // shell edge. The clip margin lets the panel's own shadow escape
552
+ // without letting the slid-out panel escape with it
553
+ collapsible === "offcanvas" &&
554
+ "overflow-x-visible [overflow-clip-margin:1rem]",
531
555
  side === "right" && "order-last",
532
- className,
556
+ className
533
557
  )}
534
558
  >
559
+ {side === "right" && !collapsed && (
560
+ <motion.div className="h-full p-0.5 flex flex-1 bg-radial/decreasing from-amber-400 to-0% hover:to-50% ease-smooth-out to-amber-500/0 cursor-col-resize transition-colors duration-1000 animate-in" />
561
+ )}
535
562
  <motion.div
536
563
  initial={false}
537
564
  animate={{
538
565
  opacity: offcanvas ? 0 : 1,
539
566
  x: offcanvas ? (side === "left" ? "-100%" : "100%") : "0%",
540
567
  }}
541
- transition={
542
- context.reduce ? REDUCED_TRANSITION : PANEL_TRANSITION
543
- }
568
+ transition={context.reduce ? REDUCED_TRANSITION : PANEL_TRANSITION}
544
569
  className={cn(
545
- "sticky top-0 flex h-svh w-full flex-col overflow-hidden bg-background",
570
+ "sticky top-0 flex h-svh w-full flex-col overflow-hidden",
546
571
  collapsible === "offcanvas" && "w-[var(--sidebar-width)]",
547
- variant === "sidebar" &&
548
- (side === "left" ? "border-border border-r" : "border-border border-l"),
549
- variant === "floating" &&
550
- "m-2 h-[calc(100svh-1rem)] rounded-2xl border border-border shadow-sm",
551
- variant === "inset" && "m-2 h-[calc(100svh-1rem)] rounded-2xl",
552
- panelClassName,
572
+
573
+ variant === "floating" && "m-2 h-[calc(100svh-1rem)]",
574
+ variant === "inset" && "m-2 h-[calc(100svh-1rem)]",
575
+ panelClassName
553
576
  )}
554
577
  >
555
578
  <AnimatedSidebarPanelContext.Provider
@@ -559,94 +582,106 @@ export const AnimatedSidebar = forwardRef<HTMLElement, AnimatedSidebarProps>(
559
582
  </AnimatedSidebarPanelContext.Provider>
560
583
  </motion.div>
561
584
  </motion.aside>
562
- );
563
- },
564
- );
585
+ )
586
+ }
587
+ )
565
588
 
566
- export interface AnimatedSidebarTriggerProps
567
- extends ButtonHTMLAttributes<HTMLButtonElement> {}
589
+ export interface AnimatedSidebarTriggerProps extends ButtonHTMLAttributes<HTMLButtonElement> {
590
+ side?: SidebarSide
591
+ }
568
592
 
569
593
  export const AnimatedSidebarTrigger = forwardRef<
570
594
  HTMLButtonElement,
571
595
  AnimatedSidebarTriggerProps
572
596
  >(function AnimatedSidebarTrigger(
573
- { className, onClick, type = "button", ...props },
574
- forwardedRef,
597
+ { className, onClick, side = "left", type = "button", ...props },
598
+ forwardedRef
575
599
  ) {
576
- const context = useAnimatedSidebar();
577
- const expanded = context.isMobile ? context.openMobile : context.open;
600
+ const context = useAnimatedSidebar()
601
+ const isRight = side === "right"
602
+ const expanded = isRight
603
+ ? context.openRight
604
+ : context.isMobile
605
+ ? context.openMobile
606
+ : context.open
607
+ const triggerRef = isRight ? context.rightTriggerRef : context.triggerRef
578
608
 
579
609
  return (
580
610
  <button
581
611
  {...props}
582
612
  ref={(node) => {
583
- context.triggerRef.current = node;
584
- if (typeof forwardedRef === "function") forwardedRef(node);
585
- else if (forwardedRef) forwardedRef.current = node;
613
+ triggerRef.current = node
614
+ if (typeof forwardedRef === "function") forwardedRef(node)
615
+ else if (forwardedRef) forwardedRef.current = node
586
616
  }}
587
617
  type={type}
588
618
  aria-label={props["aria-label"] ?? "Toggle sidebar"}
589
619
  aria-expanded={expanded}
590
620
  data-slot="sidebar-trigger"
621
+ data-side={side}
591
622
  data-state={expanded ? "expanded" : "collapsed"}
592
623
  onClick={(event) => {
593
- onClick?.(event);
594
- if (!event.defaultPrevented) context.toggleSidebar();
624
+ onClick?.(event)
625
+ if (event.defaultPrevented) return
626
+ if (isRight) context.toggleRightSidebar()
627
+ else context.toggleSidebar()
595
628
  }}
596
629
  className={cn(
597
630
  "inline-flex size-10 shrink-0 items-center justify-center rounded-xl outline-none",
598
631
  "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
599
- className,
632
+ className
600
633
  )}
601
634
  />
602
- );
603
- });
635
+ )
636
+ })
604
637
 
605
- export interface AnimatedSidebarCloseProps
606
- extends ButtonHTMLAttributes<HTMLButtonElement> {}
638
+ export interface AnimatedSidebarCloseProps extends ButtonHTMLAttributes<HTMLButtonElement> {
639
+ side?: SidebarSide
640
+ }
607
641
 
608
642
  export const AnimatedSidebarClose = forwardRef<
609
643
  HTMLButtonElement,
610
644
  AnimatedSidebarCloseProps
611
645
  >(function AnimatedSidebarClose(
612
- { className, onClick, type = "button", ...props },
613
- forwardedRef,
646
+ { className, onClick, side = "left", type = "button", ...props },
647
+ forwardedRef
614
648
  ) {
615
- const context = useAnimatedSidebar();
649
+ const context = useAnimatedSidebar()
616
650
 
617
651
  return (
618
652
  <button
619
653
  {...props}
620
654
  ref={forwardedRef}
621
655
  type={type}
656
+ data-side={side}
622
657
  aria-label={props["aria-label"] ?? "Close sidebar"}
623
658
  onClick={(event) => {
624
- onClick?.(event);
625
- if (event.defaultPrevented) return;
626
- if (context.isMobile) context.setOpenMobile(false);
627
- else context.setOpen(false);
659
+ onClick?.(event)
660
+ if (event.defaultPrevented) return
661
+ if (side === "right") context.setOpenRight(false)
662
+ else if (context.isMobile) context.setOpenMobile(false)
663
+ else context.setOpen(false)
628
664
  }}
629
665
  className={cn(
630
666
  "inline-flex size-10 shrink-0 items-center justify-center rounded-xl outline-none",
631
667
  "focus-visible:ring-2 focus-visible:ring-ring",
632
- className,
668
+ className
633
669
  )}
634
670
  />
635
- );
636
- });
671
+ )
672
+ })
637
673
 
638
- export interface AnimatedSidebarRailProps
639
- extends ButtonHTMLAttributes<HTMLButtonElement> {}
674
+ export interface AnimatedSidebarRailProps extends ButtonHTMLAttributes<HTMLButtonElement> {}
640
675
 
641
676
  export const AnimatedSidebarRail = forwardRef<
642
677
  HTMLButtonElement,
643
678
  AnimatedSidebarRailProps
644
679
  >(function AnimatedSidebarRail(
645
680
  { className, onClick, type = "button", ...props },
646
- forwardedRef,
681
+ forwardedRef
647
682
  ) {
648
- const context = useAnimatedSidebar();
649
- const panel = useAnimatedSidebarPanel();
683
+ const context = useAnimatedSidebar()
684
+ const panel = useAnimatedSidebarPanel()
650
685
 
651
686
  return (
652
687
  <button
@@ -658,21 +693,22 @@ export const AnimatedSidebarRail = forwardRef<
658
693
  title="Toggle sidebar"
659
694
  tabIndex={-1}
660
695
  onClick={(event) => {
661
- onClick?.(event);
662
- if (!event.defaultPrevented) context.toggleSidebar();
696
+ onClick?.(event)
697
+ if (event.defaultPrevented) return
698
+ if (panel.side === "right") context.toggleRightSidebar()
699
+ else context.toggleSidebar()
663
700
  }}
664
701
  className={cn(
665
702
  "absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 outline-none md:block",
666
703
  "after:absolute after:inset-y-0 after:left-1/2 after:w-px after:bg-transparent after:transition-colors hover:after:bg-border",
667
- "data-[side=right]:right-0 data-[side=right]:translate-x-1/2 data-[side=left]:left-full",
668
- className,
704
+ "data-[side=left]:left-full data-[side=right]:right-0 data-[side=right]:translate-x-1/2",
705
+ className
669
706
  )}
670
707
  />
671
- );
672
- });
708
+ )
709
+ })
673
710
 
674
- export interface AnimatedSidebarInsetProps
675
- extends HTMLMotionProps<"main"> {}
711
+ export interface AnimatedSidebarInsetProps extends HTMLMotionProps<"main"> {}
676
712
 
677
713
  export const AnimatedSidebarInset = forwardRef<
678
714
  HTMLElement,
@@ -684,13 +720,13 @@ export const AnimatedSidebarInset = forwardRef<
684
720
  ref={forwardedRef}
685
721
  data-slot="sidebar-inset"
686
722
  className={cn(
687
- "relative flex min-h-svh min-w-0 flex-1 flex-col bg-background",
688
- "md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-2xl md:peer-data-[variant=inset]:shadow-sm",
689
- className,
723
+ "relative flex min-w-0 flex-1 flex-col bg-background",
724
+ "md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:mr-1.5 md:peer-data-[variant=inset]:rounded-md md:peer-data-[variant=inset]:shadow-md",
725
+ className
690
726
  )}
691
727
  />
692
- );
693
- });
728
+ )
729
+ })
694
730
 
695
731
  export const AnimatedSidebarHeader = forwardRef<
696
732
  HTMLDivElement,
@@ -703,8 +739,8 @@ export const AnimatedSidebarHeader = forwardRef<
703
739
  data-slot="sidebar-header"
704
740
  className={cn("flex shrink-0 flex-col gap-2 p-3", className)}
705
741
  />
706
- );
707
- });
742
+ )
743
+ })
708
744
 
709
745
  export const AnimatedSidebarContent = forwardRef<
710
746
  HTMLDivElement,
@@ -716,12 +752,12 @@ export const AnimatedSidebarContent = forwardRef<
716
752
  ref={forwardedRef}
717
753
  data-slot="sidebar-content"
718
754
  className={cn(
719
- "flex min-h-0 flex-1 flex-col gap-2 overflow-y-auto overflow-x-hidden overscroll-contain px-2 py-2",
720
- className,
755
+ "flex min-h-0 flex-1 flex-col gap-2 overflow-x-hidden overflow-y-auto overscroll-contain px-2 py-2",
756
+ className
721
757
  )}
722
758
  />
723
- );
724
- });
759
+ )
760
+ })
725
761
 
726
762
  export const AnimatedSidebarFooter = forwardRef<
727
763
  HTMLDivElement,
@@ -733,12 +769,12 @@ export const AnimatedSidebarFooter = forwardRef<
733
769
  ref={forwardedRef}
734
770
  data-slot="sidebar-footer"
735
771
  className={cn(
736
- "flex shrink-0 flex-col gap-2 border-border border-t p-3 pb-[max(0.75rem,env(safe-area-inset-bottom))]",
737
- className,
772
+ "flex shrink-0 flex-col gap-2 border-t border-border p-3 pb-[max(0.75rem,env(safe-area-inset-bottom))]",
773
+ className
738
774
  )}
739
775
  />
740
- );
741
- });
776
+ )
777
+ })
742
778
 
743
779
  export const AnimatedSidebarGroup = forwardRef<
744
780
  HTMLDivElement,
@@ -751,17 +787,17 @@ export const AnimatedSidebarGroup = forwardRef<
751
787
  data-slot="sidebar-group"
752
788
  className={cn("flex w-full min-w-0 flex-col px-1 py-1.5", className)}
753
789
  />
754
- );
755
- });
790
+ )
791
+ })
756
792
 
757
793
  export const AnimatedSidebarGroupLabel = forwardRef<
758
794
  HTMLDivElement,
759
795
  HTMLAttributes<HTMLDivElement>
760
796
  >(function AnimatedSidebarGroupLabel(
761
797
  { children, className, ...props },
762
- forwardedRef,
798
+ forwardedRef
763
799
  ) {
764
- const { collapsed } = useAnimatedSidebarPanel();
800
+ const { collapsed } = useAnimatedSidebarPanel()
765
801
 
766
802
  return (
767
803
  <div
@@ -770,23 +806,20 @@ export const AnimatedSidebarGroupLabel = forwardRef<
770
806
  aria-hidden={collapsed}
771
807
  data-slot="sidebar-group-label"
772
808
  className={cn(
773
- "mb-1 h-7 overflow-hidden px-2 text-[10px] font-medium uppercase tracking-[0.14em] text-muted-foreground transition-opacity",
809
+ "mb-1 h-7 overflow-hidden px-2 text-[10px] font-medium tracking-[0.14em] text-muted-foreground uppercase transition-opacity",
774
810
  collapsed ? "opacity-0" : "opacity-100",
775
- className,
811
+ className
776
812
  )}
777
813
  >
778
814
  {children}
779
815
  </div>
780
- );
781
- });
816
+ )
817
+ })
782
818
 
783
819
  export const AnimatedSidebarGroupContent = forwardRef<
784
820
  HTMLDivElement,
785
821
  HTMLAttributes<HTMLDivElement>
786
- >(function AnimatedSidebarGroupContent(
787
- { className, ...props },
788
- forwardedRef,
789
- ) {
822
+ >(function AnimatedSidebarGroupContent({ className, ...props }, forwardedRef) {
790
823
  return (
791
824
  <div
792
825
  {...props}
@@ -794,15 +827,15 @@ export const AnimatedSidebarGroupContent = forwardRef<
794
827
  data-slot="sidebar-group-content"
795
828
  className={cn("w-full min-w-0", className)}
796
829
  />
797
- );
798
- });
830
+ )
831
+ })
799
832
 
800
833
  export const AnimatedSidebarMenu = forwardRef<
801
834
  HTMLUListElement,
802
835
  HTMLAttributes<HTMLUListElement>
803
836
  >(function AnimatedSidebarMenu(
804
837
  { children, className, ...props },
805
- forwardedRef,
838
+ forwardedRef
806
839
  ) {
807
840
  return (
808
841
  <SharedLayoutBg
@@ -813,12 +846,15 @@ export const AnimatedSidebarMenu = forwardRef<
813
846
  pillClassName="rounded-xl bg-muted/70"
814
847
  pillContainerClassName="inset-y-auto top-0 h-9"
815
848
  data-slot="sidebar-menu"
816
- className={cn("flex w-full min-w-0 list-none flex-col gap-0.5", className)}
849
+ className={cn(
850
+ "flex w-full min-w-0 list-none flex-col gap-0.5",
851
+ className
852
+ )}
817
853
  >
818
854
  {children}
819
855
  </SharedLayoutBg>
820
- );
821
- });
856
+ )
857
+ })
822
858
 
823
859
  export const AnimatedSidebarMenuItem = forwardRef<
824
860
  HTMLLIElement,
@@ -833,13 +869,15 @@ export const AnimatedSidebarMenuItem = forwardRef<
833
869
  data-slot="sidebar-menu-item"
834
870
  className={cn("relative", className)}
835
871
  />
836
- );
837
- });
838
-
839
- export interface AnimatedSidebarMenuSubProps
840
- extends Omit<HTMLMotionProps<"ul">, "children"> {
841
- open: boolean;
842
- children?: ReactNode;
872
+ )
873
+ })
874
+
875
+ export interface AnimatedSidebarMenuSubProps extends Omit<
876
+ HTMLMotionProps<"ul">,
877
+ "children"
878
+ > {
879
+ open: boolean
880
+ children?: ReactNode
843
881
  }
844
882
 
845
883
  export const AnimatedSidebarMenuSub = forwardRef<
@@ -847,10 +885,10 @@ export const AnimatedSidebarMenuSub = forwardRef<
847
885
  AnimatedSidebarMenuSubProps
848
886
  >(function AnimatedSidebarMenuSub(
849
887
  { open, children, className, ...props },
850
- forwardedRef,
888
+ forwardedRef
851
889
  ) {
852
- const context = useAnimatedSidebar();
853
- const panel = useAnimatedSidebarPanel();
890
+ const context = useAnimatedSidebar()
891
+ const panel = useAnimatedSidebarPanel()
854
892
 
855
893
  return (
856
894
  <AnimatePresence initial={false} mode="popLayout">
@@ -866,24 +904,21 @@ export const AnimatedSidebarMenuSub = forwardRef<
866
904
  transition={context.reduce ? { duration: 0.12 } : undefined}
867
905
  data-slot="sidebar-menu-sub"
868
906
  className={cn(
869
- "relative mt-1 ml-5 flex min-w-0 flex-col gap-0.5 border-border border-l pl-3",
870
- className,
907
+ "relative mt-1 ml-5 flex min-w-0 flex-col gap-0.5 border-l border-border pl-3",
908
+ className
871
909
  )}
872
910
  >
873
911
  {children}
874
912
  </motion.ul>
875
913
  ) : null}
876
914
  </AnimatePresence>
877
- );
878
- });
915
+ )
916
+ })
879
917
 
880
918
  export const AnimatedSidebarMenuSubItem = forwardRef<
881
919
  HTMLLIElement,
882
920
  HTMLMotionProps<"li">
883
- >(function AnimatedSidebarMenuSubItem(
884
- { className, ...props },
885
- forwardedRef,
886
- ) {
921
+ >(function AnimatedSidebarMenuSubItem({ className, ...props }, forwardedRef) {
887
922
  return (
888
923
  <motion.li
889
924
  {...props}
@@ -892,20 +927,20 @@ export const AnimatedSidebarMenuSubItem = forwardRef<
892
927
  data-slot="sidebar-menu-sub-item"
893
928
  className={cn("relative min-w-0", className)}
894
929
  />
895
- );
896
- });
930
+ )
931
+ })
897
932
 
898
933
  export interface AnimatedSidebarMenuSubButtonProps {
899
- children: ReactNode;
900
- icon?: ReactNode;
901
- href?: string;
902
- isActive?: boolean;
903
- disabled?: boolean;
904
- closeOnSelect?: boolean;
905
- target?: "_blank" | "_self" | "_parent" | "_top";
906
- rel?: string;
907
- onSelect?: () => void;
908
- className?: string;
934
+ children: ReactNode
935
+ icon?: ReactNode
936
+ href?: string
937
+ isActive?: boolean
938
+ disabled?: boolean
939
+ closeOnSelect?: boolean
940
+ target?: "_blank" | "_self" | "_parent" | "_top"
941
+ rel?: string
942
+ onSelect?: () => void
943
+ className?: string
909
944
  }
910
945
 
911
946
  export function AnimatedSidebarMenuSubButton({
@@ -920,18 +955,18 @@ export function AnimatedSidebarMenuSubButton({
920
955
  onSelect,
921
956
  className,
922
957
  }: AnimatedSidebarMenuSubButtonProps) {
923
- const context = useAnimatedSidebar();
958
+ const context = useAnimatedSidebar()
924
959
 
925
960
  const select = (
926
- event: React.MouseEvent<HTMLAnchorElement | HTMLButtonElement>,
961
+ event: React.MouseEvent<HTMLAnchorElement | HTMLButtonElement>
927
962
  ) => {
928
963
  if (disabled) {
929
- event.preventDefault();
930
- return;
964
+ event.preventDefault()
965
+ return
931
966
  }
932
- onSelect?.();
933
- if (context.isMobile && closeOnSelect) context.setOpenMobile(false);
934
- };
967
+ onSelect?.()
968
+ if (context.isMobile && closeOnSelect) context.setOpenMobile(false)
969
+ }
935
970
 
936
971
  const content = (
937
972
  <>
@@ -943,7 +978,7 @@ export function AnimatedSidebarMenuSubButton({
943
978
  </span>
944
979
  <span className="min-w-0 flex-1 truncate">{children}</span>
945
980
  </>
946
- );
981
+ )
947
982
 
948
983
  const interactiveClassName = cn(
949
984
  "flex min-h-8 w-full min-w-0 items-center gap-2 rounded-lg px-2 text-left text-xs outline-none",
@@ -951,17 +986,14 @@ export function AnimatedSidebarMenuSubButton({
951
986
  "focus-visible:bg-muted/70 focus-visible:ring-2 focus-visible:ring-ring",
952
987
  isActive && "bg-muted/70 text-foreground",
953
988
  disabled && "cursor-not-allowed opacity-40",
954
- className,
955
- );
989
+ className
990
+ )
956
991
 
957
992
  return href ? (
958
993
  <motion.a
959
994
  href={href}
960
995
  target={target}
961
- rel={
962
- rel ??
963
- (target === "_blank" ? "noreferrer noopener" : undefined)
964
- }
996
+ rel={rel ?? (target === "_blank" ? "noreferrer noopener" : undefined)}
965
997
  aria-current={isActive ? "page" : undefined}
966
998
  aria-disabled={disabled || undefined}
967
999
  tabIndex={disabled ? -1 : undefined}
@@ -984,22 +1016,22 @@ export function AnimatedSidebarMenuSubButton({
984
1016
  >
985
1017
  {content}
986
1018
  </motion.button>
987
- );
1019
+ )
988
1020
  }
989
1021
 
990
1022
  export interface AnimatedSidebarMenuButtonProps {
991
- children: ReactNode;
992
- icon?: ReactNode;
993
- badge?: ReactNode;
994
- href?: string;
995
- isActive?: boolean;
996
- ariaExpanded?: boolean;
997
- disabled?: boolean;
998
- closeOnSelect?: boolean;
999
- target?: "_blank" | "_self" | "_parent" | "_top";
1000
- rel?: string;
1001
- onSelect?: () => void;
1002
- className?: string;
1023
+ children: ReactNode
1024
+ icon?: ReactNode
1025
+ badge?: ReactNode
1026
+ href?: string
1027
+ isActive?: boolean
1028
+ ariaExpanded?: boolean
1029
+ disabled?: boolean
1030
+ closeOnSelect?: boolean
1031
+ target?: "_blank" | "_self" | "_parent" | "_top"
1032
+ rel?: string
1033
+ onSelect?: () => void
1034
+ className?: string
1003
1035
  }
1004
1036
 
1005
1037
  export function AnimatedSidebarMenuButton({
@@ -1016,24 +1048,23 @@ export function AnimatedSidebarMenuButton({
1016
1048
  onSelect,
1017
1049
  className,
1018
1050
  }: AnimatedSidebarMenuButtonProps) {
1019
- const context = useAnimatedSidebar();
1020
- const panel = useAnimatedSidebarPanel();
1021
- const textLabel = typeof children === "string" ? children : undefined;
1051
+ const context = useAnimatedSidebar()
1052
+ const panel = useAnimatedSidebarPanel()
1053
+ const textLabel = typeof children === "string" ? children : undefined
1022
1054
 
1023
1055
  const select = (
1024
- event: React.MouseEvent<HTMLAnchorElement | HTMLButtonElement>,
1056
+ event: React.MouseEvent<HTMLAnchorElement | HTMLButtonElement>
1025
1057
  ) => {
1026
1058
  if (disabled) {
1027
- event.preventDefault();
1028
- return;
1059
+ event.preventDefault()
1060
+ return
1029
1061
  }
1030
- onSelect?.();
1031
- const shouldCloseOnSelect =
1032
- closeOnSelect ?? ariaExpanded === undefined;
1062
+ onSelect?.()
1063
+ const shouldCloseOnSelect = closeOnSelect ?? ariaExpanded === undefined
1033
1064
  if (context.isMobile && shouldCloseOnSelect) {
1034
- context.setOpenMobile(false);
1065
+ context.setOpenMobile(false)
1035
1066
  }
1036
- };
1067
+ }
1037
1068
 
1038
1069
  const content = (
1039
1070
  <>
@@ -1068,7 +1099,7 @@ export function AnimatedSidebarMenuButton({
1068
1099
  aria-hidden={panel.collapsed}
1069
1100
  className={cn(
1070
1101
  "relative z-10 min-w-0 flex-1 truncate",
1071
- panel.collapsed && "pointer-events-none",
1102
+ panel.collapsed && "pointer-events-none"
1072
1103
  )}
1073
1104
  >
1074
1105
  {children}
@@ -1094,7 +1125,7 @@ export function AnimatedSidebarMenuButton({
1094
1125
  </motion.span>
1095
1126
  ) : null}
1096
1127
  </>
1097
- );
1128
+ )
1098
1129
 
1099
1130
  const interactiveClassName = cn(
1100
1131
  "relative flex min-h-9 w-full min-w-0 items-center gap-2.5 overflow-hidden rounded-xl px-3 text-left text-sm font-medium outline-none",
@@ -1102,17 +1133,14 @@ export function AnimatedSidebarMenuButton({
1102
1133
  "focus-visible:bg-muted/70 focus-visible:ring-2 focus-visible:ring-ring",
1103
1134
  isActive && "text-foreground",
1104
1135
  disabled && "cursor-not-allowed opacity-40",
1105
- className,
1106
- );
1136
+ className
1137
+ )
1107
1138
 
1108
1139
  return href ? (
1109
1140
  <motion.a
1110
1141
  href={href}
1111
1142
  target={target}
1112
- rel={
1113
- rel ??
1114
- (target === "_blank" ? "noreferrer noopener" : undefined)
1115
- }
1143
+ rel={rel ?? (target === "_blank" ? "noreferrer noopener" : undefined)}
1116
1144
  aria-current={isActive ? "page" : undefined}
1117
1145
  aria-expanded={ariaExpanded}
1118
1146
  aria-disabled={disabled || undefined}
@@ -1141,5 +1169,5 @@ export function AnimatedSidebarMenuButton({
1141
1169
  >
1142
1170
  {content}
1143
1171
  </motion.button>
1144
- );
1172
+ )
1145
1173
  }