@steveencues/starter-react-ui 1.0.6 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["TranslateContext","createContext","ThemeContext","useLanguage","context","useContext","useTheme","saveEncryptedPreference","value","encryptionKey","storage","getLocalStorage","encryptedValue","encryptText","storageKey","loadEncryptedPreference","decryptText","LANGUAGE_PREFERENCE_STORAGE_KEY","saveLanguagePreference","language","loadLanguagePreference","savedLanguage","availableLanguages","THEME_PREFERENCE_STORAGE_KEY","saveThemePreference","themeName","loadThemePreference","savedTheme","themes","theme","createLanguagePreferenceState","configuration","matchesLanguageConfiguration","state","restoreSavedLanguage","useRestoreLanguagePreference","isActive","defaultLanguage","resolveInitialLanguage","currentState","setPreferenceState","usePersistLanguagePreference","isLoaded","useSetLanguagePreference","useCallback","assertLanguageExists","nextLanguage","useLanguagePreference","preferenceState","useState","isCurrentConfiguration","selectedLanguage","setLanguage","resolveActiveLanguage","TranslateProvider","languages","useMemo","translations","initialLanguage","fallbackLanguage","languageEncryptionKey","preference","contextValue","useTranslationContext","currentDictionary","fallbackDictionary","t","translateMessage","key","params","resolveActiveTheme","activeTheme","findThemeByName","updateCustomThemes","currentThemes","item","customTheme","createThemeContextValue","ThemeProvider","baseThemes","customThemes","setCustomThemes","setThemeName","resolveInitialTheme","initialTheme","registerTheme","customThemeService","setTheme","selectedTheme","name","isCustomThemeDefinition","applyThemeToDocument","persistTheme","TranslationRegex","GLOBAL_REGEX_FLAG","AES_GCM_ALGORITHM","AES_GCM_IV_LENGTH","SHA_256_ALGORITHM","RAW_KEY_FORMAT","STORAGE_VALUE_SEPARATOR","KEY_USAGES","ENCRYPTED_VALUE_PART_COUNT","CSS_THEME_TOKENS","CSS_THEME_TOKEN_ENTRIES","THEME_FONT_WEIGHT","isLanguageAvailable","findFirstLanguage","firstLanguage","findTranslationValue","current","dictionary","segment","findTranslation","resolveInterpolationValue","placeholder","interpolateTranslation","placeholderRegex","message","resolveTranslationMessage","currentTranslation","configuredTheme","getCryptoApi","encodeText","decodeText","encodeBase64","binary","byte","bytes","decodeBase64","index","deriveAesKey","cryptoApi","secret","digest","createInitializationVector","decodeInitializationVector","iv","hasExpectedEncryptedParts","parts","validateEncryptedParts","parseEncryptedPayload","encodedIv","encodedCiphertext","encryptPayload","requireCryptoApi","plaintext","decryptPayload","payload","ciphertext","CustomThemeService","styleElement","declarations","token","variable","color","styleId","existingElement","Button","props","className","label"],"sources":["../src/libs/aplicacion/contexts/translate.context.ts","../src/libs/aplicacion/contexts/theme.context.ts","../src/libs/aplicacion/hooks/use-language.ts","../src/libs/aplicacion/hooks/use-theme.ts","../src/libs/infraestructura/storage/encrypted-preference.storage.ts","../src/libs/infraestructura/storage/language-preference.storage.ts","../src/libs/infraestructura/storage/theme-preference.storage.ts","../src/libs/aplicacion/providers/translate.provider.tsx","../src/libs/aplicacion/providers/theme-provider.tsx","../src/libs/domain/enums/translation-regex.enum.ts","../src/libs/domain/constants/translation.constants.ts","../src/libs/domain/constants/encrypted-preference.constanst.ts","../src/libs/domain/constants/theme-css-variable.const.ts","../src/libs/domain/constants/theme-font-weight.const.ts","../src/libs/aplicacion/utils/translation.utils.ts","../src/libs/aplicacion/utils/theme.utils.ts","../src/libs/aplicacion/utils/crypto-api.util.ts","../src/libs/aplicacion/utils/storage-api.util.ts","../src/libs/aplicacion/utils/text-encoding.util.ts","../src/libs/aplicacion/utils/base64.util.ts","../src/libs/aplicacion/services/encryption.service.ts","../src/libs/aplicacion/services/custom-theme.service.ts","../src/libs/components/Button/Button.tsx"],"sourcesContent":["import type { ILanguageContextValue } from \"@domain\";\nimport { createContext } from \"react\";\n\n/**\n * @description Contexto interno de traducciones de la libreria.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport const TranslateContext = createContext<ILanguageContextValue | null>(\n null,\n);\n","import { createContext } from \"react\";\nimport type { ThemeContextValue } from \"@domain\";\n\n/**\n * @description Contexto compartido para gestionar los temas de la aplicacion.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport const ThemeContext = createContext<ThemeContextValue | null>(null);\n","import { TranslateContext } from \"@aplicacion\";\nimport type { ILanguageContextValue } from \"@domain\";\nimport { useContext } from \"react\";\n\n/**\n * @description Obtiene el idioma actual y la funcion de traduccion.\n * Requiere que el componente este dentro de TranslateProvider.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport function useLanguage(): ILanguageContextValue {\n const context = useContext(TranslateContext);\n\n if (context === null) {\n throw new Error(\"useLanguage must be used inside TranslateProvider.\");\n }\n\n return context;\n}\n","import { ThemeContext } from \"@aplicacion\";\nimport { useContext } from \"react\";\nimport type { ThemeContextValue } from \"@domain\";\n\n/**\n * @description Permite consultar y modificar el tema activo.\n * @returns El contexto con el estado y las acciones del tema.\n * @throws Error si se utiliza fuera de ThemeProvider.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport const useTheme = (): ThemeContextValue => {\n const context = useContext(ThemeContext);\n\n if (!context) {\n throw new Error(\"useTheme must be used inside ThemeProvider.\");\n }\n\n return context;\n};\n","import { decryptText, encryptText, getLocalStorage } from \"@aplicacion\";\n\n/**\n * @description Encrypts and saves a preference in localStorage.\n * @param storageKey Storage key.\n * @param value Value to store.\n * @param encryptionKey Encryption secret.\n * @returns Promise resolved after the save attempt.\n * @author Steveen Cues\n * @version 3.0.0\n */\nexport async function saveEncryptedPreference(\n storageKey: string,\n value: string,\n encryptionKey: string,\n): Promise<void> {\n if (value.length === 0 || encryptionKey.length === 0) {\n return;\n }\n\n try {\n const storage = getLocalStorage();\n\n if (storage === undefined) {\n return;\n }\n\n const encryptedValue = await encryptText(value, encryptionKey);\n\n if (encryptedValue === undefined) {\n return;\n }\n\n storage.setItem(storageKey, encryptedValue);\n } catch {\n // Preference persistence must not interrupt application usage.\n }\n}\n\n/**\n * @description Loads and decrypts a preference from localStorage.\n * @param storageKey Storage key.\n * @param encryptionKey Encryption secret.\n * @returns Decrypted preference or undefined when unavailable.\n * @author Steveen Cues\n * @version 3.0.0\n */\nexport async function loadEncryptedPreference(\n storageKey: string,\n encryptionKey: string,\n): Promise<string | undefined> {\n if (encryptionKey.length === 0) {\n return undefined;\n }\n\n try {\n const storage = getLocalStorage();\n\n if (storage === undefined) {\n return undefined;\n }\n\n const encryptedValue = storage.getItem(storageKey);\n\n if (encryptedValue === null) {\n return undefined;\n }\n\n return await decryptText(encryptedValue, encryptionKey);\n } catch {\n return undefined;\n }\n}\n","import {\n loadEncryptedPreference,\n saveEncryptedPreference,\n} from \"./encrypted-preference.storage\";\n\nconst LANGUAGE_PREFERENCE_STORAGE_KEY =\n \"starter-react-ui:language-preference:v1\";\n\n/**\n * @description Guarda el idioma activo de forma cifrada.\n * @param language Codigo del idioma activo.\n * @param encryptionKey Clave de cifrado proporcionada por el consumidor.\n * @returns Promesa completada al finalizar el intento de guardado.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport function saveLanguagePreference(\n language: string,\n encryptionKey: string,\n): Promise<void> {\n return saveEncryptedPreference(\n LANGUAGE_PREFERENCE_STORAGE_KEY,\n language,\n encryptionKey,\n );\n}\n\n/**\n * @description Recupera el idioma guardado y verifica que siga disponible.\n * @param availableLanguages Idiomas admitidos por el proyecto.\n * @param encryptionKey Clave de cifrado proporcionada por el consumidor.\n * @returns Idioma recuperado o undefined si no existe o no es valido.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport async function loadLanguagePreference(\n availableLanguages: readonly string[],\n encryptionKey: string,\n): Promise<string | undefined> {\n const savedLanguage = await loadEncryptedPreference(\n LANGUAGE_PREFERENCE_STORAGE_KEY,\n encryptionKey,\n );\n\n if (\n savedLanguage === undefined ||\n !availableLanguages.includes(savedLanguage)\n ) {\n return undefined;\n }\n\n return savedLanguage;\n}\n","import type { AppTheme, ThemeName } from \"@domain\";\n\nimport {\n loadEncryptedPreference,\n saveEncryptedPreference,\n} from \"./encrypted-preference.storage\";\n\nconst THEME_PREFERENCE_STORAGE_KEY = \"starter-react-ui:theme-preference:v1\";\n\n/**\n * @description Guarda el nombre del tema activo de forma cifrada.\n * @param themeName Nombre del tema.\n * @param encryptionKey Clave de cifrado proporcionada por el consumidor.\n * @returns Promesa completada al finalizar el intento de guardado.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport function saveThemePreference(\n themeName: ThemeName,\n encryptionKey: string,\n): Promise<void> {\n return saveEncryptedPreference(\n THEME_PREFERENCE_STORAGE_KEY,\n themeName,\n encryptionKey,\n );\n}\n\n/**\n * @description Recupera el tema guardado si existe entre los temas admitidos.\n * @param themes Temas definidos por el proyecto consumidor.\n * @param encryptionKey Clave de cifrado proporcionada por el consumidor.\n * @returns Nombre del tema recuperado o undefined.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport async function loadThemePreference(\n themes: readonly AppTheme[],\n encryptionKey: string,\n): Promise<ThemeName | undefined> {\n const savedTheme = await loadEncryptedPreference(\n THEME_PREFERENCE_STORAGE_KEY,\n encryptionKey,\n );\n\n if (savedTheme === undefined) {\n return undefined;\n }\n\n const availableTheme = themes.find(\n (theme: AppTheme): boolean => theme.name === savedTheme,\n );\n\n return availableTheme?.name;\n}\n","import { useCallback, useEffect, useMemo, useState } from \"react\";\n\nimport type { JSX } from \"react\";\n\nimport type {\n ILanguageContextValue,\n ITranslateProviderProps,\n TranslationParams,\n TranslationDictionary,\n} from \"@domain\";\n\nimport { TranslateContext } from \"@aplicacion\";\n\nimport {\n loadLanguagePreference,\n saveLanguagePreference,\n} from \"@infraestructura\";\n\nimport {\n assertLanguageExists,\n resolveActiveLanguage,\n resolveInitialLanguage,\n translateMessage,\n} from \"@aplicacion\";\n\ninterface LanguagePreferenceConfiguration {\n readonly translations: ITranslateProviderProps[\"translations\"];\n readonly initialLanguage: ITranslateProviderProps[\"initialLanguage\"];\n readonly fallbackLanguage: string;\n readonly languageEncryptionKey: ITranslateProviderProps[\"languageEncryptionKey\"];\n readonly languages: readonly string[];\n}\n\ninterface LanguagePreferenceState extends LanguagePreferenceConfiguration {\n readonly selectedLanguage: string;\n readonly isLoaded: boolean;\n}\n\ninterface LanguagePreferenceResult {\n readonly language: string;\n readonly isLoaded: boolean;\n readonly setLanguage: (language: string) => void;\n}\n\ntype SetLanguagePreferenceState = React.Dispatch<\n React.SetStateAction<LanguagePreferenceState>\n>;\n\n/**\n * @description Creates the initial language preference state.\n * @author Steveen Cues\n * @version 4.0.0\n */\nfunction createLanguagePreferenceState(\n configuration: LanguagePreferenceConfiguration,\n selectedLanguage: string,\n isLoaded: boolean,\n): LanguagePreferenceState {\n return {\n ...configuration,\n selectedLanguage,\n isLoaded,\n };\n}\n\n/**\n * @description Checks whether a state matches the active configuration.\n * @author Steveen Cues\n * @version 4.0.0\n */\nfunction matchesLanguageConfiguration(\n state: LanguagePreferenceState,\n configuration: LanguagePreferenceConfiguration,\n): boolean {\n return (\n state.translations === configuration.translations &&\n state.initialLanguage === configuration.initialLanguage &&\n state.fallbackLanguage === configuration.fallbackLanguage &&\n state.languageEncryptionKey === configuration.languageEncryptionKey &&\n state.languages === configuration.languages\n );\n}\n\n/**\n * @description Restores the saved language and falls back when loading fails.\n * @author Steveen Cues\n * @version 4.0.0\n */\nasync function restoreSavedLanguage(\n configuration: LanguagePreferenceConfiguration,\n): Promise<string | undefined> {\n try {\n return await loadLanguagePreference(\n configuration.languages,\n configuration.languageEncryptionKey,\n );\n } catch {\n return undefined;\n }\n}\n\n/**\n * @description Restores the saved language for the active configuration.\n * @author Steveen Cues\n * @version 4.0.0\n */\nfunction useRestoreLanguagePreference(\n configuration: LanguagePreferenceConfiguration,\n setPreferenceState: SetLanguagePreferenceState,\n): void {\n useEffect((): (() => void) => {\n let isActive = true;\n\n const restorePreference = async (): Promise<void> => {\n const savedLanguage = await restoreSavedLanguage(configuration);\n\n if (!isActive) {\n return;\n }\n\n const defaultLanguage = resolveInitialLanguage(\n configuration.translations,\n configuration.initialLanguage,\n configuration.fallbackLanguage,\n );\n\n setPreferenceState(\n (currentState: LanguagePreferenceState): LanguagePreferenceState => {\n if (\n matchesLanguageConfiguration(currentState, configuration) &&\n currentState.isLoaded\n ) {\n return currentState;\n }\n\n return createLanguagePreferenceState(\n configuration,\n savedLanguage ?? defaultLanguage,\n true,\n );\n },\n );\n };\n\n void restorePreference();\n\n return (): void => {\n isActive = false;\n };\n }, [configuration, setPreferenceState]);\n}\n\n/**\n * @description Persists the active language after preference restoration.\n * @author Steveen Cues\n * @version 4.0.0\n */\nfunction usePersistLanguagePreference(\n configuration: LanguagePreferenceConfiguration,\n language: string,\n isLoaded: boolean,\n): void {\n useEffect((): void => {\n if (!isLoaded) {\n return;\n }\n\n void saveLanguagePreference(language, configuration.languageEncryptionKey);\n }, [configuration.languageEncryptionKey, language, isLoaded]);\n}\n\n/**\n * @description Creates a validated language selection handler.\n * @author Steveen Cues\n * @version 4.0.0\n */\nfunction useSetLanguagePreference(\n configuration: LanguagePreferenceConfiguration,\n setPreferenceState: SetLanguagePreferenceState,\n): (language: string) => void {\n return useCallback(\n (nextLanguage: string): void => {\n assertLanguageExists(configuration.translations, nextLanguage);\n\n setPreferenceState(\n createLanguagePreferenceState(configuration, nextLanguage, true),\n );\n },\n [configuration, setPreferenceState],\n );\n}\n\n/**\n * @description Manages language selection, restoration, and persistence.\n * @author Steveen Cues\n * @version 4.0.0\n */\nfunction useLanguagePreference(\n configuration: LanguagePreferenceConfiguration,\n): LanguagePreferenceResult {\n const [preferenceState, setPreferenceState] =\n useState<LanguagePreferenceState>((): LanguagePreferenceState =>\n createLanguagePreferenceState(\n configuration,\n configuration.initialLanguage,\n false,\n ),\n );\n\n useRestoreLanguagePreference(configuration, setPreferenceState);\n\n const isCurrentConfiguration = matchesLanguageConfiguration(\n preferenceState,\n configuration,\n );\n\n const selectedLanguage = isCurrentConfiguration\n ? preferenceState.selectedLanguage\n : configuration.initialLanguage;\n\n const isLoaded = isCurrentConfiguration && preferenceState.isLoaded;\n\n const setLanguage = useSetLanguagePreference(\n configuration,\n setPreferenceState,\n );\n\n return {\n language: resolveActiveLanguage(\n configuration.translations,\n selectedLanguage,\n configuration.initialLanguage,\n configuration.fallbackLanguage,\n ),\n isLoaded,\n setLanguage,\n };\n}\n\n/**\n * @description Provides the active language and translation functions.\n * @author Steveen Cues\n * @version 4.0.0\n */\nexport function TranslateProvider({\n translations,\n initialLanguage,\n fallbackLanguage = \"en\",\n languageEncryptionKey,\n children,\n}: ITranslateProviderProps): JSX.Element {\n const languages = useMemo(\n (): readonly string[] => Object.keys(translations),\n [translations],\n );\n\n const configuration = useMemo(\n (): LanguagePreferenceConfiguration => ({\n translations,\n initialLanguage,\n fallbackLanguage,\n languageEncryptionKey,\n languages,\n }),\n [\n translations,\n initialLanguage,\n fallbackLanguage,\n languageEncryptionKey,\n languages,\n ],\n );\n\n const preference = useLanguagePreference(configuration);\n\n usePersistLanguagePreference(\n configuration,\n preference.language,\n preference.isLoaded,\n );\n\n const contextValue = useTranslationContext(configuration, preference);\n\n return (\n <TranslateContext.Provider value={contextValue}>\n {children}\n </TranslateContext.Provider>\n );\n}\n\n/**\n * @description Creates the translation context value.\n * @author Steveen Cues\n * @version 4.0.0\n */\nfunction useTranslationContext(\n configuration: LanguagePreferenceConfiguration,\n preference: LanguagePreferenceResult,\n): ILanguageContextValue {\n const currentDictionary: TranslationDictionary =\n configuration.translations[preference.language];\n\n const fallbackDictionary: TranslationDictionary =\n configuration.translations[configuration.fallbackLanguage];\n\n const t = useCallback(\n (key: string, params?: TranslationParams): string =>\n translateMessage(currentDictionary, fallbackDictionary, key, params),\n [currentDictionary, fallbackDictionary],\n );\n\n return useMemo(\n (): ILanguageContextValue => ({\n language: preference.language,\n languages: configuration.languages,\n setLanguage: preference.setLanguage,\n t,\n }),\n [preference.language, preference.setLanguage, configuration.languages, t],\n );\n}\n","import { useCallback, useEffect, useMemo, useState, type FC } from \"react\";\nimport type {\n AppTheme,\n CustomThemeDefinition,\n ThemeContextValue,\n ThemeProviderProps,\n} from \"@domain\";\nimport {\n applyThemeToDocument,\n customThemeService,\n findThemeByName,\n isCustomThemeDefinition,\n persistTheme,\n resolveInitialTheme,\n ThemeContext,\n} from \"@aplicacion\";\n\n/**\n * @description Resuelve el tema activo o selecciona el primer tema disponible.\n * @param themes Coleccion de temas disponibles.\n * @param themeName Nombre del tema seleccionado.\n * @returns Tema activo resuelto.\n * @author Steveen Cues\n * @version 1.0.0\n */\nconst resolveActiveTheme = (\n themes: readonly AppTheme[],\n themeName: string,\n): AppTheme => {\n const activeTheme = findThemeByName(themes, themeName);\n\n if (activeTheme !== undefined) {\n return activeTheme;\n }\n\n if (themes.length === 0) {\n throw new Error(\"ThemeProvider requires at least one theme.\");\n }\n\n return themes[0];\n};\n\n/**\n * @description Anade o actualiza un tema personalizado en la coleccion.\n * @param currentThemes Coleccion actual de temas personalizados.\n * @param customTheme Tema que se registrara.\n * @returns Nueva coleccion de temas personalizados.\n * @author Steveen Cues\n * @version 1.0.0\n */\nconst updateCustomThemes = (\n currentThemes: readonly CustomThemeDefinition[],\n customTheme: CustomThemeDefinition,\n): readonly CustomThemeDefinition[] => {\n const existingTheme = currentThemes.some(\n (item) => item.name === customTheme.name,\n );\n\n if (existingTheme) {\n return currentThemes.map((item) =>\n item.name === customTheme.name ? customTheme : item,\n );\n }\n\n return [...currentThemes, customTheme];\n};\n\n/**\n * @description Construye el valor compartido del contexto de temas.\n * @param theme Tema activo.\n * @param themes Coleccion de temas disponibles.\n * @param setTheme Funcion para cambiar el tema.\n * @param registerTheme Funcion para registrar un tema personalizado.\n * @returns Valor del contexto de temas.\n * @author Steveen Cues\n * @version 1.0.0\n */\nconst createThemeContextValue = (\n theme: AppTheme,\n themes: readonly AppTheme[],\n setTheme: (name: string) => void,\n registerTheme: (customTheme: CustomThemeDefinition) => void,\n): ThemeContextValue => ({\n theme,\n themeName: theme.name,\n themes,\n setTheme,\n registerTheme,\n});\n\n/**\n * @description Proporciona los temas base y personalizados a los componentes.\n * Gestiona la seleccion, el registro y la persistencia del tema activo.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport const ThemeProvider: FC<ThemeProviderProps> = ({\n themes: baseThemes,\n initialTheme,\n children,\n}) => {\n const [customThemes, setCustomThemes] = useState<\n readonly CustomThemeDefinition[]\n >([]);\n\n const themes = useMemo<readonly AppTheme[]>(\n () => [...baseThemes, ...customThemes],\n [baseThemes, customThemes],\n );\n\n const [themeName, setThemeName] = useState<string>(() =>\n resolveInitialTheme(baseThemes, initialTheme),\n );\n\n const theme = useMemo<AppTheme>(\n () => resolveActiveTheme(themes, themeName),\n [themes, themeName],\n );\n\n const registerTheme = useCallback(\n (customTheme: CustomThemeDefinition): void => {\n customThemeService.register(customTheme);\n setCustomThemes((currentThemes) =>\n updateCustomThemes(currentThemes, customTheme),\n );\n },\n [],\n );\n\n const setTheme = useCallback(\n (name: string): void => {\n const selectedTheme = findThemeByName(themes, name);\n\n if (selectedTheme === undefined) {\n return;\n }\n\n if (isCustomThemeDefinition(selectedTheme)) {\n customThemeService.register(selectedTheme);\n }\n\n setThemeName(selectedTheme.name);\n },\n [themes],\n );\n\n useEffect((): void => {\n applyThemeToDocument(theme.name);\n persistTheme(theme.name);\n }, [theme.name]);\n\n const contextValue = useMemo(\n () => createThemeContextValue(theme, themes, setTheme, registerTheme),\n [theme, themes, setTheme, registerTheme],\n );\n\n return (\n <ThemeContext.Provider value={contextValue}>\n {children}\n </ThemeContext.Provider>\n );\n};\n","/**\n * @description Expresiones regulares reutilizables para traducciones.\n * @author Steveen Cues\n * @version 2.0.0\n */\nexport const TranslationRegex = Object.freeze({\n INTERPOLATION_PLACEHOLDER: String.raw`\\{\\{([a-zA-Z0-9_]+)\\}\\}`,\n});\n","export const GLOBAL_REGEX_FLAG = \"g\";\n","export const AES_GCM_ALGORITHM = \"AES-GCM\";\nexport const AES_GCM_IV_LENGTH = 12;\nexport const SHA_256_ALGORITHM = \"SHA-256\";\nexport const RAW_KEY_FORMAT = \"raw\";\nexport const STORAGE_VALUE_SEPARATOR = \".\";\nexport const KEY_USAGES: KeyUsage[] = [\"encrypt\", \"decrypt\"];\nexport const ENCRYPTED_VALUE_PART_COUNT = 2;\n","import type { ThemeColorToken } from \"@domain\";\n\n/**\n * @description Define los nombres de las variables CSS de DaisyUI.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport const CSS_THEME_TOKENS = {\n primary: \"--color-primary\",\n primaryContent: \"--color-primary-content\",\n secondary: \"--color-secondary\",\n secondaryContent: \"--color-secondary-content\",\n accent: \"--color-accent\",\n accentContent: \"--color-accent-content\",\n neutral: \"--color-neutral\",\n neutralContent: \"--color-neutral-content\",\n base100: \"--color-base-100\",\n base200: \"--color-base-200\",\n base300: \"--color-base-300\",\n baseContent: \"--color-base-content\",\n info: \"--color-info\",\n infoContent: \"--color-info-content\",\n success: \"--color-success\",\n successContent: \"--color-success-content\",\n warning: \"--color-warning\",\n warningContent: \"--color-warning-content\",\n error: \"--color-error\",\n errorContent: \"--color-error-content\",\n} as const;\n\n/**\n * @description Define la coleccion de claves y variables CSS de DaisyUI.\n * Cada entrada relaciona una clave de color con su variable CSS.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport const CSS_THEME_TOKEN_ENTRIES: readonly [\n ThemeColorToken,\n (typeof CSS_THEME_TOKENS)[ThemeColorToken],\n][] = [\n [\"primary\", CSS_THEME_TOKENS.primary],\n [\"primaryContent\", CSS_THEME_TOKENS.primaryContent],\n [\"secondary\", CSS_THEME_TOKENS.secondary],\n [\"secondaryContent\", CSS_THEME_TOKENS.secondaryContent],\n [\"accent\", CSS_THEME_TOKENS.accent],\n [\"accentContent\", CSS_THEME_TOKENS.accentContent],\n [\"neutral\", CSS_THEME_TOKENS.neutral],\n [\"neutralContent\", CSS_THEME_TOKENS.neutralContent],\n [\"base100\", CSS_THEME_TOKENS.base100],\n [\"base200\", CSS_THEME_TOKENS.base200],\n [\"base300\", CSS_THEME_TOKENS.base300],\n [\"baseContent\", CSS_THEME_TOKENS.baseContent],\n [\"info\", CSS_THEME_TOKENS.info],\n [\"infoContent\", CSS_THEME_TOKENS.infoContent],\n [\"success\", CSS_THEME_TOKENS.success],\n [\"successContent\", CSS_THEME_TOKENS.successContent],\n [\"warning\", CSS_THEME_TOKENS.warning],\n [\"warningContent\", CSS_THEME_TOKENS.warningContent],\n [\"error\", CSS_THEME_TOKENS.error],\n [\"errorContent\", CSS_THEME_TOKENS.errorContent],\n];\n","/**\n * @description Defines the standard typography weights.\n * @author Steveen Cues\n * @version 2.0.0\n */\nconst THEME_FONT_WEIGHT_THIN = 100;\nconst THEME_FONT_WEIGHT_EXTRA_LIGHT = 200;\nconst THEME_FONT_WEIGHT_LIGHT = 300;\nconst THEME_FONT_WEIGHT_REGULAR = 400;\nconst THEME_FONT_WEIGHT_MEDIUM = 500;\nconst THEME_FONT_WEIGHT_SEMI_BOLD = 600;\nconst THEME_FONT_WEIGHT_BOLD = 700;\nconst THEME_FONT_WEIGHT_EXTRA_BOLD = 800;\nconst THEME_FONT_WEIGHT_BLACK = 900;\n\n/**\n * @description Defines the available typography weights.\n * @author Steveen Cues\n * @version 2.0.0\n */\nexport const THEME_FONT_WEIGHT = {\n THIN: THEME_FONT_WEIGHT_THIN,\n EXTRA_LIGHT: THEME_FONT_WEIGHT_EXTRA_LIGHT,\n LIGHT: THEME_FONT_WEIGHT_LIGHT,\n REGULAR: THEME_FONT_WEIGHT_REGULAR,\n MEDIUM: THEME_FONT_WEIGHT_MEDIUM,\n SEMI_BOLD: THEME_FONT_WEIGHT_SEMI_BOLD,\n BOLD: THEME_FONT_WEIGHT_BOLD,\n EXTRA_BOLD: THEME_FONT_WEIGHT_EXTRA_BOLD,\n BLACK: THEME_FONT_WEIGHT_BLACK,\n} as const;\n","import {\n type TranslationCatalog,\n type TranslationDictionary,\n type TranslationParams,\n type TranslationValue,\n GLOBAL_REGEX_FLAG,\n TranslationRegex,\n} from \"@domain\";\n\n/**\n * @description Comprueba si un idioma existe en el catalogo.\n * @param translations Catalogo disponible.\n * @param language Codigo del idioma.\n * @returns Indica si el idioma esta registrado.\n * @author Steveen Cues\n * @version 2.0.0\n */\nfunction isLanguageAvailable(\n translations: TranslationCatalog,\n language: string,\n): boolean {\n return Object.hasOwn(translations, language);\n}\n\n/**\n * @description Obtiene el primer idioma registrado en el catalogo.\n * @param translations Catalogo de traducciones.\n * @returns Codigo del primer idioma o undefined si esta vacio.\n * @author Steveen Cues\n * @version 2.0.0\n */\nfunction findFirstLanguage(\n translations: TranslationCatalog,\n): string | undefined {\n const [firstLanguage] = Object.keys(translations);\n\n return firstLanguage;\n}\n\n/**\n * @description Obtiene el idioma inicial disponible, priorizando el\n * idioma solicitado, el idioma de respaldo y el primer idioma registrado.\n * @param translations Catalogo de traducciones.\n * @param initialLanguage Idioma solicitado inicialmente.\n * @param fallbackLanguage Idioma de respaldo.\n * @returns Codigo del idioma inicial disponible.\n * @throws Error cuando el catalogo esta vacio.\n * @author Steveen Cues\n * @version 2.0.0\n */\nexport function resolveInitialLanguage(\n translations: TranslationCatalog,\n initialLanguage: string,\n fallbackLanguage: string,\n): string {\n if (isLanguageAvailable(translations, initialLanguage)) {\n return initialLanguage;\n }\n\n if (isLanguageAvailable(translations, fallbackLanguage)) {\n return fallbackLanguage;\n }\n\n const firstLanguage = findFirstLanguage(translations);\n\n if (firstLanguage === undefined) {\n throw new Error(\n \"TranslateProvider requires at least one translation catalog.\",\n );\n }\n\n return firstLanguage;\n}\n\n/**\n * @description Resuelve el idioma seleccionado y utiliza el idioma\n * inicial, el respaldo o el primer idioma disponible si es necesario.\n * @param translations Catalogo de traducciones.\n * @param selectedLanguage Idioma seleccionado actualmente.\n * @param initialLanguage Idioma inicial configurado.\n * @param fallbackLanguage Idioma de respaldo.\n * @returns Codigo del idioma activo.\n * @author Steveen Cues\n * @version 2.0.0\n */\nexport function resolveActiveLanguage(\n translations: TranslationCatalog,\n selectedLanguage: string,\n initialLanguage: string,\n fallbackLanguage: string,\n): string {\n if (isLanguageAvailable(translations, selectedLanguage)) {\n return selectedLanguage;\n }\n\n return resolveInitialLanguage(\n translations,\n initialLanguage,\n fallbackLanguage,\n );\n}\n\n/**\n * @description Valida que un idioma este registrado en el catalogo.\n * @param translations Catalogo de traducciones.\n * @param language Codigo del idioma que se desea validar.\n * @returns No devuelve ningun valor.\n * @throws Error cuando el idioma no esta disponible.\n * @author Steveen Cues\n * @version 2.0.0\n */\nexport function assertLanguageExists(\n translations: TranslationCatalog,\n language: string,\n): void {\n if (isLanguageAvailable(translations, language)) {\n return;\n }\n\n throw new Error(\n `Language \"${language}\" is not available in the translation catalog.`,\n );\n}\n\n/**\n * @description Obtiene el valor anidado de una clave usando notacion\n * de puntos, por ejemplo \"navigation.contact\".\n * @param dictionary Diccionario donde se realiza la busqueda.\n * @param key Clave de traduccion.\n * @returns Valor encontrado o undefined si no existe.\n * @author Steveen Cues\n * @version 2.0.0\n */\nfunction findTranslationValue(\n dictionary: TranslationDictionary,\n key: string,\n): TranslationValue | undefined {\n let current: TranslationValue = dictionary;\n\n for (const segment of key.split(\".\")) {\n if (typeof current === \"string\") {\n return undefined;\n }\n\n if (!Object.hasOwn(current, segment)) {\n return undefined;\n }\n\n current = current[segment];\n }\n\n return current;\n}\n\n/**\n * @description Busca una traduccion dentro de un diccionario.\n * @param dictionary Diccionario del idioma.\n * @param key Clave con notacion de puntos.\n * @returns Traduccion encontrada o undefined.\n * @author Steveen Cues\n * @version 2.0.0\n */\nexport function findTranslation(\n dictionary: TranslationDictionary | undefined,\n key: string,\n): string | undefined {\n if (dictionary === undefined || key === \"\") {\n return undefined;\n }\n\n const value = findTranslationValue(dictionary, key);\n\n return typeof value === \"string\" ? value : undefined;\n}\n\n/**\n * @description Resuelve el texto correspondiente a un marcador de\n * interpolacion sin eliminarlo cuando falta su parametro.\n * @param placeholder Marcador original, por ejemplo {{name}}.\n * @param key Nombre del parametro.\n * @param params Parametros disponibles.\n * @returns Valor interpolado o marcador original.\n * @author Steveen Cues\n * @version 2.0.0\n */\nfunction resolveInterpolationValue(\n placeholder: string,\n key: string,\n params: TranslationParams | undefined,\n): string {\n const value = params?.[key];\n\n return value === undefined ? placeholder : String(value);\n}\n\n/**\n * @description Sustituye los marcadores {{name}} usando los parametros.\n * @param message Mensaje que se desea interpolar.\n * @param params Valores disponibles para la interpolacion.\n * @returns Mensaje con los parametros sustituidos.\n * @author Steveen Cues\n * @version 2.0.0\n */\nexport function interpolateTranslation(\n message: string,\n params?: TranslationParams,\n): string {\n const placeholderRegex = new RegExp(\n TranslationRegex.INTERPOLATION_PLACEHOLDER,\n GLOBAL_REGEX_FLAG,\n );\n\n return message.replace(\n placeholderRegex,\n (placeholder: string, key: string): string =>\n resolveInterpolationValue(placeholder, key, params),\n );\n}\n\n/**\n * @description Busca una traduccion en el idioma activo y utiliza el\n * idioma de respaldo cuando la clave no esta disponible.\n * @param currentDictionary Diccionario del idioma activo.\n * @param fallbackDictionary Diccionario del idioma de respaldo.\n * @param key Clave de traduccion.\n * @returns Traduccion encontrada o clave original.\n * @author Steveen Cues\n * @version 2.0.0\n */\nfunction resolveTranslationMessage(\n currentDictionary: TranslationDictionary | undefined,\n fallbackDictionary: TranslationDictionary | undefined,\n key: string,\n): string {\n const currentTranslation = findTranslation(currentDictionary, key);\n\n if (currentTranslation !== undefined) {\n return currentTranslation;\n }\n\n const fallbackTranslation = findTranslation(fallbackDictionary, key);\n\n return fallbackTranslation ?? key;\n}\n\n/**\n * @description Traduce un mensaje aplicando respaldo e interpolacion.\n * @param currentDictionary Diccionario del idioma activo.\n * @param fallbackDictionary Diccionario del idioma de respaldo.\n * @param key Clave de traduccion.\n * @param params Parametros de interpolacion.\n * @returns Mensaje final traducido e interpolado.\n * @author Steveen Cues\n * @version 2.0.0\n */\nexport function translateMessage(\n currentDictionary: TranslationDictionary | undefined,\n fallbackDictionary: TranslationDictionary | undefined,\n key: string,\n params?: TranslationParams,\n): string {\n const message = resolveTranslationMessage(\n currentDictionary,\n fallbackDictionary,\n key,\n );\n\n return interpolateTranslation(message, params);\n}\n","import type { AppTheme, CustomThemeDefinition } from \"@domain\";\n\n/**\n * @description Busca un tema por su nombre dentro de una coleccion.\n * @param themes Coleccion de temas disponibles.\n * @param themeName Nombre del tema que se desea encontrar.\n * @returns El tema encontrado o undefined si no existe.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport const findThemeByName = (\n themes: readonly AppTheme[],\n themeName: string,\n): AppTheme | undefined => themes.find((theme) => theme.name === themeName);\n\n/**\n * @description Comprueba si un tema contiene la configuracion de DaisyUI.\n * @param theme Tema que se desea comprobar.\n * @returns true si el tema es una definicion personalizada.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport const isCustomThemeDefinition = (\n theme: AppTheme,\n): theme is CustomThemeDefinition => \"daisyUI\" in theme;\n\n/**\n * @description Resuelve el tema inicial segun la preferencia almacenada,\n * el tema configurado y el primer tema disponible.\n * @param themes Coleccion de temas disponibles.\n * @param initialTheme Nombre del tema inicial configurado.\n * @returns Nombre del tema inicial resuelto.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport const resolveInitialTheme = (\n themes: readonly AppTheme[],\n initialTheme: string,\n): string => {\n if (typeof window !== \"undefined\") {\n const savedTheme = window.localStorage.getItem(\"app_theme\");\n\n if (\n savedTheme !== null &&\n savedTheme.length > 0 &&\n findThemeByName(themes, savedTheme) !== undefined\n ) {\n return savedTheme;\n }\n }\n\n const configuredTheme = findThemeByName(themes, initialTheme);\n\n if (configuredTheme !== undefined) {\n return configuredTheme.name;\n }\n\n if (themes.length === 0) {\n return initialTheme;\n }\n\n return themes[0].name;\n};\n\n/**\n * @description Actualiza el atributo data-theme del documento.\n * @param themeName Nombre del tema que se aplicara.\n * @returns void.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport const applyThemeToDocument = (themeName: string): void => {\n if (typeof document === \"undefined\") {\n return;\n }\n\n document.documentElement.setAttribute(\"data-theme\", themeName);\n};\n\n/**\n * @description Persiste el nombre del tema seleccionado en el navegador.\n * @param themeName Nombre del tema que se almacenara.\n * @returns void.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport const persistTheme = (themeName: string): void => {\n if (typeof window === \"undefined\") {\n return;\n }\n\n window.localStorage.setItem(\"app_theme\", themeName);\n};\n","/**\n * @description Obtains the cryptographic API when available.\n * @returns Cryptographic API or undefined if unavailable.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport function getCryptoApi(): Crypto | undefined {\n try {\n return globalThis.crypto;\n } catch {\n return undefined;\n }\n}\n","/**\n * @description Obtains localStorage when available.\n * @returns Browser storage or undefined if unavailable.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport function getLocalStorage(): Storage | undefined {\n try {\n return globalThis.localStorage;\n } catch {\n return undefined;\n }\n}\n","/**\n * @description Encodes text into UTF-8 bytes.\n * @param value Text to encode.\n * @returns Encoded bytes.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport function encodeText(value: string): Uint8Array<ArrayBuffer> {\n return new TextEncoder().encode(value);\n}\n\n/**\n * @description Decodes UTF-8 bytes into text.\n * @param value Encoded bytes.\n * @returns Decoded text.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport function decodeText(value: ArrayBuffer): string {\n return new TextDecoder().decode(value);\n}\n","/**\n * @description Encodes bytes into Base64.\n * @param bytes Bytes to encode.\n * @returns Base64 string.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport function encodeBase64(bytes: Uint8Array): string {\n let binary = \"\";\n\n for (const byte of bytes) {\n binary += String.fromCharCode(byte);\n }\n\n return btoa(binary);\n}\n\n/**\n * @description Decodes Base64 into bytes.\n * @param value Base64 string.\n * @returns Decoded bytes.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport function decodeBase64(value: string): Uint8Array<ArrayBuffer> {\n const binary = atob(value);\n const bytes = new Uint8Array(binary.length);\n\n for (let index = 0; index < binary.length; index += 1) {\n bytes[index] = binary.charCodeAt(index);\n }\n\n return bytes;\n}\n","import {\n AES_GCM_ALGORITHM,\n AES_GCM_IV_LENGTH,\n ENCRYPTED_VALUE_PART_COUNT,\n KEY_USAGES,\n RAW_KEY_FORMAT,\n SHA_256_ALGORITHM,\n STORAGE_VALUE_SEPARATOR,\n} from \"@domain\";\n\nimport {\n decodeBase64,\n encodeBase64,\n getCryptoApi,\n decodeText,\n encodeText,\n} from \"@aplicacion\";\n\n/**\n * @description Represents validated encrypted data.\n * @author Steveen Cues\n * @version 2.0.0\n */\ninterface EncryptedPayload {\n iv: Uint8Array<ArrayBuffer>;\n ciphertext: Uint8Array<ArrayBuffer>;\n}\n\n/**\n * @description Derives an AES-GCM key from a configured secret.\n * @param secret Encryption secret.\n * @returns Crypto key or undefined when unavailable.\n * @author Steveen Cues\n * @version 2.0.0\n */\nasync function deriveAesKey(secret: string): Promise<CryptoKey | undefined> {\n const cryptoApi = getCryptoApi();\n\n if (cryptoApi === undefined || secret.length === 0) {\n return undefined;\n }\n\n const digest = await cryptoApi.subtle.digest(\n SHA_256_ALGORITHM,\n encodeText(secret),\n );\n\n return cryptoApi.subtle.importKey(\n RAW_KEY_FORMAT,\n digest,\n { name: AES_GCM_ALGORITHM },\n false,\n KEY_USAGES,\n );\n}\n\n/**\n * @description Creates a cryptographically random initialization vector.\n * @returns Initialization vector.\n * @author Steveen Cues\n * @version 2.0.0\n */\nfunction createInitializationVector(): Uint8Array<ArrayBuffer> {\n const cryptoApi = getCryptoApi();\n\n if (cryptoApi === undefined) {\n throw new Error(\"Cryptographic API is unavailable.\");\n }\n\n return cryptoApi.getRandomValues(new Uint8Array(AES_GCM_IV_LENGTH));\n}\n\n/**\n * @description Validates and decodes an initialization vector.\n * @param value Base64-encoded initialization vector.\n * @returns Valid vector or undefined.\n * @author Steveen Cues\n * @version 2.0.0\n */\nfunction decodeInitializationVector(\n value: string,\n): Uint8Array<ArrayBuffer> | undefined {\n try {\n const iv = decodeBase64(value);\n\n return iv.length === AES_GCM_IV_LENGTH ? iv : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * @description Checks whether the encrypted value has the expected segments.\n * @param parts Encrypted value segments.\n * @returns Whether the segments contain an IV and ciphertext.\n * @author Steveen Cues\n * @version 2.2.0\n */\nfunction hasExpectedEncryptedParts(parts: string[]): parts is [string, string] {\n return parts.length === ENCRYPTED_VALUE_PART_COUNT;\n}\n\n/**\n * @description Validates the encrypted value segments.\n * @param parts Encrypted value segments.\n * @returns Validated segments or undefined.\n * @author Steveen Cues\n * @version 2.2.0\n */\nfunction validateEncryptedParts(parts: string[]): [string, string] | undefined {\n if (!hasExpectedEncryptedParts(parts)) {\n return undefined;\n }\n\n return [parts[0], parts[1]];\n}\n\n/**\n * @description Splits and validates an encrypted value.\n * @param value Encoded initialization vector and ciphertext.\n * @returns Validated payload or undefined.\n * @author Steveen Cues\n * @version 2.1.0\n */\nfunction parseEncryptedPayload(value: string): EncryptedPayload | undefined {\n const parts = validateEncryptedParts(value.split(STORAGE_VALUE_SEPARATOR));\n\n if (parts === undefined) {\n return undefined;\n }\n\n const [encodedIv, encodedCiphertext] = parts;\n const iv = decodeInitializationVector(encodedIv);\n\n if (iv === undefined) {\n return undefined;\n }\n\n try {\n return {\n iv,\n ciphertext: decodeBase64(encodedCiphertext),\n };\n } catch {\n return undefined;\n }\n}\n\n/**\n * @description Encrypts bytes using AES-GCM.\n * @param plaintext Plaintext bytes.\n * @param key Encryption key.\n * @param iv Initialization vector.\n * @returns Encrypted bytes.\n * @author Steveen Cues\n * @version 2.1.0\n */\nasync function encryptPayload(\n plaintext: Uint8Array<ArrayBuffer>,\n key: CryptoKey,\n iv: Uint8Array<ArrayBuffer>,\n): Promise<ArrayBuffer> {\n const cryptoApi = requireCryptoApi();\n\n return cryptoApi.subtle.encrypt(\n { name: AES_GCM_ALGORITHM, iv },\n key,\n plaintext,\n );\n}\n\n/**\n * @description Decrypts bytes using AES-GCM.\n * @param payload Validated encrypted payload.\n * @param key Decryption key.\n * @returns Decrypted bytes.\n * @author Steveen Cues\n * @version 2.1.0\n */\nasync function decryptPayload(\n payload: EncryptedPayload,\n key: CryptoKey,\n): Promise<ArrayBuffer> {\n const cryptoApi = requireCryptoApi();\n\n return cryptoApi.subtle.decrypt(\n {\n name: AES_GCM_ALGORITHM,\n iv: payload.iv,\n },\n key,\n payload.ciphertext,\n );\n}\n\n/**\n * @description Obtains the cryptographic API or throws when unavailable.\n * @returns Available cryptographic API.\n * @author Steveen Cues\n * @version 2.2.0\n */\nfunction requireCryptoApi(): Crypto {\n const cryptoApi = getCryptoApi();\n\n if (cryptoApi === undefined) {\n throw new Error(\"Cryptographic API is unavailable.\");\n }\n\n return cryptoApi;\n}\n\n/**\n * @description Encrypts text using AES-GCM.\n * @param value Text to encrypt.\n * @param secret Encryption secret.\n * @returns Encoded encrypted value or undefined when unavailable.\n * @author Steveen Cues\n * @version 2.0.0\n */\nexport async function encryptText(\n value: string,\n secret: string,\n): Promise<string | undefined> {\n try {\n const key = await deriveAesKey(secret);\n\n if (key === undefined) {\n return undefined;\n }\n\n const iv = createInitializationVector();\n const ciphertext = await encryptPayload(encodeText(value), key, iv);\n\n return [encodeBase64(iv), encodeBase64(new Uint8Array(ciphertext))].join(\n STORAGE_VALUE_SEPARATOR,\n );\n } catch {\n return undefined;\n }\n}\n\n/**\n * @description Decrypts an AES-GCM encrypted value.\n * @param value Encoded initialization vector and ciphertext.\n * @param secret Encryption secret.\n * @returns Original text or undefined if decryption fails.\n * @author Steveen Cues\n * @version 2.0.0\n */\nexport async function decryptText(\n value: string,\n secret: string,\n): Promise<string | undefined> {\n try {\n const payload = parseEncryptedPayload(value);\n\n if (payload === undefined) {\n return undefined;\n }\n\n const key = await deriveAesKey(secret);\n\n if (key === undefined) {\n return undefined;\n }\n\n const plaintext = await decryptPayload(payload, key);\n\n return decodeText(plaintext);\n } catch {\n return undefined;\n }\n}\n","import {\n CSS_THEME_TOKEN_ENTRIES,\n type CustomThemeDefinition,\n type ThemeColorToken,\n} from \"@domain\";\n\n/**\n * @description Gestiona el registro de temas personalizados de DaisyUI.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport class CustomThemeService {\n private static readonly STYLE_ID_PREFIX = \"daisyui-custom-theme-\";\n\n /**\n * @description Registra o actualiza las variables CSS de un tema.\n * @param theme Definicion del tema personalizado.\n */\n public register(theme: CustomThemeDefinition): void {\n this.validateThemeName(theme.name);\n\n const styleElement = this.getOrCreateStyleElement(theme.name);\n const declarations = this.createColorDeclarations(theme);\n\n styleElement.textContent = this.createThemeStylesheet(theme, declarations);\n }\n\n /**\n * @description Elimina el registro CSS de un tema personalizado.\n * @param themeName Nombre del tema.\n */\n public unregister(themeName: string): void {\n this.validateThemeName(themeName);\n\n const styleElement = document.getElementById(\n this.getStyleElementId(themeName),\n );\n\n styleElement?.remove();\n }\n\n /**\n * @description Comprueba si un tema tiene estilos registrados.\n * @param themeName Nombre del tema.\n * @returns Indica si el tema esta registrado.\n */\n public isRegistered(themeName: string): boolean {\n this.validateThemeName(themeName);\n\n return document.getElementById(this.getStyleElementId(themeName)) !== null;\n }\n\n /**\n * @description Valida el nombre del tema.\n * @param themeName Nombre del tema.\n */\n private validateThemeName(themeName: string): void {\n const validThemeName = /^[a-zA-Z][a-zA-Z0-9_-]*$/.test(themeName);\n\n if (!validThemeName) {\n throw new Error(`Invalid theme name: ${themeName}`);\n }\n }\n\n /**\n * @description Genera las declaraciones CSS de los colores del tema.\n * @param theme Definicion del tema.\n * @returns Declaraciones CSS.\n * @author Steveen Cues\n * @version 1.0.0\n */\n private createColorDeclarations(theme: CustomThemeDefinition): string {\n return CSS_THEME_TOKEN_ENTRIES.map(([token, variable]) => {\n const color = theme.daisyUI[token];\n\n this.validateCssColor(token, color);\n\n return `${variable}: ${color};`;\n }).join(\"\\n\");\n }\n\n /**\n * @description Valida un color CSS.\n * @param token Nombre del token.\n * @param color Valor CSS.\n */\n private validateCssColor(token: ThemeColorToken, color: string): void {\n if (typeof CSS === \"undefined\" || !CSS.supports(\"color\", color)) {\n throw new Error(`Invalid CSS color for theme token: ${token}`);\n }\n }\n\n /**\n * @description Genera las reglas CSS del tema.\n * @param theme Definicion del tema.\n * @param declarations Declaraciones CSS de los colores.\n * @returns Hoja de estilos CSS.\n */\n private createThemeStylesheet(\n theme: CustomThemeDefinition,\n declarations: string,\n ): string {\n return `\n [data-theme=\"${theme.name}\"] {\n color-scheme: ${theme.daisyUI.colorScheme};\n ${declarations}\n }\n `;\n }\n\n /**\n * @description Obtiene el elemento de estilos o lo crea si no existe.\n * @param themeName Nombre del tema.\n * @returns Elemento de estilos CSS.\n */\n private getOrCreateStyleElement(themeName: string): HTMLStyleElement {\n const styleId = this.getStyleElementId(themeName);\n const existingElement = document.getElementById(styleId);\n\n if (existingElement instanceof HTMLStyleElement) {\n return existingElement;\n }\n\n existingElement?.remove();\n\n const styleElement = document.createElement(\"style\");\n styleElement.id = styleId;\n\n document.head.appendChild(styleElement);\n\n return styleElement;\n }\n\n /**\n * @description Construye el identificador del elemento de estilos.\n * @param themeName Nombre del tema.\n * @returns Identificador HTML.\n */\n private getStyleElementId(themeName: string): string {\n return `${CustomThemeService.STYLE_ID_PREFIX}${themeName}`;\n }\n}\n\n/**\n * @description Instancia compartida del servicio de temas personalizados.\n */\nexport const customThemeService = new CustomThemeService();\n","import type { FC, ButtonHTMLAttributes } from \"react\";\nimport { useLanguage } from \"@aplicacion\";\n\n/**\n * @description Boton de prueba con DaisyUI y traduccion.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport interface TestButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n readonly label?: string;\n}\n\n/**\n * @description Renderiza un boton adaptable al tema activo.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport const Button: FC<TestButtonProps> = ({\n label,\n className = \"\",\n ...props\n}) => {\n const { t } = useLanguage();\n\n return (\n <button\n {...props}\n type={props.type ?? \"button\"}\n className={`btn btn-primary ${className}`}\n >\n {label ?? t(\"common.button\")}\n </button>\n );\n};\n\nexport default Button;\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["TranslateContext","createContext","ThemeContext","useLanguage","context","useContext","useTheme","saveEncryptedPreference","value","encryptionKey","storage","getLocalStorage","encryptedValue","encryptText","storageKey","loadEncryptedPreference","decryptText","LANGUAGE_PREFERENCE_STORAGE_KEY","saveLanguagePreference","language","loadLanguagePreference","savedLanguage","availableLanguages","THEME_PREFERENCE_STORAGE_KEY","saveThemePreference","themeName","loadThemePreference","savedTheme","themes","theme","createLanguagePreferenceState","configuration","matchesLanguageConfiguration","state","restoreSavedLanguage","useRestoreLanguagePreference","isActive","defaultLanguage","resolveInitialLanguage","currentState","setPreferenceState","usePersistLanguagePreference","isLoaded","useSetLanguagePreference","useCallback","assertLanguageExists","nextLanguage","useLanguagePreference","preferenceState","useState","isCurrentConfiguration","selectedLanguage","setLanguage","resolveActiveLanguage","TranslateProvider","languages","useMemo","translations","initialLanguage","fallbackLanguage","languageEncryptionKey","preference","contextValue","useTranslationContext","currentDictionary","fallbackDictionary","t","translateMessage","key","params","resolveActiveTheme","activeTheme","findThemeByName","updateCustomThemes","currentThemes","item","customTheme","createThemeContextValue","ThemeProvider","baseThemes","customThemes","setCustomThemes","setThemeName","resolveInitialTheme","initialTheme","registerTheme","customThemeService","setTheme","selectedTheme","name","isCustomThemeDefinition","applyThemeToDocument","persistTheme","TranslationRegex","GLOBAL_REGEX_FLAG","AES_GCM_ALGORITHM","AES_GCM_IV_LENGTH","SHA_256_ALGORITHM","RAW_KEY_FORMAT","STORAGE_VALUE_SEPARATOR","KEY_USAGES","ENCRYPTED_VALUE_PART_COUNT","CSS_THEME_TOKENS","CSS_THEME_TOKEN_ENTRIES","THEME_FONT_WEIGHT","isLanguageAvailable","findFirstLanguage","firstLanguage","findTranslationValue","current","dictionary","segment","findTranslation","resolveInterpolationValue","placeholder","interpolateTranslation","placeholderRegex","message","resolveTranslationMessage","currentTranslation","configuredTheme","getCryptoApi","encodeText","decodeText","encodeBase64","binary","byte","bytes","decodeBase64","index","deriveAesKey","cryptoApi","secret","digest","createInitializationVector","decodeInitializationVector","iv","hasExpectedEncryptedParts","parts","validateEncryptedParts","parseEncryptedPayload","encodedIv","encodedCiphertext","encryptPayload","requireCryptoApi","plaintext","decryptPayload","payload","ciphertext","CustomThemeService","styleElement","declarations","token","variable","color","styleId","existingElement","Button","props","className","label","ThemeSelectorTrigger","isOpen","onToggle","ThemeSelectorSearch","onChange","event","ThemeSelectorOption","isSelected","onSelect","showDescription","ThemeSelectorList","availableTheme","showDescriptions","ThemeSelectorPanel","showSearch","search","onSearchChange","onSelectTheme","totalThemes","SEARCH_THRESHOLD","filterThemes","normalizedSearch","ThemeSelector","setIsOpen","setSearch","filteredThemes","previousValue"],"sources":["../src/libs/aplicacion/contexts/translate.context.ts","../src/libs/aplicacion/contexts/theme.context.ts","../src/libs/aplicacion/hooks/use-language.ts","../src/libs/aplicacion/hooks/use-theme.ts","../src/libs/infraestructura/storage/encrypted-preference.storage.ts","../src/libs/infraestructura/storage/language-preference.storage.ts","../src/libs/infraestructura/storage/theme-preference.storage.ts","../src/libs/aplicacion/providers/translate.provider.tsx","../src/libs/aplicacion/providers/theme-provider.tsx","../src/libs/domain/enums/translation-regex.enum.ts","../src/libs/domain/constants/translation.constants.ts","../src/libs/domain/constants/encrypted-preference.constanst.ts","../src/libs/domain/constants/theme-css-variable.const.ts","../src/libs/domain/constants/theme-font-weight.const.ts","../src/libs/aplicacion/utils/translation.utils.ts","../src/libs/aplicacion/utils/theme.utils.ts","../src/libs/aplicacion/utils/crypto-api.util.ts","../src/libs/aplicacion/utils/storage-api.util.ts","../src/libs/aplicacion/utils/text-encoding.util.ts","../src/libs/aplicacion/utils/base64.util.ts","../src/libs/aplicacion/services/encryption.service.ts","../src/libs/aplicacion/services/custom-theme.service.ts","../src/libs/components/Button/Button.tsx","../src/libs/components/theme-selector/theme-selector-trigger.tsx","../src/libs/components/theme-selector/theme-selector-search.tsx","../src/libs/components/theme-selector/theme-selector-option.tsx","../src/libs/components/theme-selector/theme-selector-list.tsx","../src/libs/components/theme-selector/theme-selector-panel.tsx","../src/libs/components/theme-selector/theme-selector.tsx"],"sourcesContent":["import type { ILanguageContextValue } from \"@domain\";\nimport { createContext } from \"react\";\n\n/**\n * @description Contexto interno de traducciones de la libreria.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport const TranslateContext = createContext<ILanguageContextValue | null>(\n null,\n);\n","import { createContext } from \"react\";\nimport type { ThemeContextValue } from \"@domain\";\n\n/**\n * @description Contexto compartido para gestionar los temas de la aplicacion.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport const ThemeContext = createContext<ThemeContextValue | null>(null);\n","import { TranslateContext } from \"@aplicacion\";\nimport type { ILanguageContextValue } from \"@domain\";\nimport { useContext } from \"react\";\n\n/**\n * @description Obtiene el idioma actual y la funcion de traduccion.\n * Requiere que el componente este dentro de TranslateProvider.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport function useLanguage(): ILanguageContextValue {\n const context = useContext(TranslateContext);\n\n if (context === null) {\n throw new Error(\"useLanguage must be used inside TranslateProvider.\");\n }\n\n return context;\n}\n","import { ThemeContext } from \"@aplicacion\";\nimport { useContext } from \"react\";\nimport type { ThemeContextValue } from \"@domain\";\n\n/**\n * @description Permite consultar y modificar el tema activo.\n * @returns El contexto con el estado y las acciones del tema.\n * @throws Error si se utiliza fuera de ThemeProvider.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport const useTheme = (): ThemeContextValue => {\n const context = useContext(ThemeContext);\n\n if (!context) {\n throw new Error(\"useTheme must be used inside ThemeProvider.\");\n }\n\n return context;\n};\n","import { decryptText, encryptText, getLocalStorage } from \"@aplicacion\";\n\n/**\n * @description Encrypts and saves a preference in localStorage.\n * @param storageKey Storage key.\n * @param value Value to store.\n * @param encryptionKey Encryption secret.\n * @returns Promise resolved after the save attempt.\n * @author Steveen Cues\n * @version 3.0.0\n */\nexport async function saveEncryptedPreference(\n storageKey: string,\n value: string,\n encryptionKey: string,\n): Promise<void> {\n if (value.length === 0 || encryptionKey.length === 0) {\n return;\n }\n\n try {\n const storage = getLocalStorage();\n\n if (storage === undefined) {\n return;\n }\n\n const encryptedValue = await encryptText(value, encryptionKey);\n\n if (encryptedValue === undefined) {\n return;\n }\n\n storage.setItem(storageKey, encryptedValue);\n } catch {\n // Preference persistence must not interrupt application usage.\n }\n}\n\n/**\n * @description Loads and decrypts a preference from localStorage.\n * @param storageKey Storage key.\n * @param encryptionKey Encryption secret.\n * @returns Decrypted preference or undefined when unavailable.\n * @author Steveen Cues\n * @version 3.0.0\n */\nexport async function loadEncryptedPreference(\n storageKey: string,\n encryptionKey: string,\n): Promise<string | undefined> {\n if (encryptionKey.length === 0) {\n return undefined;\n }\n\n try {\n const storage = getLocalStorage();\n\n if (storage === undefined) {\n return undefined;\n }\n\n const encryptedValue = storage.getItem(storageKey);\n\n if (encryptedValue === null) {\n return undefined;\n }\n\n return await decryptText(encryptedValue, encryptionKey);\n } catch {\n return undefined;\n }\n}\n","import {\n loadEncryptedPreference,\n saveEncryptedPreference,\n} from \"./encrypted-preference.storage\";\n\nconst LANGUAGE_PREFERENCE_STORAGE_KEY =\n \"starter-react-ui:language-preference:v1\";\n\n/**\n * @description Guarda el idioma activo de forma cifrada.\n * @param language Codigo del idioma activo.\n * @param encryptionKey Clave de cifrado proporcionada por el consumidor.\n * @returns Promesa completada al finalizar el intento de guardado.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport function saveLanguagePreference(\n language: string,\n encryptionKey: string,\n): Promise<void> {\n return saveEncryptedPreference(\n LANGUAGE_PREFERENCE_STORAGE_KEY,\n language,\n encryptionKey,\n );\n}\n\n/**\n * @description Recupera el idioma guardado y verifica que siga disponible.\n * @param availableLanguages Idiomas admitidos por el proyecto.\n * @param encryptionKey Clave de cifrado proporcionada por el consumidor.\n * @returns Idioma recuperado o undefined si no existe o no es valido.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport async function loadLanguagePreference(\n availableLanguages: readonly string[],\n encryptionKey: string,\n): Promise<string | undefined> {\n const savedLanguage = await loadEncryptedPreference(\n LANGUAGE_PREFERENCE_STORAGE_KEY,\n encryptionKey,\n );\n\n if (\n savedLanguage === undefined ||\n !availableLanguages.includes(savedLanguage)\n ) {\n return undefined;\n }\n\n return savedLanguage;\n}\n","import type { AppTheme, ThemeName } from \"@domain\";\n\nimport {\n loadEncryptedPreference,\n saveEncryptedPreference,\n} from \"./encrypted-preference.storage\";\n\nconst THEME_PREFERENCE_STORAGE_KEY = \"starter-react-ui:theme-preference:v1\";\n\n/**\n * @description Guarda el nombre del tema activo de forma cifrada.\n * @param themeName Nombre del tema.\n * @param encryptionKey Clave de cifrado proporcionada por el consumidor.\n * @returns Promesa completada al finalizar el intento de guardado.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport function saveThemePreference(\n themeName: ThemeName,\n encryptionKey: string,\n): Promise<void> {\n return saveEncryptedPreference(\n THEME_PREFERENCE_STORAGE_KEY,\n themeName,\n encryptionKey,\n );\n}\n\n/**\n * @description Recupera el tema guardado si existe entre los temas admitidos.\n * @param themes Temas definidos por el proyecto consumidor.\n * @param encryptionKey Clave de cifrado proporcionada por el consumidor.\n * @returns Nombre del tema recuperado o undefined.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport async function loadThemePreference(\n themes: readonly AppTheme[],\n encryptionKey: string,\n): Promise<ThemeName | undefined> {\n const savedTheme = await loadEncryptedPreference(\n THEME_PREFERENCE_STORAGE_KEY,\n encryptionKey,\n );\n\n if (savedTheme === undefined) {\n return undefined;\n }\n\n const availableTheme = themes.find(\n (theme: AppTheme): boolean => theme.name === savedTheme,\n );\n\n return availableTheme?.name;\n}\n","import { useCallback, useEffect, useMemo, useState } from \"react\";\n\nimport type { JSX } from \"react\";\n\nimport type {\n ILanguageContextValue,\n ITranslateProviderProps,\n TranslationParams,\n TranslationDictionary,\n} from \"@domain\";\n\nimport { TranslateContext } from \"@aplicacion\";\n\nimport {\n loadLanguagePreference,\n saveLanguagePreference,\n} from \"@infraestructura\";\n\nimport {\n assertLanguageExists,\n resolveActiveLanguage,\n resolveInitialLanguage,\n translateMessage,\n} from \"@aplicacion\";\n\ninterface LanguagePreferenceConfiguration {\n readonly translations: ITranslateProviderProps[\"translations\"];\n readonly initialLanguage: ITranslateProviderProps[\"initialLanguage\"];\n readonly fallbackLanguage: string;\n readonly languageEncryptionKey: ITranslateProviderProps[\"languageEncryptionKey\"];\n readonly languages: readonly string[];\n}\n\ninterface LanguagePreferenceState extends LanguagePreferenceConfiguration {\n readonly selectedLanguage: string;\n readonly isLoaded: boolean;\n}\n\ninterface LanguagePreferenceResult {\n readonly language: string;\n readonly isLoaded: boolean;\n readonly setLanguage: (language: string) => void;\n}\n\ntype SetLanguagePreferenceState = React.Dispatch<\n React.SetStateAction<LanguagePreferenceState>\n>;\n\n/**\n * @description Creates the initial language preference state.\n * @author Steveen Cues\n * @version 4.0.0\n */\nfunction createLanguagePreferenceState(\n configuration: LanguagePreferenceConfiguration,\n selectedLanguage: string,\n isLoaded: boolean,\n): LanguagePreferenceState {\n return {\n ...configuration,\n selectedLanguage,\n isLoaded,\n };\n}\n\n/**\n * @description Checks whether a state matches the active configuration.\n * @author Steveen Cues\n * @version 4.0.0\n */\nfunction matchesLanguageConfiguration(\n state: LanguagePreferenceState,\n configuration: LanguagePreferenceConfiguration,\n): boolean {\n return (\n state.translations === configuration.translations &&\n state.initialLanguage === configuration.initialLanguage &&\n state.fallbackLanguage === configuration.fallbackLanguage &&\n state.languageEncryptionKey === configuration.languageEncryptionKey &&\n state.languages === configuration.languages\n );\n}\n\n/**\n * @description Restores the saved language and falls back when loading fails.\n * @author Steveen Cues\n * @version 4.0.0\n */\nasync function restoreSavedLanguage(\n configuration: LanguagePreferenceConfiguration,\n): Promise<string | undefined> {\n try {\n return await loadLanguagePreference(\n configuration.languages,\n configuration.languageEncryptionKey,\n );\n } catch {\n return undefined;\n }\n}\n\n/**\n * @description Restores the saved language for the active configuration.\n * @author Steveen Cues\n * @version 4.0.0\n */\nfunction useRestoreLanguagePreference(\n configuration: LanguagePreferenceConfiguration,\n setPreferenceState: SetLanguagePreferenceState,\n): void {\n useEffect((): (() => void) => {\n let isActive = true;\n\n const restorePreference = async (): Promise<void> => {\n const savedLanguage = await restoreSavedLanguage(configuration);\n\n if (!isActive) {\n return;\n }\n\n const defaultLanguage = resolveInitialLanguage(\n configuration.translations,\n configuration.initialLanguage,\n configuration.fallbackLanguage,\n );\n\n setPreferenceState(\n (currentState: LanguagePreferenceState): LanguagePreferenceState => {\n if (\n matchesLanguageConfiguration(currentState, configuration) &&\n currentState.isLoaded\n ) {\n return currentState;\n }\n\n return createLanguagePreferenceState(\n configuration,\n savedLanguage ?? defaultLanguage,\n true,\n );\n },\n );\n };\n\n void restorePreference();\n\n return (): void => {\n isActive = false;\n };\n }, [configuration, setPreferenceState]);\n}\n\n/**\n * @description Persists the active language after preference restoration.\n * @author Steveen Cues\n * @version 4.0.0\n */\nfunction usePersistLanguagePreference(\n configuration: LanguagePreferenceConfiguration,\n language: string,\n isLoaded: boolean,\n): void {\n useEffect((): void => {\n if (!isLoaded) {\n return;\n }\n\n void saveLanguagePreference(language, configuration.languageEncryptionKey);\n }, [configuration.languageEncryptionKey, language, isLoaded]);\n}\n\n/**\n * @description Creates a validated language selection handler.\n * @author Steveen Cues\n * @version 4.0.0\n */\nfunction useSetLanguagePreference(\n configuration: LanguagePreferenceConfiguration,\n setPreferenceState: SetLanguagePreferenceState,\n): (language: string) => void {\n return useCallback(\n (nextLanguage: string): void => {\n assertLanguageExists(configuration.translations, nextLanguage);\n\n setPreferenceState(\n createLanguagePreferenceState(configuration, nextLanguage, true),\n );\n },\n [configuration, setPreferenceState],\n );\n}\n\n/**\n * @description Manages language selection, restoration, and persistence.\n * @author Steveen Cues\n * @version 4.0.0\n */\nfunction useLanguagePreference(\n configuration: LanguagePreferenceConfiguration,\n): LanguagePreferenceResult {\n const [preferenceState, setPreferenceState] =\n useState<LanguagePreferenceState>((): LanguagePreferenceState =>\n createLanguagePreferenceState(\n configuration,\n configuration.initialLanguage,\n false,\n ),\n );\n\n useRestoreLanguagePreference(configuration, setPreferenceState);\n\n const isCurrentConfiguration = matchesLanguageConfiguration(\n preferenceState,\n configuration,\n );\n\n const selectedLanguage = isCurrentConfiguration\n ? preferenceState.selectedLanguage\n : configuration.initialLanguage;\n\n const isLoaded = isCurrentConfiguration && preferenceState.isLoaded;\n\n const setLanguage = useSetLanguagePreference(\n configuration,\n setPreferenceState,\n );\n\n return {\n language: resolveActiveLanguage(\n configuration.translations,\n selectedLanguage,\n configuration.initialLanguage,\n configuration.fallbackLanguage,\n ),\n isLoaded,\n setLanguage,\n };\n}\n\n/**\n * @description Provides the active language and translation functions.\n * @author Steveen Cues\n * @version 4.0.0\n */\nexport function TranslateProvider({\n translations,\n initialLanguage,\n fallbackLanguage = \"en\",\n languageEncryptionKey,\n children,\n}: ITranslateProviderProps): JSX.Element {\n const languages = useMemo(\n (): readonly string[] => Object.keys(translations),\n [translations],\n );\n\n const configuration = useMemo(\n (): LanguagePreferenceConfiguration => ({\n translations,\n initialLanguage,\n fallbackLanguage,\n languageEncryptionKey,\n languages,\n }),\n [\n translations,\n initialLanguage,\n fallbackLanguage,\n languageEncryptionKey,\n languages,\n ],\n );\n\n const preference = useLanguagePreference(configuration);\n\n usePersistLanguagePreference(\n configuration,\n preference.language,\n preference.isLoaded,\n );\n\n const contextValue = useTranslationContext(configuration, preference);\n\n return (\n <TranslateContext.Provider value={contextValue}>\n {children}\n </TranslateContext.Provider>\n );\n}\n\n/**\n * @description Creates the translation context value.\n * @author Steveen Cues\n * @version 4.0.0\n */\nfunction useTranslationContext(\n configuration: LanguagePreferenceConfiguration,\n preference: LanguagePreferenceResult,\n): ILanguageContextValue {\n const currentDictionary: TranslationDictionary =\n configuration.translations[preference.language];\n\n const fallbackDictionary: TranslationDictionary =\n configuration.translations[configuration.fallbackLanguage];\n\n const t = useCallback(\n (key: string, params?: TranslationParams): string =>\n translateMessage(currentDictionary, fallbackDictionary, key, params),\n [currentDictionary, fallbackDictionary],\n );\n\n return useMemo(\n (): ILanguageContextValue => ({\n language: preference.language,\n languages: configuration.languages,\n setLanguage: preference.setLanguage,\n t,\n }),\n [preference.language, preference.setLanguage, configuration.languages, t],\n );\n}\n","import { useCallback, useEffect, useMemo, useState, type FC } from \"react\";\nimport type {\n AppTheme,\n CustomThemeDefinition,\n ThemeContextValue,\n ThemeProviderProps,\n} from \"@domain\";\nimport {\n applyThemeToDocument,\n customThemeService,\n findThemeByName,\n isCustomThemeDefinition,\n persistTheme,\n resolveInitialTheme,\n ThemeContext,\n} from \"@aplicacion\";\n\n/**\n * @description Resuelve el tema activo o selecciona el primer tema disponible.\n * @param themes Coleccion de temas disponibles.\n * @param themeName Nombre del tema seleccionado.\n * @returns Tema activo resuelto.\n * @author Steveen Cues\n * @version 1.0.0\n */\nconst resolveActiveTheme = (\n themes: readonly AppTheme[],\n themeName: string,\n): AppTheme => {\n const activeTheme = findThemeByName(themes, themeName);\n\n if (activeTheme !== undefined) {\n return activeTheme;\n }\n\n if (themes.length === 0) {\n throw new Error(\"ThemeProvider requires at least one theme.\");\n }\n\n return themes[0];\n};\n\n/**\n * @description Anade o actualiza un tema personalizado en la coleccion.\n * @param currentThemes Coleccion actual de temas personalizados.\n * @param customTheme Tema que se registrara.\n * @returns Nueva coleccion de temas personalizados.\n * @author Steveen Cues\n * @version 1.0.0\n */\nconst updateCustomThemes = (\n currentThemes: readonly CustomThemeDefinition[],\n customTheme: CustomThemeDefinition,\n): readonly CustomThemeDefinition[] => {\n const existingTheme = currentThemes.some(\n (item) => item.name === customTheme.name,\n );\n\n if (existingTheme) {\n return currentThemes.map((item) =>\n item.name === customTheme.name ? customTheme : item,\n );\n }\n\n return [...currentThemes, customTheme];\n};\n\n/**\n * @description Construye el valor compartido del contexto de temas.\n * @param theme Tema activo.\n * @param themes Coleccion de temas disponibles.\n * @param setTheme Funcion para cambiar el tema.\n * @param registerTheme Funcion para registrar un tema personalizado.\n * @returns Valor del contexto de temas.\n * @author Steveen Cues\n * @version 1.0.0\n */\nconst createThemeContextValue = (\n theme: AppTheme,\n themes: readonly AppTheme[],\n setTheme: (name: string) => void,\n registerTheme: (customTheme: CustomThemeDefinition) => void,\n): ThemeContextValue => ({\n theme,\n themeName: theme.name,\n themes,\n setTheme,\n registerTheme,\n});\n\n/**\n * @description Proporciona los temas base y personalizados a los componentes.\n * Gestiona la seleccion, el registro y la persistencia del tema activo.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport const ThemeProvider: FC<ThemeProviderProps> = ({\n themes: baseThemes,\n initialTheme,\n children,\n}) => {\n const [customThemes, setCustomThemes] = useState<\n readonly CustomThemeDefinition[]\n >([]);\n\n const themes = useMemo<readonly AppTheme[]>(\n () => [...baseThemes, ...customThemes],\n [baseThemes, customThemes],\n );\n\n const [themeName, setThemeName] = useState<string>(() =>\n resolveInitialTheme(baseThemes, initialTheme),\n );\n\n const theme = useMemo<AppTheme>(\n () => resolveActiveTheme(themes, themeName),\n [themes, themeName],\n );\n\n const registerTheme = useCallback(\n (customTheme: CustomThemeDefinition): void => {\n customThemeService.register(customTheme);\n setCustomThemes((currentThemes) =>\n updateCustomThemes(currentThemes, customTheme),\n );\n },\n [],\n );\n\n const setTheme = useCallback(\n (name: string): void => {\n const selectedTheme = findThemeByName(themes, name);\n\n if (selectedTheme === undefined) {\n return;\n }\n\n if (isCustomThemeDefinition(selectedTheme)) {\n customThemeService.register(selectedTheme);\n }\n\n setThemeName(selectedTheme.name);\n },\n [themes],\n );\n\n useEffect((): void => {\n applyThemeToDocument(theme.name);\n persistTheme(theme.name);\n }, [theme.name]);\n\n const contextValue = useMemo(\n () => createThemeContextValue(theme, themes, setTheme, registerTheme),\n [theme, themes, setTheme, registerTheme],\n );\n\n return (\n <ThemeContext.Provider value={contextValue}>\n {children}\n </ThemeContext.Provider>\n );\n};\n","/**\n * @description Expresiones regulares reutilizables para traducciones.\n * @author Steveen Cues\n * @version 2.0.0\n */\nexport const TranslationRegex = Object.freeze({\n INTERPOLATION_PLACEHOLDER: String.raw`\\{\\{([a-zA-Z0-9_]+)\\}\\}`,\n});\n","export const GLOBAL_REGEX_FLAG = \"g\";\n","export const AES_GCM_ALGORITHM = \"AES-GCM\";\nexport const AES_GCM_IV_LENGTH = 12;\nexport const SHA_256_ALGORITHM = \"SHA-256\";\nexport const RAW_KEY_FORMAT = \"raw\";\nexport const STORAGE_VALUE_SEPARATOR = \".\";\nexport const KEY_USAGES: KeyUsage[] = [\"encrypt\", \"decrypt\"];\nexport const ENCRYPTED_VALUE_PART_COUNT = 2;\n","import type { ThemeColorToken } from \"@domain\";\n\n/**\n * @description Define los nombres de las variables CSS de DaisyUI.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport const CSS_THEME_TOKENS = {\n primary: \"--color-primary\",\n primaryContent: \"--color-primary-content\",\n secondary: \"--color-secondary\",\n secondaryContent: \"--color-secondary-content\",\n accent: \"--color-accent\",\n accentContent: \"--color-accent-content\",\n neutral: \"--color-neutral\",\n neutralContent: \"--color-neutral-content\",\n base100: \"--color-base-100\",\n base200: \"--color-base-200\",\n base300: \"--color-base-300\",\n baseContent: \"--color-base-content\",\n info: \"--color-info\",\n infoContent: \"--color-info-content\",\n success: \"--color-success\",\n successContent: \"--color-success-content\",\n warning: \"--color-warning\",\n warningContent: \"--color-warning-content\",\n error: \"--color-error\",\n errorContent: \"--color-error-content\",\n} as const;\n\n/**\n * @description Define la coleccion de claves y variables CSS de DaisyUI.\n * Cada entrada relaciona una clave de color con su variable CSS.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport const CSS_THEME_TOKEN_ENTRIES: readonly [\n ThemeColorToken,\n (typeof CSS_THEME_TOKENS)[ThemeColorToken],\n][] = [\n [\"primary\", CSS_THEME_TOKENS.primary],\n [\"primaryContent\", CSS_THEME_TOKENS.primaryContent],\n [\"secondary\", CSS_THEME_TOKENS.secondary],\n [\"secondaryContent\", CSS_THEME_TOKENS.secondaryContent],\n [\"accent\", CSS_THEME_TOKENS.accent],\n [\"accentContent\", CSS_THEME_TOKENS.accentContent],\n [\"neutral\", CSS_THEME_TOKENS.neutral],\n [\"neutralContent\", CSS_THEME_TOKENS.neutralContent],\n [\"base100\", CSS_THEME_TOKENS.base100],\n [\"base200\", CSS_THEME_TOKENS.base200],\n [\"base300\", CSS_THEME_TOKENS.base300],\n [\"baseContent\", CSS_THEME_TOKENS.baseContent],\n [\"info\", CSS_THEME_TOKENS.info],\n [\"infoContent\", CSS_THEME_TOKENS.infoContent],\n [\"success\", CSS_THEME_TOKENS.success],\n [\"successContent\", CSS_THEME_TOKENS.successContent],\n [\"warning\", CSS_THEME_TOKENS.warning],\n [\"warningContent\", CSS_THEME_TOKENS.warningContent],\n [\"error\", CSS_THEME_TOKENS.error],\n [\"errorContent\", CSS_THEME_TOKENS.errorContent],\n];\n","/**\n * @description Defines the standard typography weights.\n * @author Steveen Cues\n * @version 2.0.0\n */\nconst THEME_FONT_WEIGHT_THIN = 100;\nconst THEME_FONT_WEIGHT_EXTRA_LIGHT = 200;\nconst THEME_FONT_WEIGHT_LIGHT = 300;\nconst THEME_FONT_WEIGHT_REGULAR = 400;\nconst THEME_FONT_WEIGHT_MEDIUM = 500;\nconst THEME_FONT_WEIGHT_SEMI_BOLD = 600;\nconst THEME_FONT_WEIGHT_BOLD = 700;\nconst THEME_FONT_WEIGHT_EXTRA_BOLD = 800;\nconst THEME_FONT_WEIGHT_BLACK = 900;\n\n/**\n * @description Defines the available typography weights.\n * @author Steveen Cues\n * @version 2.0.0\n */\nexport const THEME_FONT_WEIGHT = {\n THIN: THEME_FONT_WEIGHT_THIN,\n EXTRA_LIGHT: THEME_FONT_WEIGHT_EXTRA_LIGHT,\n LIGHT: THEME_FONT_WEIGHT_LIGHT,\n REGULAR: THEME_FONT_WEIGHT_REGULAR,\n MEDIUM: THEME_FONT_WEIGHT_MEDIUM,\n SEMI_BOLD: THEME_FONT_WEIGHT_SEMI_BOLD,\n BOLD: THEME_FONT_WEIGHT_BOLD,\n EXTRA_BOLD: THEME_FONT_WEIGHT_EXTRA_BOLD,\n BLACK: THEME_FONT_WEIGHT_BLACK,\n} as const;\n","import {\n type TranslationCatalog,\n type TranslationDictionary,\n type TranslationParams,\n type TranslationValue,\n GLOBAL_REGEX_FLAG,\n TranslationRegex,\n} from \"@domain\";\n\n/**\n * @description Comprueba si un idioma existe en el catalogo.\n * @param translations Catalogo disponible.\n * @param language Codigo del idioma.\n * @returns Indica si el idioma esta registrado.\n * @author Steveen Cues\n * @version 2.0.0\n */\nfunction isLanguageAvailable(\n translations: TranslationCatalog,\n language: string,\n): boolean {\n return Object.hasOwn(translations, language);\n}\n\n/**\n * @description Obtiene el primer idioma registrado en el catalogo.\n * @param translations Catalogo de traducciones.\n * @returns Codigo del primer idioma o undefined si esta vacio.\n * @author Steveen Cues\n * @version 2.0.0\n */\nfunction findFirstLanguage(\n translations: TranslationCatalog,\n): string | undefined {\n const [firstLanguage] = Object.keys(translations);\n\n return firstLanguage;\n}\n\n/**\n * @description Obtiene el idioma inicial disponible, priorizando el\n * idioma solicitado, el idioma de respaldo y el primer idioma registrado.\n * @param translations Catalogo de traducciones.\n * @param initialLanguage Idioma solicitado inicialmente.\n * @param fallbackLanguage Idioma de respaldo.\n * @returns Codigo del idioma inicial disponible.\n * @throws Error cuando el catalogo esta vacio.\n * @author Steveen Cues\n * @version 2.0.0\n */\nexport function resolveInitialLanguage(\n translations: TranslationCatalog,\n initialLanguage: string,\n fallbackLanguage: string,\n): string {\n if (isLanguageAvailable(translations, initialLanguage)) {\n return initialLanguage;\n }\n\n if (isLanguageAvailable(translations, fallbackLanguage)) {\n return fallbackLanguage;\n }\n\n const firstLanguage = findFirstLanguage(translations);\n\n if (firstLanguage === undefined) {\n throw new Error(\n \"TranslateProvider requires at least one translation catalog.\",\n );\n }\n\n return firstLanguage;\n}\n\n/**\n * @description Resuelve el idioma seleccionado y utiliza el idioma\n * inicial, el respaldo o el primer idioma disponible si es necesario.\n * @param translations Catalogo de traducciones.\n * @param selectedLanguage Idioma seleccionado actualmente.\n * @param initialLanguage Idioma inicial configurado.\n * @param fallbackLanguage Idioma de respaldo.\n * @returns Codigo del idioma activo.\n * @author Steveen Cues\n * @version 2.0.0\n */\nexport function resolveActiveLanguage(\n translations: TranslationCatalog,\n selectedLanguage: string,\n initialLanguage: string,\n fallbackLanguage: string,\n): string {\n if (isLanguageAvailable(translations, selectedLanguage)) {\n return selectedLanguage;\n }\n\n return resolveInitialLanguage(\n translations,\n initialLanguage,\n fallbackLanguage,\n );\n}\n\n/**\n * @description Valida que un idioma este registrado en el catalogo.\n * @param translations Catalogo de traducciones.\n * @param language Codigo del idioma que se desea validar.\n * @returns No devuelve ningun valor.\n * @throws Error cuando el idioma no esta disponible.\n * @author Steveen Cues\n * @version 2.0.0\n */\nexport function assertLanguageExists(\n translations: TranslationCatalog,\n language: string,\n): void {\n if (isLanguageAvailable(translations, language)) {\n return;\n }\n\n throw new Error(\n `Language \"${language}\" is not available in the translation catalog.`,\n );\n}\n\n/**\n * @description Obtiene el valor anidado de una clave usando notacion\n * de puntos, por ejemplo \"navigation.contact\".\n * @param dictionary Diccionario donde se realiza la busqueda.\n * @param key Clave de traduccion.\n * @returns Valor encontrado o undefined si no existe.\n * @author Steveen Cues\n * @version 2.0.0\n */\nfunction findTranslationValue(\n dictionary: TranslationDictionary,\n key: string,\n): TranslationValue | undefined {\n let current: TranslationValue = dictionary;\n\n for (const segment of key.split(\".\")) {\n if (typeof current === \"string\") {\n return undefined;\n }\n\n if (!Object.hasOwn(current, segment)) {\n return undefined;\n }\n\n current = current[segment];\n }\n\n return current;\n}\n\n/**\n * @description Busca una traduccion dentro de un diccionario.\n * @param dictionary Diccionario del idioma.\n * @param key Clave con notacion de puntos.\n * @returns Traduccion encontrada o undefined.\n * @author Steveen Cues\n * @version 2.0.0\n */\nexport function findTranslation(\n dictionary: TranslationDictionary | undefined,\n key: string,\n): string | undefined {\n if (dictionary === undefined || key === \"\") {\n return undefined;\n }\n\n const value = findTranslationValue(dictionary, key);\n\n return typeof value === \"string\" ? value : undefined;\n}\n\n/**\n * @description Resuelve el texto correspondiente a un marcador de\n * interpolacion sin eliminarlo cuando falta su parametro.\n * @param placeholder Marcador original, por ejemplo {{name}}.\n * @param key Nombre del parametro.\n * @param params Parametros disponibles.\n * @returns Valor interpolado o marcador original.\n * @author Steveen Cues\n * @version 2.0.0\n */\nfunction resolveInterpolationValue(\n placeholder: string,\n key: string,\n params: TranslationParams | undefined,\n): string {\n const value = params?.[key];\n\n return value === undefined ? placeholder : String(value);\n}\n\n/**\n * @description Sustituye los marcadores {{name}} usando los parametros.\n * @param message Mensaje que se desea interpolar.\n * @param params Valores disponibles para la interpolacion.\n * @returns Mensaje con los parametros sustituidos.\n * @author Steveen Cues\n * @version 2.0.0\n */\nexport function interpolateTranslation(\n message: string,\n params?: TranslationParams,\n): string {\n const placeholderRegex = new RegExp(\n TranslationRegex.INTERPOLATION_PLACEHOLDER,\n GLOBAL_REGEX_FLAG,\n );\n\n return message.replace(\n placeholderRegex,\n (placeholder: string, key: string): string =>\n resolveInterpolationValue(placeholder, key, params),\n );\n}\n\n/**\n * @description Busca una traduccion en el idioma activo y utiliza el\n * idioma de respaldo cuando la clave no esta disponible.\n * @param currentDictionary Diccionario del idioma activo.\n * @param fallbackDictionary Diccionario del idioma de respaldo.\n * @param key Clave de traduccion.\n * @returns Traduccion encontrada o clave original.\n * @author Steveen Cues\n * @version 2.0.0\n */\nfunction resolveTranslationMessage(\n currentDictionary: TranslationDictionary | undefined,\n fallbackDictionary: TranslationDictionary | undefined,\n key: string,\n): string {\n const currentTranslation = findTranslation(currentDictionary, key);\n\n if (currentTranslation !== undefined) {\n return currentTranslation;\n }\n\n const fallbackTranslation = findTranslation(fallbackDictionary, key);\n\n return fallbackTranslation ?? key;\n}\n\n/**\n * @description Traduce un mensaje aplicando respaldo e interpolacion.\n * @param currentDictionary Diccionario del idioma activo.\n * @param fallbackDictionary Diccionario del idioma de respaldo.\n * @param key Clave de traduccion.\n * @param params Parametros de interpolacion.\n * @returns Mensaje final traducido e interpolado.\n * @author Steveen Cues\n * @version 2.0.0\n */\nexport function translateMessage(\n currentDictionary: TranslationDictionary | undefined,\n fallbackDictionary: TranslationDictionary | undefined,\n key: string,\n params?: TranslationParams,\n): string {\n const message = resolveTranslationMessage(\n currentDictionary,\n fallbackDictionary,\n key,\n );\n\n return interpolateTranslation(message, params);\n}\n","import type { AppTheme, CustomThemeDefinition } from \"@domain\";\n\n/**\n * @description Busca un tema por su nombre dentro de una coleccion.\n * @param themes Coleccion de temas disponibles.\n * @param themeName Nombre del tema que se desea encontrar.\n * @returns El tema encontrado o undefined si no existe.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport const findThemeByName = (\n themes: readonly AppTheme[],\n themeName: string,\n): AppTheme | undefined => themes.find((theme) => theme.name === themeName);\n\n/**\n * @description Comprueba si un tema contiene la configuracion de DaisyUI.\n * @param theme Tema que se desea comprobar.\n * @returns true si el tema es una definicion personalizada.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport const isCustomThemeDefinition = (\n theme: AppTheme,\n): theme is CustomThemeDefinition => \"daisyUI\" in theme;\n\n/**\n * @description Resuelve el tema inicial segun la preferencia almacenada,\n * el tema configurado y el primer tema disponible.\n * @param themes Coleccion de temas disponibles.\n * @param initialTheme Nombre del tema inicial configurado.\n * @returns Nombre del tema inicial resuelto.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport const resolveInitialTheme = (\n themes: readonly AppTheme[],\n initialTheme: string,\n): string => {\n if (typeof window !== \"undefined\") {\n const savedTheme = window.localStorage.getItem(\"app_theme\");\n\n if (\n savedTheme !== null &&\n savedTheme.length > 0 &&\n findThemeByName(themes, savedTheme) !== undefined\n ) {\n return savedTheme;\n }\n }\n\n const configuredTheme = findThemeByName(themes, initialTheme);\n\n if (configuredTheme !== undefined) {\n return configuredTheme.name;\n }\n\n if (themes.length === 0) {\n return initialTheme;\n }\n\n return themes[0].name;\n};\n\n/**\n * @description Actualiza el atributo data-theme del documento.\n * @param themeName Nombre del tema que se aplicara.\n * @returns void.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport const applyThemeToDocument = (themeName: string): void => {\n if (typeof document === \"undefined\") {\n return;\n }\n\n document.documentElement.setAttribute(\"data-theme\", themeName);\n};\n\n/**\n * @description Persiste el nombre del tema seleccionado en el navegador.\n * @param themeName Nombre del tema que se almacenara.\n * @returns void.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport const persistTheme = (themeName: string): void => {\n if (typeof window === \"undefined\") {\n return;\n }\n\n window.localStorage.setItem(\"app_theme\", themeName);\n};\n","/**\n * @description Obtains the cryptographic API when available.\n * @returns Cryptographic API or undefined if unavailable.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport function getCryptoApi(): Crypto | undefined {\n try {\n return globalThis.crypto;\n } catch {\n return undefined;\n }\n}\n","/**\n * @description Obtains localStorage when available.\n * @returns Browser storage or undefined if unavailable.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport function getLocalStorage(): Storage | undefined {\n try {\n return globalThis.localStorage;\n } catch {\n return undefined;\n }\n}\n","/**\n * @description Encodes text into UTF-8 bytes.\n * @param value Text to encode.\n * @returns Encoded bytes.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport function encodeText(value: string): Uint8Array<ArrayBuffer> {\n return new TextEncoder().encode(value);\n}\n\n/**\n * @description Decodes UTF-8 bytes into text.\n * @param value Encoded bytes.\n * @returns Decoded text.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport function decodeText(value: ArrayBuffer): string {\n return new TextDecoder().decode(value);\n}\n","/**\n * @description Encodes bytes into Base64.\n * @param bytes Bytes to encode.\n * @returns Base64 string.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport function encodeBase64(bytes: Uint8Array): string {\n let binary = \"\";\n\n for (const byte of bytes) {\n binary += String.fromCharCode(byte);\n }\n\n return btoa(binary);\n}\n\n/**\n * @description Decodes Base64 into bytes.\n * @param value Base64 string.\n * @returns Decoded bytes.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport function decodeBase64(value: string): Uint8Array<ArrayBuffer> {\n const binary = atob(value);\n const bytes = new Uint8Array(binary.length);\n\n for (let index = 0; index < binary.length; index += 1) {\n bytes[index] = binary.charCodeAt(index);\n }\n\n return bytes;\n}\n","import {\n AES_GCM_ALGORITHM,\n AES_GCM_IV_LENGTH,\n ENCRYPTED_VALUE_PART_COUNT,\n KEY_USAGES,\n RAW_KEY_FORMAT,\n SHA_256_ALGORITHM,\n STORAGE_VALUE_SEPARATOR,\n} from \"@domain\";\n\nimport {\n decodeBase64,\n encodeBase64,\n getCryptoApi,\n decodeText,\n encodeText,\n} from \"@aplicacion\";\n\n/**\n * @description Represents validated encrypted data.\n * @author Steveen Cues\n * @version 2.0.0\n */\ninterface EncryptedPayload {\n iv: Uint8Array<ArrayBuffer>;\n ciphertext: Uint8Array<ArrayBuffer>;\n}\n\n/**\n * @description Derives an AES-GCM key from a configured secret.\n * @param secret Encryption secret.\n * @returns Crypto key or undefined when unavailable.\n * @author Steveen Cues\n * @version 2.0.0\n */\nasync function deriveAesKey(secret: string): Promise<CryptoKey | undefined> {\n const cryptoApi = getCryptoApi();\n\n if (cryptoApi === undefined || secret.length === 0) {\n return undefined;\n }\n\n const digest = await cryptoApi.subtle.digest(\n SHA_256_ALGORITHM,\n encodeText(secret),\n );\n\n return cryptoApi.subtle.importKey(\n RAW_KEY_FORMAT,\n digest,\n { name: AES_GCM_ALGORITHM },\n false,\n KEY_USAGES,\n );\n}\n\n/**\n * @description Creates a cryptographically random initialization vector.\n * @returns Initialization vector.\n * @author Steveen Cues\n * @version 2.0.0\n */\nfunction createInitializationVector(): Uint8Array<ArrayBuffer> {\n const cryptoApi = getCryptoApi();\n\n if (cryptoApi === undefined) {\n throw new Error(\"Cryptographic API is unavailable.\");\n }\n\n return cryptoApi.getRandomValues(new Uint8Array(AES_GCM_IV_LENGTH));\n}\n\n/**\n * @description Validates and decodes an initialization vector.\n * @param value Base64-encoded initialization vector.\n * @returns Valid vector or undefined.\n * @author Steveen Cues\n * @version 2.0.0\n */\nfunction decodeInitializationVector(\n value: string,\n): Uint8Array<ArrayBuffer> | undefined {\n try {\n const iv = decodeBase64(value);\n\n return iv.length === AES_GCM_IV_LENGTH ? iv : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * @description Checks whether the encrypted value has the expected segments.\n * @param parts Encrypted value segments.\n * @returns Whether the segments contain an IV and ciphertext.\n * @author Steveen Cues\n * @version 2.2.0\n */\nfunction hasExpectedEncryptedParts(parts: string[]): parts is [string, string] {\n return parts.length === ENCRYPTED_VALUE_PART_COUNT;\n}\n\n/**\n * @description Validates the encrypted value segments.\n * @param parts Encrypted value segments.\n * @returns Validated segments or undefined.\n * @author Steveen Cues\n * @version 2.2.0\n */\nfunction validateEncryptedParts(parts: string[]): [string, string] | undefined {\n if (!hasExpectedEncryptedParts(parts)) {\n return undefined;\n }\n\n return [parts[0], parts[1]];\n}\n\n/**\n * @description Splits and validates an encrypted value.\n * @param value Encoded initialization vector and ciphertext.\n * @returns Validated payload or undefined.\n * @author Steveen Cues\n * @version 2.1.0\n */\nfunction parseEncryptedPayload(value: string): EncryptedPayload | undefined {\n const parts = validateEncryptedParts(value.split(STORAGE_VALUE_SEPARATOR));\n\n if (parts === undefined) {\n return undefined;\n }\n\n const [encodedIv, encodedCiphertext] = parts;\n const iv = decodeInitializationVector(encodedIv);\n\n if (iv === undefined) {\n return undefined;\n }\n\n try {\n return {\n iv,\n ciphertext: decodeBase64(encodedCiphertext),\n };\n } catch {\n return undefined;\n }\n}\n\n/**\n * @description Encrypts bytes using AES-GCM.\n * @param plaintext Plaintext bytes.\n * @param key Encryption key.\n * @param iv Initialization vector.\n * @returns Encrypted bytes.\n * @author Steveen Cues\n * @version 2.1.0\n */\nasync function encryptPayload(\n plaintext: Uint8Array<ArrayBuffer>,\n key: CryptoKey,\n iv: Uint8Array<ArrayBuffer>,\n): Promise<ArrayBuffer> {\n const cryptoApi = requireCryptoApi();\n\n return cryptoApi.subtle.encrypt(\n { name: AES_GCM_ALGORITHM, iv },\n key,\n plaintext,\n );\n}\n\n/**\n * @description Decrypts bytes using AES-GCM.\n * @param payload Validated encrypted payload.\n * @param key Decryption key.\n * @returns Decrypted bytes.\n * @author Steveen Cues\n * @version 2.1.0\n */\nasync function decryptPayload(\n payload: EncryptedPayload,\n key: CryptoKey,\n): Promise<ArrayBuffer> {\n const cryptoApi = requireCryptoApi();\n\n return cryptoApi.subtle.decrypt(\n {\n name: AES_GCM_ALGORITHM,\n iv: payload.iv,\n },\n key,\n payload.ciphertext,\n );\n}\n\n/**\n * @description Obtains the cryptographic API or throws when unavailable.\n * @returns Available cryptographic API.\n * @author Steveen Cues\n * @version 2.2.0\n */\nfunction requireCryptoApi(): Crypto {\n const cryptoApi = getCryptoApi();\n\n if (cryptoApi === undefined) {\n throw new Error(\"Cryptographic API is unavailable.\");\n }\n\n return cryptoApi;\n}\n\n/**\n * @description Encrypts text using AES-GCM.\n * @param value Text to encrypt.\n * @param secret Encryption secret.\n * @returns Encoded encrypted value or undefined when unavailable.\n * @author Steveen Cues\n * @version 2.0.0\n */\nexport async function encryptText(\n value: string,\n secret: string,\n): Promise<string | undefined> {\n try {\n const key = await deriveAesKey(secret);\n\n if (key === undefined) {\n return undefined;\n }\n\n const iv = createInitializationVector();\n const ciphertext = await encryptPayload(encodeText(value), key, iv);\n\n return [encodeBase64(iv), encodeBase64(new Uint8Array(ciphertext))].join(\n STORAGE_VALUE_SEPARATOR,\n );\n } catch {\n return undefined;\n }\n}\n\n/**\n * @description Decrypts an AES-GCM encrypted value.\n * @param value Encoded initialization vector and ciphertext.\n * @param secret Encryption secret.\n * @returns Original text or undefined if decryption fails.\n * @author Steveen Cues\n * @version 2.0.0\n */\nexport async function decryptText(\n value: string,\n secret: string,\n): Promise<string | undefined> {\n try {\n const payload = parseEncryptedPayload(value);\n\n if (payload === undefined) {\n return undefined;\n }\n\n const key = await deriveAesKey(secret);\n\n if (key === undefined) {\n return undefined;\n }\n\n const plaintext = await decryptPayload(payload, key);\n\n return decodeText(plaintext);\n } catch {\n return undefined;\n }\n}\n","import {\n CSS_THEME_TOKEN_ENTRIES,\n type CustomThemeDefinition,\n type ThemeColorToken,\n} from \"@domain\";\n\n/**\n * @description Gestiona el registro de temas personalizados de DaisyUI.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport class CustomThemeService {\n private static readonly STYLE_ID_PREFIX = \"daisyui-custom-theme-\";\n\n /**\n * @description Registra o actualiza las variables CSS de un tema.\n * @param theme Definicion del tema personalizado.\n */\n public register(theme: CustomThemeDefinition): void {\n this.validateThemeName(theme.name);\n\n const styleElement = this.getOrCreateStyleElement(theme.name);\n const declarations = this.createColorDeclarations(theme);\n\n styleElement.textContent = this.createThemeStylesheet(theme, declarations);\n }\n\n /**\n * @description Elimina el registro CSS de un tema personalizado.\n * @param themeName Nombre del tema.\n */\n public unregister(themeName: string): void {\n this.validateThemeName(themeName);\n\n const styleElement = document.getElementById(\n this.getStyleElementId(themeName),\n );\n\n styleElement?.remove();\n }\n\n /**\n * @description Comprueba si un tema tiene estilos registrados.\n * @param themeName Nombre del tema.\n * @returns Indica si el tema esta registrado.\n */\n public isRegistered(themeName: string): boolean {\n this.validateThemeName(themeName);\n\n return document.getElementById(this.getStyleElementId(themeName)) !== null;\n }\n\n /**\n * @description Valida el nombre del tema.\n * @param themeName Nombre del tema.\n */\n private validateThemeName(themeName: string): void {\n const validThemeName = /^[a-zA-Z][a-zA-Z0-9_-]*$/.test(themeName);\n\n if (!validThemeName) {\n throw new Error(`Invalid theme name: ${themeName}`);\n }\n }\n\n /**\n * @description Genera las declaraciones CSS de los colores del tema.\n * @param theme Definicion del tema.\n * @returns Declaraciones CSS.\n * @author Steveen Cues\n * @version 1.0.0\n */\n private createColorDeclarations(theme: CustomThemeDefinition): string {\n return CSS_THEME_TOKEN_ENTRIES.map(([token, variable]) => {\n const color = theme.daisyUI[token];\n\n this.validateCssColor(token, color);\n\n return `${variable}: ${color};`;\n }).join(\"\\n\");\n }\n\n /**\n * @description Valida un color CSS.\n * @param token Nombre del token.\n * @param color Valor CSS.\n */\n private validateCssColor(token: ThemeColorToken, color: string): void {\n if (typeof CSS === \"undefined\" || !CSS.supports(\"color\", color)) {\n throw new Error(`Invalid CSS color for theme token: ${token}`);\n }\n }\n\n /**\n * @description Genera las reglas CSS del tema.\n * @param theme Definicion del tema.\n * @param declarations Declaraciones CSS de los colores.\n * @returns Hoja de estilos CSS.\n */\n private createThemeStylesheet(\n theme: CustomThemeDefinition,\n declarations: string,\n ): string {\n return `\n [data-theme=\"${theme.name}\"] {\n color-scheme: ${theme.daisyUI.colorScheme};\n ${declarations}\n }\n `;\n }\n\n /**\n * @description Obtiene el elemento de estilos o lo crea si no existe.\n * @param themeName Nombre del tema.\n * @returns Elemento de estilos CSS.\n */\n private getOrCreateStyleElement(themeName: string): HTMLStyleElement {\n const styleId = this.getStyleElementId(themeName);\n const existingElement = document.getElementById(styleId);\n\n if (existingElement instanceof HTMLStyleElement) {\n return existingElement;\n }\n\n existingElement?.remove();\n\n const styleElement = document.createElement(\"style\");\n styleElement.id = styleId;\n\n document.head.appendChild(styleElement);\n\n return styleElement;\n }\n\n /**\n * @description Construye el identificador del elemento de estilos.\n * @param themeName Nombre del tema.\n * @returns Identificador HTML.\n */\n private getStyleElementId(themeName: string): string {\n return `${CustomThemeService.STYLE_ID_PREFIX}${themeName}`;\n }\n}\n\n/**\n * @description Instancia compartida del servicio de temas personalizados.\n */\nexport const customThemeService = new CustomThemeService();\n","import type { FC, ButtonHTMLAttributes } from \"react\";\nimport { useLanguage } from \"@aplicacion\";\n\n/**\n * @description Boton de prueba con DaisyUI y traduccion.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport interface TestButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n readonly label?: string;\n}\n\n/**\n * @description Renderiza un boton adaptable al tema activo.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport const Button: FC<TestButtonProps> = ({\n label,\n className = \"\",\n ...props\n}) => {\n const { t } = useLanguage();\n\n return (\n <button\n {...props}\n type={props.type ?? \"button\"}\n className={`btn btn-primary ${className}`}\n >\n {label ?? t(\"common.button\")}\n </button>\n );\n};\n\nexport default Button;\n","import type { FC, JSX } from \"react\";\nimport type { ThemeSelectorTriggerProps } from \"./theme-selector.types\";\n\n/**\n * @description Muestra el tema activo y controla la apertura del selector.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport const ThemeSelectorTrigger: FC<ThemeSelectorTriggerProps> = ({\n theme,\n isOpen,\n onToggle,\n}): JSX.Element => {\n return (\n <button\n type=\"button\"\n aria-haspopup=\"listbox\"\n aria-expanded={isOpen}\n onClick={onToggle}\n className=\"flex w-full items-center gap-3 rounded-2xl border border-base-300 bg-base-100 p-3 text-left shadow-sm transition hover:border-primary/50 hover:shadow-md focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary\"\n >\n <span\n data-theme={theme.name}\n className=\"flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-primary text-primary-content shadow-sm\"\n >\n <span className=\"text-lg font-bold\">\n {theme.manifest.name.charAt(0).toUpperCase()}\n </span>\n </span>\n\n <span className=\"min-w-0 flex-1\">\n <span className=\"block truncate font-semibold text-base-content\">\n {theme.manifest.name}\n </span>\n <span className=\"block text-xs text-base-content/60\">\n Tema seleccionado\n </span>\n </span>\n\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n className={`h-5 w-5 shrink-0 transition-transform ${\n isOpen ? \"rotate-180\" : \"\"\n }`}\n >\n <path d=\"m6 9 6 6 6-6\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n </button>\n );\n};\n","import type { FC, JSX } from \"react\";\nimport type { ThemeSelectorSearchProps } from \"./theme-selector.types\";\n\n/**\n * @description Permite buscar temas por nombre o descripcion.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport const ThemeSelectorSearch: FC<ThemeSelectorSearchProps> = ({\n value,\n placeholder,\n onChange,\n}): JSX.Element => {\n return (\n <label className=\"input input-bordered flex w-full items-center gap-2 rounded-xl\">\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n className=\"h-4 w-4 opacity-60\"\n >\n <circle cx=\"11\" cy=\"11\" r=\"7\" />\n <path d=\"m16 16 4 4\" strokeLinecap=\"round\" />\n </svg>\n\n <input\n type=\"search\"\n value={value}\n onChange={(event) => onChange(event.target.value)}\n placeholder={placeholder}\n aria-label={placeholder}\n className=\"grow\"\n />\n </label>\n );\n};\n","import type { FC, JSX } from \"react\";\nimport type { ThemeSelectorOptionProps } from \"./theme-selector.types\";\n\n/**\n * @description Representa una opcion individual del selector de temas.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport const ThemeSelectorOption: FC<ThemeSelectorOptionProps> = ({\n theme,\n isSelected,\n showDescription,\n onSelect,\n}): JSX.Element => {\n return (\n <li>\n <button\n type=\"button\"\n role=\"option\"\n aria-selected={isSelected}\n onClick={() => onSelect(theme.name)}\n className={`flex w-full items-center gap-3 rounded-xl p-3 text-left transition ${\n isSelected\n ? \"bg-primary/10 text-primary\"\n : \"text-base-content hover:bg-base-200\"\n }`}\n >\n <span\n data-theme={theme.name}\n className=\"flex h-10 w-10 shrink-0 items-center justify-center gap-1 overflow-hidden rounded-xl border border-base-300 bg-base-100 p-1\"\n >\n <span className=\"h-full w-2 rounded-full bg-primary\" />\n <span className=\"h-full w-2 rounded-full bg-secondary\" />\n <span className=\"h-full w-2 rounded-full bg-accent\" />\n </span>\n\n <span className=\"min-w-0 flex-1\">\n <span className=\"block truncate text-sm font-semibold\">\n {theme.manifest.name}\n </span>\n\n {showDescription && (\n <span className=\"mt-0.5 block truncate text-xs text-base-content/60\">\n {theme.manifest.description}\n </span>\n )}\n </span>\n\n {isSelected && (\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n className=\"h-5 w-5 shrink-0\"\n >\n <path\n d=\"m5 12 4 4L19 6\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n )}\n </button>\n </li>\n );\n};\n","import type { FC, JSX } from \"react\";\nimport { ThemeSelectorOption } from \"./theme-selector-option\";\nimport type { ThemeSelectorListProps } from \"./theme-selector.types\";\n\n/**\n * @description Renderiza las opciones disponibles y el estado vacio.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport const ThemeSelectorList: FC<ThemeSelectorListProps> = ({\n themes,\n selectedTheme,\n showDescriptions,\n label,\n onSelect,\n}): JSX.Element => {\n return (\n <ul\n role=\"listbox\"\n aria-label={label}\n className=\"max-h-72 space-y-1 overflow-y-auto p-2\"\n >\n {themes.map((availableTheme) => (\n <ThemeSelectorOption\n key={availableTheme.name}\n theme={availableTheme}\n isSelected={availableTheme.name === selectedTheme}\n showDescription={showDescriptions}\n onSelect={onSelect}\n />\n ))}\n\n {themes.length === 0 && (\n <li className=\"px-3 py-8 text-center text-sm text-base-content/60\">\n No se encontraron temas.\n </li>\n )}\n </ul>\n );\n};\n","import type { FC, JSX } from \"react\";\nimport type { AppTheme } from \"@domain\";\nimport { ThemeSelectorSearch } from \"./theme-selector-search\";\nimport { ThemeSelectorList } from \"./theme-selector-list\";\n\ninterface ThemeSelectorPanelProps {\n readonly themes: readonly AppTheme[];\n readonly totalThemes: number;\n readonly selectedTheme: string;\n readonly search: string;\n readonly showSearch: boolean;\n readonly placeholder: string;\n readonly label: string;\n readonly showDescriptions: boolean;\n readonly onSearchChange: (value: string) => void;\n readonly onSelectTheme: (themeName: string) => void;\n}\n\n/**\n * @description Renderiza el panel desplegable para seleccionar temas.\n * @author Steveen Cues\n * @version 1.0.0\n */\nexport const ThemeSelectorPanel: FC<ThemeSelectorPanelProps> = ({\n themes,\n totalThemes,\n selectedTheme,\n search,\n showSearch,\n placeholder,\n label,\n showDescriptions,\n onSearchChange,\n onSelectTheme,\n}): JSX.Element => (\n <div className=\"absolute left-0 right-0 top-full z-50 mt-2 overflow-hidden rounded-2xl border border-base-300 bg-base-100 shadow-xl\">\n <div className=\"border-b border-base-300 p-3\">\n <p className=\"mb-2 text-xs font-semibold uppercase tracking-wider text-base-content/60\">\n Selecciona un tema\n </p>\n\n {showSearch && (\n <ThemeSelectorSearch\n value={search}\n placeholder={placeholder}\n onChange={onSearchChange}\n />\n )}\n </div>\n\n <ThemeSelectorList\n themes={themes}\n selectedTheme={selectedTheme}\n showDescriptions={showDescriptions}\n label={label}\n onSelect={onSelectTheme}\n />\n\n <div className=\"border-t border-base-300 px-3 py-2\">\n <p className=\"text-xs text-base-content/50\">\n {themes.length} de {totalThemes} temas\n </p>\n </div>\n </div>\n);\n","import { useState, type FC, type JSX } from \"react\";\nimport { useTheme } from \"@aplicacion\";\nimport type { AppTheme } from \"@domain\";\nimport type { ThemeSelectorProps } from \"./theme-selector.types\";\nimport { ThemeSelectorTrigger } from \"./theme-selector-trigger\";\nimport { ThemeSelectorPanel } from \"./theme-selector-panel\";\n\nconst SEARCH_THRESHOLD = 5;\n\n/**\n * @description Filtra los temas por nombre y descripcion.\n * @author Steveen Cues\n * @version 1.0.0\n */\nconst filterThemes = (\n themes: readonly AppTheme[],\n search: string,\n): readonly AppTheme[] => {\n const normalizedSearch = search.trim().toLocaleLowerCase();\n\n return themes.filter((availableTheme) =>\n [\n availableTheme.name,\n availableTheme.manifest.name,\n availableTheme.manifest.description,\n ].some((value) => value.toLocaleLowerCase().includes(normalizedSearch)),\n );\n};\n\n/**\n * @description Permite buscar, visualizar y seleccionar temas disponibles.\n * @author Steveen Cues\n * @version 2.2.0\n */\nexport const ThemeSelector: FC<ThemeSelectorProps> = ({\n label = \"Tema visual\",\n placeholder = \"Buscar tema...\",\n className = \"\",\n showDescriptions = true,\n}): JSX.Element => {\n const { theme, themes, setTheme } = useTheme();\n const [isOpen, setIsOpen] = useState<boolean>(false);\n const [search, setSearch] = useState<string>(\"\");\n\n const filteredThemes = filterThemes(themes, search);\n\n const handleToggle = (): void => {\n setIsOpen((previousValue) => !previousValue);\n setSearch(\"\");\n };\n\n const handleSelectTheme = (themeName: string): void => {\n setTheme(themeName);\n setIsOpen(false);\n setSearch(\"\");\n };\n\n return (\n <div className={`relative w-full max-w-sm ${className}`}>\n <label className=\"mb-2 block text-sm font-semibold text-base-content\">\n {label}\n </label>\n\n <ThemeSelectorTrigger\n theme={theme}\n isOpen={isOpen}\n onToggle={handleToggle}\n />\n\n {isOpen && (\n <ThemeSelectorPanel\n themes={filteredThemes}\n totalThemes={themes.length}\n selectedTheme={theme.name}\n search={search}\n showSearch={themes.length > SEARCH_THRESHOLD}\n placeholder={placeholder}\n label={label}\n showDescriptions={showDescriptions}\n onSearchChange={setSearch}\n onSelectTheme={handleSelectTheme}\n />\n )}\n </div>\n );\n};\n\nexport default ThemeSelector;\n"],"mappings":"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"}
@@ -1 +1,2 @@
1
1
  export * from './Button';
2
+ export * from './theme-selector';
@@ -0,0 +1,2 @@
1
+ export { ThemeSelector } from './theme-selector';
2
+ export type { ThemeSelectorProps } from './theme-selector.types';
@@ -0,0 +1,8 @@
1
+ import { FC } from 'react';
2
+ import { ThemeSelectorListProps } from './theme-selector.types';
3
+ /**
4
+ * @description Renderiza las opciones disponibles y el estado vacio.
5
+ * @author Steveen Cues
6
+ * @version 1.0.0
7
+ */
8
+ export declare const ThemeSelectorList: FC<ThemeSelectorListProps>;
@@ -0,0 +1,8 @@
1
+ import { FC } from 'react';
2
+ import { ThemeSelectorOptionProps } from './theme-selector.types';
3
+ /**
4
+ * @description Representa una opcion individual del selector de temas.
5
+ * @author Steveen Cues
6
+ * @version 1.0.0
7
+ */
8
+ export declare const ThemeSelectorOption: FC<ThemeSelectorOptionProps>;
@@ -0,0 +1,21 @@
1
+ import { FC } from 'react';
2
+ import { AppTheme } from '../../domain';
3
+ interface ThemeSelectorPanelProps {
4
+ readonly themes: readonly AppTheme[];
5
+ readonly totalThemes: number;
6
+ readonly selectedTheme: string;
7
+ readonly search: string;
8
+ readonly showSearch: boolean;
9
+ readonly placeholder: string;
10
+ readonly label: string;
11
+ readonly showDescriptions: boolean;
12
+ readonly onSearchChange: (value: string) => void;
13
+ readonly onSelectTheme: (themeName: string) => void;
14
+ }
15
+ /**
16
+ * @description Renderiza el panel desplegable para seleccionar temas.
17
+ * @author Steveen Cues
18
+ * @version 1.0.0
19
+ */
20
+ export declare const ThemeSelectorPanel: FC<ThemeSelectorPanelProps>;
21
+ export {};
@@ -0,0 +1,8 @@
1
+ import { FC } from 'react';
2
+ import { ThemeSelectorSearchProps } from './theme-selector.types';
3
+ /**
4
+ * @description Permite buscar temas por nombre o descripcion.
5
+ * @author Steveen Cues
6
+ * @version 1.0.0
7
+ */
8
+ export declare const ThemeSelectorSearch: FC<ThemeSelectorSearchProps>;
@@ -0,0 +1,8 @@
1
+ import { FC } from 'react';
2
+ import { ThemeSelectorTriggerProps } from './theme-selector.types';
3
+ /**
4
+ * @description Muestra el tema activo y controla la apertura del selector.
5
+ * @author Steveen Cues
6
+ * @version 1.0.0
7
+ */
8
+ export declare const ThemeSelectorTrigger: FC<ThemeSelectorTriggerProps>;
@@ -0,0 +1,9 @@
1
+ import { FC } from 'react';
2
+ import { ThemeSelectorProps } from './theme-selector.types';
3
+ /**
4
+ * @description Permite buscar, visualizar y seleccionar temas disponibles.
5
+ * @author Steveen Cues
6
+ * @version 2.2.0
7
+ */
8
+ export declare const ThemeSelector: FC<ThemeSelectorProps>;
9
+ export default ThemeSelector;
@@ -0,0 +1,55 @@
1
+ import { AppTheme } from '../../domain';
2
+ /**
3
+ * @description Define las propiedades del selector de temas.
4
+ * @author Steveen Cues
5
+ * @version 1.0.0
6
+ */
7
+ export interface ThemeSelectorProps {
8
+ readonly label?: string;
9
+ readonly placeholder?: string;
10
+ readonly className?: string;
11
+ readonly showDescriptions?: boolean;
12
+ }
13
+ /**
14
+ * @description Define las propiedades del boton desplegable.
15
+ * @author Steveen Cues
16
+ * @version 1.0.0
17
+ */
18
+ export interface ThemeSelectorTriggerProps {
19
+ readonly theme: AppTheme;
20
+ readonly isOpen: boolean;
21
+ readonly onToggle: () => void;
22
+ }
23
+ /**
24
+ * @description Define las propiedades del buscador de temas.
25
+ * @author Steveen Cues
26
+ * @version 1.0.0
27
+ */
28
+ export interface ThemeSelectorSearchProps {
29
+ readonly value: string;
30
+ readonly placeholder: string;
31
+ readonly onChange: (value: string) => void;
32
+ }
33
+ /**
34
+ * @description Define las propiedades de una opcion de tema.
35
+ * @author Steveen Cues
36
+ * @version 1.0.0
37
+ */
38
+ export interface ThemeSelectorOptionProps {
39
+ readonly theme: AppTheme;
40
+ readonly isSelected: boolean;
41
+ readonly showDescription: boolean;
42
+ readonly onSelect: (themeName: string) => void;
43
+ }
44
+ /**
45
+ * @description Define las propiedades de la lista de temas.
46
+ * @author Steveen Cues
47
+ * @version 1.0.0
48
+ */
49
+ export interface ThemeSelectorListProps {
50
+ readonly themes: readonly AppTheme[];
51
+ readonly selectedTheme: string;
52
+ readonly showDescriptions: boolean;
53
+ readonly label: string;
54
+ readonly onSelect: (themeName: string) => void;
55
+ }