@flanksource/clicky-ui
Version:
Flanksource Clicky UI — React component library built on shadcn/ui with light/dark and density theming.
202 lines (201 loc) • 5.43 kB
JavaScript
import { jsx } from "react/jsx-runtime";
import { cn } from "../lib/utils.js";
import { fnv1a32 } from "../lib/palette.js";
import { resolveSize } from "../lib/size.js";
import { useDensityValue } from "../hooks/use-density.js";
function hueForKey(key) {
return fnv1a32(key) % 360;
}
function resolveInitials(alt, initials, maxLetters) {
if (initials == null ? void 0 : initials.trim()) {
return initials.trim().slice(0, maxLetters).toUpperCase();
}
const cleaned = alt.replace(/^@/, "").trim();
const lastSegment = cleaned.split(/[\\/]/).filter(Boolean).at(-1) ?? cleaned;
const parts = lastSegment.replace(/[^a-zA-Z0-9]+/g, " ").trim().split(/\s+/).filter(Boolean);
const letters = parts.map((part) => {
var _a;
return ((_a = part.match(/[a-zA-Z0-9]/)) == null ? void 0 : _a[0]) ?? "";
}).filter(Boolean).slice(0, maxLetters).join("");
if (letters) {
return letters.toUpperCase();
}
const compact = cleaned.replace(/[^a-zA-Z0-9]+/g, "");
return (compact.slice(0, maxLetters) || "?").toUpperCase();
}
function fallbackTone(variant, kind, hue) {
if (kind === "group") {
switch (variant) {
case "solid":
return {
background: "#d8d4cc",
borderColor: "#d8d4cc",
color: "#5a574e"
};
case "stamp":
return {
background: "#fafaf7",
borderColor: "#8d8778",
color: "#5a574e",
shadow: "2px 2px 0 rgba(90, 87, 78, 0.08)"
};
case "mono":
return {
background: "#f5f2eb",
borderColor: "#8d8778",
color: "#5a574e"
};
case "duotone":
default:
return {
background: "#f0ede6",
borderColor: "#b8b3a7",
borderStyle: "dashed",
color: "#5a574e"
};
}
}
switch (variant) {
case "solid":
return {
background: `oklch(0.55 0.12 ${hue})`,
borderColor: `oklch(0.55 0.12 ${hue})`,
color: "#fff"
};
case "stamp":
return {
background: "#fafaf7",
borderColor: `oklch(0.42 0.15 ${hue})`,
color: `oklch(0.42 0.15 ${hue})`,
shadow: "2px 2px 0 rgba(26, 26, 26, 0.08)"
};
case "mono":
return {
background: "#fff",
borderColor: "#1a1a1a",
color: "#1a1a1a"
};
case "duotone":
default:
return {
background: `oklch(0.93 0.04 ${hue})`,
borderColor: `oklch(0.55 0.14 ${hue} / 0.25)`,
color: `oklch(0.35 0.14 ${hue})`
};
}
}
function fontSizeFor(variant, size) {
const minimum = size <= 16 ? 7 : size <= 20 ? 8 : 9;
switch (variant) {
case "stamp":
return Math.max(minimum, Math.round(size * 0.34));
case "mono":
return Math.max(minimum, Math.round(size * 0.36));
case "solid":
case "duotone":
default:
return Math.max(minimum, Math.round(size * 0.38));
}
}
function Avatar({
src,
alt,
initials,
size = "sm",
rounded,
kind = "user",
variant = "duotone",
title,
href,
colorKey,
onError,
className
}) {
const density = useDensityValue();
const px = resolveSize(size, density);
const effectiveRounded = rounded ?? (kind === "group" ? "md" : "full");
const shape = effectiveRounded === "full" ? "rounded-full" : "rounded-md";
const key = colorKey ?? alt;
const hue = hueForKey(key);
const tone = fallbackTone(variant, kind, hue);
const maxLetters = size === "xs" || size === "sm" ? 1 : 2;
const text = resolveInitials(alt, initials, maxLetters);
const base = cn(
"inline-flex shrink-0 select-none items-center justify-center overflow-hidden border",
shape
);
const frameStyle = {
width: px,
height: px,
borderColor: tone.borderColor,
borderStyle: tone.borderStyle ?? "solid"
};
if (variant === "stamp") {
frameStyle.transform = "rotate(-4deg)";
frameStyle.boxShadow = tone.shadow;
}
const weightFor = (v) => {
if (v === "stamp") return 700;
if (size === "sm") return 500;
return 600;
};
const content = src ? /* @__PURE__ */ jsx(
"img",
{
src,
alt,
title: title ?? alt,
width: px,
height: px,
"data-avatar-kind": kind,
"data-avatar-variant": variant,
"data-avatar-size": size,
className: cn(base, "bg-muted object-cover", className),
style: frameStyle,
loading: "lazy",
onError
}
) : /* @__PURE__ */ jsx(
"span",
{
role: "img",
"aria-label": alt,
"data-avatar-kind": kind,
"data-avatar-variant": variant,
"data-avatar-size": size,
className: cn(
base,
variant === "mono" || variant === "stamp" ? "font-mono" : void 0,
variant === "mono" ? "tracking-[-0.05em]" : "tracking-[-0.02em]",
className
),
style: {
...frameStyle,
background: tone.background,
color: tone.color,
fontSize: fontSizeFor(variant, px),
fontWeight: weightFor(variant)
},
title: title ?? alt,
children: text
}
);
if (href) {
return /* @__PURE__ */ jsx(
"a",
{
href,
target: "_blank",
rel: "noopener noreferrer",
className: "inline-flex shrink-0",
onClick: (e) => e.stopPropagation(),
children: content
}
);
}
return content;
}
export {
Avatar
};
//# sourceMappingURL=Avatar.js.map