Initialer Laravel Commit für BetiX
This commit is contained in:
129
resources/js/composables/usePrimaryColor.ts
Normal file
129
resources/js/composables/usePrimaryColor.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
import { ref, onMounted } from 'vue';
|
||||
|
||||
export type UsePrimaryColorReturn = {
|
||||
primaryColor: ReturnType<typeof ref<string>>;
|
||||
updatePrimaryColor: (value: string) => void;
|
||||
};
|
||||
|
||||
const STORAGE_KEY = 'primaryColor';
|
||||
|
||||
function setCookie(name: string, value: string, days = 365) {
|
||||
if (typeof document === 'undefined') return;
|
||||
const maxAge = days * 24 * 60 * 60;
|
||||
document.cookie = `${name}=${value};path=/;max-age=${maxAge};SameSite=Lax`;
|
||||
}
|
||||
|
||||
function getStoredPrimaryColor(): string | null {
|
||||
if (typeof window === 'undefined') return null;
|
||||
return localStorage.getItem(STORAGE_KEY);
|
||||
}
|
||||
|
||||
function parseColorToRGB(color: string): { r: number; g: number; b: number } | null {
|
||||
// Supports #rgb, #rrggbb, rgb(), hsl()
|
||||
color = color.trim().toLowerCase();
|
||||
|
||||
// Hex
|
||||
if (color.startsWith('#')) {
|
||||
let hex = color.slice(1);
|
||||
if (hex.length === 3) {
|
||||
hex = hex.split('').map((c) => c + c).join('');
|
||||
}
|
||||
if (hex.length === 6) {
|
||||
const num = parseInt(hex, 16);
|
||||
return { r: (num >> 16) & 255, g: (num >> 8) & 255, b: num & 255 };
|
||||
}
|
||||
}
|
||||
|
||||
// rgb/rgba
|
||||
if (color.startsWith('rgb')) {
|
||||
const m = color.match(/rgba?\(([^)]+)\)/);
|
||||
if (m) {
|
||||
const [r, g, b] = m[1]
|
||||
.split(',')
|
||||
.slice(0, 3)
|
||||
.map((v) => parseFloat(v.trim()));
|
||||
return { r, g, b };
|
||||
}
|
||||
}
|
||||
|
||||
// hsl/hsla
|
||||
if (color.startsWith('hsl')) {
|
||||
const m = color.match(/hsla?\(([^)]+)\)/);
|
||||
if (m) {
|
||||
const [hStr, sStr, lStr] = m[1].split(',').map((v) => v.trim());
|
||||
const h = parseFloat(hStr);
|
||||
const s = parseFloat(sStr) / 100;
|
||||
const l = parseFloat(lStr) / 100;
|
||||
// convert HSL to RGB
|
||||
const c = (1 - Math.abs(2 * l - 1)) * s;
|
||||
const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
|
||||
const m2 = l - c / 2;
|
||||
let r1 = 0, g1 = 0, b1 = 0;
|
||||
if (0 <= h && h < 60) [r1, g1, b1] = [c, x, 0];
|
||||
else if (60 <= h && h < 120) [r1, g1, b1] = [x, c, 0];
|
||||
else if (120 <= h && h < 180) [r1, g1, b1] = [0, c, x];
|
||||
else if (180 <= h && h < 240) [r1, g1, b1] = [0, x, c];
|
||||
else if (240 <= h && h < 300) [r1, g1, b1] = [x, 0, c];
|
||||
else [r1, g1, b1] = [c, 0, x];
|
||||
return {
|
||||
r: Math.round((r1 + m2) * 255),
|
||||
g: Math.round((g1 + m2) * 255),
|
||||
b: Math.round((b1 + m2) * 255),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function getContrastColor(bg: string): '#000000' | '#FFFFFF' {
|
||||
const rgb = parseColorToRGB(bg) || { r: 0, g: 0, b: 0 };
|
||||
// Relative luminance
|
||||
const srgb = [rgb.r, rgb.g, rgb.b].map((v) => {
|
||||
const c = v / 255;
|
||||
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
||||
});
|
||||
const L = 0.2126 * srgb[0] + 0.7152 * srgb[1] + 0.0722 * srgb[2];
|
||||
return L > 0.179 ? '#000000' : '#FFFFFF';
|
||||
}
|
||||
|
||||
export function applyPrimaryColor(value: string): void {
|
||||
if (typeof document === 'undefined') return;
|
||||
const root = document.documentElement;
|
||||
|
||||
// Apply to global primary variables used by Tailwind theme tokens
|
||||
root.style.setProperty('--primary', value);
|
||||
root.style.setProperty('--primary-foreground', getContrastColor(value));
|
||||
|
||||
// Align commonly used accent variables in user layout to the same main color
|
||||
root.style.setProperty('--magenta', value);
|
||||
root.style.setProperty('--sidebar-primary', value);
|
||||
root.style.setProperty('--sidebar-primary-foreground', getContrastColor(value));
|
||||
}
|
||||
|
||||
export function initializePrimaryColor(): void {
|
||||
const saved = getStoredPrimaryColor();
|
||||
if (saved) applyPrimaryColor(saved);
|
||||
}
|
||||
|
||||
const primaryColorRef = ref<string>(getStoredPrimaryColor() || '#ff007a');
|
||||
|
||||
export function usePrimaryColor(): UsePrimaryColorReturn {
|
||||
onMounted(() => {
|
||||
const saved = getStoredPrimaryColor();
|
||||
if (saved) {
|
||||
primaryColorRef.value = saved;
|
||||
}
|
||||
});
|
||||
|
||||
function updatePrimaryColor(value: string) {
|
||||
primaryColorRef.value = value;
|
||||
if (typeof window !== 'undefined') {
|
||||
localStorage.setItem(STORAGE_KEY, value);
|
||||
}
|
||||
setCookie(STORAGE_KEY, value);
|
||||
applyPrimaryColor(value);
|
||||
}
|
||||
|
||||
return { primaryColor: primaryColorRef, updatePrimaryColor };
|
||||
}
|
||||
Reference in New Issue
Block a user