साइडबार
डैशबोर्ड, दस्तावेज़ और एडमिन टूल्स के लिए एक तह हो सकने वाला ऐप-शेल नेविगेशन पैनल — नियोब्रूटलिस्ट बॉर्डर्स-एंड-शैडोज़ ट्रीटमेंट के साथ।
"use client"
import * as React from "react"साइडबार बनाने के लिहाज़ से सबसे जटिल कंपोनेंट्स में से एक हैं। ये किसी भी एप्लिकेशन के केंद्र में होते हैं और अक्सर इनमें कई चलायमान हिस्से होते हैं।
अब हमारे पास आगे निर्माण करने के लिए एक ठोस आधार है। कंपोज़ेबल। थीम-योग्य। अनुकूलन-योग्य।
इंस्टॉलेशन#
pnpm dlx shadcn@latest add https://neobrutalism.com/r/base/sidebar.jsonnpx shadcn@latest add https://neobrutalism.com/r/base/sidebar.jsonyarn dlx shadcn@latest add https://neobrutalism.com/r/base/sidebar.jsonbunx --bun shadcn@latest add https://neobrutalism.com/r/base/sidebar.json
"use client"
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { PanelLeftIcon } from "lucide-react"
import { Slot } from "radix-ui"
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 flex min-h-svh w-full has-data-[variant=inset]:bg-sidebar",
className
)}
{...props}
>
{children}
</div>
</TooltipProvider>
</SidebarContext.Provider>
)
}
function Sidebar({
side = "left",
variant = "sidebar",
collapsible = "offcanvas",
className,
children,
dir,
...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(
"flex h-full w-(--sidebar-width) flex-col bg-sidebar text-sidebar-foreground",
className
)}
{...props}
>
{children}
</div>
)
}
if (isMobile) {
return (
<Sheet open={openMobile} onOpenChange={setOpenMobile} {...props}>
<SheetContent
dir={dir}
data-sidebar="sidebar"
data-slot="sidebar"
data-mobile="true"
className="w-(--sidebar-width) bg-sidebar p-0 text-sidebar-foreground [&>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 hidden text-sidebar-foreground 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"
data-side={side}
className={cn(
"fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear data-[side=left]:left-0 data-[side=left]:group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)] data-[side=right]:right-0 data-[side=right]:group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)] md:flex",
// 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-r-2 group-data-[side=right]:border-l-2",
className
)}
{...props}
>
<div
data-sidebar="sidebar"
data-slot="sidebar-inner"
className="flex size-full flex-col bg-sidebar group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border-2 group-data-[variant=floating]:border-sidebar-border group-data-[variant=floating]:shadow-md"
>
{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-sm"
className={cn(className)}
onClick={(event) => {
onClick?.(event)
toggleSidebar()
}}
{...props}
>
<PanelLeftIcon className="cn-rtl-flip" />
<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(
"absolute inset-y-0 z-20 hidden w-4 transition-all ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:start-1/2 after:w-[2px] hover:after:bg-sidebar-border sm:flex ltr:-translate-x-1/2 rtl:-translate-x-1/2",
"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",
"group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full hover:group-data-[collapsible=offcanvas]:bg-sidebar",
"[[data-side=left][data-collapsible=offcanvas]_&]:-right-2",
"[[data-side=right][data-collapsible=offcanvas]_&]:-left-2",
className
)}
{...props}
/>
)
}
function SidebarInset({ className, ...props }: React.ComponentProps<"main">) {
return (
<main
data-slot="sidebar-inset"
className={cn(
"relative flex w-full flex-1 flex-col bg-background md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:border-2 md:peer-data-[variant=inset]:border-border md:peer-data-[variant=inset]:shadow-md md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2",
className
)}
{...props}
/>
)
}
function SidebarInput({
className,
...props
}: React.ComponentProps<typeof Input>) {
return (
<Input
data-slot="sidebar-input"
data-sidebar="input"
className={cn("h-8 w-full bg-background 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("mx-2 w-auto bg-sidebar-border", className)}
{...props}
/>
)
}
function SidebarContent({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="sidebar-content"
data-sidebar="content"
className={cn(
"no-scrollbar flex min-h-0 flex-1 flex-col gap-0 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.Root : "div"
return (
<Comp
data-slot="sidebar-group-label"
data-sidebar="group-label"
className={cn(
"flex h-8 shrink-0 items-center rounded px-2 font-head text-xs font-semibold tracking-wider text-sidebar-foreground/70 uppercase ring-sidebar-ring outline-hidden transition-[margin,opacity] duration-200 ease-linear group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0 focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
className
)}
{...props}
/>
)
}
function SidebarGroupAction({
className,
asChild = false,
...props
}: React.ComponentProps<"button"> & { asChild?: boolean }) {
const Comp = asChild ? Slot.Root : "button"
return (
<Comp
data-slot="sidebar-group-action"
data-sidebar="group-action"
className={cn(
"absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded border-2 border-transparent p-0 text-sidebar-foreground ring-sidebar-ring outline-hidden transition-transform group-data-[collapsible=icon]:hidden after:absolute after:-inset-2 hover:bg-accent hover:text-accent-foreground focus-visible:ring-2 md:after:hidden [&>svg]:size-4 [&>svg]:shrink-0",
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-0", 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 group/menu-button flex w-full items-center gap-2 overflow-hidden rounded border-2 border-transparent p-2 text-left font-head text-sm ring-sidebar-ring outline-hidden transition-[width,height,padding] group-has-data-[sidebar=menu-action]/menu-item:pr-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! hover:bg-accent hover:text-accent-foreground focus-visible:ring-2 active:bg-accent active:text-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-open:hover:bg-accent data-open:hover:text-accent-foreground data-active:border-border data-active:bg-primary data-active:font-medium data-active:text-primary-foreground data-active:shadow-md [&_svg]:size-4 [&_svg]:shrink-0 [&>span:last-child]:truncate",
{
variants: {
variant: {
default: "hover:bg-accent hover:text-accent-foreground",
outline:
"border-border bg-background shadow-md hover:bg-accent hover:text-accent-foreground hover:shadow-lg",
},
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.Root : "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.Root : "button"
return (
<Comp
data-slot="sidebar-menu-action"
data-sidebar="menu-action"
className={cn(
"absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded border-2 border-transparent p-0 text-sidebar-foreground ring-sidebar-ring outline-hidden transition-transform group-data-[collapsible=icon]:hidden peer-hover/menu-button:text-sidebar-accent-foreground 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 after:absolute after:-inset-2 hover:bg-accent hover:text-accent-foreground focus-visible:ring-2 md:after:hidden [&>svg]:size-4 [&>svg]:shrink-0",
showOnHover &&
"group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 peer-data-active/menu-button:text-sidebar-accent-foreground aria-expanded: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(
"pointer-events-none absolute right-1 flex h-5 min-w-5 items-center justify-center rounded border-2 border-border px-1 font-head text-xs font-medium text-sidebar-foreground tabular-nums select-none group-data-[collapsible=icon]:hidden peer-hover/menu-button:text-sidebar-accent-foreground 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",
className
)}
{...props}
/>
)
}
function SidebarMenuSkeleton({
className,
showIcon = false,
...props
}: React.ComponentProps<"div"> & {
showIcon?: boolean
}) {
// Random width between 50 to 90%.
const [width] = React.useState(() => {
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(
"mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l border-sidebar-border 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.Root : "a"
return (
<Comp
data-slot="sidebar-menu-sub-button"
data-sidebar="menu-sub-button"
data-size={size}
data-active={isActive}
className={cn(
"flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded border-2 border-transparent px-2 font-head text-sidebar-foreground ring-sidebar-ring outline-hidden group-data-[collapsible=icon]:hidden hover:bg-accent hover:text-accent-foreground focus-visible:ring-2 active:bg-accent active:text-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[size=md]:text-sm data-[size=sm]:text-xs data-active:border-border data-active:bg-primary data-active:text-primary-foreground data-active:shadow-md [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-accent-foreground",
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,
}
उपयोग#
import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar"
import { AppSidebar } from "@/components/app-sidebar"
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<SidebarProvider>
<AppSidebar />
<main>
<SidebarTrigger />
{children}
</main>
</SidebarProvider>
)
}import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarGroup,
SidebarHeader,
} from "@/components/ui/sidebar"
export function AppSidebar() {
return (
<Sidebar>
<SidebarHeader />
<SidebarContent>
<SidebarGroup />
<SidebarGroup />
</SidebarContent>
<SidebarFooter />
</Sidebar>
)
}कंपोज़िशन#
Sidebar लेआउट बनाने के लिए निम्नलिखित कंपोज़िशन का उपयोग करें:
SidebarProvider
├── Sidebar
│ ├── SidebarHeader
│ ├── SidebarContent
│ │ ├── SidebarGroup
│ │ │ ├── SidebarGroupLabel
│ │ │ ├── SidebarGroupAction
│ │ │ ├── SidebarGroupContent
│ │ │ └── SidebarMenu
│ │ │ ├── SidebarMenuItem
│ │ │ │ ├── SidebarMenuButton
│ │ │ │ ├── SidebarMenuAction
│ │ │ │ └── SidebarMenuBadge
│ │ │ └── SidebarMenuItem
│ │ │ ├── SidebarMenuButton
│ │ │ └── SidebarMenuSub
│ │ │ ├── SidebarMenuSubItem
│ │ │ └── SidebarMenuSubItem
│ │ └── SidebarGroup
│ │ └── SidebarMenu
│ │ ├── SidebarMenuItem
│ │ └── SidebarMenuItem
│ ├── SidebarFooter
│ └── SidebarRail
├── SidebarInset
└── SidebarTriggerसंरचना#
- SidebarProvider — कोलैप्सिबल स्टेट संभालता है और चाइल्ड कंपोनेंट्स को साइडबार कॉन्टेक्स्ट प्रदान करता है।
- Sidebar — मुख्य कोलैप्सिबल साइडबार पैनल।
- SidebarHeader — ऊपर चिपका रहता है; ब्रांडिंग, शीर्षकों या वर्कस्पेस स्विचर्स के लिए उपयोग करें।
- SidebarFooter — नीचे चिपका रहता है; यूज़र मेन्यू, सेटिंग्स या एक्शन्स के लिए उपयोग करें।
- SidebarContent — हेडर और फ़ुटर के बीच का स्क्रॉल-योग्य क्षेत्र।
- SidebarGroup — संबंधित नेविगेशन को एक साथ समूहित करता है, वैकल्पिक लेबल, एक्शन और कॉन्टेंट क्षेत्रों के साथ।
- SidebarMenu / SidebarMenuItem — लिंक, बैज, एक्शन और नेस्टेड सबमेन्यू के लिए मेन्यू संरचना।
- SidebarRail — जहाँ लागू हो, वहाँ साइडबार की चौड़ाई समायोजित करने के लिए रीसाइज़ हैंडल।
- SidebarInset —
insetवैरिएंट का उपयोग करते समय मुख्य कॉन्टेंट को लपेटता है। - SidebarTrigger — वह नियंत्रण जो साइडबार को खुला या कोलैप्स्ड टॉगल करता है।
SidebarProvider#
SidebarProvider कंपोनेंट का उपयोग Sidebar कंपोनेंट को साइडबार कॉन्टेक्स्ट प्रदान करने के लिए किया जाता है। आपको हमेशा अपनी एप्लिकेशन को SidebarProvider कंपोनेंट में लपेटना चाहिए।
Props#
| Name | Type | Description |
|---|---|---|
defaultOpen | boolean | साइडबार की डिफ़ॉल्ट खुली स्थिति। |
open | boolean | साइडबार की खुली स्थिति (नियंत्रित)। |
onOpenChange | (open: boolean) => void | साइडबार की खुली स्थिति सेट करता है (नियंत्रित)। |
चौड़ाई#
अगर आपकी एप्लिकेशन में एक ही साइडबार है, तो आप उसकी चौड़ाई सेट करने के लिए sidebar.tsx में SIDEBAR_WIDTH और SIDEBAR_WIDTH_MOBILE वेरिएबल्स का उपयोग कर सकते हैं।
const SIDEBAR_WIDTH = "16rem"
const SIDEBAR_WIDTH_MOBILE = "18rem"अपनी एप्लिकेशन में कई साइडबार के लिए, आप style प्रॉप में --sidebar-width और --sidebar-width-mobile CSS वेरिएबल्स का उपयोग कर सकते हैं।
<SidebarProvider
style={
{
"--sidebar-width": "20rem",
"--sidebar-width-mobile": "20rem",
} as React.CSSProperties
}
>
<Sidebar />
</SidebarProvider>कीबोर्ड शॉर्टकट#
साइडबार को ट्रिगर करने के लिए, Mac पर cmd+b और Windows पर ctrl+b कीबोर्ड शॉर्टकट का उपयोग करें।
const SIDEBAR_KEYBOARD_SHORTCUT = "b"Sidebar#
मुख्य Sidebar कंपोनेंट, जिसका उपयोग एक कोलैप्सिबल साइडबार रेंडर करने के लिए किया जाता है।
Props#
| Property | Type | Description |
|---|---|---|
side | left or right | साइडबार का पक्ष। |
variant | sidebar, floating, or inset | साइडबार का वैरिएंट। |
collapsible | offcanvas, icon, or none | साइडबार की कोलैप्सिबल स्थिति। |
| Prop | Description |
|---|---|
offcanvas | एक कोलैप्सिबल साइडबार जो बाएँ या दाएँ से स्लाइड करके आता है। |
icon | एक साइडबार जो आइकनों में कोलैप्स हो जाता है। |
none | एक ग़ैर-कोलैप्सिबल साइडबार। |
नोट: अगर आप inset वैरिएंट का उपयोग करते हैं, तो अपने मुख्य कॉन्टेंट को
SidebarInset कंपोनेंट में लपेटना याद रखें।
<SidebarProvider>
<Sidebar variant="inset" />
<SidebarInset>
<main>{children}</main>
</SidebarInset>
</SidebarProvider>useSidebar#
useSidebar हुक का उपयोग साइडबार को नियंत्रित करने के लिए किया जाता है।
import { useSidebar } from "@/components/ui/sidebar"
export function AppSidebar() {
const {
state,
open,
setOpen,
openMobile,
setOpenMobile,
isMobile,
toggleSidebar,
} = useSidebar()
}| Property | Type | Description |
|---|---|---|
state | expanded or collapsed | साइडबार की वर्तमान स्थिति। |
open | boolean | साइडबार खुला है या नहीं। |
setOpen | (open: boolean) => void | साइडबार की खुली स्थिति सेट करता है। |
openMobile | boolean | मोबाइल पर साइडबार खुला है या नहीं। |
setOpenMobile | (open: boolean) => void | मोबाइल पर साइडबार की खुली स्थिति सेट करता है। |
isMobile | boolean | साइडबार मोबाइल पर है या नहीं। |
toggleSidebar | () => void | साइडबार को टॉगल करता है। डेस्कटॉप और मोबाइल। |
SidebarHeader#
साइडबार में एक चिपका हुआ हेडर जोड़ने के लिए SidebarHeader कंपोनेंट का उपयोग करें।
<Sidebar>
<SidebarHeader>
<SidebarMenu>
<SidebarMenuItem>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<SidebarMenuButton>
Select Workspace
<ChevronDown className="ml-auto" />
</SidebarMenuButton>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-[--radix-popper-anchor-width]">
<DropdownMenuItem>
<span>Acme Inc</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</SidebarMenuItem>
</SidebarMenu>
</SidebarHeader>
</Sidebar>SidebarFooter#
साइडबार में एक चिपका हुआ फ़ुटर जोड़ने के लिए SidebarFooter कंपोनेंट का उपयोग करें।
<Sidebar>
<SidebarFooter>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton>
<User2 /> Username
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarFooter>
</Sidebar>SidebarContent#
SidebarContent कंपोनेंट का उपयोग साइडबार के कॉन्टेंट को लपेटने के लिए किया जाता है। यहीं आप अपने SidebarGroup कंपोनेंट्स जोड़ते हैं। यह स्क्रॉल-योग्य है।
<Sidebar>
<SidebarContent>
<SidebarGroup />
<SidebarGroup />
</SidebarContent>
</Sidebar>SidebarGroup#
साइडबार के भीतर एक सेक्शन बनाने के लिए SidebarGroup कंपोनेंट का उपयोग करें।
एक SidebarGroup में एक SidebarGroupLabel, एक SidebarGroupContent और एक वैकल्पिक SidebarGroupAction होता है।
<SidebarGroup>
<SidebarGroupLabel>Application</SidebarGroupLabel>
<SidebarGroupAction>
<Plus /> <span className="sr-only">Add Project</span>
</SidebarGroupAction>
<SidebarGroupContent></SidebarGroupContent>
</SidebarGroup>SidebarGroup को कोलैप्सिबल बनाने के लिए, इसे एक Collapsible में लपेटें।
<Collapsible defaultOpen className="group/collapsible">
<SidebarGroup>
<SidebarGroupLabel asChild>
<CollapsibleTrigger>
Help
<ChevronDown className="ml-auto transition-transform group-data-[state=open]/collapsible:rotate-180" />
</CollapsibleTrigger>
</SidebarGroupLabel>
<CollapsibleContent>
<SidebarGroupContent />
</CollapsibleContent>
</SidebarGroup>
</Collapsible>SidebarMenu#
SidebarMenu कंपोनेंट का उपयोग SidebarGroup के भीतर एक मेन्यू बनाने के लिए किया जाता है।
<SidebarMenu>
{projects.map((project) => (
<SidebarMenuItem key={project.name}>
<SidebarMenuButton asChild>
<a href={project.url}>
<project.icon />
<span>{project.name}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu>SidebarMenuButton#
SidebarMenuButton कंपोनेंट का उपयोग SidebarMenuItem के भीतर एक मेन्यू बटन रेंडर करने के लिए किया जाता है।
डिफ़ॉल्ट रूप से, SidebarMenuButton एक बटन रेंडर करता है, लेकिन आप asChild प्रॉप का उपयोग करके कोई अन्य कंपोनेंट रेंडर कर सकते हैं, जैसे Link या a टैग।
किसी मेन्यू आइटम को सक्रिय के रूप में चिह्नित करने के लिए isActive प्रॉप का उपयोग करें।
<SidebarMenuButton asChild isActive>
<a href="#">Home</a>
</SidebarMenuButton>SidebarMenuAction#
SidebarMenuAction कंपोनेंट का उपयोग SidebarMenuItem के भीतर एक मेन्यू एक्शन रेंडर करने के लिए किया जाता है।
<SidebarMenuItem>
<SidebarMenuButton asChild>
<a href="#">
<Home />
<span>Home</span>
</a>
</SidebarMenuButton>
<SidebarMenuAction>
<Plus /> <span className="sr-only">Add Project</span>
</SidebarMenuAction>
</SidebarMenuItem>SidebarMenuSub#
SidebarMenuSub कंपोनेंट का उपयोग SidebarMenu के भीतर एक सबमेन्यू रेंडर करने के लिए किया जाता है।
<SidebarMenuItem>
<SidebarMenuButton />
<SidebarMenuSub>
<SidebarMenuSubItem>
<SidebarMenuSubButton />
</SidebarMenuSubItem>
</SidebarMenuSub>
</SidebarMenuItem>SidebarMenuBadge#
SidebarMenuBadge कंपोनेंट का उपयोग SidebarMenuItem के भीतर एक बैज रेंडर करने के लिए किया जाता है।
<SidebarMenuItem>
<SidebarMenuButton />
<SidebarMenuBadge>24</SidebarMenuBadge>
</SidebarMenuItem>SidebarMenuSkeleton#
SidebarMenuSkeleton कंपोनेंट का उपयोग SidebarMenu के लिए एक स्केलेटन रेंडर करने के लिए किया जाता है।
<SidebarMenu>
{Array.from({ length: 5 }).map((_, index) => (
<SidebarMenuItem key={index}>
<SidebarMenuSkeleton />
</SidebarMenuItem>
))}
</SidebarMenu>SidebarTrigger#
एक ऐसा बटन रेंडर करने के लिए SidebarTrigger कंपोनेंट का उपयोग करें जो साइडबार को टॉगल करता है।
import { useSidebar } from "@/components/ui/sidebar"
export function CustomTrigger() {
const { toggleSidebar } = useSidebar()
return <button onClick={toggleSidebar}>Toggle Sidebar</button>
}SidebarRail#
SidebarRail कंपोनेंट का उपयोग Sidebar के भीतर एक रेल रेंडर करने के लिए किया जाता है। इस रेल का उपयोग साइडबार को टॉगल करने के लिए किया जा सकता है।
<Sidebar>
<SidebarHeader />
<SidebarContent>
<SidebarGroup />
</SidebarContent>
<SidebarFooter />
<SidebarRail />
</Sidebar>नियंत्रित साइडबार#
साइडबार को नियंत्रित करने के लिए open और onOpenChange प्रॉप्स का उपयोग करें।
export function AppSidebar() {
const [open, setOpen] = React.useState(false)
return (
<SidebarProvider open={open} onOpenChange={setOpen}>
<Sidebar />
</SidebarProvider>
)
}थीमिंग#
हम साइडबार को थीम देने के लिए निम्नलिखित CSS वेरिएबल्स का उपयोग करते हैं।
@layer base {
:root {
--sidebar-background: 0 0% 98%;
--sidebar-foreground: 240 5.3% 26.1%;
--sidebar-primary: 240 5.9% 10%;
--sidebar-primary-foreground: 0 0% 98%;
--sidebar-accent: 240 4.8% 95.9%;
--sidebar-accent-foreground: 240 5.9% 10%;
--sidebar-border: 220 13% 91%;
--sidebar-ring: 217.2 91.2% 59.8%;
}
.dark {
--sidebar-background: 240 5.9% 10%;
--sidebar-foreground: 240 4.8% 95.9%;
--sidebar-primary: 0 0% 98%;
--sidebar-primary-foreground: 240 5.9% 10%;
--sidebar-accent: 240 3.7% 15.9%;
--sidebar-accent-foreground: 240 4.8% 95.9%;
--sidebar-border: 240 3.7% 15.9%;
--sidebar-ring: 217.2 91.2% 59.8%;
}
}स्टाइलिंग#
साइडबार को उसकी विभिन्न स्थितियों के आधार पर स्टाइल करने के लिए यहाँ कुछ सुझाव दिए गए हैं।
<Sidebar collapsible="icon">
<SidebarContent>
<SidebarGroup className="group-data-[collapsible=icon]:hidden" />
</SidebarContent>
</Sidebar><SidebarMenuItem>
<SidebarMenuButton />
<SidebarMenuAction className="peer-data-[active=true]/menu-button:opacity-100" />
</SidebarMenuItem>RTL#
Neobrutalism में RTL समर्थन सक्षम करने के लिए, RTL कॉन्फ़िगरेशन गाइड देखें।
"use client"
import * as React from "react"चेंजलॉग#
RTL समर्थन#
अगर आप Sidebar कंपोनेंट के किसी पिछले संस्करण से अपग्रेड कर रहे हैं, तो RTL समर्थन जोड़ने के लिए आपको निम्नलिखित अपडेट लागू करने होंगे:
Sidebar कंपोनेंट में dir प्रॉप जोड़ें।
dir को डिस्ट्रक्चर्ड प्रॉप्स में जोड़ें और मोबाइल के लिए इसे SheetContent को पास करें:
function Sidebar({
side = "left",
variant = "sidebar",
collapsible = "offcanvas",
className,
children,
+ dir,
...props
}: React.ComponentProps<"div"> & {
side?: "left" | "right"
variant?: "sidebar" | "floating" | "inset"
collapsible?: "offcanvas" | "icon" | "none"
}) {फिर इसे मोबाइल व्यू में SheetContent को पास करें:
<Sheet open={openMobile} onOpenChange={setOpenMobile} {...props}>
<SheetContent
+ dir={dir}
data-sidebar="sidebar"
data-slot="sidebar"
data-mobile="true"साइडबार कंटेनर में data-side एट्रिब्यूट जोड़ें।
साइडबार कंटेनर एलिमेंट में data-side={side} जोड़ें:
<div
data-slot="sidebar-container"
+ data-side={side}
className={cn(साइडबार कंटेनर की पोज़िशनिंग क्लासेस अपडेट करें।
JavaScript टर्नरी कंडीशनल क्लासेस को CSS डेटा एट्रिब्यूट सेलेक्टर्स से बदलें:
className={cn(
- "fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex",
- side === "left"
- ? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]"
- : "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]",
+ "fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex data-[side=left]:left-0 data-[side=right]:right-0 data-[side=left]:group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)] data-[side=right]:group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]",SidebarRail की पोज़िशनिंग क्लासेस अपडेट करें।
रेल के लिए फ़िज़िकल पोज़िशनिंग का उपयोग करने हेतु SidebarRail कंपोनेंट अपडेट करें:
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",
+ "hover:after:bg-sidebar-border absolute inset-y-0 z-20 hidden w-4 ltr:-translate-x-1/2 rtl:-translate-x-1/2 transition-all ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:start-1/2 after:w-[2px] sm:flex",SidebarTrigger आइकन में RTL फ़्लिप जोड़ें।
RTL मोड में आइकन को पलटने के लिए SidebarTrigger के आइकन में className="rtl:rotate-180" जोड़ें:
<Button ...>
- <PanelLeftIcon />
+ <PanelLeftIcon className="rtl:rotate-180" />
<span className="sr-only">Toggle Sidebar</span>
</Button>इन बदलावों को लागू करने के बाद, आप दिशा सेट करने के लिए dir प्रॉप का उपयोग कर सकते हैं:
<Sidebar dir="rtl" side="right">
{/* ... */}
</Sidebar>साइडबार LTR और RTL दोनों लेआउट में खुद को सही ढंग से पोज़िशन करेगा और इंटरैक्शन संभालेगा।
एक्सेसिबिलिटी#
ऐप साइडबार के लिए कोई एक WAI-ARIA पैटर्न नहीं है — मेन्यू सादे बटन और लिंक हैं जिनमें Tab से नेविगेट होता है, कोई ARIA मेन्यू नहीं, इसलिए एरो कुंजियों वाली रोविंग यहाँ लागू नहीं होती। मोबाइल पर साइडबार एक Sheet के भीतर रेंडर होता है, यानी WAI-ARIA Dialog (Modal) पैटर्न वाला मोडल डायलॉग: फ़ोकस भीतर रुका रहता है, Escape उसे बंद करता है, और दृश्य रूप से छिपा शीर्षक व विवरण स्क्रीन रीडर को उसकी जानकारी देते हैं। SidebarTrigger, aria-label="Toggle Sidebar" के साथ आता है; SidebarGroupAction और SidebarMenuAction जैसे सिर्फ़-आइकन कंट्रोल्स को अपने sr-only लेबल चाहिए — ऊपर दिए उदाहरणों में वे शामिल हैं।
कीबोर्ड इंटरैक्शन:
| कुंजी | क्रिया |
|---|---|
Cmd + B / Ctrl + B | साइडबार टॉगल करें (SIDEBAR_KEYBOARD_SHORTCUT से बदलें) |
Tab / Shift + Tab | ट्रिगर, मेन्यू बटन और क्रियाओं के बीच फ़ोकस आगे बढ़ाएँ |
Enter / Space | फ़ोकस किया गया मेन्यू बटन या क्रिया चलाएँ |
Escape | मोबाइल साइडबार (मोडल Sheet) बंद करें |