Desenvolvedores
src_
components_
ui_
Sidebar.jsx

Explicação do código

Vamos analisar o código passo a passo. O código define um componente de sidebar responsivo utilizando o React, integrando com funcionalidades específicas como cookies, atalhos de teclado, e uma interface interativa para dispositivos móveis.

1. Declaração de Constantes e Cookies 📊

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'

Aqui, são definidas constantes que ajudam no controle do estado do sidebar. O cookie sidebar:state armazena a informação sobre o estado do sidebar (aberto ou fechado). Também são configuradas as larguras do sidebar para diferentes dispositivos, além de um atalho de teclado (Ctrl + B ou Cmd + B) para alternar a visibilidade do sidebar.

2. Contexto do Sidebar 🧩

const SidebarContext = React.createContext(null)

A React Context API é usada para compartilhar o estado do sidebar entre componentes sem a necessidade de passá-lo por props manualmente. Aqui, criamos o contexto SidebarContext que será utilizado para gerenciar o estado do sidebar em toda a aplicação.

3. Hook Customizado useSidebar 🦸‍♂️

function useSidebar() {
  const context = React.useContext(SidebarContext)
  if (!context) {
    throw new Error('useSidebar must be used within a SidebarProvider.')
  }
 
  return context
}

Este hook customizado permite acessar o contexto do sidebar de forma simplificada. Ele verifica se o contexto foi configurado corretamente e retorna o valor de contexto.

4. Componente SidebarProvider 🛠️

const SidebarProvider = React.forwardRef(
  ({ defaultOpen = true, open: openProp, onOpenChange: setOpenProp, className, style, children, ...props }, ref) => {
    const isMobile = useIsMobile()
    const [openMobile, setOpenMobile] = React.useState(false)
    const [_open, _setOpen] = React.useState(defaultOpen)
    const open = openProp ?? _open
    const setOpen = React.useCallback(
      (value) => {
        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]
    )

O SidebarProvider é um componente de contexto que envolve os filhos do aplicativo e fornece o estado do sidebar para qualquer componente que utilize o useSidebar. Ele também define a lógica para alternar o estado do sidebar, incluindo a manipulação do cookie para persistir o estado entre as sessões do usuário.

5. Alternância da Visibilidade no Mobile e Desktop 📱💻

O sidebar pode ser visível ou oculto dependendo da plataforma (desktop ou mobile). O código ajusta a visibilidade e largura do sidebar conforme o dispositivo.

  • Para dispositivos móveis, utiliza-se o componente Sheet da biblioteca @radix-ui/react-slot.
  • Para desktop, a visibilidade do sidebar é gerenciada com classes do Tailwind CSS e ajustada de acordo com o estado de expansão ou colapso.

6. Controle de Teclado e Atalho 🧑‍💻

React.useEffect(() => {
  const handleKeyDown = (event) => {
    if (event.key === SIDEBAR_KEYBOARD_SHORTCUT && (event.metaKey || event.ctrlKey)) {
      event.preventDefault()
      toggleSidebar()
    }
  }
 
  window.addEventListener('keydown', handleKeyDown)
  return () => window.removeEventListener('keydown', handleKeyDown)
}, [toggleSidebar])

Aqui, foi adicionado um ouvinte de evento para capturar atalhos de teclado. Quando o usuário pressiona Ctrl + B ou Cmd + B, a função toggleSidebar é chamada para alternar o estado do sidebar.

7. Componentes do Sidebar ⬇️

O código também define os componentes estruturais do sidebar, como:

  • Sidebar: Responsável pela renderização do sidebar.
  • SidebarTrigger: Um botão de ativação do sidebar.
  • SidebarRail: Controle de largura do sidebar em desktop.

Esses componentes ajudam a estruturar e controlar a aparência e o comportamento do sidebar conforme o contexto.

Código Completo 📜

'use client'
import * as React from 'react'
import { Slot } from '@radix-ui/react-slot'
import { cva } from 'class-variance-authority'
import { PanelLeft } from 'lucide-react'
 
import { useIsMobile } from '@/hooks/use-mobile'
import { cn } from '@/lib/utils'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Separator } from '@/components/ui/separator'
import { Sheet, SheetContent } 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'
 
const SidebarContext = React.createContext(null)
 
function useSidebar() {
  const context = React.useContext(SidebarContext)
  if (!context) {
    throw new Error('useSidebar must be used within a SidebarProvider.')
  }
 
  return context
}
 
const SidebarProvider = React.forwardRef(
  (
    {
      defaultOpen = true,
      open: openProp,
      onOpenChange: setOpenProp,
      className,
      style,
      children,
      ...props
    },
    ref,
  ) => {
    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) => {
        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) => {
        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(
      () => ({
        state,
        open,
        setOpen,
        isMobile,
        openMobile,
        setOpenMobile,
        toggleSidebar,
      }),
      [state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar],
    )
 
    return (
      <SidebarContext.Provider value={contextValue}>
        <TooltipProvider delayDuration={0}>
          <div
            style={{
              '--sidebar-width': SIDEBAR_WIDTH,
              '--sidebar-width-icon': SIDEBAR_WIDTH_ICON,
              ...style,
            }}
            className={cn(
              'group/sidebar-wrapper flex min-h-svh w-full has-[[data-variant=inset]]:bg-sidebar',
              className,
            )}
            ref={ref}
            {...props}
          >
            {children}
          </div>
        </TooltipProvider>
      </SidebarContext.Provider>
    )
  },
)
SidebarProvider.displayName = 'SidebarProvider'
 
const Sidebar = React.forwardRef(
  (
    {
      side = 'left',
      variant = 'sidebar',
      collapsible = 'offcanvas',
      className,
     
 
 style,
      children,
      ...props
    },
    ref,
  ) => {
    const { state, toggleSidebar, isMobile, openMobile } = useSidebar()
    const isDesktop = !isMobile
    const sidebarVariantClass = variant === 'sidebar' ? 'Sidebar' : 'Offcanvas'
 
    const sidebarClass = cn(
      'transition-all duration-500 transform px-4 overflow-auto',
      isMobile && openMobile ? 'translate-x-0' : 'translate-x-full',
    )
 
    const transitionVariant = isDesktop
      ? sidebarVariantClass
      : isMobile
      ? `${sidebarVariantClass} ${collapsible}`
      : 'hidden'
 
    return (
      <div ref={ref} {...props} className={cn(sidebarClass, transitionVariant)} style={style}>
        <div>{children}</div>
      </div>
    )
  },
)
 
Sidebar.displayName = 'Sidebar'
 
export { SidebarProvider, Sidebar, useSidebar }