UNPKG

lightswind

Version:

A collection of beautifully crafted React Components, Blocks & Templates for Modern Developers. Create stunning web applications effortlessly by using our 160+ professional and animated react components.

155 lines (154 loc) 9.39 kB
// @ts-nocheck "use client"; import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; import * as React from "react"; import { ChevronRight, ChevronLeft } from "lucide-react"; import { motion, AnimatePresence, LayoutGroup } from "framer-motion"; import { twMerge } from "tailwind-merge"; import { clsx } from "clsx"; // Re-implementing the 'cn' utility function directly for self-containment function cn(...inputs) { return twMerge(clsx(inputs)); } const SidebarContext = React.createContext(undefined); export function SidebarProvider({ defaultExpanded = true, expanded: controlledExpanded, onExpandedChange, children, }) { const [expanded, setExpandedState] = React.useState(defaultExpanded); const [activeMenuItem, setActiveMenuItem] = React.useState(null); const isControlled = controlledExpanded !== undefined; const actualExpanded = isControlled ? controlledExpanded : expanded; const setExpanded = React.useCallback((value) => { if (!isControlled) { setExpandedState(value); } onExpandedChange?.(value); }, [isControlled, onExpandedChange]); // Sync active menu item from URL on mount React.useEffect(() => { if (typeof window === 'undefined') return; const url = new URL(window.location.href); const searchParams = url.searchParams; const path = url.pathname; let potentialMenuItemValue = null; if (searchParams.has("component")) { potentialMenuItemValue = searchParams.get("component"); } else { const pathSegments = path.split("/").filter((segment) => segment); if (pathSegments.length > 0) { potentialMenuItemValue = pathSegments[pathSegments.length - 1]; } } setActiveMenuItem(potentialMenuItemValue); }, []); return (_jsx(SidebarContext.Provider, { value: { expanded: actualExpanded, setExpanded, activeMenuItem, setActiveMenuItem, }, children: _jsx(LayoutGroup, { id: "sidebar-indicator", children: children }) })); } export function useSidebar() { const context = React.useContext(SidebarContext); if (!context) { throw new Error("useSidebar must be used within a SidebarProvider"); } return context; } export function Sidebar({ className, children, ...props }) { const { expanded } = useSidebar(); return (_jsx("div", { className: cn("h-full min-h-screen z-40 w-64 relative", "bg-background border-r border-border/40 shadow-sm", "fixed lg:sticky top-0", expanded ? "left-0" : "md:left-0 -left-full", className), role: "complementary", "data-collapsed": !expanded, ...props, children: children })); } export function SidebarTrigger({ className, ...props }) { const { expanded, setExpanded } = useSidebar(); return (_jsx("button", { type: "button", className: cn("inline-flex items-center justify-center rounded-lg p-2 text-muted-foreground", "hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", "fixed md:static z-50 left-4 top-20 bg-background/80 backdrop-blur-sm border border-border shadow-sm", className), onClick: () => setExpanded(!expanded), "aria-label": expanded ? "Close sidebar" : "Open sidebar", ...props, children: expanded ? (_jsx(ChevronLeft, { className: "h-4 w-4" })) : (_jsx(ChevronRight, { className: "h-4 w-4" })) })); } export function SidebarHeader({ className, children, ...props }) { const { expanded } = useSidebar(); return (_jsx("div", { className: cn("flex h-16 items-center border-b border-border/40 px-6", expanded ? "justify-between" : "justify-center", className), ...props, children: _jsx(AnimatePresence, { mode: "wait", children: expanded && (_jsx(motion.div, { initial: { opacity: 0, x: -10 }, animate: { opacity: 1, x: 0 }, exit: { opacity: 0, x: -10 }, transition: { duration: 0.2 }, className: "flex-1", children: children })) }) })); } const SCROLL_STORAGE_KEY = "sidebarScrollTop"; export function SidebarContent({ className, children, ...props }) { const scrollRef = React.useRef(null); // Load/Save scroll position remains similar but cleaner React.useEffect(() => { const scrollElement = scrollRef.current; if (scrollElement) { const savedScrollTop = localStorage.getItem(SCROLL_STORAGE_KEY); if (savedScrollTop) scrollElement.scrollTop = parseInt(savedScrollTop, 10); const handleScroll = () => { localStorage.setItem(SCROLL_STORAGE_KEY, scrollElement.scrollTop.toString()); }; scrollElement.addEventListener("scroll", handleScroll, { passive: true }); return () => scrollElement.removeEventListener("scroll", handleScroll); } }, []); return (_jsx("div", { className: cn("flex-1 overflow-hidden h-full flex flex-col", className), ...props, children: _jsx("div", { ref: scrollRef, className: "flex-1 overflow-y-auto overflow-x-hidden [scrollbar-width:none] [-ms-overflow-style:none] [&::-webkit-scrollbar]:hidden py-4 px-3", children: children }) })); } // --- Grouping Components --- export function SidebarGroup({ className, children, ...props }) { return (_jsx("div", { className: cn("mb-6 last:mb-0", className), ...props, children: children })); } export function SidebarGroupLabel({ className, children, ...props }) { const { expanded } = useSidebar(); if (!expanded) return null; return (_jsx("div", { className: cn("mb-2 px-3 text-[11px] font-bold uppercase tracking-widest text-muted-foreground/70", className), ...props, children: children })); } export function SidebarGroupContent({ className, children, ...props }) { return (_jsx("div", { className: cn("space-y-0.5", className), ...props, children: children })); } // --- Menu Item Components (using LayoutID for Indicator) --- export function SidebarMenu({ className, children, ...props }) { return (_jsx("div", { className: cn("relative flex flex-col", className), ...props, children: children })); } export function SidebarMenuItem({ className, children, value, ...props }) { return (_jsx("div", { className: cn("group relative w-full", className), "data-value": value, ...props, children: children })); } export function SidebarMenuButton({ className, children, asChild = false, value, isActive: propIsActive, ...props }) { const { expanded, activeMenuItem, setActiveMenuItem } = useSidebar(); const isActive = propIsActive ?? (activeMenuItem === value); const handleClick = (e) => { if (value) setActiveMenuItem(value); if (props.onClick) props.onClick(e); }; const content = (_jsxs("div", { className: cn("relative flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm outline-none ", "hover:bg-secondary/50 active:scale-[0.98]", isActive ? "text-primary font-bold bg-primary/5 shadow-sm ring-1 ring-primary/10" : "text-muted-foreground hover:text-foreground", !expanded && "justify-center px-0 py-3", className), onClick: handleClick, ...props, children: [isActive && (_jsx(motion.div, { layoutId: "active-indicator", className: "absolute inset-0 rounded-lg bg-primary/5 dark:bg-primary/10 border-l-2 border-primary z-0", initial: false, transition: { type: "spring", stiffness: 400, damping: 35 } })), _jsx("span", { className: "relative z-10 flex items-center gap-3 w-full", children: children })] })); if (asChild) { const child = React.Children.only(children); const childProps = child.props; return React.cloneElement(child, { ...props, className: cn("relative flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-sm outline-none group", "hover:bg-secondary/50 active:scale-[0.98]", isActive ? "text-primary font-bold bg-primary/5 shadow-sm ring-1 ring-primary/10" : "text-muted-foreground hover:text-foreground", !expanded && "justify-center px-0 py-3", className, childProps.className), onClick: (e) => { if (value) setActiveMenuItem(value); if (childProps.onClick) childProps.onClick(e); if (props.onClick) props.onClick(e); }, children: (_jsxs(_Fragment, { children: [isActive && (_jsx(motion.div, { layoutId: "active-indicator", className: "absolute inset-0 rounded-lg bg-primary/5 dark:bg-primary/10 border-l-2 border-primary z-0", initial: false, transition: { type: "spring", stiffness: 400, damping: 35 } })), _jsx("span", { className: "relative z-10 flex items-center gap-3 w-full", children: childProps.children })] })) }); } return content; } export function SidebarFooter({ className, children, ...props }) { const { expanded } = useSidebar(); return (_jsx("div", { className: cn("mt-auto border-t border-border/40 p-4", expanded ? "flex-row items-center justify-between" : "flex-col justify-center", className), ...props, children: children })); } export { Sidebar as SidebarRoot };