@brutalcomponent/react
Version:
Brutalist React components
1,119 lines (1,113 loc) • 184 kB
JavaScript
'use strict';
var chunkR74LWSGE_js = require('./chunk-R74LWSGE.js');
var chunk7GZ4CFXP_js = require('./chunk-7GZ4CFXP.js');
var chunk7YHHVG7W_js = require('./chunk-7YHHVG7W.js');
var chunk7T4KDGYW_js = require('./chunk-7T4KDGYW.js');
var React35 = require('react');
var fa = require('react-icons/fa');
var clsx = require('clsx');
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
var React35__default = /*#__PURE__*/_interopDefault(React35);
/**
* @brutalcomponent/react
* (c) David Heffler (https://dvh.sh)
* Licensed under MIT
*/
var StatCard = ({
title,
value,
previousValue,
format = "number",
trend,
icon: Icon2,
color = "default",
size = "md",
brutal = true,
className
}) => {
const sizeClasses = {
sm: "p-4",
md: "p-6",
lg: "p-8"
};
const valueSizes = {
sm: "text-2xl",
md: "text-3xl",
lg: "text-4xl"
};
const colorClasses = {
default: "border-l-brutal-black",
success: "border-l-brutal-mint",
warning: "border-l-brutal-yellow",
danger: "border-l-brutal-coral"
};
const calculateChange = () => {
if (!previousValue || typeof value !== "number" || typeof previousValue !== "number") {
return null;
}
return (value - previousValue) / previousValue * 100;
};
const change = calculateChange();
return /* @__PURE__ */ React35__default.default.createElement(
"div",
{
className: chunk7T4KDGYW_js.cn(
// Base brutal styling
"bg-brutal-surface0 shadow-brutal",
brutal && "border-4 border-brutal-black border-l-8",
!brutal && "border border-brutal-gray-300",
colorClasses[color],
sizeClasses[size],
"hover:shadow-brutal-md hover:-translate-y-0.5 transition-all duration-200",
"transform hover:rotate-0",
brutal && "-rotate-1",
className
)
},
/* @__PURE__ */ React35__default.default.createElement("div", { className: "flex justify-between items-start mb-4" }, /* @__PURE__ */ React35__default.default.createElement("div", { className: "flex-1" }, /* @__PURE__ */ React35__default.default.createElement("p", { className: "text-xs font-black uppercase text-brutal-gray-600 tracking-wider mb-2 transform skew-x-1" }, title), /* @__PURE__ */ React35__default.default.createElement(
"p",
{
className: chunk7T4KDGYW_js.cn(
"font-black text-brutal-black transform -skew-x-2",
valueSizes[size]
)
},
format === "currency" && "$",
typeof value === "number" ? value.toLocaleString() : value,
format === "percentage" && "%"
)), Icon2 && /* @__PURE__ */ React35__default.default.createElement(
"div",
{
className: chunk7T4KDGYW_js.cn(
"p-2 transform rotate-3",
brutal && "bg-brutal-gray-100 border-2 border-brutal-black shadow-brutal-sm",
!brutal && "bg-brutal-gray-100 rounded"
)
},
/* @__PURE__ */ React35__default.default.createElement(Icon2, { className: "w-6 h-6 text-brutal-gray-700" })
)),
change !== null && /* @__PURE__ */ React35__default.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React35__default.default.createElement(
"span",
{
className: chunk7T4KDGYW_js.cn(
"text-sm font-black uppercase tracking-wider px-2 py-1",
brutal && "border-2 border-brutal-black shadow-brutal-sm",
change >= 0 ? "bg-brutal-mint text-brutal-black" : "bg-brutal-coral text-brutal-black"
)
},
change >= 0 ? "+" : "",
change.toFixed(1),
"%"
), /* @__PURE__ */ React35__default.default.createElement("span", { className: "text-xs text-brutal-gray-500 font-mono" }, "from", " ", typeof previousValue === "number" ? previousValue.toLocaleString() : previousValue))
);
};
var ExperienceCard = ({
title,
company,
location,
startDate,
endDate,
type,
description,
bullets,
keywords = [],
brutal = true,
variant = "default",
accentColor = "brutal-pink",
className
}) => {
const duration = chunkR74LWSGE_js.formatDuration(
chunkR74LWSGE_js.parseLooseDate(startDate),
endDate ? chunkR74LWSGE_js.parseLooseDate(endDate) : null
);
chunk7T4KDGYW_js.getAccentClasses(accentColor);
const highlightedDescription = description ? chunkR74LWSGE_js.emphasizeText(description, keywords) : [];
const highlightedBullets = bullets?.map(
(bullet) => chunkR74LWSGE_js.emphasizeText(bullet, keywords)
);
return /* @__PURE__ */ React35__default.default.createElement(
"div",
{
className: chunk7T4KDGYW_js.cn(
// Base styling - match original exactly
"relative bg-brutal-surface0 p-6 shadow-brutal border-4 overflow-hidden",
"border-accent",
// Use CSS variable for theming
"transition-all duration-300",
className
)
},
/* @__PURE__ */ React35__default.default.createElement("div", { className: "flex flex-col sm:flex-row sm:justify-between sm:items-start gap-3 mb-4" }, /* @__PURE__ */ React35__default.default.createElement("div", null, /* @__PURE__ */ React35__default.default.createElement("h3", { className: "text-2xl font-black text-accent uppercase tracking-wider transform -skew-x-3" }, title), /* @__PURE__ */ React35__default.default.createElement("p", { className: "text-lg text-brutal-gray-700 font-bold mt-1" }, company)), variant !== "compact" && /* @__PURE__ */ React35__default.default.createElement("div", { className: "text-right" }, /* @__PURE__ */ React35__default.default.createElement("div", { className: "flex items-center gap-2 text-brutal-gray-600 text-sm" }, /* @__PURE__ */ React35__default.default.createElement(chunk7YHHVG7W_js.Icon, { icon: fa.FaClock, size: "xs" }), /* @__PURE__ */ React35__default.default.createElement("span", { className: "font-mono" }, startDate, " - ", endDate || "Present")), /* @__PURE__ */ React35__default.default.createElement("span", { className: "inline-block text-xs font-black text-brutal-pink uppercase tracking-wider mt-1 px-2 py-1 bg-brutal-surface1" }, duration))),
(type || location) && variant !== "compact" && /* @__PURE__ */ React35__default.default.createElement("div", { className: "flex flex-wrap gap-4 text-xs text-brutal-gray-600 mb-4 font-mono" }, type && /* @__PURE__ */ React35__default.default.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ React35__default.default.createElement(chunk7YHHVG7W_js.Icon, { icon: fa.FaBriefcase, size: "xs" }), /* @__PURE__ */ React35__default.default.createElement("span", null, type)), location && /* @__PURE__ */ React35__default.default.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ React35__default.default.createElement(chunk7YHHVG7W_js.Icon, { icon: fa.FaMapMarkerAlt, size: "xs" }), /* @__PURE__ */ React35__default.default.createElement("span", null, location))),
description && variant !== "compact" && /* @__PURE__ */ React35__default.default.createElement("p", { className: "text-sm text-brutal-gray-700 mb-4 leading-relaxed" }, highlightedDescription.map(
(segment, idx) => segment.bold ? /* @__PURE__ */ React35__default.default.createElement(
"strong",
{
key: idx,
className: "font-black text-brutal-pink inline-block"
},
segment.text
) : /* @__PURE__ */ React35__default.default.createElement(React35.Fragment, { key: idx }, segment.text)
)),
bullets && bullets.length > 0 && variant === "detailed" && /* @__PURE__ */ React35__default.default.createElement("ul", { className: "space-y-2" }, highlightedBullets?.map((bulletSegments, idx) => /* @__PURE__ */ React35__default.default.createElement("li", { key: idx, className: "flex text-sm text-brutal-gray-700" }, /* @__PURE__ */ React35__default.default.createElement("span", { className: "text-brutal-pink font-black mr-2 flex-shrink-0" }, "\u2192"), /* @__PURE__ */ React35__default.default.createElement("span", null, bulletSegments.map(
(segment, segIdx) => segment.bold ? /* @__PURE__ */ React35__default.default.createElement(
"strong",
{
key: segIdx,
className: "font-black text-brutal-pink inline-block"
},
segment.text
) : /* @__PURE__ */ React35__default.default.createElement(React35.Fragment, { key: segIdx }, segment.text)
)))))
);
};
var Badge = ({
variant = "default",
size = "sm",
brutal = true,
icon: Icon2,
iconPosition = "left",
dot = false,
removable = false,
onRemove,
className,
children,
...props
}) => {
const variantClasses = {
default: "bg-brutal-surface0 text-brutal-black border-brutal-black",
primary: "bg-brutal-black text-brutal-white border-brutal-black",
secondary: "bg-brutal-white text-brutal-black border-brutal-black",
success: "bg-brutal-mint text-brutal-black border-brutal-black",
warning: "bg-brutal-yellow text-brutal-black border-brutal-black",
danger: "bg-brutal-coral text-brutal-black border-brutal-black",
info: "bg-brutal-sky text-brutal-black border-brutal-black"
};
const sizeClasses = {
xs: "px-1.5 py-0.5 text-xs",
sm: "px-2 py-1 text-xs",
md: "px-3 py-1.5 text-sm",
lg: "px-4 py-2 text-base"
};
const iconSizes = {
xs: "w-2.5 h-2.5",
sm: "w-3 h-3",
md: "w-3.5 h-3.5",
lg: "w-4 h-4"
};
return /* @__PURE__ */ React35__default.default.createElement(
"span",
{
className: chunk7T4KDGYW_js.cn(
"inline-flex items-center gap-1",
"font-black uppercase tracking-wider",
brutal ? "border-2 shadow-brutal-sm" : "border rounded",
sizeClasses[size],
variantClasses[variant],
"transition-all duration-200",
brutal && "hover:shadow-brutal hover:-translate-x-0.5 hover:-translate-y-0.5",
brutal && "transform hover:rotate-0",
brutal && size !== "xs" && "rotate-1",
className
),
...props
},
dot && /* @__PURE__ */ React35__default.default.createElement(
"span",
{
className: chunk7T4KDGYW_js.cn(
"rounded-full bg-current",
size === "xs" ? "w-1 h-1" : size === "sm" ? "w-1.5 h-1.5" : "w-2 h-2"
)
}
),
Icon2 && iconPosition === "left" && /* @__PURE__ */ React35__default.default.createElement(Icon2, { className: iconSizes[size] }),
children,
Icon2 && iconPosition === "right" && /* @__PURE__ */ React35__default.default.createElement(Icon2, { className: iconSizes[size] }),
removable && /* @__PURE__ */ React35__default.default.createElement(
"button",
{
onClick: (e) => {
e.stopPropagation();
onRemove?.();
},
className: chunk7T4KDGYW_js.cn(
"ml-1 -mr-1 hover:bg-brutal-black/20 transition-colors",
size === "xs" || size === "sm" ? "p-0.5" : "p-1"
),
"aria-label": "Remove"
},
/* @__PURE__ */ React35__default.default.createElement(
"svg",
{
className: iconSizes[size],
fill: "none",
strokeLinecap: "round",
strokeLinejoin: "round",
strokeWidth: "3",
viewBox: "0 0 24 24",
stroke: "currentColor"
},
/* @__PURE__ */ React35__default.default.createElement("path", { d: "M6 18L18 6M6 6l12 12" })
)
)
);
};
var BadgeGroup = ({
children,
className
}) => /* @__PURE__ */ React35__default.default.createElement("div", { className: clsx.clsx("flex flex-wrap gap-2", className) }, children);
// src/components/core/Card/ProjectCard.tsx
var ProjectCard = ({
title,
description,
technologies,
demoLink,
sourceLink,
image,
keywords = [],
variant = "default",
brutal = true,
accentColor = "brutal-pink",
className
}) => {
const highlightedDescription = chunkR74LWSGE_js.emphasizeText(description, keywords);
return /* @__PURE__ */ React35__default.default.createElement(
"div",
{
className: chunk7T4KDGYW_js.cn(
// Base styling - match original exactly
"relative bg-brutal-surface0 border-4 border-accent p-6 shadow-brutal",
"overflow-hidden flex flex-col h-full",
className
)
},
/* @__PURE__ */ React35__default.default.createElement("h3", { className: "text-2xl font-black text-accent mb-3 uppercase tracking-wider transform -skew-x-6" }, title),
/* @__PURE__ */ React35__default.default.createElement("p", { className: "text-brutal-gray-700 mb-4 font-mono text-sm leading-relaxed flex-grow" }, highlightedDescription.map(
(segment, idx) => segment.bold ? /* @__PURE__ */ React35__default.default.createElement(
"strong",
{
key: idx,
className: "font-black text-brutal-pink inline-block"
},
segment.text
) : /* @__PURE__ */ React35__default.default.createElement(React35.Fragment, { key: idx }, segment.text)
)),
technologies.length > 0 && /* @__PURE__ */ React35__default.default.createElement("div", { className: "mb-5" }, /* @__PURE__ */ React35__default.default.createElement("div", { className: "flex items-center gap-2 mb-2" }, /* @__PURE__ */ React35__default.default.createElement(chunk7YHHVG7W_js.Icon, { icon: fa.FaCode, size: "xs", className: "text-brutal-gray-600" }), /* @__PURE__ */ React35__default.default.createElement("span", { className: "text-xs font-bold text-brutal-gray-600 uppercase tracking-wider" }, "Built with")), /* @__PURE__ */ React35__default.default.createElement("div", { className: "flex flex-wrap gap-2" }, technologies.map((tech, idx) => /* @__PURE__ */ React35__default.default.createElement(Badge, { key: idx, size: "xs", variant: "secondary", brutal }, tech)))),
/* @__PURE__ */ React35__default.default.createElement("div", { className: "flex gap-3 mt-auto" }, demoLink && /* @__PURE__ */ React35__default.default.createElement(
"a",
{
href: demoLink,
target: "_blank",
rel: "noopener noreferrer",
className: "inline-flex items-center gap-2 font-black uppercase tracking-wider px-3 py-2 border-2 transition-all duration-200 shadow-brutal border-accent bg-accent text-brutal-black hover:bg-transparent hover:text-accent"
},
/* @__PURE__ */ React35__default.default.createElement(chunk7YHHVG7W_js.Icon, { icon: fa.FaExternalLinkAlt, size: "sm" }),
/* @__PURE__ */ React35__default.default.createElement("span", { className: "text-xs" }, "Demo")
), sourceLink && /* @__PURE__ */ React35__default.default.createElement(
"a",
{
href: sourceLink,
target: "_blank",
rel: "noopener noreferrer",
className: "inline-flex items-center gap-2 font-black uppercase tracking-wider px-3 py-2 border-2 transition-all duration-200 shadow-brutal border-accent text-accent hover:bg-accent hover:text-brutal-black"
},
/* @__PURE__ */ React35__default.default.createElement(chunk7YHHVG7W_js.Icon, { icon: fa.FaGithub, size: "sm" }),
/* @__PURE__ */ React35__default.default.createElement("span", { className: "text-xs" }, "Code")
))
);
};
var WorkCard = ({
title,
shortDescription,
technologies,
link,
date,
client,
testimonial,
keywords = [],
brutal = true,
accentColor = "brutal-pink",
className
}) => {
const highlightedDescription = chunkR74LWSGE_js.emphasizeText(shortDescription, keywords);
const normalizedTechs = Array.isArray(technologies) ? technologies.map((tech) => tech.toLowerCase()) : [];
const safeLink = link && typeof link === "string" && link.startsWith("http") ? link : `https://${String(link ?? "")}`;
return /* @__PURE__ */ React35__default.default.createElement(
"div",
{
className: chunk7T4KDGYW_js.cn(
// Base styling - match original exactly
"relative bg-brutal-surface0 border-4 border-accent p-6 shadow-brutal overflow-hidden",
"transform rotate-1 hover:rotate-0 hover:scale-1.02 hover:translate-x-1.5",
"transition-all duration-300",
className
)
},
/* @__PURE__ */ React35__default.default.createElement("div", { className: "flex flex-col sm:flex-row sm:justify-between sm:items-start gap-3 mb-3" }, /* @__PURE__ */ React35__default.default.createElement("div", null, /* @__PURE__ */ React35__default.default.createElement("h3", { className: "text-2xl font-black text-accent uppercase tracking-wider transform -skew-x-6" }, title), client && /* @__PURE__ */ React35__default.default.createElement("p", { className: "text-sm font-bold text-brutal-gray-600 mt-1" }, "for ", client)), /* @__PURE__ */ React35__default.default.createElement("div", { className: "flex items-center gap-2 text-brutal-gray-600" }, /* @__PURE__ */ React35__default.default.createElement(chunk7YHHVG7W_js.Icon, { icon: fa.FaCalendar, size: "xs" }), /* @__PURE__ */ React35__default.default.createElement("span", { className: "font-mono text-xs font-bold" }, date))),
/* @__PURE__ */ React35__default.default.createElement("p", { className: "text-brutal-gray-700 mb-4 font-mono text-sm leading-relaxed" }, highlightedDescription.map(
(segment, idx) => segment.bold ? /* @__PURE__ */ React35__default.default.createElement(
"strong",
{
key: idx,
className: "font-black text-brutal-pink inline-block"
},
segment.text
) : /* @__PURE__ */ React35__default.default.createElement(React35.Fragment, { key: idx }, segment.text)
)),
testimonial && /* @__PURE__ */ React35__default.default.createElement("blockquote", { className: "mb-4 p-3 italic bg-brutal-gray-100 border-l-4 border-accent" }, /* @__PURE__ */ React35__default.default.createElement("p", { className: "text-sm text-brutal-gray-700" }, '"', testimonial, '"')),
normalizedTechs.length > 0 && /* @__PURE__ */ React35__default.default.createElement("div", { className: "mb-5" }, /* @__PURE__ */ React35__default.default.createElement("div", { className: "flex items-center gap-2 mb-2" }, /* @__PURE__ */ React35__default.default.createElement(chunk7YHHVG7W_js.Icon, { icon: fa.FaCode, size: "xs", className: "text-brutal-gray-600" }), /* @__PURE__ */ React35__default.default.createElement("span", { className: "text-xs font-bold text-brutal-gray-600 uppercase tracking-wider" }, "Built with")), /* @__PURE__ */ React35__default.default.createElement("div", { className: "flex flex-wrap gap-2" }, normalizedTechs.map((tech, idx) => /* @__PURE__ */ React35__default.default.createElement(Badge, { key: idx, size: "xs", variant: "secondary", brutal }, tech)))),
link && /* @__PURE__ */ React35__default.default.createElement(
"a",
{
href: safeLink,
target: "_blank",
rel: "noopener noreferrer",
className: "inline-flex items-center gap-2 text-accent font-black uppercase tracking-wider px-3 py-2 border-2 border-accent bg-transparent shadow-brutal hover:bg-accent hover:text-brutal-black transition-colors"
},
"Live Demo",
/* @__PURE__ */ React35__default.default.createElement(chunk7YHHVG7W_js.Icon, { icon: fa.FaExternalLinkAlt, size: "sm" })
),
/* @__PURE__ */ React35__default.default.createElement("div", { className: "absolute top-0 right-0 w-16 h-16 bg-accent opacity-10 -mr-8 -mt-8" })
);
};
var SoftwareCard = ({
title,
description,
link,
price,
brewInstall,
operatingSystem,
features = [],
variant = "default",
brutal = true,
accentColor = "brutal-pink",
className
}) => {
const [copied, setCopied] = React35.useState(false);
const copyToClipboard = React35.useCallback((text) => {
try {
navigator.clipboard.writeText(text);
setCopied(true);
setTimeout(() => setCopied(false), 1400);
} catch (e) {
console.error("Clipboard write failed:", e);
}
}, []);
const safeLink = typeof link === "string" && link ? link.startsWith("http") ? link : `https://${link}` : void 0;
const osTags = operatingSystem ? operatingSystem.split(",").map((s) => s.trim()).filter(Boolean) : [];
return /* @__PURE__ */ React35__default.default.createElement(
"div",
{
className: chunk7T4KDGYW_js.cn(
// Base styling - match original exactly
"relative bg-brutal-surface0 border-4 border-accent p-6 shadow-brutal",
"overflow-hidden flex flex-col h-full",
"transform -rotate-1 hover:rotate-0 hover:scale-1.02 hover:translate-x-1.5",
"transition-all duration-300",
className
)
},
/* @__PURE__ */ React35__default.default.createElement("div", { className: "flex items-start justify-between gap-3 mb-3" }, /* @__PURE__ */ React35__default.default.createElement("h3", { className: "text-xl font-black text-accent uppercase tracking-wider transform -skew-x-6" }, title), price && /* @__PURE__ */ React35__default.default.createElement("span", { className: "inline-block bg-brutal-surface1 text-brutal-gray-600 px-3 py-1 text-xs font-black uppercase tracking-wider border-2 border-accent" }, price)),
/* @__PURE__ */ React35__default.default.createElement("p", { className: "text-brutal-gray-700 mb-4 flex-grow font-mono text-sm leading-relaxed" }, description),
features.length > 0 && variant === "detailed" && /* @__PURE__ */ React35__default.default.createElement("ul", { className: "mb-4 space-y-1" }, features.map((feature, idx) => /* @__PURE__ */ React35__default.default.createElement(
"li",
{
key: idx,
className: "flex items-start text-xs text-brutal-gray-600"
},
/* @__PURE__ */ React35__default.default.createElement("span", { className: "mr-2 text-accent" }, "\u2713"),
feature
))),
/* @__PURE__ */ React35__default.default.createElement("div", { className: "mt-auto flex items-center justify-between gap-3" }, /* @__PURE__ */ React35__default.default.createElement("div", { className: "flex flex-wrap items-center gap-1 text-xs font-black uppercase tracking-wider text-brutal-gray-600" }, osTags.length ? osTags.map((os, idx) => /* @__PURE__ */ React35__default.default.createElement(
"span",
{
key: idx,
className: "px-2 py-0.5 border border-accent bg-brutal-surface1"
},
os
)) : /* @__PURE__ */ React35__default.default.createElement("span", { className: "px-2 py-0.5 border border-accent bg-brutal-surface1" }, "N/A")), /* @__PURE__ */ React35__default.default.createElement("div", { className: "flex items-center gap-3" }, brewInstall && /* @__PURE__ */ React35__default.default.createElement(
"button",
{
onClick: () => copyToClipboard(brewInstall),
className: "inline-flex items-center gap-2 text-brutal-gray-600 hover:text-accent transition-colors px-2 py-1 border-2 border-accent bg-brutal-surface1 font-black uppercase tracking-wider text-xs",
"aria-label": "Copy Brew Install Command",
title: "Copy Brew Install Command"
},
/* @__PURE__ */ React35__default.default.createElement(chunk7YHHVG7W_js.Icon, { icon: fa.FaCopy, size: "xs" }),
"Copy"
), safeLink && /* @__PURE__ */ React35__default.default.createElement(
"a",
{
href: safeLink,
className: "inline-flex items-center gap-2 text-accent border-2 border-accent px-2 py-1 bg-transparent hover:bg-accent hover:text-brutal-black transition-colors font-black uppercase tracking-wider text-xs",
target: "_blank",
rel: "noopener noreferrer",
"aria-label": "View software"
},
/* @__PURE__ */ React35__default.default.createElement(chunk7YHHVG7W_js.Icon, { icon: fa.FaExternalLinkAlt, size: "xs" }),
"View"
))),
copied && /* @__PURE__ */ React35__default.default.createElement("div", { className: "absolute bottom-3 right-3 bg-brutal-surface1 border-2 border-accent text-brutal-gray-700 px-3 py-1 font-mono text-xs shadow-brutal" }, "Copied!"),
/* @__PURE__ */ React35__default.default.createElement("div", { className: "pointer-events-none absolute inset-1 border-2 border-accent opacity-30" })
);
};
var SoftwareCardSkeleton = () => /* @__PURE__ */ React35__default.default.createElement("div", { className: "bg-brutal-surface0 border-4 border-accent p-6 shadow-brutal h-[200px] flex flex-col animate-pulse" }, /* @__PURE__ */ React35__default.default.createElement("div", { className: "h-6 bg-brutal-gray-300 w-3/4 mb-3" }), /* @__PURE__ */ React35__default.default.createElement("div", { className: "h-4 bg-brutal-gray-300 w-full mb-2" }), /* @__PURE__ */ React35__default.default.createElement("div", { className: "h-4 bg-brutal-gray-300 w-5/6 mb-4" }), /* @__PURE__ */ React35__default.default.createElement("div", { className: "mt-auto flex items-center gap-2" }, /* @__PURE__ */ React35__default.default.createElement("div", { className: "h-7 w-20 bg-brutal-gray-300" }), /* @__PURE__ */ React35__default.default.createElement("div", { className: "h-7 w-16 bg-brutal-gray-300" })));
var UserCard = ({
name,
avatar,
titles = [],
bio,
links = [],
rotationInterval = 2500,
variant = "default",
brutal = true,
accentColor = "brutal-pink",
className
}) => {
const [titleIndex, setTitleIndex] = React35.useState(0);
const [isImageLoaded, setIsImageLoaded] = React35.useState(false);
React35.useEffect(() => {
if (titles.length <= 1) return;
const interval = setInterval(() => {
setTitleIndex((prev) => (prev + 1) % titles.length);
}, rotationInterval);
return () => clearInterval(interval);
}, [titles.length, rotationInterval]);
return /* @__PURE__ */ React35__default.default.createElement("div", { className: chunk7T4KDGYW_js.cn("text-center mb-8", className) }, /* @__PURE__ */ React35__default.default.createElement("div", { className: "relative mx-auto mb-4 w-32 h-32 group" }, /* @__PURE__ */ React35__default.default.createElement("div", { className: "absolute inset-0 bg-accent opacity-30 rounded-full animate-pulse" }), /* @__PURE__ */ React35__default.default.createElement("div", { className: "relative rounded-full overflow-hidden border-4 border-accent shadow-brutal group-hover:shadow-brutal-md transition-all duration-300" }, !isImageLoaded && /* @__PURE__ */ React35__default.default.createElement("div", { className: "w-full h-full bg-brutal-gray-200 animate-pulse" }), /* @__PURE__ */ React35__default.default.createElement(
"img",
{
src: avatar,
alt: name,
width: 128,
height: 128,
onLoad: () => setIsImageLoaded(true),
className: chunk7T4KDGYW_js.cn(
"w-full h-full object-cover",
"group-hover:scale-110 transition-transform duration-300",
!isImageLoaded && "invisible"
)
}
))), /* @__PURE__ */ React35__default.default.createElement("h3", { className: "text-xl font-black mb-2 text-brutal-gray-700 uppercase tracking-widest transform hover:-skew-x-6 transition-transform duration-200" }, name), titles.length > 0 && /* @__PURE__ */ React35__default.default.createElement("div", { className: "h-6 relative overflow-hidden" }, /* @__PURE__ */ React35__default.default.createElement(
"div",
{
className: "absolute inset-x-0 transition-transform duration-300",
style: {
transform: `translateY(-${titleIndex * 100}%)`
}
},
titles.map((title, index) => /* @__PURE__ */ React35__default.default.createElement(
"p",
{
key: index,
className: "text-sm text-brutal-gray-600 font-mono h-6 flex items-center justify-center"
},
title
))
)), bio && variant !== "compact" && /* @__PURE__ */ React35__default.default.createElement("p", { className: "mt-4 text-sm text-brutal-gray-700 font-mono max-w-xs mx-auto" }, bio), links.length > 0 && variant === "detailed" && /* @__PURE__ */ React35__default.default.createElement("div", { className: "mt-4 flex justify-center gap-3" }, links.map((link, idx) => /* @__PURE__ */ React35__default.default.createElement(
"a",
{
key: idx,
href: link.href,
target: "_blank",
rel: "noopener noreferrer",
className: "inline-flex items-center gap-1 px-3 py-1 text-xs font-bold uppercase tracking-wider border-2 border-brutal-black hover:bg-brutal-black hover:text-brutal-white transition-colors"
},
link.icon,
link.label
))));
};
var PositionCard = ({
company,
position,
description,
technologies,
companyLink,
dateRange,
location,
achievements = [],
variant = "default",
brutal = true,
accentColor = "brutal-pink",
className
}) => {
return /* @__PURE__ */ React35__default.default.createElement(
"div",
{
className: chunk7T4KDGYW_js.cn(
// Base styling - match original exactly
"bg-brutal-surface0 p-6 shadow-md hover:shadow-lg transition-shadow duration-300",
"transform -skew-x-2 border-l-4 border-accent",
className
)
},
/* @__PURE__ */ React35__default.default.createElement("div", { className: "flex flex-col mb-4" }, /* @__PURE__ */ React35__default.default.createElement("h3", { className: "text-3xl font-bold text-accent mb-1 uppercase tracking-wide" }, company), /* @__PURE__ */ React35__default.default.createElement("span", { className: "text-lg text-brutal-gray-600 font-mono" }, dateRange)),
/* @__PURE__ */ React35__default.default.createElement("h4", { className: "text-2xl font-semibold text-brutal-gray-700 mb-3 transform skew-x-2" }, position),
/* @__PURE__ */ React35__default.default.createElement("p", { className: "text-brutal-gray-700 mb-4 text-base font-mono leading-relaxed" }, description),
/* @__PURE__ */ React35__default.default.createElement("div", { className: "flex flex-wrap gap-2 mb-4" }, technologies.map((tech, idx) => /* @__PURE__ */ React35__default.default.createElement(Badge, { key: idx, size: "sm", variant: "secondary", brutal }, tech))),
companyLink && /* @__PURE__ */ React35__default.default.createElement(
"a",
{
href: companyLink,
target: "_blank",
rel: "noopener noreferrer",
className: "inline-flex items-center text-accent hover:underline text-lg font-bold"
},
"Company Website",
/* @__PURE__ */ React35__default.default.createElement(chunk7YHHVG7W_js.Icon, { icon: fa.FaExternalLinkAlt, size: "md", className: "ml-2" })
)
);
};
var BlogCard = ({
title,
excerpt,
date,
href,
views,
readingTime,
tags,
origin,
type,
brutal = true,
accentColor = "brutal-pink",
className
}) => {
const isCooking = origin && type;
return /* @__PURE__ */ React35__default.default.createElement(
"div",
{
className: chunk7T4KDGYW_js.cn(
// Base styling - match original exactly
"bg-brutal-surface0 p-6 shadow-lg flex flex-col h-full relative overflow-hidden",
"border-l-4 border-accent transition-all duration-300",
"hover:scale-[1.03] hover:-rotate-1",
className
)
},
/* @__PURE__ */ React35__default.default.createElement("div", { className: "flex flex-col mb-4" }, /* @__PURE__ */ React35__default.default.createElement("h3", { className: "text-2xl font-bold text-accent mb-2 uppercase tracking-wide" }, title), /* @__PURE__ */ React35__default.default.createElement("div", { className: "flex items-center text-brutal-gray-600 space-x-4 text-sm flex-wrap" }, /* @__PURE__ */ React35__default.default.createElement("div", { className: "flex items-center" }, /* @__PURE__ */ React35__default.default.createElement(chunk7YHHVG7W_js.Icon, { icon: fa.FaCalendarAlt, size: "xs", className: "mr-1" }), /* @__PURE__ */ React35__default.default.createElement("span", null, new Date(date).toLocaleDateString())), readingTime && /* @__PURE__ */ React35__default.default.createElement("div", { className: "flex items-center" }, /* @__PURE__ */ React35__default.default.createElement(chunk7YHHVG7W_js.Icon, { icon: fa.FaBook, size: "xs", className: "mr-1" }), /* @__PURE__ */ React35__default.default.createElement("span", null, "~", readingTime, " min")), views !== void 0 && /* @__PURE__ */ React35__default.default.createElement("div", { className: "flex items-center" }, /* @__PURE__ */ React35__default.default.createElement(chunk7YHHVG7W_js.Icon, { icon: fa.FaEye, size: "xs", className: "mr-1" }), /* @__PURE__ */ React35__default.default.createElement("span", null, views, " views")), origin && /* @__PURE__ */ React35__default.default.createElement("div", { className: "flex items-center" }, /* @__PURE__ */ React35__default.default.createElement(chunk7YHHVG7W_js.Icon, { icon: fa.FaGlobe, size: "xs", className: "mr-1" }), /* @__PURE__ */ React35__default.default.createElement("span", null, origin)), type && /* @__PURE__ */ React35__default.default.createElement("div", { className: "flex items-center" }, /* @__PURE__ */ React35__default.default.createElement(chunk7YHHVG7W_js.Icon, { icon: fa.FaUtensils, size: "xs", className: "mr-1" }), /* @__PURE__ */ React35__default.default.createElement("span", null, type)))),
/* @__PURE__ */ React35__default.default.createElement("p", { className: "text-brutal-gray-700 mb-4 flex-grow font-mono text-sm" }, excerpt),
tags && tags.length > 0 && /* @__PURE__ */ React35__default.default.createElement("div", { className: "flex flex-wrap gap-2 mb-4" }, tags.map((tag, idx) => /* @__PURE__ */ React35__default.default.createElement(Badge, { key: idx, size: "xs", variant: "secondary", brutal }, tag))),
/* @__PURE__ */ React35__default.default.createElement(
"a",
{
href: isCooking ? `/cooking/${href}` : `/blog/${href}`,
className: "text-brutal-sky hover:text-accent transition-colors duration-200 flex items-center group self-start"
},
/* @__PURE__ */ React35__default.default.createElement("span", { className: "mr-2 uppercase tracking-wide font-bold" }, "Read more"),
/* @__PURE__ */ React35__default.default.createElement(
chunk7YHHVG7W_js.Icon,
{
icon: fa.FaArrowRight,
size: "sm",
className: "transition-transform duration-300 group-hover:translate-x-2"
}
)
),
/* @__PURE__ */ React35__default.default.createElement("div", { className: "absolute top-0 right-0 w-16 h-16 bg-accent opacity-10 rounded-full -mr-8 -mt-8" })
);
};
var Chip = ({
children,
variant = "default",
size = "sm",
icon: IconComponent,
iconPosition = "left",
onRemove,
onClick,
brutal = true,
selected = false,
disabled = false,
accentColor = "brutal-pink",
className
}) => {
const Component = onClick ? "button" : "span";
const sizeClasses = chunk7T4KDGYW_js.getSizeClasses(size);
const iconSizes = {
xs: "xs",
sm: "xs",
md: "sm",
lg: "sm"
};
return /* @__PURE__ */ React35__default.default.createElement(
Component,
{
className: chunk7T4KDGYW_js.cn(
// Base styling
"inline-flex items-center font-black uppercase tracking-wider",
"transition-all duration-200",
// Size classes
sizeClasses.padding,
sizeClasses.text,
"gap-1.5",
// Brutal styling
brutal && [
sizeClasses.border,
"border-brutal-black",
sizeClasses.shadow
],
!brutal && "border rounded-md",
// Variant styling
variant === "default" && "bg-brutal-gray-100 text-brutal-black",
variant === "primary" && "bg-brutal-black text-brutal-white",
variant === "secondary" && "bg-brutal-white text-brutal-black",
variant === "success" && "bg-brutal-mint text-brutal-black",
variant === "warning" && "bg-brutal-yellow text-brutal-black",
variant === "danger" && "bg-brutal-coral text-brutal-black",
variant === "ghost" && "bg-transparent text-brutal-black border-brutal-black",
// Selected state
selected && [
"bg-accent text-brutal-black",
brutal && "shadow-brutal transform -rotate-1"
],
// Interactive states
onClick && !disabled && [
"cursor-pointer",
brutal && "hover:shadow-brutal-md hover:transform hover:-rotate-0.5",
!brutal && "hover:shadow-md hover:scale-105"
],
// Disabled state
disabled && "opacity-50 cursor-not-allowed",
className
),
onClick: !disabled ? onClick : void 0,
disabled: Component === "button" ? disabled : void 0,
style: {
"--accent-color": accentColor.startsWith("#") ? accentColor : `var(--brutal-${accentColor.replace("brutal-", "")})`
}
},
IconComponent && iconPosition === "left" && /* @__PURE__ */ React35__default.default.createElement(chunk7YHHVG7W_js.Icon, { icon: IconComponent, size: iconSizes[size] }),
/* @__PURE__ */ React35__default.default.createElement("span", { className: "select-none" }, children),
IconComponent && iconPosition === "right" && !onRemove && /* @__PURE__ */ React35__default.default.createElement(chunk7YHHVG7W_js.Icon, { icon: IconComponent, size: iconSizes[size] }),
onRemove && /* @__PURE__ */ React35__default.default.createElement(
"button",
{
onClick: (e) => {
e.stopPropagation();
onRemove();
},
className: chunk7T4KDGYW_js.cn(
"ml-1 -mr-1 p-0.5 transition-colors duration-200",
"hover:bg-brutal-black/10 rounded",
brutal && "hover:bg-accent/20"
),
"aria-label": "Remove chip"
},
/* @__PURE__ */ React35__default.default.createElement(chunk7YHHVG7W_js.Icon, { icon: fa.FaTimes, size: "xs" })
)
);
};
var ChipGroup = ({
children,
maxItems,
showMore = false,
onShowMore,
wrap = true,
spacing = "normal",
brutal = true,
className
}) => {
const spacingClasses = {
tight: "gap-1",
normal: "gap-2",
loose: "gap-3"
};
return /* @__PURE__ */ React35__default.default.createElement(
"div",
{
className: chunk7T4KDGYW_js.cn(
// Base layout
"flex items-center",
wrap ? "flex-wrap" : "flex-nowrap overflow-x-auto",
spacingClasses[spacing],
// Brutal styling
brutal && "relative",
className
)
},
children
);
};
var ChipGroupSkeleton = ({
count = 3,
brutal = true,
className
}) => /* @__PURE__ */ React35__default.default.createElement("div", { className: chunk7T4KDGYW_js.cn("flex flex-wrap gap-2 animate-pulse", className) }, Array.from({ length: count }).map((_, i) => /* @__PURE__ */ React35__default.default.createElement(
"div",
{
key: i,
className: chunk7T4KDGYW_js.cn(
"h-6 bg-brutal-gray-300 rounded",
brutal && "border-2 border-brutal-gray-400",
// Random widths for variety
i % 3 === 0 && "w-16",
i % 3 === 1 && "w-20",
i % 3 === 2 && "w-12"
)
}
)));
// src/components/core/Chip/TechChip.tsx
var TechChip = ({
name,
icon,
color,
size = "sm",
onClick,
brutal = true,
showIcon = true,
accentColor = "brutal-pink",
className
}) => {
const [rotation, setRotation] = React35.useState(0);
const [skew, setSkew] = React35.useState(0);
const [mounted, setMounted] = React35.useState(false);
React35.useEffect(() => {
if (brutal) {
setRotation(Math.random() * 4 - 2);
setSkew(Math.random() * 4 - 2);
}
setMounted(true);
}, [brutal]);
const normalizedName = chunkR74LWSGE_js.normalizeTechName(name);
let IconComponent = null;
if (showIcon) {
if (typeof icon === "string") {
IconComponent = chunkR74LWSGE_js.getTechIcon(icon);
} else if (typeof icon === "function") {
IconComponent = icon;
} else {
IconComponent = chunkR74LWSGE_js.getTechIcon(
`Si${normalizedName.replace(/[^a-zA-Z]/g, "")}`
);
}
}
const chipStyle = brutal && mounted ? {
transform: `rotate(${rotation}deg) skewX(${skew}deg)`,
transition: "transform 0.3s ease"
} : void 0;
const handleMouseEnter = (e) => {
if (brutal && mounted && e.currentTarget instanceof HTMLElement) {
e.currentTarget.style.transform = "rotate(0deg) skewX(0deg) scale(1.05)";
}
};
const handleMouseLeave = (e) => {
if (brutal && mounted && e.currentTarget instanceof HTMLElement) {
e.currentTarget.style.transform = `rotate(${rotation}deg) skewX(${skew}deg) scale(1)`;
}
};
return /* @__PURE__ */ React35__default.default.createElement(
"div",
{
style: chipStyle,
onMouseEnter: handleMouseEnter,
onMouseLeave: handleMouseLeave,
className: chunk7T4KDGYW_js.cn("inline-block", brutal && "hover:z-10 relative", className)
},
/* @__PURE__ */ React35__default.default.createElement(
Chip,
{
variant: "secondary",
size,
icon: IconComponent || void 0,
onClick,
brutal,
accentColor,
className: chunk7T4KDGYW_js.cn(
color,
brutal && [
"hover:shadow-brutal-md cursor-default select-none",
"transform-gpu will-change-transform"
],
!brutal && "hover:scale-105"
)
},
normalizedName
)
);
};
var TechChipGroup = ({
technologies,
size = "sm",
maxItems,
brutal = true,
showIcons = true,
accentColor = "brutal-pink",
onChipClick,
className
}) => {
const [showAll, setShowAll] = React35.useState(false);
const visibleTechs = showAll || !maxItems ? technologies : technologies.slice(0, maxItems);
const hasMore = !showAll && !!maxItems && technologies.length > maxItems;
return /* @__PURE__ */ React35__default.default.createElement(
ChipGroup,
{
maxItems: showAll ? void 0 : maxItems,
showMore: hasMore,
onShowMore: () => setShowAll(true),
brutal,
className
},
visibleTechs.map((tech, index) => /* @__PURE__ */ React35__default.default.createElement(
TechChip,
{
key: `${tech.name}-${index}`,
name: tech.name,
icon: tech.icon,
color: tech.color,
size,
onClick: onChipClick ? () => onChipClick(tech.name) : void 0,
brutal,
showIcon: showIcons,
accentColor
}
)),
hasMore && /* @__PURE__ */ React35__default.default.createElement(
Chip,
{
variant: "ghost",
size,
onClick: () => setShowAll(true),
brutal,
accentColor,
className: "opacity-70 hover:opacity-100"
},
"+",
technologies.length - maxItems,
" more"
)
);
};
var LoadingSpinner = ({
size = "md",
className
}) => {
const sizeClasses = {
xs: "w-3 h-3",
sm: "w-4 h-4",
md: "w-5 h-5",
lg: "w-6 h-6",
xl: "w-8 h-8"
};
return /* @__PURE__ */ React35__default.default.createElement(
"svg",
{
className: clsx.clsx("animate-spin", sizeClasses[size], className),
xmlns: "http://www.w3.org/2000/svg",
fill: "none",
viewBox: "0 0 24 24"
},
/* @__PURE__ */ React35__default.default.createElement(
"circle",
{
className: "opacity-25",
cx: "12",
cy: "12",
r: "10",
stroke: "currentColor",
strokeWidth: "4"
}
),
/* @__PURE__ */ React35__default.default.createElement(
"path",
{
className: "opacity-75",
fill: "currentColor",
d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
}
)
);
};
var Modal = ({
isOpen,
onClose,
title,
children,
footer,
size = "md",
variant = "default",
brutal = true,
closeOnBackdrop = true,
closeOnEscape = true,
animated = true,
accentColor = "brutal-pink",
className,
...props
}) => {
const modalRef = React35.useRef(null);
const previousActiveElement = React35.useRef(null);
const handleEscape = React35.useCallback(
(e) => {
if (e.key === "Escape" && closeOnEscape && isOpen) {
onClose();
}
},
[closeOnEscape, isOpen, onClose]
);
const handleBackdropClick = React35.useCallback(
(e) => {
if (closeOnBackdrop && e.target === e.currentTarget) {
onClose();
}
},
[closeOnBackdrop, onClose]
);
React35.useEffect(() => {
if (isOpen) {
previousActiveElement.current = document.activeElement;
setTimeout(() => {
modalRef.current?.focus();
}, 100);
document.body.style.overflow = "hidden";
document.addEventListener("keydown", handleEscape);
} else {
if (previousActiveElement.current) {
previousActiveElement.current.focus();
}
document.body.style.overflow = "unset";
document.removeEventListener("keydown", handleEscape);
}
return () => {
document.removeEventListener("keydown", handleEscape);
document.body.style.overflow = "unset";
};
}, [isOpen, handleEscape]);
if (!isOpen) return null;
const sizeClasses = {
xs: "max-w-xs",
sm: "max-w-sm",
md: "max-w-md",
lg: "max-w-2xl",
xl: "max-w-4xl",
"2xl": "max-w-6xl",
full: "max-w-[95vw]"
};
const getVariantClasses = () => {
const variants = {
default: "bg-brutal-white",
success: "bg-brutal-mint/10 border-brutal-mint",
warning: "bg-brutal-yellow/10 border-brutal-yellow",
danger: "bg-brutal-coral/10 border-brutal-coral",
info: "bg-brutal-sky/10 border-brutal-sky"
};
return variants[variant];
};
return /* @__PURE__ */ React35__default.default.createElement(React35__default.default.Fragment, null, /* @__PURE__ */ React35__default.default.createElement(
"div",
{
className: chunk7T4KDGYW_js.cn(
"fixed inset-0 z-40 bg-brutal-black/50",
animated && [
"transition-opacity duration-300",
isOpen ? "opacity-100" : "opacity-0"
]
),
onClick: handleBackdropClick,
"aria-hidden": "true"
}
), /* @__PURE__ */ React35__default.default.createElement(
"div",
{
className: "fixed inset-0 z-50 flex items-center justify-center p-4 overflow-y-auto",
role: "dialog",
"aria-modal": "true",
"aria-labelledby": "modal-title",
"aria-describedby": "modal-description"
},
/* @__PURE__ */ React35__default.default.createElement(
"div",
{
ref: modalRef,
className: chunk7T4KDGYW_js.cn(
// Base styling
"w-full max-h-[90vh] flex flex-col relative",
sizeClasses[size],
getVariantClasses(),
// Brutal styling
brutal && [
"border-4 border-brutal-black shadow-brutal-lg",
animated && [
"transition-all duration-300 transform",
"animate-scale-in",
"hover:shadow-brutal-xl"
]
],
!brutal && [
"rounded-lg shadow-2xl border",
animated && "transition-all duration-300 transform animate-scale-in"
],
// Focus styles
"focus:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2",
className
),
style: {
"--accent-color": accentColor.startsWith("#") ? accentColor : `var(--brutal-${accentColor.replace("brutal-", "")})`
},
tabIndex: -1,
...props
},
/* @__PURE__ */ React35__default.default.createElement(
"div",
{
className: chunk7T4KDGYW_js.cn(
"flex items-center justify-between p-6 flex-shrink-0",
brutal && [
"border-b-4 border-brutal-black bg-brutal-black text-brutal-white"
],
!brutal && ["border-b bg-brutal-gray-50 rounded-t-lg"]
)
},
/* @__PURE__ */ React35__default.default.createElement(
"h2",
{
id: "modal-title",
className: chunk7T4KDGYW_js.cn(
"font-black uppercase tracking-wider flex-1 min-w-0 truncate",
brutal ? "text-brutal-white" : "text-brutal-black",
size === "xs" ? "text-lg" : size === "sm" ? "text-xl" : "text-2xl"
)
},
title
),
/* @__PURE__ */ React35__default.default.createElement(
"button",
{
onClick: onClose,
className: chunk7T4KDGYW_js.cn(
"p-2 ml-4 flex-shrink-0 transition-all duration-200",
brutal ? [
"hover:bg-brutal-white hover:text-brutal-black",
"border-2 border-transparent hover:border-brutal-white"
] : [
"hover:bg-brutal-gray-200 rounded-md",
"text-brutal-gray-500 hover:text-brutal-black"
],
"focus:outline-none focus-visible:ring-2 focus-visible:ring-accent"
),
"aria-label": "Close modal",
type: "button"
},
/* @__PURE__ */ React35__default.default.createElement(chunk7YHHVG7W_js.Icon, { icon: fa.FaTimes, size: "md", brutal: brutal && animated })
)
),
/* @__PURE__ */ React35__default.default.createElement(
"div",
{
id: "modal-description",
className: chunk7T4KDGYW_js.cn(
"flex-1 overflow-y-auto p-6",
"scrollbar-thin scrollbar-track-transparent scrollbar-thumb-brutal-gray-300"
)
},
children
),
footer && /* @__PURE__ */ React35__default.default.createElement(
"div",
{
className: chunk7T4KDGYW_js.cn(
"p-4 flex-shrink-0",
brutal && "border-t-4 border-brutal-black bg-brutal-gray-50",
!brutal && "border-t bg-brutal-gray-50 rounded-b-lg"
)
},
footer
),
brutal && animated && /* @__PURE__ */ React35__default.default.createElement("div", { className: "absolute top-0 right-0 w-8 h-8 bg-accent opacity-20 -mr-4 -mt-4 rotate-45"