Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 23x 23x 23x 23x 23x 23x 23x 23x 23x 23x 23x 23x 23x 23x 23x 4x 4x 1x 4x 3x 3x 4x 4x 4x 4x 4x 4x 4x 23x 23x 23x 3x 23x 23x 16x 1x 1x 1x 1x 1x 1x 1x 1x 16x 16x 23x 23x 23x 23x 17x 17x 17x 17x 17x 17x 17x 17x 17x 17x 23x 23x 23x 23x 23x 23x 23x 23x 23x 23x 23x 23x 23x 23x 23x 23x 23x 23x 23x 23x 23x 23x 23x 1x 18x 18x 2x 2x 15x 15x | 'use client';
import { TooltipProvider } from '@radix-ui/react-tooltip';
import { cn } from '@web/utils/ui/utils';
import React from 'react';
import { useIsMobile } from '../hooks/use-mobile';
const SIDEBAR_COOKIE_NAME = 'sidebar_state';
const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
const SIDEBAR_WIDTH = '16rem';
const SIDEBAR_WIDTH_ICON = '3rem';
const SIDEBAR_KEYBOARD_SHORTCUT = 'b';
interface SidebarContextProps {
state: 'expanded' | 'collapsed';
open: boolean;
setOpen: (open: boolean) => void;
openMobile: boolean;
setOpenMobile: (open: boolean) => void;
isMobile: boolean;
toggleSidebar: () => void;
openedSections: string[];
setOpenedSections: (sections: string[]) => void;
}
const SidebarContext = React.createContext<SidebarContextProps | null>(null);
interface SidebarProviderProps extends React.ComponentProps<'div'> {
defaultOpen?: boolean;
open?: boolean;
onOpenChange?: (open: boolean) => void;
className?: string;
style?: React.CSSProperties;
}
export function SidebarProvider({
defaultOpen = true,
open: openProp,
onOpenChange: onOpenChangeProp,
className,
style,
children,
...props
}: SidebarProviderProps): React.ReactNode {
const isMobile = useIsMobile();
const [openMobile, setOpenMobile] = React.useState(false);
const [openedSections, setOpenedSections] = React.useState<string[]>([]);
// 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 (onOpenChangeProp) {
onOpenChangeProp(openState);
} else {
_setOpen(openState);
}
// This sets the cookie to keep the sidebar state.
try {
// Prefer Cookie Store API when available
if (window?.cookieStore?.set) {
window.cookieStore.set({
name: SIDEBAR_COOKIE_NAME,
value: String(openState),
expires: Date.now() + SIDEBAR_COOKIE_MAX_AGE * 1000,
path: '/',
});
} else {
// biome-ignore lint/suspicious/noDocumentCookie: Fallback for browsers without Cookie Store API
document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
}
} catch {
// ignore
}
},
[onOpenChangeProp, open],
);
// Helper to toggle the sidebar.
const toggleSidebar = React.useCallback(() => {
return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open);
}, [isMobile, setOpen]);
// Adds a keyboard shortcut to toggle the sidebar.
React.useEffect(() => {
const handleKeyDown = (event: KeyboardEvent): void => {
if (
event.key === SIDEBAR_KEYBOARD_SHORTCUT &&
(event.metaKey || event.ctrlKey)
) {
event.preventDefault();
toggleSidebar();
}
};
window.addEventListener('keydown', handleKeyDown);
return (): void => 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,
openedSections,
setOpenedSections,
}),
[state, open, setOpen, isMobile, openMobile, toggleSidebar, openedSections],
);
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>
);
}
export const useSidebar = (): SidebarContextProps => {
const context = React.useContext(SidebarContext);
if (!context) {
throw new Error('useSidebar must be used within a SidebarProvider.');
}
return context;
};
|