| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728 |
- import * as React from 'react'
- import { Slot } from '@radix-ui/react-slot'
- import { VariantProps, cva } from 'class-variance-authority'
- import { PanelLeftIcon } from 'lucide-react'
- import { cn } from '@/lib/utils'
- import { useIsMobile } from '@/hooks/use-mobile'
- import { Button } from '@/components/ui/button'
- import { Input } from '@/components/ui/input'
- import { Separator } from '@/components/ui/separator'
- import {
- Sheet,
- SheetContent,
- SheetDescription,
- SheetHeader,
- SheetTitle,
- } from '@/components/ui/sheet'
- import { Skeleton } from '@/components/ui/skeleton'
- import {
- Tooltip,
- TooltipContent,
- TooltipProvider,
- TooltipTrigger,
- } from '@/components/ui/tooltip'
- const SIDEBAR_COOKIE_NAME = 'sidebar_state'
- const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7
- const SIDEBAR_WIDTH = '16rem'
- const SIDEBAR_WIDTH_MOBILE = '18rem'
- const SIDEBAR_WIDTH_ICON = '3rem'
- const SIDEBAR_KEYBOARD_SHORTCUT = 'b'
- type SidebarContextProps = {
- state: 'expanded' | 'collapsed'
- open: boolean
- setOpen: (open: boolean) => void
- openMobile: boolean
- setOpenMobile: (open: boolean) => void
- isMobile: boolean
- toggleSidebar: () => void
- }
- const SidebarContext = React.createContext<SidebarContextProps | null>(null)
- function useSidebar() {
- const context = React.useContext(SidebarContext)
- if (!context) {
- throw new Error('useSidebar must be used within a SidebarProvider.')
- }
- return context
- }
- function SidebarProvider({
- defaultOpen = true,
- open: openProp,
- onOpenChange: setOpenProp,
- className,
- style,
- children,
- ...props
- }: React.ComponentProps<'div'> & {
- defaultOpen?: boolean
- open?: boolean
- onOpenChange?: (open: boolean) => void
- }) {
- const isMobile = useIsMobile()
- const [openMobile, setOpenMobile] = React.useState(false)
- // This is the internal state of the sidebar.
- // We use openProp and setOpenProp for control from outside the component.
- const [_open, _setOpen] = React.useState(defaultOpen)
- const open = openProp ?? _open
- const setOpen = React.useCallback(
- (value: boolean | ((value: boolean) => boolean)) => {
- const openState = typeof value === 'function' ? value(open) : value
- if (setOpenProp) {
- setOpenProp(openState)
- } else {
- _setOpen(openState)
- }
- // This sets the cookie to keep the sidebar state.
- document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`
- },
- [setOpenProp, open]
- )
- // Helper to toggle the sidebar.
- const toggleSidebar = React.useCallback(() => {
- return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open)
- }, [isMobile, setOpen, setOpenMobile])
- // Adds a keyboard shortcut to toggle the sidebar.
- React.useEffect(() => {
- const handleKeyDown = (event: KeyboardEvent) => {
- if (
- event.key === SIDEBAR_KEYBOARD_SHORTCUT &&
- (event.metaKey || event.ctrlKey)
- ) {
- event.preventDefault()
- toggleSidebar()
- }
- }
- window.addEventListener('keydown', handleKeyDown)
- return () => window.removeEventListener('keydown', handleKeyDown)
- }, [toggleSidebar])
- // We add a state so that we can do data-state="expanded" or "collapsed".
- // This makes it easier to style the sidebar with Tailwind classes.
- const state = open ? 'expanded' : 'collapsed'
- const contextValue = React.useMemo<SidebarContextProps>(
- () => ({
- state,
- open,
- setOpen,
- isMobile,
- openMobile,
- setOpenMobile,
- toggleSidebar,
- }),
- [state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar]
- )
- return (
- <SidebarContext.Provider value={contextValue}>
- <TooltipProvider delayDuration={0}>
- <div
- data-slot='sidebar-wrapper'
- style={
- {
- '--sidebar-width': SIDEBAR_WIDTH,
- '--sidebar-width-icon': SIDEBAR_WIDTH_ICON,
- ...style,
- } as React.CSSProperties
- }
- className={cn(
- 'group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex min-h-svh w-full',
- className
- )}
- {...props}
- >
- {children}
- </div>
- </TooltipProvider>
- </SidebarContext.Provider>
- )
- }
- function Sidebar({
- side = 'left',
- variant = 'sidebar',
- collapsible = 'offcanvas',
- className,
- children,
- ...props
- }: React.ComponentProps<'div'> & {
- side?: 'left' | 'right'
- variant?: 'sidebar' | 'floating' | 'inset'
- collapsible?: 'offcanvas' | 'icon' | 'none'
- }) {
- const { isMobile, state, openMobile, setOpenMobile } = useSidebar()
- if (collapsible === 'none') {
- return (
- <div
- data-slot='sidebar'
- className={cn(
- 'bg-sidebar text-sidebar-foreground flex h-full w-(--sidebar-width) flex-col',
- className
- )}
- {...props}
- >
- {children}
- </div>
- )
- }
- if (isMobile) {
- return (
- <Sheet open={openMobile} onOpenChange={setOpenMobile} {...props}>
- <SheetContent
- data-sidebar='sidebar'
- data-slot='sidebar'
- data-mobile='true'
- className='bg-sidebar text-sidebar-foreground w-(--sidebar-width) p-0 [&>button]:hidden'
- style={
- {
- '--sidebar-width': SIDEBAR_WIDTH_MOBILE,
- } as React.CSSProperties
- }
- side={side}
- >
- <SheetHeader className='sr-only'>
- <SheetTitle>Sidebar</SheetTitle>
- <SheetDescription>Displays the mobile sidebar.</SheetDescription>
- </SheetHeader>
- <div className='flex h-full w-full flex-col'>{children}</div>
- </SheetContent>
- </Sheet>
- )
- }
- return (
- <div
- className='group peer text-sidebar-foreground hidden md:block'
- data-state={state}
- data-collapsible={state === 'collapsed' ? collapsible : ''}
- data-variant={variant}
- data-side={side}
- data-slot='sidebar'
- >
- {/* This is what handles the sidebar gap on desktop */}
- <div
- data-slot='sidebar-gap'
- 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',
- variant === 'floating' || variant === 'inset'
- ? 'group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]'
- : 'group-data-[collapsible=icon]:w-(--sidebar-width-icon)'
- )}
- />
- <div
- data-slot='sidebar-container'
- className={cn(
- 'fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[inset-inline,width] duration-200 ease-linear md:flex',
- side === 'left'
- ? 'start-0 group-data-[collapsible=offcanvas]:-start-[calc(var(--sidebar-width))]'
- : 'end-0 group-data-[collapsible=offcanvas]:-end-[calc(var(--sidebar-width))]',
- // Adjust the padding for floating and inset variants.
- variant === 'floating' || variant === 'inset'
- ? 'p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]'
- : 'group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-e group-data-[side=right]:border-s',
- className
- )}
- {...props}
- >
- <div
- data-sidebar='sidebar'
- data-slot='sidebar-inner'
- className='bg-sidebar group-data-[variant=floating]:border-sidebar-border flex h-full w-full flex-col group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:shadow-sm'
- >
- {children}
- </div>
- </div>
- </div>
- )
- }
- function SidebarTrigger({
- className,
- onClick,
- ...props
- }: React.ComponentProps<typeof Button>) {
- const { toggleSidebar } = useSidebar()
- return (
- <Button
- data-sidebar='trigger'
- data-slot='sidebar-trigger'
- variant='ghost'
- size='icon'
- className={cn('size-7', className)}
- onClick={(event) => {
- onClick?.(event)
- toggleSidebar()
- }}
- {...props}
- >
- <PanelLeftIcon />
- <span className='sr-only'>Toggle Sidebar</span>
- </Button>
- )
- }
- function SidebarRail({ className, ...props }: React.ComponentProps<'button'>) {
- const { toggleSidebar } = useSidebar()
- return (
- <button
- data-sidebar='rail'
- data-slot='sidebar-rail'
- aria-label='Toggle Sidebar'
- tabIndex={-1}
- onClick={toggleSidebar}
- title='Toggle Sidebar'
- className={cn(
- 'hover:after:bg-sidebar-border absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear group-data-[side=left]:-end-4 group-data-[side=right]:start-0 after:absolute after:inset-y-0 after:start-1/2 after:w-[2px] sm:flex',
- 'in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize',
- '[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize',
- 'hover:group-data-[collapsible=offcanvas]:bg-sidebar group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:start-full',
- '[[data-side=left][data-collapsible=offcanvas]_&]:-end-2',
- '[[data-side=right][data-collapsible=offcanvas]_&]:-start-2',
- // RTL support
- 'rtl:translate-x-1/2',
- 'rtl:in-data-[side=left]:cursor-e-resize rtl:in-data-[side=right]:cursor-w-resize',
- 'rtl:[[data-side=left][data-state=collapsed]_&]:cursor-w-resize rtl:[[data-side=right][data-state=collapsed]_&]:cursor-e-resize',
- className
- )}
- {...props}
- />
- )
- }
- function SidebarInset({ className, ...props }: React.ComponentProps<'div'>) {
- return (
- <div
- data-slot='sidebar-inset'
- className={cn(
- 'bg-background relative flex w-full flex-1 flex-col',
- '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 md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ms-2',
- className
- )}
- {...props}
- />
- )
- }
- function SidebarInput({
- className,
- ...props
- }: React.ComponentProps<typeof Input>) {
- return (
- <Input
- data-slot='sidebar-input'
- data-sidebar='input'
- className={cn('bg-background h-8 w-full shadow-none', className)}
- {...props}
- />
- )
- }
- function SidebarHeader({ className, ...props }: React.ComponentProps<'div'>) {
- return (
- <div
- data-slot='sidebar-header'
- data-sidebar='header'
- className={cn('flex flex-col gap-2 p-2', className)}
- {...props}
- />
- )
- }
- function SidebarFooter({ className, ...props }: React.ComponentProps<'div'>) {
- return (
- <div
- data-slot='sidebar-footer'
- data-sidebar='footer'
- className={cn('flex flex-col gap-2 p-2', className)}
- {...props}
- />
- )
- }
- function SidebarSeparator({
- className,
- ...props
- }: React.ComponentProps<typeof Separator>) {
- return (
- <Separator
- data-slot='sidebar-separator'
- data-sidebar='separator'
- className={cn('bg-sidebar-border mx-2 w-auto', className)}
- {...props}
- />
- )
- }
- function SidebarContent({ className, ...props }: React.ComponentProps<'div'>) {
- return (
- <div
- data-slot='sidebar-content'
- data-sidebar='content'
- className={cn(
- 'flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden',
- className
- )}
- {...props}
- />
- )
- }
- function SidebarGroup({ className, ...props }: React.ComponentProps<'div'>) {
- return (
- <div
- data-slot='sidebar-group'
- data-sidebar='group'
- className={cn('relative flex w-full min-w-0 flex-col p-2', className)}
- {...props}
- />
- )
- }
- function SidebarGroupLabel({
- className,
- asChild = false,
- ...props
- }: React.ComponentProps<'div'> & { asChild?: boolean }) {
- const Comp = asChild ? Slot : 'div'
- return (
- <Comp
- data-slot='sidebar-group-label'
- data-sidebar='group-label'
- className={cn(
- 'text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
- 'group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0',
- className
- )}
- {...props}
- />
- )
- }
- function SidebarGroupAction({
- className,
- asChild = false,
- ...props
- }: React.ComponentProps<'button'> & { asChild?: boolean }) {
- const Comp = asChild ? Slot : 'button'
- return (
- <Comp
- data-slot='sidebar-group-action'
- data-sidebar='group-action'
- className={cn(
- 'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground absolute end-3 top-3.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
- // Increases the hit area of the button on mobile.
- 'after:absolute after:-inset-2 md:after:hidden',
- 'group-data-[collapsible=icon]:hidden',
- className
- )}
- {...props}
- />
- )
- }
- function SidebarGroupContent({
- className,
- ...props
- }: React.ComponentProps<'div'>) {
- return (
- <div
- data-slot='sidebar-group-content'
- data-sidebar='group-content'
- className={cn('w-full text-sm', className)}
- {...props}
- />
- )
- }
- function SidebarMenu({ className, ...props }: React.ComponentProps<'ul'>) {
- return (
- <ul
- data-slot='sidebar-menu'
- data-sidebar='menu'
- className={cn('flex w-full min-w-0 flex-col gap-1', className)}
- {...props}
- />
- )
- }
- function SidebarMenuItem({ className, ...props }: React.ComponentProps<'li'>) {
- return (
- <li
- data-slot='sidebar-menu-item'
- data-sidebar='menu-item'
- className={cn('group/menu-item relative', className)}
- {...props}
- />
- )
- }
- const sidebarMenuButtonVariants = cva(
- 'peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-start text-sm outline-hidden ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pe-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0',
- {
- variants: {
- variant: {
- default: 'hover:bg-sidebar-accent hover:text-sidebar-accent-foreground',
- outline:
- 'bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]',
- },
- size: {
- default: 'h-8 text-sm',
- sm: 'h-7 text-xs',
- lg: 'h-12 text-sm group-data-[collapsible=icon]:p-0!',
- },
- },
- defaultVariants: {
- variant: 'default',
- size: 'default',
- },
- }
- )
- function SidebarMenuButton({
- asChild = false,
- isActive = false,
- variant = 'default',
- size = 'default',
- tooltip,
- className,
- ...props
- }: React.ComponentProps<'button'> & {
- asChild?: boolean
- isActive?: boolean
- tooltip?: string | React.ComponentProps<typeof TooltipContent>
- } & VariantProps<typeof sidebarMenuButtonVariants>) {
- const Comp = asChild ? Slot : 'button'
- const { isMobile, state } = useSidebar()
- const button = (
- <Comp
- data-slot='sidebar-menu-button'
- data-sidebar='menu-button'
- data-size={size}
- data-active={isActive}
- className={cn(sidebarMenuButtonVariants({ variant, size }), className)}
- {...props}
- />
- )
- if (!tooltip) {
- return button
- }
- if (typeof tooltip === 'string') {
- tooltip = {
- children: tooltip,
- }
- }
- return (
- <Tooltip>
- <TooltipTrigger asChild>{button}</TooltipTrigger>
- <TooltipContent
- side='right'
- align='center'
- hidden={state !== 'collapsed' || isMobile}
- {...tooltip}
- />
- </Tooltip>
- )
- }
- function SidebarMenuAction({
- className,
- asChild = false,
- showOnHover = false,
- ...props
- }: React.ComponentProps<'button'> & {
- asChild?: boolean
- showOnHover?: boolean
- }) {
- const Comp = asChild ? Slot : 'button'
- return (
- <Comp
- data-slot='sidebar-menu-action'
- data-sidebar='menu-action'
- className={cn(
- 'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground peer-hover/menu-button:text-sidebar-accent-foreground absolute end-1 top-1.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
- // Increases the hit area of the button on mobile.
- 'after:absolute after:-inset-2 md:after:hidden',
- 'peer-data-[size=sm]/menu-button:top-1',
- 'peer-data-[size=default]/menu-button:top-1.5',
- 'peer-data-[size=lg]/menu-button:top-2.5',
- 'group-data-[collapsible=icon]:hidden',
- showOnHover &&
- 'peer-data-[active=true]/menu-button:text-sidebar-accent-foreground group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 md:opacity-0',
- className
- )}
- {...props}
- />
- )
- }
- function SidebarMenuBadge({
- className,
- ...props
- }: React.ComponentProps<'div'>) {
- return (
- <div
- data-slot='sidebar-menu-badge'
- data-sidebar='menu-badge'
- className={cn(
- 'text-sidebar-foreground pointer-events-none absolute end-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium tabular-nums select-none',
- 'peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground',
- 'peer-data-[size=sm]/menu-button:top-1',
- 'peer-data-[size=default]/menu-button:top-1.5',
- 'peer-data-[size=lg]/menu-button:top-2.5',
- 'group-data-[collapsible=icon]:hidden',
- className
- )}
- {...props}
- />
- )
- }
- function SidebarMenuSkeleton({
- className,
- showIcon = false,
- ...props
- }: React.ComponentProps<'div'> & {
- showIcon?: boolean
- }) {
- // Random width between 50 to 90%.
- const width = React.useMemo(() => {
- return `${Math.floor(Math.random() * 40) + 50}%`
- }, [])
- return (
- <div
- data-slot='sidebar-menu-skeleton'
- data-sidebar='menu-skeleton'
- className={cn('flex h-8 items-center gap-2 rounded-md px-2', className)}
- {...props}
- >
- {showIcon && (
- <Skeleton
- className='size-4 rounded-md'
- data-sidebar='menu-skeleton-icon'
- />
- )}
- <Skeleton
- className='h-4 max-w-(--skeleton-width) flex-1'
- data-sidebar='menu-skeleton-text'
- style={
- {
- '--skeleton-width': width,
- } as React.CSSProperties
- }
- />
- </div>
- )
- }
- function SidebarMenuSub({ className, ...props }: React.ComponentProps<'ul'>) {
- return (
- <ul
- data-slot='sidebar-menu-sub'
- data-sidebar='menu-sub'
- className={cn(
- 'border-sidebar-border mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-s px-2.5 py-0.5',
- 'group-data-[collapsible=icon]:hidden',
- className
- )}
- {...props}
- />
- )
- }
- function SidebarMenuSubItem({
- className,
- ...props
- }: React.ComponentProps<'li'>) {
- return (
- <li
- data-slot='sidebar-menu-sub-item'
- data-sidebar='menu-sub-item'
- className={cn('group/menu-sub-item relative', className)}
- {...props}
- />
- )
- }
- function SidebarMenuSubButton({
- asChild = false,
- size = 'md',
- isActive = false,
- className,
- ...props
- }: React.ComponentProps<'a'> & {
- asChild?: boolean
- size?: 'sm' | 'md'
- isActive?: boolean
- }) {
- const Comp = asChild ? Slot : 'a'
- return (
- <Comp
- data-slot='sidebar-menu-sub-button'
- data-sidebar='menu-sub-button'
- data-size={size}
- data-active={isActive}
- className={cn(
- 'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 outline-hidden focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-inherit',
- 'data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground',
- size === 'sm' && 'text-xs',
- size === 'md' && 'text-sm',
- 'group-data-[collapsible=icon]:hidden',
- className
- )}
- {...props}
- />
- )
- }
- 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,
- }
|