main
js 115 lines 4.62 KB
Raw
1 /**
2 * MAPS Connect - Personalización del color de la página
3 * Permite elegir un color de acento (preestablecido o a medida) que se
4 * aplica en toda la aplicación (sidebar, topbar, botones, enlaces,
5 * tarjetas, chat...). Guarda la preferencia en localStorage y la aplica
6 * como atributo "data-color" + variables --accent-* sobre <html>;
7 * frontend/css/colores.css se encarga de mapearlas a las variables
8 * existentes del tema (claro y oscuro).
9 */
10 const Colores = {
11 KEY: 'color-maps',
12
13 PRESETS: [
14 { id: 'verde', hex: '#087527', nombre: 'Verde' },
15 { id: 'rosa', hex: '#e1367f', nombre: 'Rosa' },
16 { id: 'azul', hex: '#0a6ab3', nombre: 'Azul' },
17 { id: 'morado', hex: '#7c3aed', nombre: 'Morado' },
18 { id: 'naranja', hex: '#ea580c', nombre: 'Naranja' },
19 { id: 'teal', hex: '#0d9488', nombre: 'Teal' }
20 ],
21
22 guardado() {
23 try { return localStorage.getItem(this.KEY); } catch (e) { return null; }
24 },
25
26 limpiar() {
27 const r = document.documentElement;
28 r.removeAttribute('data-color');
29 const vars = ['--accent', '--accent-dark', '--accent-mid', '--accent-bright',
30 '--accent-deep', '--accent-soft', '--accent-soft-border', '--accent-contrast'];
31 for (const k of vars) r.style.removeProperty(k);
32 try { localStorage.removeItem(this.KEY); } catch (e) { /* almacenamiento no disponible */ }
33 },
34
35 /* --- Utilidades de color (HSL) --- */
36 _alHsl(hex) {
37 hex = hex.replace('#', '');
38 const r = parseInt(hex.slice(0, 2), 16) / 255;
39 const g = parseInt(hex.slice(2, 4), 16) / 255;
40 const b = parseInt(hex.slice(4, 6), 16) / 255;
41 const max = Math.max(r, g, b), min = Math.min(r, g, b);
42 let h = 0, s = 0;
43 const l = (max + min) / 2;
44 if (max !== min) {
45 const d = max - min;
46 s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
47 switch (max) {
48 case r: h = (g - b) / d + (g < b ? 6 : 0); break;
49 case g: h = (b - r) / d + 2; break;
50 default: h = (r - g) / d + 4;
51 }
52 h *= 60;
53 }
54 return { h, s, l };
55 },
56
57 _aHex({ h, s, l }) {
58 h = ((h % 360) + 360) % 360;
59 const c = (1 - Math.abs(2 * l - 1)) * s;
60 const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
61 const m = l - c / 2;
62 let r = 0, g = 0, b = 0;
63 if (h < 60) { r = c; g = x; }
64 else if (h < 120) { r = x; g = c; }
65 else if (h < 180) { g = c; b = x; }
66 else if (h < 240) { g = x; b = c; }
67 else if (h < 300) { r = x; b = c; }
68 else { r = c; b = x; }
69 const to = (v) => Math.round((v + m) * 255).toString(16).padStart(2, '0');
70 return '#' + to(r) + to(g) + to(b);
71 },
72
73 _conLuz({ h, s, l }, nl) { return this._aHex({ h, s, l: nl }); },
74
75 /* --- Aplicación --- */
76
77 /** Aplica el color guardado; si no hay, restaura el color por defecto. */
78 aplicar() {
79 const hex = this.guardado();
80 if (!hex) { this.limpiar(); return; }
81 this.set(hex, false);
82 },
83
84 /** Aplica un color de acento y (por defecto) lo guarda como preferencia.
85 * Calcula toda la paleta derivada (oscuros, claros, tintes) y la aplica
86 * como variables CSS, de modo que toda la página se "sintoniza". */
87 set(hex, guardar = true) {
88 if (!/^#[0-9a-f]{6}$/i.test(hex)) return;
89 const base = this._alHsl(hex);
90 const clampL = (l) => Math.min(Math.max(l, 0), 1);
91 const accentDark = clampL(Math.max(base.l * 0.62, 0.3));
92 const midL = clampL(Math.max(Math.min(base.l + 0.14, 0.52), 0.38));
93 const brightL = clampL(Math.max(Math.min(base.l + 0.3, 0.68), 0.5));
94 const deepL = clampL(Math.max(Math.min(base.l * 0.55, 0.32), 0.16));
95 const vars = {
96 '--accent': hex,
97 '--accent-soft': this._conLuz(base, 0.93),
98 '--accent-soft-border': this._conLuz(base, 0.8),
99 '--accent-dark': this._conLuz(base, accentDark),
100 '--accent-mid': this._conLuz(base, midL),
101 '--accent-bright': this._conLuz(base, brightL),
102 '--accent-deep': this._conLuz(base, deepL),
103 '--accent-contrast': base.l > 0.62 ? '#1f2937' : '#ffffff'
104 };
105 const r = document.documentElement;
106 r.setAttribute('data-color', 'personalizado');
107 for (const k in vars) r.style.setProperty(k, vars[k]);
108 if (guardar) {
109 try { localStorage.setItem(this.KEY, hex); } catch (e) { /* almacenamiento no disponible */ }
110 }
111 }
112 };
113
114 /* Se aplica en cuanto se carga el script, igual que Tema (evita parpadeo). */
115 Colores.aplicar();