import { computed, watch } from 'vue'; import { find, kebabCase, reject } from 'lodash-es'; import JSON5 from 'json5'; import { useLocalStorage } from '@vueuse/core'; import { useTheme } from 'vuetify'; const DEFAULT_THEME = 'light'; // Themes registered in `plugins/vuetify.ts`; keys must match. const BUILT_IN_THEMES = [ { value: 'light', title: 'Tailor Light' }, { value: 'dark', title: 'Tailor Dark' }, ]; interface CustomTheme { name: string; key: string; definition: { dark?: boolean; colors?: Record; variables?: Record; }; } export function useThemeState() { const { change, computedThemes, themes } = useTheme(); const activeTheme = useLocalStorage('cek-edit-theme-active', DEFAULT_THEME); const customThemes = useLocalStorage( 'cek-edit-custom-themes', [], ); customThemes.value.forEach((t) => registerTheme(t)); const setTheme = (key: string) => (activeTheme.value = key); function registerTheme({ key, definition }: CustomTheme) { themes.value[key] = { dark: definition.dark ?? false, colors: { ...definition.colors }, variables: { ...definition.variables }, }; } const removeCustomTheme = (name: string) => { const key = kebabCase(name); customThemes.value = reject(customThemes.value, { key }); if (activeTheme.value === key) setTheme(DEFAULT_THEME); }; const addCustomTheme = (name: string, input: string) => { removeCustomTheme(name); const key = kebabCase(name); const theme: CustomTheme = { name, key, definition: JSON5.parse(input), }; customThemes.value.push(theme); registerTheme(theme); setTheme(key); }; const getCustomTheme = (name: string) => find(customThemes.value, { key: kebabCase(name) }); const themeItems = computed(() => [ ...BUILT_IN_THEMES.map((theme) => ({ ...theme, removable: false })), ...customThemes.value.map((theme) => ({ value: theme.key, title: theme.name, removable: true, })), ]); const getThemeColors = (key: string) => { const theme = computedThemes.value[key]; if (!theme?.colors) return []; return Object.keys(theme.colors).filter( (color) => !color.startsWith('on-'), ); }; watch(activeTheme, (key) => change(key), { immediate: true }); return { activeTheme, addCustomTheme, getCustomTheme, getThemeColors, removeCustomTheme, setTheme, themeItems, }; }