@brutalcomponent/react
Version:
Brutalist React components
1,530 lines (1,525 loc) • 48.2 kB
JavaScript
;
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