@gentleduck/registry-ui 0.2.6 → 0.2.8

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.
@@ -113,112 +113,120 @@ function SidebarProvider({
113
113
  </SidebarContext.Provider>
114
114
  )
115
115
  }
116
+ SidebarProvider.displayName = 'SidebarProvider'
117
+
118
+ const Sidebar = React.forwardRef<HTMLDivElement, SidebarProps>(
119
+ (
120
+ {
121
+ side = 'left',
122
+ variant = 'sidebar',
123
+ collapsible = 'offcanvas',
124
+ className,
125
+ children,
126
+ dir,
127
+ mobileTitle = 'Sidebar',
128
+ mobileDescription = 'Displays the mobile sidebar.',
129
+ ...props
130
+ },
131
+ ref,
132
+ ) => {
133
+ const { isMobile, state, openMobile, setOpenMobile } = useSidebar()
134
+ const direction = useDirection(dir as Direction)
116
135
 
117
- function Sidebar({
118
- side = 'left',
119
- variant = 'sidebar',
120
- collapsible = 'offcanvas',
121
- className,
122
- children,
123
- dir,
124
- mobileTitle = 'Sidebar',
125
- mobileDescription = 'Displays the mobile sidebar.',
126
- ...props
127
- }: SidebarProps) {
128
- const { isMobile, state, openMobile, setOpenMobile } = useSidebar()
129
- const direction = useDirection(dir as Direction)
130
-
131
- if (collapsible === 'none') {
132
- return (
133
- <div
134
- dir={direction}
135
- data-slot="sidebar"
136
- className={cn('flex h-full w-(--sidebar-width) flex-col bg-sidebar text-sidebar-foreground', className)}
137
- {...props}>
138
- {children}
139
- </div>
140
- )
141
- }
142
-
143
- if (isMobile) {
144
- return (
145
- <Sheet dir={direction} open={openMobile} onOpenChange={setOpenMobile} {...props}>
146
- <SheetContent
136
+ if (collapsible === 'none') {
137
+ return (
138
+ <div
139
+ ref={ref}
147
140
  dir={direction}
148
- data-sidebar="sidebar"
149
141
  data-slot="sidebar"
150
- data-mobile="true"
151
- className="w-[--sidebar-width] bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden"
152
- style={
153
- {
154
- '--sidebar-width': SIDEBAR_WIDTH_MOBILE,
155
- } as React.CSSProperties
156
- }
157
- side={side}>
158
- <SheetHeader className="sr-only">
159
- <SheetTitle>{mobileTitle}</SheetTitle>
160
- <SheetDescription>{mobileDescription}</SheetDescription>
161
- </SheetHeader>
162
- <div className="flex h-full w-full flex-col">{children}</div>
163
- </SheetContent>
164
- </Sheet>
165
- )
166
- }
142
+ className={cn('flex h-full w-(--sidebar-width) flex-col bg-sidebar text-sidebar-foreground', className)}
143
+ {...props}>
144
+ {children}
145
+ </div>
146
+ )
147
+ }
167
148
 
168
- return (
169
- <div
170
- dir={direction}
171
- className="group peer hidden text-sidebar-foreground md:block"
172
- data-state={state}
173
- data-collapsible={state === 'collapsed' ? collapsible : ''}
174
- data-variant={variant}
175
- data-side={side}
176
- data-slot="sidebar">
177
- {/* This is what handles the sidebar gap on desktop */}
178
- <div
179
- data-slot="sidebar-gap"
180
- className={cn(
181
- 'relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear',
182
- 'group-data-[collapsible=offcanvas]:w-0',
183
- variant === 'floating' || variant === 'inset'
184
- ? 'group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]'
185
- : 'group-data-[collapsible=icon]:w-(--sidebar-width-icon)',
186
- )}
187
- />
149
+ if (isMobile) {
150
+ return (
151
+ <Sheet dir={direction} open={openMobile} onOpenChange={setOpenMobile} {...props}>
152
+ <SheetContent
153
+ dir={direction}
154
+ data-sidebar="sidebar"
155
+ data-slot="sidebar"
156
+ data-mobile="true"
157
+ className="w-[--sidebar-width] bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden"
158
+ style={
159
+ {
160
+ '--sidebar-width': SIDEBAR_WIDTH_MOBILE,
161
+ } as React.CSSProperties
162
+ }
163
+ side={side}>
164
+ <SheetHeader className="sr-only">
165
+ <SheetTitle>{mobileTitle}</SheetTitle>
166
+ <SheetDescription>{mobileDescription}</SheetDescription>
167
+ </SheetHeader>
168
+ <div className="flex h-full w-full flex-col">{children}</div>
169
+ </SheetContent>
170
+ </Sheet>
171
+ )
172
+ }
173
+
174
+ return (
188
175
  <div
189
- data-slot="sidebar-container"
176
+ ref={ref}
177
+ dir={direction}
178
+ className="group peer hidden text-sidebar-foreground md:block"
179
+ data-state={state}
180
+ data-collapsible={state === 'collapsed' ? collapsible : ''}
181
+ data-variant={variant}
190
182
  data-side={side}
191
- className={cn(
192
- 'fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear data-[side=right]:right-0 data-[side=left]:left-0 data-[side=right]:group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)] data-[side=left]:group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)] md:flex',
193
- // Adjust the padding for floating and inset variants.
194
- variant === 'floating' || variant === 'inset'
195
- ? 'p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]'
196
- : 'group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l',
197
- className,
198
- )}
199
- {...props}>
183
+ data-slot="sidebar">
184
+ {/* This is what handles the sidebar gap on desktop */}
200
185
  <div
201
- data-sidebar="sidebar"
202
- data-slot="sidebar-inner"
203
- className="flex size-full flex-col overflow-hidden bg-sidebar group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:shadow-sm group-data-[variant=floating]:ring-1 group-data-[variant=floating]:ring-sidebar-border">
204
- {children}
186
+ data-slot="sidebar-gap"
187
+ className={cn(
188
+ 'relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear',
189
+ 'group-data-[collapsible=offcanvas]:w-0',
190
+ variant === 'floating' || variant === 'inset'
191
+ ? 'group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]'
192
+ : 'group-data-[collapsible=icon]:w-(--sidebar-width-icon)',
193
+ )}
194
+ />
195
+ <div
196
+ data-slot="sidebar-container"
197
+ data-side={side}
198
+ className={cn(
199
+ 'fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear data-[side=right]:right-0 data-[side=left]:left-0 data-[side=right]:group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)] data-[side=left]:group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)] md:flex',
200
+ // Adjust the padding for floating and inset variants.
201
+ variant === 'floating' || variant === 'inset'
202
+ ? 'p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]'
203
+ : 'group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l',
204
+ className,
205
+ )}
206
+ {...props}>
207
+ <div
208
+ data-sidebar="sidebar"
209
+ data-slot="sidebar-inner"
210
+ className="flex size-full flex-col overflow-hidden bg-sidebar group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:shadow-sm group-data-[variant=floating]:ring-1 group-data-[variant=floating]:ring-sidebar-border">
211
+ {children}
212
+ </div>
205
213
  </div>
206
214
  </div>
207
- </div>
208
- )
209
- }
210
-
211
- function SidebarTrigger({
212
- className,
213
- onClick,
214
- text = 'Toggle Sidebar',
215
- ...props
216
- }: React.ComponentProps<typeof Button> & { text?: string }) {
215
+ )
216
+ },
217
+ )
218
+ Sidebar.displayName = 'Sidebar'
219
+
220
+ const SidebarTrigger = React.forwardRef<
221
+ React.ComponentRef<typeof Button>,
222
+ React.ComponentPropsWithoutRef<typeof Button> & { text?: string }
223
+ >(({ className, onClick, text = 'Toggle Sidebar', ...props }, ref) => {
217
224
  const { toggleSidebar } = useSidebar()
218
225
  const direction = useDirection()
219
226
 
220
227
  return (
221
228
  <Button
229
+ ref={ref}
222
230
  data-sidebar="trigger"
223
231
  data-slot="sidebar-trigger"
224
232
  variant="ghost"
@@ -234,101 +242,122 @@ function SidebarTrigger({
234
242
  <span className="sr-only">{text}</span>
235
243
  </Button>
236
244
  )
237
- }
245
+ })
246
+ SidebarTrigger.displayName = 'SidebarTrigger'
238
247
 
239
- function SidebarRail({
240
- className,
241
- text = 'Toggle Sidebar',
242
- ...props
243
- }: React.ComponentProps<'button'> & { text?: string }) {
244
- const { toggleSidebar } = useSidebar()
245
- const direction = useDirection()
248
+ const SidebarRail = React.forwardRef<HTMLButtonElement, React.ComponentPropsWithoutRef<'button'> & { text?: string }>(
249
+ ({ className, text = 'Toggle Sidebar', ...props }, ref) => {
250
+ const { toggleSidebar } = useSidebar()
251
+ const direction = useDirection()
246
252
 
247
- return (
248
- <button
249
- data-sidebar="rail"
250
- data-slot="sidebar-rail"
251
- aria-label={text}
252
- tabIndex={-1}
253
- onClick={toggleSidebar}
254
- dir={direction}
255
- className={cn(
256
- 'absolute inset-y-0 z-20 hidden w-4 transition-all ease-linear after:absolute after:inset-y-0 after:start-1/2 after:w-0.5 hover:after:bg-sidebar-border group-data-[side=left]:-right-4 group-data-[side=right]:left-0 sm:flex ltr:-translate-x-1/2 rtl:-translate-x-1/2',
257
- 'in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize',
258
- '[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize',
259
- 'group-data-[collapsible=offcanvas]:translate-x-0 hover:group-data-[collapsible=offcanvas]:bg-sidebar group-data-[collapsible=offcanvas]:after:left-full',
260
- '[[data-side=left][data-collapsible=offcanvas]_&]:-right-2',
261
- '[[data-side=right][data-collapsible=offcanvas]_&]:-left-2',
262
- className,
263
- )}
264
- {...props}
265
- />
266
- )
267
- }
268
-
269
- function SidebarInset({ className, ...props }: React.ComponentProps<'main'>) {
270
- const direction = useDirection()
253
+ return (
254
+ <button
255
+ ref={ref}
256
+ data-sidebar="rail"
257
+ data-slot="sidebar-rail"
258
+ aria-label={text}
259
+ tabIndex={-1}
260
+ onClick={toggleSidebar}
261
+ dir={direction}
262
+ className={cn(
263
+ 'absolute inset-y-0 z-20 hidden w-4 transition-all ease-linear after:absolute after:inset-y-0 after:start-1/2 after:w-0.5 hover:after:bg-sidebar-border group-data-[side=left]:-right-4 group-data-[side=right]:left-0 sm:flex ltr:-translate-x-1/2 rtl:-translate-x-1/2',
264
+ 'in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize',
265
+ '[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize',
266
+ 'group-data-[collapsible=offcanvas]:translate-x-0 hover:group-data-[collapsible=offcanvas]:bg-sidebar group-data-[collapsible=offcanvas]:after:left-full',
267
+ '[[data-side=left][data-collapsible=offcanvas]_&]:-right-2',
268
+ '[[data-side=right][data-collapsible=offcanvas]_&]:-left-2',
269
+ className,
270
+ )}
271
+ {...props}
272
+ />
273
+ )
274
+ },
275
+ )
276
+ SidebarRail.displayName = 'SidebarRail'
271
277
 
272
- return (
273
- <main
274
- data-slot="sidebar-inset"
275
- dir={direction}
276
- className={cn(
277
- 'relative flex w-full flex-1 flex-col bg-background md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ms-2 md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ms-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm',
278
- className,
279
- )}
280
- {...props}
281
- />
282
- )
283
- }
278
+ const SidebarInset = React.forwardRef<HTMLElement, React.ComponentPropsWithoutRef<'main'>>(
279
+ ({ className, ...props }, ref) => {
280
+ const direction = useDirection()
284
281
 
285
- function SidebarInput({ className, ...props }: React.ComponentProps<typeof Input>) {
286
- const direction = useDirection()
282
+ return (
283
+ <main
284
+ ref={ref}
285
+ data-slot="sidebar-inset"
286
+ dir={direction}
287
+ className={cn(
288
+ 'relative flex w-full flex-1 flex-col bg-background md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ms-2 md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ms-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm',
289
+ className,
290
+ )}
291
+ {...props}
292
+ />
293
+ )
294
+ },
295
+ )
296
+ SidebarInset.displayName = 'SidebarInset'
287
297
 
288
- return (
289
- <Input
290
- data-slot="sidebar-input"
291
- data-sidebar="input"
292
- dir={direction}
293
- className={cn('h-8 w-full bg-background shadow-none', className)}
294
- {...props}
295
- />
296
- )
297
- }
298
+ const SidebarInput = React.forwardRef<React.ComponentRef<typeof Input>, React.ComponentPropsWithoutRef<typeof Input>>(
299
+ ({ className, ...props }, ref) => {
300
+ const direction = useDirection()
298
301
 
299
- function SidebarHeader({ className, ...props }: React.ComponentProps<'div'>) {
300
- const direction = useDirection()
302
+ return (
303
+ <Input
304
+ ref={ref}
305
+ data-slot="sidebar-input"
306
+ data-sidebar="input"
307
+ dir={direction}
308
+ className={cn('h-8 w-full bg-background shadow-none', className)}
309
+ {...props}
310
+ />
311
+ )
312
+ },
313
+ )
314
+ SidebarInput.displayName = 'SidebarInput'
301
315
 
302
- return (
303
- <div
304
- data-slot="sidebar-header"
305
- data-sidebar="header"
306
- dir={direction}
307
- className={cn('flex flex-col gap-2 p-2', className)}
308
- {...props}
309
- />
310
- )
311
- }
316
+ const SidebarHeader = React.forwardRef<HTMLDivElement, React.ComponentPropsWithoutRef<'div'>>(
317
+ ({ className, ...props }, ref) => {
318
+ const direction = useDirection()
312
319
 
313
- function SidebarFooter({ className, ...props }: React.ComponentProps<'div'>) {
314
- const direction = useDirection()
320
+ return (
321
+ <div
322
+ ref={ref}
323
+ data-slot="sidebar-header"
324
+ data-sidebar="header"
325
+ dir={direction}
326
+ className={cn('flex flex-col gap-2 p-2', className)}
327
+ {...props}
328
+ />
329
+ )
330
+ },
331
+ )
332
+ SidebarHeader.displayName = 'SidebarHeader'
315
333
 
316
- return (
317
- <div
318
- data-slot="sidebar-footer"
319
- data-sidebar="footer"
320
- dir={direction}
321
- className={cn('flex flex-col gap-2 p-2', className)}
322
- {...props}
323
- />
324
- )
325
- }
334
+ const SidebarFooter = React.forwardRef<HTMLDivElement, React.ComponentPropsWithoutRef<'div'>>(
335
+ ({ className, ...props }, ref) => {
336
+ const direction = useDirection()
326
337
 
327
- function SidebarSeparator({ className, ...props }: React.ComponentProps<typeof Separator>) {
338
+ return (
339
+ <div
340
+ ref={ref}
341
+ data-slot="sidebar-footer"
342
+ data-sidebar="footer"
343
+ dir={direction}
344
+ className={cn('flex flex-col gap-2 p-2', className)}
345
+ {...props}
346
+ />
347
+ )
348
+ },
349
+ )
350
+ SidebarFooter.displayName = 'SidebarFooter'
351
+
352
+ const SidebarSeparator = React.forwardRef<
353
+ React.ComponentRef<typeof Separator>,
354
+ React.ComponentPropsWithoutRef<typeof Separator>
355
+ >(({ className, ...props }, ref) => {
328
356
  const direction = useDirection()
329
357
 
330
358
  return (
331
359
  <Separator
360
+ ref={ref}
332
361
  data-slot="sidebar-separator"
333
362
  data-sidebar="separator"
334
363
  dir={direction}
@@ -336,54 +365,59 @@ function SidebarSeparator({ className, ...props }: React.ComponentProps<typeof S
336
365
  {...props}
337
366
  />
338
367
  )
339
- }
368
+ })
369
+ SidebarSeparator.displayName = 'SidebarSeparator'
340
370
 
341
- function SidebarContent({
342
- className,
343
- noScroll = true,
344
- ...props
345
- }: React.ComponentProps<'div'> & { noScroll?: boolean }) {
346
- const direction = useDirection()
371
+ const SidebarContent = React.forwardRef<HTMLDivElement, React.ComponentPropsWithoutRef<'div'> & { noScroll?: boolean }>(
372
+ ({ className, noScroll = true, ...props }, ref) => {
373
+ const direction = useDirection()
347
374
 
348
- return (
349
- <div
350
- data-slot="sidebar-content"
351
- data-sidebar="content"
352
- dir={direction}
353
- className={cn(
354
- 'flex min-h-0 flex-1 flex-col gap-0 overflow-auto group-data-[collapsible=icon]:overflow-hidden',
355
- noScroll && 'no-scrollbar',
356
- className,
357
- )}
358
- {...props}
359
- />
360
- )
361
- }
362
-
363
- function SidebarGroup({ className, ...props }: React.ComponentProps<'div'>) {
364
- const direction = useDirection()
375
+ return (
376
+ <div
377
+ ref={ref}
378
+ data-slot="sidebar-content"
379
+ data-sidebar="content"
380
+ dir={direction}
381
+ className={cn(
382
+ 'flex min-h-0 flex-1 flex-col gap-0 overflow-auto group-data-[collapsible=icon]:overflow-hidden',
383
+ noScroll && 'no-scrollbar',
384
+ className,
385
+ )}
386
+ {...props}
387
+ />
388
+ )
389
+ },
390
+ )
391
+ SidebarContent.displayName = 'SidebarContent'
365
392
 
366
- return (
367
- <div
368
- data-slot="sidebar-group"
369
- data-sidebar="group"
370
- dir={direction}
371
- className={cn('relative flex w-full min-w-0 flex-col p-2', className)}
372
- {...props}
373
- />
374
- )
375
- }
393
+ const SidebarGroup = React.forwardRef<HTMLDivElement, React.ComponentPropsWithoutRef<'div'>>(
394
+ ({ className, ...props }, ref) => {
395
+ const direction = useDirection()
376
396
 
377
- function SidebarGroupLabel({
378
- className,
379
- asChild = false,
380
- ...props
381
- }: React.ComponentProps<'div'> & { asChild?: boolean }) {
397
+ return (
398
+ <div
399
+ ref={ref}
400
+ data-slot="sidebar-group"
401
+ data-sidebar="group"
402
+ dir={direction}
403
+ className={cn('relative flex w-full min-w-0 flex-col p-2', className)}
404
+ {...props}
405
+ />
406
+ )
407
+ },
408
+ )
409
+ SidebarGroup.displayName = 'SidebarGroup'
410
+
411
+ const SidebarGroupLabel = React.forwardRef<
412
+ HTMLDivElement,
413
+ React.ComponentPropsWithoutRef<'div'> & { asChild?: boolean }
414
+ >(({ className, asChild = false, ...props }, ref) => {
382
415
  const Comp = asChild ? Slot : 'div'
383
416
  const direction = useDirection()
384
417
 
385
418
  return (
386
419
  <Comp
420
+ ref={ref}
387
421
  data-slot="sidebar-group-label"
388
422
  data-sidebar="group-label"
389
423
  dir={direction}
@@ -394,18 +428,19 @@ function SidebarGroupLabel({
394
428
  {...props}
395
429
  />
396
430
  )
397
- }
431
+ })
432
+ SidebarGroupLabel.displayName = 'SidebarGroupLabel'
398
433
 
399
- function SidebarGroupAction({
400
- className,
401
- asChild = false,
402
- ...props
403
- }: React.ComponentProps<'button'> & { asChild?: boolean }) {
434
+ const SidebarGroupAction = React.forwardRef<
435
+ HTMLButtonElement,
436
+ React.ComponentPropsWithoutRef<'button'> & { asChild?: boolean }
437
+ >(({ className, asChild = false, ...props }, ref) => {
404
438
  const Comp = asChild ? Slot : 'button'
405
439
  const direction = useDirection()
406
440
 
407
441
  return (
408
442
  <Comp
443
+ ref={ref}
409
444
  data-slot="sidebar-group-action"
410
445
  data-sidebar="group-action"
411
446
  dir={direction}
@@ -416,112 +451,125 @@ function SidebarGroupAction({
416
451
  {...props}
417
452
  />
418
453
  )
419
- }
454
+ })
455
+ SidebarGroupAction.displayName = 'SidebarGroupAction'
420
456
 
421
- function SidebarGroupContent({ className, ...props }: React.ComponentProps<'div'>) {
422
- const direction = useDirection()
457
+ const SidebarGroupContent = React.forwardRef<HTMLDivElement, React.ComponentPropsWithoutRef<'div'>>(
458
+ ({ className, ...props }, ref) => {
459
+ const direction = useDirection()
423
460
 
424
- return (
425
- <div
426
- data-slot="sidebar-group-content"
427
- data-sidebar="group-content"
428
- dir={direction}
429
- className={cn('w-full text-sm', className)}
430
- {...props}
431
- />
432
- )
433
- }
461
+ return (
462
+ <div
463
+ ref={ref}
464
+ data-slot="sidebar-group-content"
465
+ data-sidebar="group-content"
466
+ dir={direction}
467
+ className={cn('w-full text-sm', className)}
468
+ {...props}
469
+ />
470
+ )
471
+ },
472
+ )
473
+ SidebarGroupContent.displayName = 'SidebarGroupContent'
434
474
 
435
- function SidebarMenu({ className, ...props }: React.ComponentProps<'ul'>) {
436
- const direction = useDirection()
475
+ const SidebarMenu = React.forwardRef<HTMLUListElement, React.ComponentPropsWithoutRef<'ul'>>(
476
+ ({ className, ...props }, ref) => {
477
+ const direction = useDirection()
437
478
 
438
- return (
439
- <ul
440
- data-slot="sidebar-menu"
441
- data-sidebar="menu"
442
- dir={direction}
443
- className={cn('flex w-full min-w-0 flex-col gap-0', className)}
444
- {...props}
445
- />
446
- )
447
- }
479
+ return (
480
+ <ul
481
+ ref={ref}
482
+ data-slot="sidebar-menu"
483
+ data-sidebar="menu"
484
+ dir={direction}
485
+ className={cn('flex w-full min-w-0 flex-col gap-0', className)}
486
+ {...props}
487
+ />
488
+ )
489
+ },
490
+ )
491
+ SidebarMenu.displayName = 'SidebarMenu'
448
492
 
449
- function SidebarMenuItem({ className, dir, ...props }: React.ComponentProps<'li'>) {
450
- const direction = useDirection(dir as Direction)
451
- return (
452
- <li
453
- data-slot="sidebar-menu-item"
454
- dir={direction}
455
- data-sidebar="menu-item"
456
- className={cn('group/menu-item relative focus-within:z-10', className)}
457
- {...props}
458
- />
459
- )
460
- }
493
+ const SidebarMenuItem = React.forwardRef<HTMLLIElement, React.ComponentPropsWithoutRef<'li'>>(
494
+ ({ className, dir, ...props }, ref) => {
495
+ const direction = useDirection(dir as Direction)
496
+ return (
497
+ <li
498
+ ref={ref}
499
+ data-slot="sidebar-menu-item"
500
+ dir={direction}
501
+ data-sidebar="menu-item"
502
+ className={cn('group/menu-item relative focus-within:z-10', className)}
503
+ {...props}
504
+ />
505
+ )
506
+ },
507
+ )
508
+ SidebarMenuItem.displayName = 'SidebarMenuItem'
509
+
510
+ const SidebarMenuButton = React.forwardRef<
511
+ HTMLButtonElement,
512
+ React.ComponentPropsWithoutRef<'button'> & {
513
+ asChild?: boolean
514
+ isActive?: boolean
515
+ tooltip?: string | React.ComponentProps<typeof TooltipContent>
516
+ } & VariantProps<typeof sidebarMenuButtonVariants>
517
+ >(
518
+ (
519
+ { asChild = false, isActive = false, variant = 'default', size = 'default', tooltip, dir, className, ...props },
520
+ ref,
521
+ ) => {
522
+ const Comp = asChild ? Slot : 'button'
523
+ const { isMobile, state } = useSidebar()
524
+ const direction = useDirection(dir as Direction)
525
+ const fallbackTooltipSide = direction === 'rtl' ? 'left' : 'right'
526
+
527
+ const button = (
528
+ <Comp
529
+ ref={ref}
530
+ data-slot="sidebar-menu-button"
531
+ data-sidebar="menu-button"
532
+ data-size={size}
533
+ data-active={isActive || undefined}
534
+ className={cn(sidebarMenuButtonVariants({ variant, size }), className)}
535
+ {...props}
536
+ />
537
+ )
461
538
 
462
- function SidebarMenuButton({
463
- asChild = false,
464
- isActive = false,
465
- variant = 'default',
466
- size = 'default',
467
- tooltip,
468
- dir,
469
- className,
470
- ...props
471
- }: React.ComponentProps<'button'> & {
472
- asChild?: boolean
473
- isActive?: boolean
474
- tooltip?: string | React.ComponentProps<typeof TooltipContent>
475
- } & VariantProps<typeof sidebarMenuButtonVariants>) {
476
- const Comp = asChild ? Slot : 'button'
477
- const { isMobile, state } = useSidebar()
478
- const direction = useDirection(dir as Direction)
479
- const fallbackTooltipSide = direction === 'rtl' ? 'left' : 'right'
539
+ if (!tooltip) {
540
+ return button
541
+ }
480
542
 
481
- const button = (
482
- <Comp
483
- data-slot="sidebar-menu-button"
484
- data-sidebar="menu-button"
485
- data-size={size}
486
- data-active={isActive || undefined}
487
- className={cn(sidebarMenuButtonVariants({ variant, size }), className)}
488
- {...props}
489
- />
490
- )
543
+ const tooltipProps = typeof tooltip === 'string' ? { children: tooltip } : tooltip
491
544
 
492
- if (!tooltip) {
493
- return button
545
+ return (
546
+ <Tooltip dir={direction}>
547
+ <TooltipTrigger asChild>{button}</TooltipTrigger>
548
+ <TooltipContent
549
+ side={fallbackTooltipSide}
550
+ align="center"
551
+ hidden={state !== 'collapsed' || isMobile}
552
+ {...tooltipProps}
553
+ />
554
+ </Tooltip>
555
+ )
556
+ },
557
+ )
558
+ SidebarMenuButton.displayName = 'SidebarMenuButton'
559
+
560
+ const SidebarMenuAction = React.forwardRef<
561
+ HTMLButtonElement,
562
+ React.ComponentPropsWithoutRef<'button'> & {
563
+ asChild?: boolean
564
+ showOnHover?: boolean
494
565
  }
495
-
496
- const tooltipProps = typeof tooltip === 'string' ? { children: tooltip } : tooltip
497
-
498
- return (
499
- <Tooltip dir={direction}>
500
- <TooltipTrigger asChild>{button}</TooltipTrigger>
501
- <TooltipContent
502
- side={fallbackTooltipSide}
503
- align="center"
504
- hidden={state !== 'collapsed' || isMobile}
505
- {...tooltipProps}
506
- />
507
- </Tooltip>
508
- )
509
- }
510
-
511
- function SidebarMenuAction({
512
- className,
513
- asChild = false,
514
- showOnHover = false,
515
- ...props
516
- }: React.ComponentProps<'button'> & {
517
- asChild?: boolean
518
- showOnHover?: boolean
519
- }) {
566
+ >(({ className, asChild = false, showOnHover = false, ...props }, ref) => {
520
567
  const Comp = asChild ? Slot : 'button'
521
568
  const direction = useDirection()
522
569
 
523
570
  return (
524
571
  <Comp
572
+ ref={ref}
525
573
  data-slot="sidebar-menu-action"
526
574
  data-sidebar="menu-action"
527
575
  dir={direction}
@@ -534,32 +582,34 @@ function SidebarMenuAction({
534
582
  {...props}
535
583
  />
536
584
  )
537
- }
585
+ })
586
+ SidebarMenuAction.displayName = 'SidebarMenuAction'
538
587
 
539
- function SidebarMenuBadge({ className, ...props }: React.ComponentProps<'div'>) {
540
- const direction = useDirection()
588
+ const SidebarMenuBadge = React.forwardRef<HTMLDivElement, React.ComponentPropsWithoutRef<'div'>>(
589
+ ({ className, ...props }, ref) => {
590
+ const direction = useDirection()
541
591
 
542
- return (
543
- <div
544
- data-slot="sidebar-menu-badge"
545
- data-sidebar="menu-badge"
546
- dir={direction}
547
- className={cn(
548
- 'pointer-events-none absolute end-1 flex h-5 min-w-5 select-none items-center justify-center rounded-md px-1 font-medium text-sidebar-foreground text-xs tabular-nums peer-hover/menu-button:text-sidebar-accent-foreground group-data-[collapsible=icon]:hidden peer-data-[size=default]/menu-button:top-1.5 peer-data-[size=lg]/menu-button:top-2.5 peer-data-[size=sm]/menu-button:top-1 peer-data-active/menu-button:text-sidebar-accent-foreground',
549
- className,
550
- )}
551
- {...props}
552
- />
553
- )
554
- }
555
-
556
- function SidebarMenuSkeleton({
557
- className,
558
- showIcon = false,
559
- ...props
560
- }: React.ComponentProps<'div'> & {
561
- showIcon?: boolean
562
- }) {
592
+ return (
593
+ <div
594
+ ref={ref}
595
+ data-slot="sidebar-menu-badge"
596
+ data-sidebar="menu-badge"
597
+ dir={direction}
598
+ className={cn(
599
+ 'pointer-events-none absolute end-1 flex h-5 min-w-5 select-none items-center justify-center rounded-md px-1 font-medium text-sidebar-foreground text-xs tabular-nums peer-hover/menu-button:text-sidebar-accent-foreground group-data-[collapsible=icon]:hidden peer-data-[size=default]/menu-button:top-1.5 peer-data-[size=lg]/menu-button:top-2.5 peer-data-[size=sm]/menu-button:top-1 peer-data-active/menu-button:text-sidebar-accent-foreground',
600
+ className,
601
+ )}
602
+ {...props}
603
+ />
604
+ )
605
+ },
606
+ )
607
+ SidebarMenuBadge.displayName = 'SidebarMenuBadge'
608
+
609
+ const SidebarMenuSkeleton = React.forwardRef<
610
+ HTMLDivElement,
611
+ React.ComponentPropsWithoutRef<'div'> & { showIcon?: boolean }
612
+ >(({ className, showIcon = false, ...props }, ref) => {
563
613
  const direction = useDirection()
564
614
 
565
615
  // Random width between 50 to 90%.
@@ -569,6 +619,7 @@ function SidebarMenuSkeleton({
569
619
 
570
620
  return (
571
621
  <div
622
+ ref={ref}
572
623
  data-slot="sidebar-menu-skeleton"
573
624
  data-sidebar="menu-skeleton"
574
625
  dir={direction}
@@ -586,55 +637,61 @@ function SidebarMenuSkeleton({
586
637
  />
587
638
  </div>
588
639
  )
589
- }
590
-
591
- function SidebarMenuSub({ className, dir, ...props }: React.ComponentProps<'ul'>) {
592
- const direction = useDirection(dir as Direction)
640
+ })
641
+ SidebarMenuSkeleton.displayName = 'SidebarMenuSkeleton'
593
642
 
594
- return (
595
- <ul
596
- data-slot="sidebar-menu-sub"
597
- data-sidebar="menu-sub"
598
- dir={direction}
599
- className={cn(
600
- 'mx-3.5 flex min-w-0 flex-col gap-1 border-sidebar-border border-s px-2.5 py-0.5 group-data-[collapsible=icon]:hidden ltr:translate-x-px rtl:-translate-x-px',
601
- className,
602
- )}
603
- {...props}
604
- />
605
- )
606
- }
643
+ const SidebarMenuSub = React.forwardRef<HTMLUListElement, React.ComponentPropsWithoutRef<'ul'>>(
644
+ ({ className, dir, ...props }, ref) => {
645
+ const direction = useDirection(dir as Direction)
607
646
 
608
- function SidebarMenuSubItem({ className, dir, ...props }: React.ComponentProps<'li'>) {
609
- const direction = useDirection(dir as Direction)
610
- return (
611
- <li
612
- data-slot="sidebar-menu-sub-item"
613
- data-sidebar="menu-sub-item"
614
- dir={direction}
615
- className={cn('group/menu-sub-item relative focus-within:z-10', className)}
616
- {...props}
617
- />
618
- )
619
- }
647
+ return (
648
+ <ul
649
+ ref={ref}
650
+ data-slot="sidebar-menu-sub"
651
+ data-sidebar="menu-sub"
652
+ dir={direction}
653
+ className={cn(
654
+ 'mx-3.5 flex min-w-0 flex-col gap-1 border-sidebar-border border-s px-2.5 py-0.5 group-data-[collapsible=icon]:hidden ltr:translate-x-px rtl:-translate-x-px',
655
+ className,
656
+ )}
657
+ {...props}
658
+ />
659
+ )
660
+ },
661
+ )
662
+ SidebarMenuSub.displayName = 'SidebarMenuSub'
620
663
 
621
- function SidebarMenuSubButton({
622
- asChild = false,
623
- size = 'md',
624
- isActive = false,
625
- className,
626
- dir,
627
- ...props
628
- }: React.ComponentProps<'a'> & {
629
- asChild?: boolean
630
- size?: 'sm' | 'md'
631
- isActive?: boolean
632
- }) {
664
+ const SidebarMenuSubItem = React.forwardRef<HTMLLIElement, React.ComponentPropsWithoutRef<'li'>>(
665
+ ({ className, dir, ...props }, ref) => {
666
+ const direction = useDirection(dir as Direction)
667
+ return (
668
+ <li
669
+ ref={ref}
670
+ data-slot="sidebar-menu-sub-item"
671
+ data-sidebar="menu-sub-item"
672
+ dir={direction}
673
+ className={cn('group/menu-sub-item relative focus-within:z-10', className)}
674
+ {...props}
675
+ />
676
+ )
677
+ },
678
+ )
679
+ SidebarMenuSubItem.displayName = 'SidebarMenuSubItem'
680
+
681
+ const SidebarMenuSubButton = React.forwardRef<
682
+ HTMLAnchorElement,
683
+ React.ComponentPropsWithoutRef<'a'> & {
684
+ asChild?: boolean
685
+ size?: 'sm' | 'md'
686
+ isActive?: boolean
687
+ }
688
+ >(({ asChild = false, size = 'md', isActive = false, className, dir, ...props }, ref) => {
633
689
  const Comp = asChild ? Slot : 'a'
634
690
  const direction = useDirection(dir as Direction)
635
691
 
636
692
  return (
637
693
  <Comp
694
+ ref={ref}
638
695
  data-slot="sidebar-menu-sub-button"
639
696
  data-sidebar="menu-sub-button"
640
697
  data-size={size}
@@ -647,7 +704,8 @@ function SidebarMenuSubButton({
647
704
  {...props}
648
705
  />
649
706
  )
650
- }
707
+ })
708
+ SidebarMenuSubButton.displayName = 'SidebarMenuSubButton'
651
709
 
652
710
  export {
653
711
  Sidebar,