@brutalcomponent/react
Version:
Brutalist React components
1 lines • 23.3 kB
Source Map (JSON)
{"version":3,"sources":["../src/components/navigation/Sidebar/Sidebar.tsx","../src/components/navigation/Nav/Nav.tsx","../src/components/navigation/NavLink/NavLink.tsx","../src/components/navigation/TagFilter/TagFilter.tsx","../src/components/navigation/Nav/MobileMenu.tsx"],"names":["React","Icon","FaTimes"],"mappings":"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","file":"chunk-SASMC42V.mjs","sourcesContent":["/**\n * @file src/components/navigation/Sidebar/Sidebar.tsx\n * @author David (https://dvh.sh)\n * @license MIT\n *\n * @created Fri Sep 12 2025\n * @updated Sat Sep 13 2025\n *\n * @description\n * Sidebar navigation component with mobile support\n * @client This component requires client-side JavaScript\n */\n\"use client\";\n\nimport React, { useState, useEffect } from \"react\";\nimport { cn } from \"../../../utils/cn.utils\";\nimport { Icon, FaBars, FaTimes } from \"../../core/Icon\";\n\nexport interface SidebarProps {\n children: React.ReactNode;\n className?: string;\n logo?: React.ReactNode;\n footer?: React.ReactNode;\n defaultOpen?: boolean;\n overlay?: boolean;\n brutal?: boolean;\n}\n\nexport const Sidebar: React.FC<SidebarProps> = ({\n children,\n className,\n logo,\n footer,\n defaultOpen = true,\n overlay = true,\n brutal = true,\n}) => {\n const [isOpen, setIsOpen] = useState(defaultOpen);\n const [isMobile, setIsMobile] = useState(false);\n const [mounted, setMounted] = useState(false);\n\n useEffect(() => {\n setMounted(true);\n const checkMobile = () => {\n const mobile = window.innerWidth < 768;\n setIsMobile(mobile);\n setIsOpen(mobile ? false : defaultOpen);\n };\n\n checkMobile();\n window.addEventListener(\"resize\", checkMobile);\n return () => window.removeEventListener(\"resize\", checkMobile);\n }, [defaultOpen]);\n\n if (!mounted) return null;\n\n return (\n <>\n {/* Mobile toggle */}\n {isMobile && (\n <button\n onClick={() => setIsOpen(!isOpen)}\n className={cn(\n \"fixed top-4 left-4 z-50 p-2 md:hidden\",\n brutal\n ? \"bg-brutal-white border-4 border-brutal-black shadow-brutal hover:shadow-brutal-md hover:-translate-x-0.5 hover:-translate-y-0.5\"\n : \"bg-white border shadow\",\n \"transition-all duration-200\",\n )}\n aria-label={isOpen ? \"Close sidebar\" : \"Open sidebar\"}\n >\n <Icon\n icon={isOpen ? FaTimes : FaBars}\n size=\"md\"\n className=\"text-brutal-black\"\n />\n </button>\n )}\n\n {/* Sidebar */}\n <aside\n className={cn(\n \"fixed top-0 left-0 h-screen w-64 z-40\",\n \"flex flex-col\",\n brutal\n ? \"bg-gradient-to-br from-brutal-white to-brutal-gray-50 border-r-4 border-brutal-black shadow-brutal-lg\"\n : \"bg-white border-r shadow-lg\",\n \"transform transition-transform duration-300\",\n isOpen ? \"translate-x-0\" : \"-translate-x-full\",\n className,\n )}\n >\n {/* Logo area */}\n {logo && (\n <div\n className={cn(\n \"p-4\",\n brutal\n ? \"border-b-4 border-brutal-black bg-brutal-black text-brutal-white\"\n : \"border-b bg-black text-white\",\n )}\n >\n {logo}\n </div>\n )}\n\n {/* Main content */}\n <div className=\"flex-1 overflow-y-auto p-4 scrollbar-thin scrollbar-thumb-brutal-black scrollbar-track-brutal-gray-100\">\n {children}\n </div>\n\n {/* Footer area */}\n {footer && (\n <div\n className={cn(\n \"p-4\",\n brutal\n ? \"border-t-4 border-brutal-black bg-brutal-gray-100\"\n : \"border-t bg-gray-100\",\n )}\n >\n {footer}\n </div>\n )}\n\n {/* Subtle overlay texture */}\n {brutal && (\n <div className=\"absolute inset-0 pointer-events-none opacity-5\">\n <div className=\"w-full h/full bg-[url('data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%3E%3Cfilter%20id=%22noiseFilter%22%3E%3CfeTurbulence%20type=%22fractalNoise%22%20baseFrequency=%220.65%22%20numOctaves=%223%22%20stitchTiles=%22stitch%22/%3E%3C/filter%3E%3Crect%20width=%22100%25%22%20height=%22100%25%22%20filter=%22url(%23noiseFilter)%22/%3E%3C/svg%3E')]\" />\n </div>\n )}\n </aside>\n\n {/* Mobile backdrop */}\n {isMobile && isOpen && overlay && (\n <div\n className=\"fixed inset-0 bg-brutal-black/50 backdrop-blur-sm z-30 md:hidden\"\n onClick={() => setIsOpen(false)}\n aria-hidden=\"true\"\n />\n )}\n </>\n );\n};\n\nexport const SidebarSkeleton: React.FC = () => (\n <div className=\"w-64 h-screen bg-brutal-white border-r-4 border-brutal-black animate-pulse\">\n <div className=\"p-4 border-b-4 border-brutal-black bg-brutal-black\">\n <div className=\"h-8 bg-brutal-gray-700\" />\n </div>\n <div className=\"p-4 space-y-4\">\n {Array.from({ length: 6 }).map((_, i) => (\n <div key={i} className=\"h-10 bg-brutal-gray-200\" />\n ))}\n </div>\n </div>\n);\n","/**\n * @file src/components/navigation/Nav/Nav.tsx\n * @author David (https://dvh.sh)\n * @license MIT\n *\n * @created Thu Sep 11 2025\n * @updated Fri Sep 12 2025\n *\n * @description\n * Navigation component with active states and sub-items\n */\nimport React from \"react\";\nimport { clsx } from \"clsx\";\nimport type { IconType } from \"react-icons\";\n\nexport interface NavItem {\n name: string;\n href: string;\n icon?: IconType;\n active?: boolean;\n highlight?: boolean;\n subItems?: NavItem[];\n}\n\nexport interface NavProps {\n items: NavItem[];\n onItemClick?: (item: NavItem) => void;\n className?: string;\n}\n\nexport const Nav: React.FC<NavProps> = ({ items, onItemClick, className }) => {\n return (\n <nav className={clsx(\"space-y-2\", className)}>\n {items.map((item) => (\n <div key={item.href} className=\"group\">\n <a\n href={item.href}\n onClick={(e) => {\n if (onItemClick) {\n e.preventDefault();\n onItemClick(item);\n }\n }}\n className={clsx(\n \"flex items-center py-2 px-4 rounded-lg transition-all duration-300\",\n \"no-underline font-bold uppercase tracking-wider text-sm\",\n item.active\n ? \"bg-brutal-black text-brutal-white transform -skew-x-6\"\n : \"text-brutal-black hover:bg-brutal-gray-100 hover:transform hover:-skew-x-6\",\n item.highlight &&\n !item.active &&\n \"text-brutal-pink border-2 border-brutal-pink\",\n )}\n >\n {item.icon && (\n <item.icon className=\"mr-3 text-xl transition-transform duration-300 group-hover:rotate-12\" />\n )}\n <span>{item.name}</span>\n </a>\n\n {/* Sub-items */}\n {item.subItems && item.subItems.length > 0 && (\n <div className=\"ml-6 mt-2 space-y-1\">\n {item.subItems.map((subItem) => (\n <a\n key={subItem.href}\n href={subItem.href}\n onClick={(e) => {\n if (onItemClick) {\n e.preventDefault();\n onItemClick(subItem);\n }\n }}\n className={clsx(\n \"flex items-center py-1 px-4 rounded-lg transition-all duration-300\",\n \"no-underline text-xs uppercase tracking-wide\",\n subItem.active\n ? \"text-brutal-pink font-bold transform translate-x-2\"\n : \"text-brutal-gray-600 hover:text-brutal-black hover:transform hover:translate-x-2\",\n )}\n >\n {subItem.icon && <subItem.icon className=\"mr-2 text-sm\" />}\n <span>{subItem.name}</span>\n </a>\n ))}\n </div>\n )}\n </div>\n ))}\n </nav>\n );\n};\n","/**\n * @file src/components/navigation/NavLink/NavLink.tsx\n * @author David (https://dvh.sh)\n * @license MIT\n *\n * @created Thu Sep 11 2025\n * @updated Sat Sep 13 2025\n *\n * @description\n * Navigation link component with active state and optional icon\n */\nimport React from \"react\";\nimport { cn } from \"../../../utils/cn.utils\";\nimport type { IconType } from \"react-icons\";\n\nexport interface NavLinkProps {\n href: string;\n children: React.ReactNode;\n active?: boolean;\n icon?: IconType;\n external?: boolean;\n brutal?: boolean;\n className?: string;\n onClick?: (e: React.MouseEvent) => void;\n accentColor?: string;\n}\n\nexport const NavLink: React.FC<NavLinkProps> = ({\n href,\n children,\n active = false,\n icon: Icon,\n external = false,\n brutal = true,\n className,\n onClick,\n accentColor = \"brutal-pink\",\n}) => {\n const linkProps = external\n ? {\n target: \"_blank\",\n rel: \"noopener noreferrer\",\n }\n : {};\n\n return (\n <a\n href={href}\n onClick={onClick}\n className={cn(\n \"flex items-center gap-2 px-4 py-2\",\n \"font-black uppercase tracking-wider text-sm\",\n \"transition-all duration-200\",\n active\n ? [\"bg-brutal-black text-brutal-white\", \"transform -skew-x-3\"]\n : [\n \"text-brutal-black hover:bg-brutal-gray-100\",\n brutal && \"hover:transform hover:-skew-x-3\",\n ],\n className,\n )}\n style={\n {\n \"--accent-color\": accentColor.startsWith(\"#\")\n ? accentColor\n : `var(--brutal-${accentColor.replace(\"brutal-\", \"\")})`,\n } as React.CSSProperties\n }\n {...linkProps}\n >\n {Icon && <Icon className=\"w-5 h-5\" />}\n {children}\n </a>\n );\n};\n","/**\n * @file src/components/navigation/TagFilter/TagFilter.tsx\n * @author David (https://dvh.sh)\n * @license MIT\n *\n * @created Thu Sep 11 2025\n * @updated Sat Sep 13 2025\n *\n * @description\n * Generic tag filter component for any content type\n */\nimport React from \"react\";\nimport { FaTag, FaTimes } from \"react-icons/fa\";\nimport { Icon } from \"../../core/Icon\";\nimport { cn, getSizeClasses } from \"../../../utils/cn.utils\";\n\nexport interface TagFilterProps {\n tags: string[];\n selectedTags: string[];\n onTagSelect: (tag: string) => void;\n onTagDeselect?: (tag: string) => void;\n onClear?: () => void;\n multiSelect?: boolean;\n showCount?: boolean;\n tagCounts?: Record<string, number>;\n brutal?: boolean;\n size?: \"xs\" | \"sm\" | \"md\" | \"lg\";\n accentColor?: string;\n className?: string;\n}\n\nexport const TagFilter: React.FC<TagFilterProps> = ({\n tags,\n selectedTags,\n onTagSelect,\n onTagDeselect,\n onClear,\n multiSelect = false,\n showCount = false,\n tagCounts,\n brutal = true,\n size = \"md\",\n accentColor = \"brutal-pink\",\n className,\n}) => {\n const sizeClasses = getSizeClasses(size);\n\n const handleTagClick = (tag: string) => {\n if (selectedTags.includes(tag)) {\n if (multiSelect && onTagDeselect) {\n onTagDeselect(tag);\n } else if (!multiSelect) {\n onTagSelect(\"\");\n }\n } else {\n onTagSelect(tag);\n }\n };\n\n return (\n <div\n className={cn(\"space-y-4\", className)}\n style={\n {\n \"--accent-color\": accentColor.startsWith(\"#\")\n ? accentColor\n : `var(--brutal-${accentColor.replace(\"brutal-\", \"\")})`,\n } as React.CSSProperties\n }\n >\n <div className=\"flex flex-wrap gap-2\">\n {tags.map((tag) => {\n const isSelected = selectedTags.includes(tag);\n const count = tagCounts?.[tag];\n\n return (\n <button\n key={tag}\n onClick={() => handleTagClick(tag)}\n className={cn(\n \"px-4 py-2 font-black uppercase tracking-wider\",\n sizeClasses.text,\n \"transition-all duration-200\",\n brutal\n ? \"border-2 border-brutal-black\"\n : \"border border-brutal-gray-300 rounded\",\n isSelected\n ? [\n \"bg-brutal-black text-brutal-white\",\n brutal && \"shadow-brutal transform -rotate-2\",\n ]\n : [\n \"bg-brutal-white text-brutal-black\",\n \"hover:bg-brutal-gray-100\",\n brutal &&\n \"hover:shadow-brutal hover:transform hover:-rotate-1\",\n ],\n )}\n aria-pressed={isSelected}\n >\n <Icon icon={FaTag} size=\"xs\" className=\"inline mr-2\" />\n {tag}\n {showCount && count !== undefined && (\n <span className=\"ml-2 text-xs opacity-75\">({count})</span>\n )}\n </button>\n );\n })}\n </div>\n\n {selectedTags.length > 0 && onClear && (\n <button\n onClick={onClear}\n className={cn(\n \"flex items-center gap-2\",\n \"font-black uppercase tracking-wider\",\n sizeClasses.text === \"text-xs\" ? \"text-xs\" : \"text-sm\",\n \"text-brutal-gray-600 hover:text-brutal-black transition-colors\",\n )}\n >\n <Icon icon={FaTimes} size=\"sm\" />\n Clear filters\n </button>\n )}\n </div>\n );\n};\n","/**\n * @file src/components/navigation/Nav/MobileMenu.tsx\n * @author David (https://dvh.sh)\n * @license MIT\n *\n * @created Sat Sep 13 2025\n * @updated Sat Sep 13 2025\n *\n * @description\n * Mobile slide-in menu with backdrop\n * @client This component requires client-side JavaScript\n */\n\"use client\";\n\nimport React from \"react\";\nimport { cn } from \"../../../utils/cn.utils\";\n\nexport interface MobileMenuProps {\n isOpen: boolean;\n onClose: () => void;\n children: React.ReactNode;\n className?: string;\n brutal?: boolean;\n}\n\nexport const MobileMenu: React.FC<MobileMenuProps> = ({\n isOpen,\n onClose,\n children,\n className,\n brutal = true,\n}) => {\n if (!isOpen) return null;\n\n return (\n <>\n {/* Backdrop */}\n <div\n className=\"fixed inset-0 bg-brutal-black/50 z-40 md:hidden\"\n onClick={onClose}\n aria-hidden=\"true\"\n />\n\n {/* Menu */}\n <div\n className={cn(\n \"fixed top-0 right-0 h-full w-64 z-50\",\n \"bg-brutal-white\",\n brutal\n ? \"border-l-4 border-brutal-black shadow-brutal-xl\"\n : \"shadow-xl\",\n \"transform transition-transform duration-300\",\n isOpen ? \"translate-x-0\" : \"translate-x-full\",\n className,\n )}\n role=\"dialog\"\n aria-modal=\"true\"\n >\n <div className=\"p-4 overflow-y-auto h-full\">{children}</div>\n </div>\n </>\n );\n};\n"]}