mirror of
https://github.com/LukeHagar/website.git
synced 2025-12-06 12:57:48 +00:00
start working version of new theme handler
This commit is contained in:
@@ -1,8 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Select, { type SelectOption } from './Select.svelte';
|
import Select, { type SelectOption } from './Select.svelte';
|
||||||
import { Select as Select2 } from '$lib/components/ui';
|
import { Select as Select2 } from '$lib/components/ui';
|
||||||
import { themeStore, setTheme, type Theme } from '$lib/providers/theme';
|
import { themeStore, setTheme } from '$lib/providers/theme';
|
||||||
import { get } from 'svelte/store';
|
|
||||||
|
|
||||||
const options = [
|
const options = [
|
||||||
{
|
{
|
||||||
@@ -28,6 +27,6 @@
|
|||||||
{options}
|
{options}
|
||||||
bind:value={$themeStore.theme}
|
bind:value={$themeStore.theme}
|
||||||
onValueChange={(e) => {
|
onValueChange={(e) => {
|
||||||
setTheme(e as Theme);
|
setTheme(e);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
<div {...select.content}>
|
<div {...select.content}>
|
||||||
{#each options as option}
|
{#each options as option}
|
||||||
<div {...select.getOption(option.value)}>
|
<div {...select.getOption(option.value)}>
|
||||||
{option}
|
{option.label}
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,25 +1,34 @@
|
|||||||
import { writable } from 'svelte/store';
|
|
||||||
|
|
||||||
export type Theme = 'dark' | 'light' | 'system';
|
import { writable } from 'svelte/store';
|
||||||
|
|
||||||
export interface ThemeStore {
|
export interface ThemeStore {
|
||||||
themes: string[];
|
themes: string[];
|
||||||
forcedTheme?: Theme;
|
forcedTheme?: string;
|
||||||
theme?: Theme;
|
theme?: string;
|
||||||
resolvedTheme?: string;
|
resolvedTheme?: string;
|
||||||
systemTheme?: Omit<Theme, 'system'>;
|
systemTheme?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const setTheme = (theme?: Theme): void =>
|
// Create the theme store
|
||||||
themeStore.update((store) => ({ ...store, theme }));
|
export const themeStore = writable<ThemeStore>({
|
||||||
|
themes: ['light', 'dark'],
|
||||||
export const themeStore = writable<ThemeStore>({
|
forcedTheme: undefined,
|
||||||
themes: [],
|
theme: undefined,
|
||||||
forcedTheme: undefined,
|
resolvedTheme: undefined,
|
||||||
theme: undefined,
|
systemTheme: undefined
|
||||||
resolvedTheme: undefined,
|
});
|
||||||
systemTheme: undefined
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
export { default as ThemeProvider } from './theme.svelte'
|
export const setTheme = (theme?: string) =>
|
||||||
|
themeStore.update((store) => ({ ...store, theme }));
|
||||||
|
|
||||||
|
export const setResolvedTheme = (resolvedTheme?: string) =>
|
||||||
|
themeStore.update((store) => ({ ...store, resolvedTheme }));
|
||||||
|
|
||||||
|
export const setSystemTheme = (systemTheme?: string) =>
|
||||||
|
themeStore.update((store) => ({ ...store, systemTheme }));
|
||||||
|
|
||||||
|
export const setThemes = (themes: string[]) =>
|
||||||
|
themeStore.update((store) => ({ ...store, themes }));
|
||||||
|
|
||||||
|
export { default as ThemeProvider } from './theme.svelte';
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { colorSchemes, MEDIA } from './constants';
|
import { colorSchemes, MEDIA } from './constants';
|
||||||
import { disableAnimation, getSystemTheme, getTheme } from './helpers';
|
import { disableAnimation, getSystemTheme, getTheme } from './helpers';
|
||||||
import { themeStore, setTheme, type Theme } from './index';
|
import { themeStore, setTheme } from './index';
|
||||||
|
|
||||||
import ThemeScript from './theme-script.svelte';
|
import ThemeScript from './theme-script.svelte';
|
||||||
import { browser } from '$app/environment';
|
import { browser } from '$app/environment';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
forcedTheme?: Theme;
|
forcedTheme?: string;
|
||||||
disableTransitionOnChange?: boolean;
|
disableTransitionOnChange?: boolean;
|
||||||
enableSystem?: boolean;
|
enableSystem?: boolean;
|
||||||
enableColorScheme?: boolean;
|
enableColorScheme?: boolean;
|
||||||
storageKey?: string;
|
storageKey?: string;
|
||||||
themes?: string[];
|
themes?: string[];
|
||||||
defaultTheme?: Theme;
|
defaultTheme?: string;
|
||||||
attribute?: string | 'class';
|
attribute?: string | 'class';
|
||||||
value?: {
|
value?: {
|
||||||
[themeName: string]: string;
|
[themeName: string]: string;
|
||||||
@@ -51,13 +51,14 @@
|
|||||||
const attrs = !value ? themes : Object.values(value);
|
const attrs = !value ? themes : Object.values(value);
|
||||||
|
|
||||||
const handleMediaQuery = (e?: MediaQueryList) => {
|
const handleMediaQuery = (e?: MediaQueryList) => {
|
||||||
const systemTheme = getSystemTheme(e) as Theme;
|
const systemTheme = getSystemTheme(e) as string;
|
||||||
$themeStore.resolvedTheme = systemTheme;
|
$themeStore.resolvedTheme = systemTheme;
|
||||||
|
$themeStore.systemTheme = systemTheme;
|
||||||
|
|
||||||
if (theme === 'system' && !forcedTheme) changeTheme(systemTheme, false);
|
if (theme === 'system' && !forcedTheme) changeTheme(systemTheme, false, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const changeTheme = (theme?: Theme, updateStorage?: boolean, updateDOM?: boolean) => {
|
const changeTheme = (theme?: string, updateStorage?: boolean, updateDOM?: boolean) => {
|
||||||
if (!theme) return;
|
if (!theme) return;
|
||||||
let name = value?.[theme] || theme;
|
let name = value?.[theme] || theme;
|
||||||
|
|
||||||
@@ -77,7 +78,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (updateDOM && browser) {
|
if (updateDOM && browser) {
|
||||||
const d = document.documentElement;
|
const d = document.body;
|
||||||
|
|
||||||
if (attribute === 'class') {
|
if (attribute === 'class') {
|
||||||
d.classList.remove(...(attrs as string[]));
|
d.classList.remove(...(attrs as string[]));
|
||||||
@@ -93,20 +94,20 @@
|
|||||||
|
|
||||||
const storageHandler = (e: StorageEvent) => {
|
const storageHandler = (e: StorageEvent) => {
|
||||||
if (e.key !== storageKey) return;
|
if (e.key !== storageKey) return;
|
||||||
setTheme((e.newValue as Theme) || (defaultTheme as Theme));
|
setTheme((e.newValue as string) || (defaultTheme as string));
|
||||||
};
|
};
|
||||||
|
|
||||||
const onWindow = (window: Window) => {
|
const onWindow = (window: Window) => {
|
||||||
const media = window.matchMedia(MEDIA);
|
const media = window.matchMedia(MEDIA);
|
||||||
// Intentionally use deprecated listener methods to support iOS & old browsers
|
// Use modern event listener approach
|
||||||
media.addListener(mediaHandler);
|
media.addEventListener('change', mediaHandler);
|
||||||
mediaHandler(media);
|
mediaHandler(media);
|
||||||
|
|
||||||
window.addEventListener('storage', storageHandler);
|
window.addEventListener('storage', storageHandler);
|
||||||
return {
|
return {
|
||||||
destroy() {
|
destroy() {
|
||||||
window.removeEventListener('storage', storageHandler);
|
window.removeEventListener('storage', storageHandler);
|
||||||
media.removeListener(mediaHandler);
|
media.removeEventListener('change', mediaHandler);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -133,7 +134,7 @@
|
|||||||
if (forcedTheme) {
|
if (forcedTheme) {
|
||||||
changeTheme(theme, true, false);
|
changeTheme(theme, true, false);
|
||||||
} else {
|
} else {
|
||||||
changeTheme(theme);
|
changeTheme(theme, true, true); // Add true for updateDOM parameter
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -132,7 +132,7 @@
|
|||||||
>
|
>
|
||||||
|
|
||||||
<slot />
|
<slot />
|
||||||
<ThemeProvider />
|
<ThemeProvider attribute="class" disableTransitionOnChange />
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
:global(html) {
|
:global(html) {
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
import { Config } from 'tailwindcss';
|
|
||||||
|
|
||||||
const config = {} satisfies Config;
|
|
||||||
|
|
||||||
export default config;
|
|
||||||
Reference in New Issue
Block a user