@frank-auth/react
Version:
Flexible and customizable React UI components for Frank Authentication
322 lines (321 loc) • 10.7 kB
JavaScript
const p = (n) => {
const t = Number.parseInt(n.slice(1, 3), 16) / 255, e = Number.parseInt(n.slice(3, 5), 16) / 255, r = Number.parseInt(n.slice(5, 7), 16) / 255, c = Math.max(t, e, r), o = Math.min(t, e, r);
let i = 0, f = 0;
const a = (c + o) / 2;
if (c === o)
i = f = 0;
else {
const s = c - o;
switch (f = a > 0.5 ? s / (2 - c - o) : s / (c + o), c) {
case t:
i = (e - r) / s + (e < r ? 6 : 0);
break;
case e:
i = (r - t) / s + 2;
break;
case r:
i = (t - e) / s + 4;
break;
}
i /= 6;
}
return [Math.round(i * 360), Math.round(f * 100), Math.round(a * 100)];
}, d = (n, t, e) => {
n /= 360, t /= 100, e /= 100;
const r = (a, s, l) => (l < 0 && (l += 1), l > 1 && (l -= 1), l < 1 / 6 ? a + (s - a) * 6 * l : l < 1 / 2 ? s : l < 2 / 3 ? a + (s - a) * (2 / 3 - l) * 6 : a);
let c, o, i;
if (t === 0)
c = o = i = e;
else {
const a = e < 0.5 ? e * (1 + t) : e + t - e * t, s = 2 * e - a;
c = r(s, a, n + 0.3333333333333333), o = r(s, a, n), i = r(s, a, n - 0.3333333333333333);
}
const f = (a) => {
const s = Math.round(a * 255).toString(16);
return s.length === 1 ? "0" + s : s;
};
return `#${f(c)}${f(o)}${f(i)}`;
}, $ = (n, t) => {
const [e, r, c] = p(n), o = Math.max(0, Math.min(100, c + t));
return d(e, r, o);
}, k = (n, t) => {
const [e, r, c] = p(n), o = Math.max(0, Math.min(100, r + t));
return d(e, o, c);
}, w = (n, t) => {
const [e, r, c] = p(n), o = (e + t + 360) % 360;
return d(o, r, c);
}, y = (n, t) => {
const e = (f) => {
const a = Number.parseInt(f.slice(1, 3), 16) / 255, s = Number.parseInt(f.slice(3, 5), 16) / 255, l = Number.parseInt(f.slice(5, 7), 16) / 255, g = (u) => u <= 0.03928 ? u / 12.92 : Math.pow((u + 0.055) / 1.055, 2.4);
return 0.2126 * g(a) + 0.7152 * g(s) + 0.0722 * g(l);
}, r = e(n), c = e(t), o = Math.max(r, c), i = Math.min(r, c);
return (o + 0.05) / (i + 0.05);
}, b = (n, t, e = "AA") => {
const r = y(n, t);
return e === "AA" ? r >= 4.5 : r >= 7;
}, m = (n, t, e = "AA") => {
const r = n, [c, o, i] = p(r);
for (let s = 0; s <= 50; s += 5) {
const l = Math.min(100, i + s), g = d(c, o, l);
if (b(g, t, e))
return g;
const u = Math.max(0, i - s), M = d(c, o, u);
if (b(M, t, e))
return M;
}
const f = "#ffffff", a = "#000000";
return b(f, t, e) ? f : b(a, t, e) ? a : n;
}, h = (n) => {
const [t, e, r] = p(n);
return {
50: d(t, Math.max(10, e - 40), Math.min(95, r + 40)),
100: d(t, Math.max(20, e - 30), Math.min(90, r + 35)),
200: d(t, Math.max(30, e - 20), Math.min(85, r + 25)),
300: d(t, Math.max(40, e - 10), Math.min(75, r + 15)),
400: d(t, e, Math.min(65, r + 5)),
500: n,
600: d(t, Math.min(100, e + 10), Math.max(35, r - 5)),
700: d(t, Math.min(100, e + 15), Math.max(25, r - 15)),
800: d(t, Math.min(100, e + 20), Math.max(15, r - 25)),
900: d(t, Math.min(100, e + 25), Math.max(10, r - 35)),
950: d(t, Math.min(100, e + 30), Math.max(5, r - 45)),
DEFAULT: n,
foreground: m(n, "#ffffff")
};
}, F = (n, t = "light") => ({
success: t === "light" ? "#22c55e" : "#16a34a",
warning: t === "light" ? "#f59e0b" : "#d97706",
danger: t === "light" ? "#ef4444" : "#dc2626",
info: t === "light" ? "#3b82f6" : "#2563eb"
}), v = () => typeof window > "u" ? "light" : window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light", x = (n) => {
if (typeof window > "u") return () => {
};
const t = window.matchMedia("(prefers-color-scheme: dark)"), e = (r) => {
n(r.matches ? "dark" : "light");
};
return t.addEventListener("change", e), () => {
t.removeEventListener("change", e);
};
}, A = (n) => n === "system" ? v() : n, E = (n) => {
const t = {};
return Object.entries(n.colors).forEach(([e, r]) => {
t[`--frank-${e.replace(/([A-Z])/g, "-$1").toLowerCase()}`] = r;
}), Object.entries(n.spacing).forEach(([e, r]) => {
t[`--frank-spacing-${e}`] = r;
}), Object.entries(n.borderRadius).forEach(([e, r]) => {
t[`--frank-radius-${e}`] = r;
}), Object.entries(n.shadows).forEach(([e, r]) => {
t[`--frank-shadow-${e}`] = r;
}), Object.entries(n.typography.fontSize).forEach(([e, [r, c]]) => {
t[`--frank-text-${e}`] = r, t[`--frank-text-${e}-line-height`] = c.lineHeight, c.letterSpacing && (t[`--frank-text-${e}-letter-spacing`] = c.letterSpacing);
}), Object.entries(n.typography.fontWeight).forEach(([e, r]) => {
t[`--frank-font-${e}`] = r;
}), n.cssVariables && Object.entries(n.cssVariables).forEach(([e, r]) => {
t[e.startsWith("--") ? e : `--${e}`] = r;
}), t;
}, T = (n, t) => {
const e = t || document.documentElement;
Object.entries(n).forEach(([r, c]) => {
e.style.setProperty(r, c);
});
}, j = (n, t) => {
const e = t || document.documentElement;
n.forEach((r) => {
e.style.removeProperty(r);
});
}, C = (n) => {
const { primaryColor: t, secondaryColor: e, mode: r, colorHarmony: c } = n, o = h(t), i = h(e || w(t, 30)), a = h(r === "light" ? "#6b7280" : "#9ca3af"), s = F(t, r), l = {
background: r === "light" ? "#ffffff" : "#0f172a",
foreground: r === "light" ? "#0f172a" : "#f8fafc",
content1: r === "light" ? "#ffffff" : "#18181b",
content2: r === "light" ? "#f4f4f5" : "#27272a",
content3: r === "light" ? "#e4e4e7" : "#3f3f46",
content4: r === "light" ? "#d4d4d8" : "#52525b",
primary: o,
primaryForeground: m("#ffffff", o[500]),
secondary: i,
secondaryForeground: m("#ffffff", i[500]),
accent: o[600],
accentForeground: m("#ffffff", o[600]),
muted: a[100],
mutedForeground: a[600],
border: r === "light" ? a[200] : a[800],
divider: r === "light" ? a[100] : a[800],
input: r === "light" ? "#ffffff" : "#27272a",
inputForeground: r === "light" ? "#0f172a" : "#f8fafc",
focus: o[500],
focusVisible: o[500],
overlay: r === "light" ? "rgba(0, 0, 0, 0.5)" : "rgba(0, 0, 0, 0.8)",
success: s.success,
successForeground: m("#ffffff", s.success),
warning: s.warning,
warningForeground: m("#ffffff", s.warning),
danger: s.danger,
dangerForeground: m("#ffffff", s.danger),
info: s.info,
infoForeground: m("#ffffff", s.info),
selection: o[100],
selectionForeground: o[900],
disabled: a[300],
disabledForeground: a[500],
card: "",
cardForeground: "",
popover: "",
popoverForeground: "",
ring: "",
destructive: "",
destructiveForeground: ""
}, g = {
primary: o,
secondary: i,
neutral: a,
success: h(s.success),
warning: h(s.warning),
danger: h(s.danger),
info: h(s.info)
};
return {
name: `Generated ${r} theme`,
mode: r,
colors: l,
palette: g
};
}, L = (n) => {
const t = [], e = [], r = {};
if (!n.colors)
return t.push("Theme must have colors defined"), {
valid: !1,
errors: t,
warnings: e,
accessibility: {
contrastRatios: r,
wcagLevel: "fail"
}
};
const c = ["background", "foreground", "primary", "primaryForeground"];
for (const a of c)
a in n.colors || t.push(`Missing required color: ${a}`);
const o = (a, s) => {
const l = n.colors[a], g = n.colors[s];
if (l && g) {
const u = y(typeof l == "string" ? l : l.DEFAULT, typeof g == "string" ? g : g.DEFAULT);
r[`${a}/${s}`] = u, u < 4.5 && e.push(`Low contrast ratio for ${a} on ${s}: ${u.toFixed(2)}`);
}
};
o("foreground", "background"), o("primaryForeground", "primary"), o("secondaryForeground", "secondary"), o("successForeground", "success"), o("warningForeground", "warning"), o("dangerForeground", "danger"), o("infoForeground", "info");
const i = Math.min(...Object.values(r));
let f;
return i >= 7 ? f = "AAA" : i >= 4.5 ? f = "AA" : i >= 3 ? f = "A" : f = "fail", {
valid: t.length === 0,
errors: t,
warnings: e,
accessibility: {
contrastRatios: r,
wcagLevel: f
}
};
}, V = (n, t) => {
const e = { ...n };
return t.colors && (e.colors = { ...e.colors, ...t.colors }), t.typography && (e.typography = {
...e.typography,
...t.typography
}), t.spacing && (e.spacing = { ...e.spacing, ...t.spacing }), t.components && (e.components = {
...e.components,
...t.components
}), t.cssVariables && (e.cssVariables = {
...e.cssVariables,
...t.cssVariables
}), t.custom && (e.custom = {
...e.custom,
...t.custom
}), e;
}, S = (n, t, e, r = "default") => {
const c = n.components[t];
if (!c) return;
const o = c[e];
if (!(!o || typeof o != "object"))
return o;
}, I = (n, t, e, r = "default", c = "md") => {
const o = S(n, t, e, r);
if (!o) return "";
let i = o.base || "";
const f = o.colors?.[r];
f && (i += ` ${f.background || ""} ${f.foreground || ""} ${f.border || ""}`);
const a = o.sizes?.[c];
return a && (i += ` ${a}`), i.trim();
}, O = (n, t) => {
if (!(typeof window > "u"))
try {
localStorage.setItem("frank-auth-theme", n), localStorage.setItem("frank-auth-theme-mode", t);
} catch {
}
}, H = () => {
if (typeof window > "u") return {};
try {
const n = localStorage.getItem("frank-auth-theme"), t = localStorage.getItem("frank-auth-theme-mode");
return {
theme: n || void 0,
mode: t || void 0
};
} catch {
return {};
}
}, N = {
// Color utilities
hexToHsl: p,
hslToHex: d,
adjustBrightness: $,
adjustSaturation: k,
adjustHue: w,
getContrastRatio: y,
isValidContrast: b,
findAccessibleColor: m,
// Palette generation
generateColorPalette: h,
generateSemanticColors: F,
// Theme mode
getSystemTheme: v,
watchSystemTheme: x,
resolveThemeMode: A,
// CSS variables
generateCSSVariables: E,
applyCSSVariables: T,
removeCSSVariables: j,
// Theme generation
generateTheme: C,
validateTheme: L,
mergeThemes: V,
// Component utilities
getComponentVariant: S,
getComponentStyles: I,
// Storage
saveThemeToStorage: O,
loadThemeFromStorage: H
};
export {
N as ThemeUtils,
$ as adjustBrightness,
w as adjustHue,
k as adjustSaturation,
T as applyCSSVariables,
m as findAccessibleColor,
E as generateCSSVariables,
h as generateColorPalette,
F as generateSemanticColors,
C as generateTheme,
I as getComponentStyles,
S as getComponentVariant,
y as getContrastRatio,
v as getSystemTheme,
p as hexToHsl,
d as hslToHex,
b as isValidContrast,
H as loadThemeFromStorage,
V as mergeThemes,
j as removeCSSVariables,
A as resolveThemeMode,
O as saveThemeToStorage,
L as validateTheme,
x as watchSystemTheme
};
//# sourceMappingURL=theme.js.map