/ src / lib / stores.ts
stores.ts
  1  import { writable } from 'svelte/store';
  2  import { browser } from '$app/environment';
  3  
  4  function getInitialTheme() {
  5    if (browser) {
  6      const saved = localStorage.getItem('theme');
  7      const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
  8      return saved ? saved === 'dark' : systemPrefersDark;
  9    }
 10    return false;
 11  }
 12  
 13  export const darkMode = writable(getInitialTheme());
 14  
 15  function getInitialPrimaryColor() {
 16    if (browser) {
 17      return localStorage.getItem('primaryColor') || '#007bff';
 18    }
 19    return '#007bff';
 20  }
 21  
 22  export const primaryColor = writable(getInitialPrimaryColor());
 23  
 24  // Store para los parámetros de búsqueda
 25  export const searchParams = writable({
 26    query: '',
 27    type: 'web',
 28    language: 'all'
 29  });
 30  
 31  // Store para los mapas
 32  export const mapStore = writable({
 33    center: { lat: 40.416775, lng: -3.703790 }, // Madrid por defecto
 34    zoom: 13,
 35    markers: [],
 36    offline: true
 37  });
 38  
 39  // Store para el modo de vista (número de columnas)
 40  function getInitialViewColumns() {
 41    if (browser) {
 42      return parseInt(localStorage.getItem('viewColumns') || '4');
 43    }
 44    return 4;
 45  }
 46  export const viewColumns = writable(getInitialViewColumns());
 47  
 48  // Store para vistas personalizadas por tipo de búsqueda
 49  function getInitialViewPreferences() {
 50    if (browser) {
 51      const savedPrefs = localStorage.getItem('viewPreferences');
 52      if (savedPrefs) {
 53        try {
 54          return JSON.parse(savedPrefs);
 55        } catch (e) {
 56          console.error('Error parsing view preferences:', e);
 57        }
 58      }
 59    }
 60    
 61    // Valores por defecto
 62    return {
 63      web: { columns: 1 },        // Vista lista para web
 64      images: { columns: 4 },     // Vista grid con 4 columnas para imágenes
 65      document: { columns: 1 },   // Vista lista para documentos
 66      video: { columns: 3 },      // Vista grid con 3 columnas para videos
 67      app: { columns: 3 },        // Vista grid con 3 columnas para apps
 68      maps: { columns: 1 },       // Vista lista para mapas
 69      news: { columns: 1 }        // Vista lista para noticias
 70    };
 71  }
 72  
 73  export const viewPreferences = writable(getInitialViewPreferences());
 74  
 75  if (browser) {
 76    // Persistir las preferencias de vista por tipo
 77    viewPreferences.subscribe((prefs) => {
 78      localStorage.setItem('viewPreferences', JSON.stringify(prefs));
 79    });
 80    
 81    darkMode.subscribe((value) => {
 82      if (value) {
 83        document.body.classList.add('dark');
 84      } else {
 85        document.body.classList.remove('dark');
 86      }
 87      localStorage.setItem('theme', value ? 'dark' : 'light');
 88      
 89      // Actualizar el color primario al cambiar de tema
 90      const currentColor = localStorage.getItem('primaryColor') || '#007bff';
 91        
 92      // Forzar una actualización del color primario utilizando el mismo subscriber
 93      // Esto hace que la función de suscripción de primaryColor se ejecute con los ajustes
 94      // específicos para el tema actual
 95      primaryColor.set(currentColor);
 96    });
 97  
 98    primaryColor.subscribe((color) => {
 99      // Guardar el color original en localStorage
100      localStorage.setItem('primaryColor', color);
101      
102      // Obtener valores RGB para transparencias
103      const rgbValues = hexToRgb(color);
104      
105      // Aplicar de manera diferente según el tema
106      if (document.body.classList.contains('dark')) {
107        // En modo oscuro, usar un color más brillante
108        const lighterColor = adjustColorBrightness(color, 30);
109        
110        // Aplicar directamente el color claro para modo oscuro
111        document.documentElement.style.setProperty('--primary', lighterColor);
112        document.documentElement.style.setProperty('--primary-dark', adjustColorBrightness(lighterColor, -10));
113        document.documentElement.style.setProperty('--primary-light', adjustColorBrightness(lighterColor, 10));
114        document.documentElement.style.setProperty('--link-color', lighterColor);
115        document.documentElement.style.setProperty('--shadow-color', hexToRgba(lighterColor, 0.3));
116        
117        // Aplicar también valores RGB
118        const lighterRgbValues = hexToRgb(lighterColor);
119        if (lighterRgbValues) {
120          document.documentElement.style.setProperty('--primary-rgb', lighterRgbValues);
121        }
122      } else {
123        // En modo claro, usar el color original
124        document.documentElement.style.setProperty('--primary', color);
125        document.documentElement.style.setProperty('--primary-dark', adjustColorBrightness(color, -10));
126        document.documentElement.style.setProperty('--primary-light', adjustColorBrightness(color, 10));
127        document.documentElement.style.setProperty('--link-color', color);
128        document.documentElement.style.setProperty('--shadow-color', hexToRgba(color, 0.3));
129        
130        // Aplicar valores RGB para modo claro
131        if (rgbValues) {
132          document.documentElement.style.setProperty('--primary-rgb', rgbValues);
133        }
134      }
135    });
136  
137    viewColumns.subscribe((cols) => {
138      localStorage.setItem('viewColumns', cols.toString());
139    });
140  }
141  
142  function hexToRgba(hex: string, alpha: number) {
143    let r = 0, g = 0, b = 0;
144    if (hex.length === 4) {
145      r = parseInt(hex[1] + hex[1], 16);
146      g = parseInt(hex[2] + hex[2], 16);
147      b = parseInt(hex[3] + hex[3], 16);
148    } else if (hex.length === 7) {
149      r = parseInt(hex.slice(1, 3), 16);
150      g = parseInt(hex.slice(3, 5), 16);
151      b = parseInt(hex.slice(5, 7), 16);
152    }
153    return `rgba(${r}, ${g}, ${b}, ${alpha})`;
154  }
155  
156  // Función para convertir color hex a formato RGB (como string)
157  function hexToRgb(hex: string): string | null {
158    // Eliminar # si existe
159    hex = hex.replace(/^#/, '');
160    
161    // Valores RGB
162    let r, g, b;
163    
164    // Expandir shorthand (3 dígitos) a forma completa (6 dígitos)
165    if (hex.length === 3) {
166      r = parseInt(hex[0] + hex[0], 16);
167      g = parseInt(hex[1] + hex[1], 16);
168      b = parseInt(hex[2] + hex[2], 16);
169    } else if (hex.length === 6) {
170      r = parseInt(hex.slice(0, 2), 16);
171      g = parseInt(hex.slice(2, 4), 16);
172      b = parseInt(hex.slice(4, 6), 16);
173    } else {
174      return null;
175    }
176    
177    // Comprobar si los valores son válidos
178    if (isNaN(r) || isNaN(g) || isNaN(b)) {
179      return null;
180    }
181    
182    // Devolver valores como string para usar en CSS
183    return `${r}, ${g}, ${b}`;
184  }
185  
186  // Función para ajustar el brillo de un color hex
187  function adjustColorBrightness(hex: string, percent: number): string {
188    // Eliminar # si existe
189    hex = hex.replace(/^#/, '');
190    
191    // Expandir shorthand (3 dígitos) a forma completa (6 dígitos)
192    if (hex.length === 3) {
193      hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
194    }
195    
196    // Convertir a RGB
197    const r = parseInt(hex.slice(0, 2), 16);
198    const g = parseInt(hex.slice(2, 4), 16);
199    const b = parseInt(hex.slice(4, 6), 16);
200    
201    // Ajustar brillo
202    const adjustR = Math.max(0, Math.min(255, r + (percent * 2.55)));
203    const adjustG = Math.max(0, Math.min(255, g + (percent * 2.55)));
204    const adjustB = Math.max(0, Math.min(255, b + (percent * 2.55)));
205    
206    // Convertir de vuelta a hex
207    const rHex = Math.round(adjustR).toString(16).padStart(2, '0');
208    const gHex = Math.round(adjustG).toString(16).padStart(2, '0');
209    const bHex = Math.round(adjustB).toString(16).padStart(2, '0');
210    
211    return `#${rHex}${gHex}${bHex}`;
212  }