UNPKG

@brutalcomponent/react

Version:
1,530 lines (1,525 loc) 48.2 kB
'use strict'; var chunk7YHHVG7W_js = require('./chunk-7YHHVG7W.js'); var chunk7T4KDGYW_js = require('./chunk-7T4KDGYW.js'); var React5 = require('react'); var clsx = require('clsx'); var reactDom = require('react-dom'); function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } var React5__default = /*#__PURE__*/_interopDefault(React5); /** * @brutalcomponent/react * (c) David Heffler (https://dvh.sh) * Licensed under MIT */ var CardSkeleton = ({ className }) => /* @__PURE__ */ React5__default.default.createElement( "div", { className: clsx.clsx( "p-6 bg-brutal-white border-4 border-brutal-black shadow-brutal animate-pulse", className ) }, /* @__PURE__ */ React5__default.default.createElement("div", { className: "h-6 bg-brutal-gray-200 rounded w-3/4 mb-4" }), /* @__PURE__ */ React5__default.default.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ React5__default.default.createElement("div", { className: "h-4 bg-brutal-gray-200 rounded" }), /* @__PURE__ */ React5__default.default.createElement("div", { className: "h-4 bg-brutal-gray-200 rounded w-5/6" }), /* @__PURE__ */ React5__default.default.createElement("div", { className: "h-4 bg-brutal-gray-200 rounded w-4/6" })) ); var Card = React5__default.default.forwardRef( ({ variant = "raised", brutal = true, hover = true, rotate = false, accent, skeleton = false, className, children, ...props }, ref) => { if (skeleton) { return /* @__PURE__ */ React5__default.default.createElement(CardSkeleton, { className }); } return /* @__PURE__ */ React5__default.default.createElement( "div", { ref, className: clsx.clsx( // Base "p-6 transition-all duration-300", // Variants variant === "flat" && "bg-brutal-white", variant === "raised" && "bg-brutal-white shadow-brutal", variant === "sunken" && "bg-brutal-gray-100", variant === "bordered" && "bg-brutal-white border-4 border-brutal-black", // Brutal mode brutal && variant === "raised" && "border-4 border-brutal-black", brutal && hover && "hover:shadow-brutal-md hover:-translate-x-0.5 hover:-translate-y-0.5", // Rotation rotate === true && "transform -rotate-1 hover:rotate-0", rotate === "left" && "transform -rotate-1 hover:rotate-0", rotate === "right" && "transform rotate-1 hover:rotate-0", // Accent border accent === "pink" && "border-l-8 !border-l-brutal-pink", accent === "mint" && "border-l-8 !border-l-brutal-mint", accent === "sky" && "border-l-8 !border-l-brutal-sky", accent === "lavender" && "border-l-8 !border-l-brutal-lavender", accent === "peach" && "border-l-8 !border-l-brutal-peach", accent === "coral" && "border-l-8 !border-l-brutal-coral", accent === "yellow" && "border-l-8 !border-l-brutal-yellow", className ), ...props }, children ); } ); Card.displayName = "Card"; var Tooltip = ({ content, children, position = "top", delay = 200, hideDelay = 0, brutal = true, size = "sm", variant = "default", animated = true, disabled = false, trigger = "hover", accentColor = "brutal-pink", className }) => { const [isVisible, setIsVisible] = React5.useState(false); const [coords, setCoords] = React5.useState({ top: 0, left: 0 }); const [mounted, setMounted] = React5.useState(false); const showTimeoutRef = React5.useRef(null); const hideTimeoutRef = React5.useRef(null); const triggerRef = React5.useRef(null); const tooltipRef = React5.useRef(null); const sizeClasses = chunk7T4KDGYW_js.getSizeClasses(size); React5.useEffect(() => { setMounted(true); return () => { if (showTimeoutRef.current) clearTimeout(showTimeoutRef.current); if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current); }; }, []); const calculatePosition = React5.useCallback(() => { if (!triggerRef.current || !tooltipRef.current) return; const triggerRect = triggerRef.current.getBoundingClientRect(); const tooltipRect = tooltipRef.current.getBoundingClientRect(); const spacing = brutal ? 12 : 8; const viewport = { width: window.innerWidth, height: window.innerHeight }; let top = 0; let left = 0; switch (position) { case "top": top = triggerRect.top - tooltipRect.height - spacing; left = triggerRect.left + (triggerRect.width - tooltipRect.width) / 2; break; case "bottom": top = triggerRect.bottom + spacing; left = triggerRect.left + (triggerRect.width - tooltipRect.width) / 2; break; case "left": top = triggerRect.top + (triggerRect.height - tooltipRect.height) / 2; left = triggerRect.left - tooltipRect.width - spacing; break; case "right": top = triggerRect.top + (triggerRect.height - tooltipRect.height) / 2; left = triggerRect.right + spacing; break; } const padding = 16; left = Math.max( padding, Math.min(left, viewport.width - tooltipRect.width - padding) ); top = Math.max( padding, Math.min(top, viewport.height - tooltipRect.height - padding) ); setCoords({ top, left }); }, [position, brutal]); const showTooltip = React5.useCallback(() => { if (disabled) return; if (hideTimeoutRef.current) { clearTimeout(hideTimeoutRef.current); } showTimeoutRef.current = setTimeout(() => { setIsVisible(true); requestAnimationFrame(() => { calculatePosition(); }); }, delay); }, [delay, disabled, calculatePosition]); const hideTooltip = React5.useCallback(() => { if (showTimeoutRef.current) { clearTimeout(showTimeoutRef.current); } if (hideDelay > 0) { hideTimeoutRef.current = setTimeout(() => { setIsVisible(false); }, hideDelay); } else { setIsVisible(false); } }, [hideDelay]); const handleMouseEnter = () => { if (trigger === "hover" || trigger === "both") { showTooltip(); } }; const handleMouseLeave = () => { if (trigger === "hover" || trigger === "both") { hideTooltip(); } }; const handleClick = () => { if (trigger === "click" || trigger === "both") { if (isVisible) { hideTooltip(); } else { showTooltip(); } } }; const handleFocus = () => { if (trigger === "focus" || trigger === "both") { showTooltip(); } }; const handleBlur = () => { if (trigger === "focus" || trigger === "both") { hideTooltip(); } }; React5.useEffect(() => { const handleEscape = (e) => { if (e.key === "Escape" && isVisible) { hideTooltip(); } }; if (isVisible) { document.addEventListener("keydown", handleEscape); return () => document.removeEventListener("keydown", handleEscape); } }, [isVisible, hideTooltip]); const getVariantClasses = () => { switch (variant) { case "error": return "bg-brutal-coral text-brutal-black"; case "success": return "bg-brutal-mint text-brutal-black"; case "warning": return "bg-brutal-yellow text-brutal-black"; case "info": return "bg-brutal-sky text-brutal-black"; case "accent": return "bg-accent text-brutal-black"; default: return "bg-brutal-black text-brutal-white"; } }; const getArrowClasses = () => { const arrowColor = variant === "default" ? "brutal-black" : variant === "accent" ? "accent" : `brutal-${variant}`; const arrowSize = brutal ? "10px" : "6px"; switch (position) { case "top": return chunk7T4KDGYW_js.cn( "bottom-[-10px] left-1/2 transform -translate-x-1/2", `border-l-[${arrowSize}] border-l-transparent`, `border-r-[${arrowSize}] border-r-transparent`, `border-t-[${arrowSize}] border-t-${arrowColor}` ); case "bottom": return chunk7T4KDGYW_js.cn( "top-[-10px] left-1/2 transform -translate-x-1/2", `border-l-[${arrowSize}] border-l-transparent`, `border-r-[${arrowSize}] border-r-transparent`, `border-b-[${arrowSize}] border-b-${arrowColor}` ); case "left": return chunk7T4KDGYW_js.cn( "right-[-10px] top-1/2 transform -translate-y-1/2", `border-t-[${arrowSize}] border-t-transparent`, `border-b-[${arrowSize}] border-b-transparent`, `border-l-[${arrowSize}] border-l-${arrowColor}` ); case "right": return chunk7T4KDGYW_js.cn( "left-[-10px] top-1/2 transform -translate-y-1/2", `border-t-[${arrowSize}] border-t-transparent`, `border-b-[${arrowSize}] border-b-transparent`, `border-r-[${arrowSize}] border-r-${arrowColor}` ); } }; const tooltipElement = isVisible && mounted ? /* @__PURE__ */ React5__default.default.createElement( "div", { ref: tooltipRef, className: chunk7T4KDGYW_js.cn( // Base styling "fixed z-50 pointer-events-none max-w-xs", "font-bold uppercase tracking-wider break-words", // Size classes sizeClasses.padding, sizeClasses.text, // Variant styling getVariantClasses(), // Brutal styling brutal && [ "border-4 border-brutal-black shadow-brutal", "transform -rotate-1" ], !brutal && "rounded-md shadow-lg border", // Animation animated && ["transition-all duration-200", "animate-fade-in"], className ), style: { top: `${coords.top}px`, left: `${coords.left}px`, "--accent-color": accentColor.startsWith("#") ? accentColor : `var(--brutal-${accentColor.replace("brutal-", "")})` }, role: "tooltip", "aria-hidden": "false" }, content, /* @__PURE__ */ React5__default.default.createElement("div", { className: chunk7T4KDGYW_js.cn("absolute w-0 h-0", getArrowClasses()) }) ) : null; return /* @__PURE__ */ React5__default.default.createElement(React5__default.default.Fragment, null, /* @__PURE__ */ React5__default.default.createElement( "div", { ref: triggerRef, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, onClick: handleClick, onFocus: handleFocus, onBlur: handleBlur, className: chunk7T4KDGYW_js.cn( "inline-block", (trigger === "click" || trigger === "both") && "cursor-pointer" ), "aria-describedby": isVisible ? "tooltip" : void 0 }, children ), tooltipElement && reactDom.createPortal(tooltipElement, document.body)); }; var TooltipProvider = ({ children, delayDuration = 200, skipDelayDuration = 300 }) => { return /* @__PURE__ */ React5__default.default.createElement(React5__default.default.Fragment, null, children); }; var TooltipSkeleton = ({ position = "top", size = "sm", brutal = true, width = "medium", className }) => { const sizeClasses = chunk7T4KDGYW_js.getSizeClasses(size); const getWidthClass = () => { const widths = { short: "w-16", medium: "w-24", long: "w-32" }; return widths[width]; }; const getArrowClasses = () => { const arrowSize = brutal ? "10px" : "6px"; switch (position) { case "top": return chunk7T4KDGYW_js.cn( "bottom-[-10px] left-1/2 transform -translate-x-1/2", `border-l-[${arrowSize}] border-l-transparent`, `border-r-[${arrowSize}] border-r-transparent`, `border-t-[${arrowSize}] border-t-brutal-gray-400` ); case "bottom": return chunk7T4KDGYW_js.cn( "top-[-10px] left-1/2 transform -translate-x-1/2", `border-l-[${arrowSize}] border-l-transparent`, `border-r-[${arrowSize}] border-r-transparent`, `border-b-[${arrowSize}] border-b-brutal-gray-400` ); case "left": return chunk7T4KDGYW_js.cn( "right-[-10px] top-1/2 transform -translate-y-1/2", `border-t-[${arrowSize}] border-t-transparent`, `border-b-[${arrowSize}] border-b-transparent`, `border-l-[${arrowSize}] border-l-brutal-gray-400` ); case "right": return chunk7T4KDGYW_js.cn( "left-[-10px] top-1/2 transform -translate-y-1/2", `border-t-[${arrowSize}] border-t-transparent`, `border-b-[${arrowSize}] border-b-transparent`, `border-r-[${arrowSize}] border-r-brutal-gray-400` ); } }; return /* @__PURE__ */ React5__default.default.createElement( "div", { className: chunk7T4KDGYW_js.cn( // Base styling "relative animate-pulse bg-brutal-gray-300", "font-bold uppercase tracking-wider", // Size classes sizeClasses.padding, getWidthClass(), sizeClasses.text === "text-xs" ? "h-6" : "h-8", // Brutal styling brutal && [ "border-4 border-brutal-gray-400 shadow-brutal", "transform -rotate-1" ], !brutal && "rounded-md shadow-lg border border-brutal-gray-400", className ) }, /* @__PURE__ */ React5__default.default.createElement("div", { className: chunk7T4KDGYW_js.cn("absolute w-0 h-0", getArrowClasses()) }) ); }; var Toggle = React5__default.default.forwardRef( ({ label, description, checked = false, onChange, error, brutal = true, size = "md", variant = "default", animated = true, required = false, accentColor = "brutal-pink", className, disabled, ...props }, ref) => { const sizeClasses = chunk7T4KDGYW_js.getSizeClasses(size); chunk7T4KDGYW_js.getAccentClasses(accentColor); const handleChange = React5.useCallback( (e) => { onChange?.(e.target.checked); }, [onChange] ); const getToggleSizes = () => { const sizes = { xs: { track: "w-8 h-4", thumb: "w-2.5 h-2.5", translate: "translate-x-4", padding: "p-0.5" }, sm: { track: "w-10 h-5", thumb: "w-3 h-3", translate: "translate-x-5", padding: "p-0.5" }, md: { track: "w-14 h-7", thumb: "w-5 h-5", translate: "translate-x-7", padding: "p-1" }, lg: { track: "w-18 h-9", thumb: "w-7 h-7", translate: "translate-x-9", padding: "p-1" } }; return sizes[size]; }; const toggleSizes = getToggleSizes(); const getVariantClasses = () => { switch (variant) { case "pill": return { track: "rounded-full", thumb: "rounded-full" }; case "square": return { track: "rounded-none", thumb: "rounded-none" }; default: return { track: "rounded-lg", thumb: "rounded-md" }; } }; const variantStyles = getVariantClasses(); return /* @__PURE__ */ React5__default.default.createElement( "div", { className: chunk7T4KDGYW_js.cn("w-full", className), style: { "--accent-color": accentColor.startsWith("#") ? accentColor : `var(--brutal-${accentColor.replace("brutal-", "")})` } }, /* @__PURE__ */ React5__default.default.createElement( "label", { className: chunk7T4KDGYW_js.cn( "inline-flex items-start gap-3 cursor-pointer transition-all duration-200", disabled && [ "cursor-not-allowed opacity-50", "hover:transform-none" ], !disabled && animated && "hover:scale-105" ) }, /* @__PURE__ */ React5__default.default.createElement("div", { className: "relative flex-shrink-0" }, /* @__PURE__ */ React5__default.default.createElement( "input", { ref, type: "checkbox", checked, onChange: handleChange, disabled, required, className: "sr-only peer", "aria-describedby": error ? `${props.id || "toggle"}-error` : description ? `${props.id || "toggle"}-description` : void 0, ...props } ), /* @__PURE__ */ React5__default.default.createElement( "div", { className: chunk7T4KDGYW_js.cn( // Base track styling toggleSizes.track, toggleSizes.padding, "relative transition-all duration-300 flex items-center", variantStyles.track, // Brutal styling brutal && [ "border-4 border-brutal-black", animated && [ "hover:shadow-brutal peer-focus:shadow-brutal-md", "transform hover:-translate-y-0.5" ] ], !brutal && [ "border-2 border-brutal-gray-400", "peer-focus:ring-2 peer-focus:ring-accent peer-focus:ring-offset-2" ], // State colors checked ? "bg-accent" : "bg-brutal-white", // Error state error && "border-brutal-coral", // Disabled state disabled && [ "hover:shadow-none hover:transform-none", checked ? "bg-brutal-gray-400" : "bg-brutal-gray-200" ], // Focus styles "peer-focus:outline-none" ) }, /* @__PURE__ */ React5__default.default.createElement( "div", { className: chunk7T4KDGYW_js.cn( // Base thumb styling toggleSizes.thumb, "absolute transition-all duration-300 flex items-center justify-center", variantStyles.thumb, // Brutal styling brutal && [ "border-2 border-brutal-black shadow-brutal-sm", animated && [ "peer-checked:rotate-180", checked && "animate-bounce" ] ], !brutal && "shadow-md", // Position and colors checked ? [toggleSizes.translate, "bg-brutal-white"] : ["translate-x-0", "bg-brutal-black"], // Disabled state disabled && [ "bg-brutal-gray-500", "peer-checked:bg-brutal-gray-300" ] ) }, brutal && animated && /* @__PURE__ */ React5__default.default.createElement( "div", { className: chunk7T4KDGYW_js.cn( "w-1 h-1 rounded-full transition-all duration-200", checked ? "bg-accent" : "bg-brutal-white" ) } ) ) )), (label || description) && /* @__PURE__ */ React5__default.default.createElement("div", { className: "flex-1 min-w-0" }, label && /* @__PURE__ */ React5__default.default.createElement( "span", { className: chunk7T4KDGYW_js.cn( "block font-black uppercase tracking-wider text-brutal-black", sizeClasses.text === "text-xs" ? "text-xs" : "text-sm", required && "after:content-['*'] after:ml-1 after:text-accent", disabled && "text-brutal-gray-500" ) }, label ), description && /* @__PURE__ */ React5__default.default.createElement( "span", { id: `${props.id || "toggle"}-description`, className: chunk7T4KDGYW_js.cn( "block mt-1 text-brutal-gray-600 font-mono", sizeClasses.text === "text-xs" ? "text-xs" : "text-sm", disabled && "text-brutal-gray-400" ) }, description )) ), error && /* @__PURE__ */ React5__default.default.createElement( "p", { id: `${props.id || "toggle"}-error`, className: chunk7T4KDGYW_js.cn( "mt-2 font-black uppercase tracking-wider text-accent", sizeClasses.text === "text-xs" ? "text-xs" : "text-sm" ), role: "alert" }, error ) ); } ); Toggle.displayName = "Toggle"; var ToggleGroup = ({ options, values, onChange, label, description, error, brutal = true, size = "md", variant = "default", layout = "vertical", animated = true, required = false, accentColor = "brutal-pink", className }) => { const sizeClasses = chunk7T4KDGYW_js.getSizeClasses(size); const handleToggleChange = (optionValue, checked) => { if (checked) { onChange([...values, optionValue]); } else { onChange(values.filter((v) => v !== optionValue)); } }; return /* @__PURE__ */ React5__default.default.createElement( "fieldset", { className: chunk7T4KDGYW_js.cn("w-full", className), style: { "--accent-color": accentColor.startsWith("#") ? accentColor : `var(--brutal-${accentColor.replace("brutal-", "")})` } }, label && /* @__PURE__ */ React5__default.default.createElement( "legend", { className: chunk7T4KDGYW_js.cn( "block mb-3 font-black uppercase tracking-wider text-brutal-black", sizeClasses.text === "text-xs" ? "text-xs" : "text-sm", required && "after:content-['*'] after:ml-1 after:text-accent" ) }, label ), description && /* @__PURE__ */ React5__default.default.createElement( "p", { className: chunk7T4KDGYW_js.cn( "mb-4 text-brutal-gray-600 font-mono", sizeClasses.text === "text-xs" ? "text-xs" : "text-sm" ) }, description ), /* @__PURE__ */ React5__default.default.createElement( "div", { className: chunk7T4KDGYW_js.cn( // Layout classes layout === "horizontal" && "flex flex-wrap gap-4", layout === "vertical" && "space-y-3", layout === "grid" && "grid grid-cols-1 sm:grid-cols-2 gap-3" ), role: "group", "aria-labelledby": label ? `togglegroup-label` : void 0, "aria-describedby": error ? `togglegroup-error` : description ? `togglegroup-description` : void 0, "aria-invalid": error ? "true" : "false", "aria-required": required }, options.map((option, index) => /* @__PURE__ */ React5__default.default.createElement( Toggle, { key: option.value, id: `toggle-${option.value}`, label: option.label, description: option.description, checked: values.includes(option.value), onChange: (checked) => handleToggleChange(option.value, checked), disabled: option.disabled, brutal, size, variant, animated, accentColor, className: chunk7T4KDGYW_js.cn( // Animation delays for staggered entrance animated && layout === "vertical" && "animate-fade-in", animated && `animation-delay-${index * 100}ms` ) } )) ), error && /* @__PURE__ */ React5__default.default.createElement( "p", { id: "togglegroup-error", className: chunk7T4KDGYW_js.cn( "mt-3 font-black uppercase tracking-wider text-accent", sizeClasses.text === "text-xs" ? "text-xs" : "text-sm" ), role: "alert" }, error ) ); }; var ToggleSkeleton = ({ brutal = true, size = "md", variant = "default", showDescription = false, className }) => { const sizeClasses = chunk7T4KDGYW_js.getSizeClasses(size); const getToggleSizes = () => { const sizes = { xs: "w-8 h-4", sm: "w-10 h-5", md: "w-14 h-7", lg: "w-18 h-9" }; return sizes[size]; }; const getVariantClasses = () => { switch (variant) { case "pill": return "rounded-full"; case "square": return "rounded-none"; default: return "rounded-lg"; } }; return /* @__PURE__ */ React5__default.default.createElement( "div", { className: chunk7T4KDGYW_js.cn("animate-pulse inline-flex items-start gap-3", className) }, /* @__PURE__ */ React5__default.default.createElement( "div", { className: chunk7T4KDGYW_js.cn( "bg-brutal-gray-300 border-2 border-brutal-gray-400", getToggleSizes(), getVariantClasses(), brutal && "border-4" ) } ), /* @__PURE__ */ React5__default.default.createElement("div", { className: "flex-1 space-y-2" }, /* @__PURE__ */ React5__default.default.createElement( "div", { className: chunk7T4KDGYW_js.cn( "bg-brutal-gray-300 rounded w-20", sizeClasses.text === "text-xs" ? "h-3" : "h-4" ) } ), showDescription && /* @__PURE__ */ React5__default.default.createElement( "div", { className: chunk7T4KDGYW_js.cn( "bg-brutal-gray-300 rounded w-32", sizeClasses.text === "text-xs" ? "h-3" : "h-4" ) } )) ); }; var ToggleGroupSkeleton = ({ count = 3, brutal = true, size = "md", variant = "default", layout = "vertical", showLabel = true, showDescription = false, className }) => { const sizeClasses = chunk7T4KDGYW_js.getSizeClasses(size); return /* @__PURE__ */ React5__default.default.createElement("div", { className: chunk7T4KDGYW_js.cn("animate-pulse w-full", className) }, showLabel && /* @__PURE__ */ React5__default.default.createElement( "div", { className: chunk7T4KDGYW_js.cn( "bg-brutal-gray-300 rounded w-32 mb-3", sizeClasses.text === "text-xs" ? "h-3" : "h-4" ) } ), showDescription && /* @__PURE__ */ React5__default.default.createElement( "div", { className: chunk7T4KDGYW_js.cn( "bg-brutal-gray-300 rounded w-48 mb-4", sizeClasses.text === "text-xs" ? "h-3" : "h-4" ) } ), /* @__PURE__ */ React5__default.default.createElement( "div", { className: chunk7T4KDGYW_js.cn( layout === "horizontal" && "flex flex-wrap gap-4", layout === "vertical" && "space-y-3", layout === "grid" && "grid grid-cols-1 sm:grid-cols-2 gap-3" ) }, Array.from({ length: count }).map((_, i) => /* @__PURE__ */ React5__default.default.createElement( ToggleSkeleton, { key: i, brutal, size, variant, showDescription: showDescription && i === 0 } )) )); }; var TabsContext = React5.createContext( void 0 ); var Tabs = ({ defaultValue, value: controlledValue, onChange, orientation = "horizontal", size = "md", variant = "default", brutal = true, animated = true, accentColor = "brutal-pink", className, children }) => { const [uncontrolledValue, setUncontrolledValue] = React5.useState(defaultValue); const value = controlledValue ?? uncontrolledValue; const handleChange = React5.useCallback( (newValue) => { if (controlledValue === void 0) { setUncontrolledValue(newValue); } onChange?.(newValue); }, [controlledValue, onChange] ); const contextValue = React5.useMemo( () => ({ value, onChange: handleChange, orientation, size, variant, brutal, animated, accentColor }), [ value, handleChange, orientation, size, variant, brutal, animated, accentColor ] ); return /* @__PURE__ */ React5__default.default.createElement(TabsContext.Provider, { value: contextValue }, /* @__PURE__ */ React5__default.default.createElement( "div", { className: chunk7T4KDGYW_js.cn( "flex w-full", orientation === "vertical" ? "flex-row gap-4" : "flex-col", brutal && "gap-2", className ), style: { "--accent-color": accentColor.startsWith("#") ? accentColor : `var(--brutal-${accentColor.replace("brutal-", "")})` }, role: "tablist", "aria-orientation": orientation }, children )); }; var TabsList = ({ className, brutal: propBrutal, stretch = false, variant: propVariant, children }) => { const context = React5.useContext(TabsContext); const brutal = propBrutal ?? context?.brutal ?? true; const variant = propVariant ?? context?.variant ?? "default"; const orientation = context?.orientation ?? "horizontal"; const size = context?.size ?? "md"; chunk7T4KDGYW_js.getSizeClasses(size); const getVariantClasses = () => { switch (variant) { case "pills": return chunk7T4KDGYW_js.cn( "gap-2 p-1 rounded-lg", brutal && [ "bg-brutal-gray-100 border-4 border-brutal-black shadow-brutal-sm" ], !brutal && "bg-brutal-gray-100 rounded-lg border" ); case "underline": return chunk7T4KDGYW_js.cn( "border-b-2 border-brutal-gray-200", "relative after:absolute after:bottom-0 after:left-0 after:right-0 after:h-0.5 after:bg-accent after:transition-all after:duration-300" ); case "cards": return chunk7T4KDGYW_js.cn( "gap-1 bg-brutal-gray-50 p-1 rounded-lg", brutal && "border-2 border-brutal-black shadow-brutal-sm" ); default: return chunk7T4KDGYW_js.cn( brutal && [ "border-b-4 border-brutal-black bg-brutal-white shadow-brutal-sm" ], !brutal && "border-b-2 border-brutal-gray-300 bg-brutal-white" ); } }; return /* @__PURE__ */ React5__default.default.createElement( "div", { className: chunk7T4KDGYW_js.cn( // Base layout "flex", orientation === "vertical" ? "flex-col" : "flex-row", stretch && "w-full", // Variant styling getVariantClasses(), // Responsive behavior orientation === "horizontal" && "overflow-x-auto scrollbar-none", className ), role: "tablist", "aria-orientation": orientation }, children ); }; var TabsTrigger = ({ value, icon: IconComponent, badge, disabled = false, brutal: propBrutal, stretch = false, className, children }) => { const context = React5.useContext(TabsContext); const buttonRef = React5.useRef(null); if (!context) { throw new Error("TabsTrigger must be used within Tabs"); } const { value: activeValue, onChange, orientation = "horizontal", size = "md", variant = "default", brutal: contextBrutal = true, animated = true } = context; const brutal = propBrutal ?? contextBrutal; const isActive = activeValue === value; const sizeClasses = chunk7T4KDGYW_js.getSizeClasses(size); React5.useEffect(() => { if (isActive && buttonRef.current && document.activeElement === document.body) { buttonRef.current.focus(); } }, [isActive]); const handleClick = () => { if (!disabled) { onChange(value); } }; const handleKeyDown = (e) => { if (disabled) return; if (e.key === "ArrowLeft" || e.key === "ArrowRight" || e.key === "ArrowUp" || e.key === "ArrowDown") { e.preventDefault(); const tablist = buttonRef.current?.closest('[role="tablist"]'); const triggers = tablist?.querySelectorAll( '[role="tab"]:not([disabled])' ); if (triggers && triggers.length > 1) { const currentIndex = Array.from(triggers).indexOf(buttonRef.current); let nextIndex; if (orientation === "horizontal" && e.key === "ArrowRight" || orientation === "vertical" && e.key === "ArrowDown") { nextIndex = (currentIndex + 1) % triggers.length; } else if (orientation === "horizontal" && e.key === "ArrowLeft" || orientation === "vertical" && e.key === "ArrowUp") { nextIndex = (currentIndex - 1 + triggers.length) % triggers.length; } if (nextIndex !== void 0) { triggers[nextIndex].focus(); triggers[nextIndex].click(); } } } }; const getVariantClasses = () => { switch (variant) { case "pills": return { base: chunk7T4KDGYW_js.cn( "rounded-md transition-all duration-200", brutal && "shadow-brutal-sm hover:shadow-brutal" ), active: chunk7T4KDGYW_js.cn( "bg-accent text-brutal-black", brutal && "shadow-brutal transform -rotate-1" ), inactive: chunk7T4KDGYW_js.cn( "text-brutal-gray-600 hover:text-brutal-black hover:bg-brutal-white", brutal && "hover:transform hover:-rotate-0.5" ) }; case "underline": return { base: chunk7T4KDGYW_js.cn( "relative border-b-2 border-transparent transition-all duration-200", "hover:border-accent/50" ), active: "border-accent text-accent font-black", inactive: "text-brutal-gray-600 hover:text-brutal-black" }; case "cards": return { base: chunk7T4KDGYW_js.cn( "rounded-md transition-all duration-200", brutal && "hover:shadow-brutal-sm" ), active: chunk7T4KDGYW_js.cn( "bg-brutal-white text-brutal-black shadow-md", brutal && "border-2 border-brutal-black" ), inactive: "text-brutal-gray-600 hover:text-brutal-black hover:bg-brutal-white/50" }; default: return { base: chunk7T4KDGYW_js.cn( "transition-all duration-200", brutal && "border-r-2 last:border-r-0 border-brutal-black", orientation === "vertical" && brutal && "border-r-0 border-b-2 last:border-b-0" ), active: chunk7T4KDGYW_js.cn( "bg-brutal-black text-brutal-white", brutal && animated && "transform -skew-x-2" ), inactive: chunk7T4KDGYW_js.cn( "bg-brutal-white text-brutal-black hover:bg-brutal-gray-100", brutal && animated && "hover:transform hover:-skew-x-1" ) }; } }; const variantStyles = getVariantClasses(); return /* @__PURE__ */ React5__default.default.createElement( "button", { ref: buttonRef, onClick: handleClick, onKeyDown: handleKeyDown, disabled, role: "tab", "aria-selected": isActive, "aria-controls": `tabpanel-${value}`, tabIndex: isActive ? 0 : -1, className: chunk7T4KDGYW_js.cn( // Base styling "relative flex items-center justify-center gap-2 font-black uppercase tracking-wider", "focus:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2", // Size classes sizeClasses.padding, sizeClasses.text, // Layout stretch && "flex-1", orientation === "vertical" && "w-full justify-start", // Variant base classes variantStyles.base, // Active/inactive state isActive ? variantStyles.active : variantStyles.inactive, // Disabled state disabled && [ "opacity-50 cursor-not-allowed", "hover:transform-none hover:shadow-none hover:bg-transparent" ], className ) }, IconComponent && /* @__PURE__ */ React5__default.default.createElement( chunk7YHHVG7W_js.Icon, { icon: IconComponent, size: size === "xs" ? "xs" : size === "sm" ? "sm" : "md", brutal: brutal && animated } ), /* @__PURE__ */ React5__default.default.createElement( "span", { className: chunk7T4KDGYW_js.cn( "truncate", IconComponent && size === "xs" && "hidden sm:inline", !IconComponent && "inline" ) }, children ), badge !== void 0 && /* @__PURE__ */ React5__default.default.createElement( "span", { className: chunk7T4KDGYW_js.cn( "ml-2 px-1.5 py-0.5 text-xs font-black rounded", "transition-colors duration-200", brutal && "border-2 border-brutal-black shadow-brutal-sm", isActive ? "bg-brutal-white text-brutal-black" : "bg-accent text-brutal-black" ) }, badge ), variant === "underline" && isActive && /* @__PURE__ */ React5__default.default.createElement("div", { className: "absolute bottom-0 left-0 right-0 h-0.5 bg-accent" }) ); }; var TabsContent = ({ value, className, forceMount = false, keepMounted = false, children }) => { const context = React5.useContext(TabsContext); const contentRef = React5.useRef(null); if (!context) { throw new Error("TabsContent must be used within Tabs"); } const { value: activeValue, size = "md", variant = "default", brutal = true, animated = true } = context; const isActive = activeValue === value; const sizeClasses = chunk7T4KDGYW_js.getSizeClasses(size); React5.useEffect(() => { if (isActive && contentRef.current) { contentRef.current.focus(); } }, [isActive]); if (!isActive && !forceMount && !keepMounted) { return null; } const getVariantClasses = () => { switch (variant) { case "pills": case "cards": return chunk7T4KDGYW_js.cn( "rounded-lg", brutal && "border-2 border-brutal-black shadow-brutal-sm bg-brutal-white", !brutal && "border bg-brutal-white rounded-lg shadow-sm" ); case "underline": return chunk7T4KDGYW_js.cn( "border-t border-brutal-gray-200 bg-brutal-white", brutal && "shadow-brutal-sm" ); default: return chunk7T4KDGYW_js.cn( brutal && "bg-brutal-white shadow-brutal-sm", !brutal && "bg-brutal-white" ); } }; return /* @__PURE__ */ React5__default.default.createElement( "div", { ref: contentRef, className: chunk7T4KDGYW_js.cn( // Base styling "focus:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2", // Size classes sizeClasses.padding, // Variant styling getVariantClasses(), // Animation classes animated && [ "transition-all duration-300", isActive ? "animate-fade-in" : "animate-fade-out" ], // Visibility !isActive && keepMounted && "hidden", !isActive && forceMount && "opacity-50", className ), role: "tabpanel", id: `tabpanel-${value}`, "aria-labelledby": `tab-${value}`, tabIndex: 0, hidden: !isActive && !forceMount }, children ); }; var TabsSkeleton = ({ tabCount = 3, orientation = "horizontal", size = "md", variant = "default", brutal = true, showContent = true, contentHeight = "200px", className }) => { const sizeClasses = chunk7T4KDGYW_js.getSizeClasses(size); const getVariantClasses = () => { switch (variant) { case "pills": return chunk7T4KDGYW_js.cn( "gap-2 p-1 rounded-lg", brutal && "bg-brutal-gray-200 border-4 border-brutal-gray-300", !brutal && "bg-brutal-gray-200 rounded-lg border" ); case "underline": return "border-b-2 border-brutal-gray-300"; case "cards": return chunk7T4KDGYW_js.cn( "gap-1 bg-brutal-gray-200 p-1 rounded-lg", brutal && "border-2 border-brutal-gray-300" ); default: return chunk7T4KDGYW_js.cn( brutal && "border-b-4 border-brutal-gray-300 bg-brutal-gray-100", !brutal && "border-b-2 border-brutal-gray-300 bg-brutal-gray-100" ); } }; const getTabClasses = () => { switch (variant) { case "pills": case "cards": return "rounded-md"; case "underline": return "border-b-2 border-transparent"; default: return chunk7T4KDGYW_js.cn( brutal && "border-r-2 last:border-r-0 border-brutal-gray-300", orientation === "vertical" && brutal && "border-r-0 border-b-2 last:border-b-0" ); } }; const getContentClasses = () => { switch (variant) { case "pills": case "cards": return chunk7T4KDGYW_js.cn( "rounded-lg", brutal && "border-2 border-brutal-gray-300 bg-brutal-gray-100", !brutal && "border bg-brutal-gray-100 rounded-lg" ); case "underline": return "border-t border-brutal-gray-300 bg-brutal-gray-100"; default: return chunk7T4KDGYW_js.cn( brutal && "bg-brutal-gray-100", !brutal && "bg-brutal-gray-100" ); } }; return /* @__PURE__ */ React5__default.default.createElement( "div", { className: chunk7T4KDGYW_js.cn( "animate-pulse flex w-full", orientation === "vertical" ? "flex-row gap-4" : "flex-col", brutal && "gap-2", className ) }, /* @__PURE__ */ React5__default.default.createElement( "div", { className: chunk7T4KDGYW_js.cn( "flex", orientation === "vertical" ? "flex-col" : "flex-row", getVariantClasses() ) }, Array.from({ length: tabCount }).map((_, i) => /* @__PURE__ */ React5__default.default.createElement( "div", { key: i, className: chunk7T4KDGYW_js.cn( "flex items-center justify-center bg-brutal-gray-300", sizeClasses.padding, getTabClasses(), orientation === "vertical" && "w-full justify-start", i === 0 && "bg-brutal-gray-400" // First tab appears "active" ) }, /* @__PURE__ */ React5__default.default.createElement( "div", { className: chunk7T4KDGYW_js.cn( "bg-brutal-gray-500 rounded", sizeClasses.text === "text-xs" ? "h-3" : "h-4", // Random widths for variety i % 3 === 0 && "w-16", i % 3 === 1 && "w-20", i % 3 === 2 && "w-12" ) } ) )) ), showContent && /* @__PURE__ */ React5__default.default.createElement( "div", { className: chunk7T4KDGYW_js.cn("space-y-3", sizeClasses.padding, getContentClasses()), style: { minHeight: contentHeight } }, /* @__PURE__ */ React5__default.default.createElement("div", { className: "h-6 bg-brutal-gray-300 rounded w-1/3" }), /* @__PURE__ */ React5__default.default.createElement("div", { className: "h-4 bg-brutal-gray-300 rounded w-full" }), /* @__PURE__ */ React5__default.default.createElement("div", { className: "h-4 bg-brutal-gray-300 rounded w-5/6" }), /* @__PURE__ */ React5__default.default.createElement("div", { className: "h-4 bg-brutal-gray-300 rounded w-4/6" }), /* @__PURE__ */ React5__default.default.createElement("div", { className: "h-4 bg-brutal-gray-300 rounded w-3/4" }) ) ); }; /** * @file src/components/core/Card/CardSkeleton.tsx * @author David (https://dvh.sh) * @license MIT * * @created Thu Sep 11 2025 * @updated Fri Sep 12 2025 * * @description * Skeleton loader for Card */ /** * @file src/components/core/Card/Card.tsx * @author David (https://dvh.sh) * @license MIT * * @created Thu Sep 11 2025 * @updated Fri Sep 12 2025 * * @description * Brutal card component with multiple variants */ /** * @file src/components/core/Tooltip/Tooltip.tsx * @author David (https://dvh.sh) * @license MIT * * @created Fri Sep 12 2025 * @updated Sat Sep 13 2025 * * @description * Brutal tooltip component with enhanced positioning and accessibility * @client This component requires client-side JavaScript */ /** * @file src/components/core/Tooltip/TooltipSkeleton.tsx * @author David (https://dvh.sh) * @license MIT * * @created Sat Sep 13 2025 * @updated Sat Sep 13 2025 * * @description * Skeleton loader for Tooltip components */ /** * @file src/components/core/Tooltip/index.ts * @author David (https://dvh.sh) * @license MIT * * @created Thu Sep 11 2025 * @updated Sat Sep 13 2025 * * @description * Tooltip components barrel export */ /** * @file src/components/core/Toggle/Toggle.tsx * @author David (https://dvh.sh) * @license MIT * * @created Thu Sep 11 2025 * @updated Sat Sep 13 2025 * * @description * Brutal toggle/switch component with enhanced styling and accessibility */ /** * @file src/components/core/Toggle/ToggleGroup.tsx * @author David (https://dvh.sh) * @license MIT * * @created Sat Sep 13 2025 * @updated Sat Sep 13 2025 * * @description * Group of toggle buttons for multi-selection */ /** * @file src/components/core/Toggle/ToggleSkeleton.tsx * @author David (https://dvh.sh) * @license MIT * * @created Sat Sep 13 2025 * @updated Sat Sep 13 2025 * * @description * Skeleton loaders for Toggle components */ /** * @file src/components/core/Toggle/index.ts * @author David (https://dvh.sh) * @license MIT * * @created Thu Sep 11 2025 * @updated Sat Sep 13 2025 * * @description * Toggle components barrel export */ /** * @file src/components/core/Tabs/Tabs.tsx * @author David (https://dvh.sh) * @license MIT * * @created Fri Sep 12 2025 * @updated Sat Sep 13 2025 * * @description * Main Tabs container component with enhanced styling and accessibility * @client This component requires client-side JavaScript (uses state and context) */ /** * @file src/components/core/Tabs/TabsList.tsx * @author David (https://dvh.sh) * @license MIT * * @created Fri Sep 12 2025 * @updated Sat Sep 13 2025 * * @description * Container component for tab triggers with multiple variants * @client This component requires client-side JavaScript */ /** * @file src/components/core/Tabs/TabsTrigger.tsx * @author David (https://dvh.sh) * @license MIT * * @created Fri Sep 12 2025 * @updated Sat Sep 13 2025 * * @description * Individual tab trigger button component with enhanced styling and accessibility * @client This component requires client-side JavaScript */ /** * @file src/components/core/Tabs/TabsContent.tsx * @author David (https://dvh.sh) * @license MIT * * @created Fri Sep 12 2025 * @updated Sat Sep 13 2025 * * @description * Content panel component for tabs with enhanced animations and accessibility * @client This component requires client-side JavaScript */ /** * @file src/components/core/Tabs/TabsSkeleton.tsx * @author David (https://dvh.sh) * @license MIT * * @created Sat Sep 13 2025 * @updated Sat Sep 13 2025 * * @description * Skeleton loaders for Tabs components */ /** * @file src/components/core/Tabs/index.ts * @author David (https://dvh.sh) * @license MIT * * @created Thu Sep 12 2025 * @updated Sat Sep 13 2025 * * @description * Tabs components barrel export */ exports.Card = Card; exports.CardSkeleton = CardSkeleton; exports.Tabs = Tabs; exports.TabsContent = TabsContent; exports.TabsList = TabsList; exports.TabsSkeleton = TabsSkeleton; exports.TabsTrigger = TabsTrigger; exports.Toggle = Toggle; exports.ToggleGroup = ToggleGroup; exports.ToggleGroupSkeleton = ToggleGroupSkeleton; exports.ToggleSkeleton = ToggleSkeleton; exports.Tooltip = Tooltip; exports.TooltipProvider = TooltipProvider; exports.TooltipSkeleton = TooltipSkeleton; //# sourceMappingURL=chunk-ZEC3NQBH.js.map //# sourceMappingURL=chunk-ZEC3NQBH.js.map