| <script lang="ts">
|
| import I18nKey from "@i18n/i18nKey";
|
| import { i18n } from "@i18n/translation";
|
| import { onMount } from "svelte";
|
| import DropdownItem from "@/components/common/DropdownItem.svelte";
|
| import DropdownPanel from "@/components/common/DropdownPanel.svelte";
|
| import Icon from "@/components/common/Icon.svelte";
|
| import { DARK_MODE, LIGHT_MODE, SYSTEM_MODE } from "@/constants/constants";
|
| import type { LIGHT_DARK_MODE } from "@/types/config.ts";
|
| import {
|
| applyThemeToDocument,
|
| getStoredTheme,
|
| setTheme,
|
| } from "@/utils/setting-utils";
|
|
|
|
|
| interface SwupHooks {
|
| on(event: string, callback: () => void): void;
|
| }
|
|
|
| interface SwupInstance {
|
| hooks?: SwupHooks;
|
| }
|
|
|
| type WindowWithSwup = Window & { swup?: SwupInstance };
|
|
|
| let mode: LIGHT_DARK_MODE = $state(LIGHT_MODE);
|
| let displayedMode: LIGHT_DARK_MODE = $state(LIGHT_MODE);
|
|
|
| function switchScheme(newMode: LIGHT_DARK_MODE) {
|
| mode = newMode;
|
| setTheme(newMode);
|
| }
|
|
|
|
|
| function updateDisplayedMode() {
|
| if (mode === SYSTEM_MODE) {
|
|
|
| const isSystemDark = window.matchMedia(
|
| "(prefers-color-scheme: dark)",
|
| ).matches;
|
| displayedMode = isSystemDark ? DARK_MODE : LIGHT_MODE;
|
| } else {
|
| displayedMode = mode;
|
| }
|
| }
|
|
|
|
|
| onMount(() => {
|
|
|
| const storedTheme = getStoredTheme();
|
| mode = storedTheme;
|
| updateDisplayedMode();
|
|
|
|
|
| if (storedTheme !== SYSTEM_MODE) {
|
| const currentTheme = document.documentElement.classList.contains("dark")
|
| ? DARK_MODE
|
| : LIGHT_MODE;
|
| if (storedTheme !== currentTheme) {
|
| applyThemeToDocument(storedTheme);
|
| }
|
| }
|
|
|
|
|
| if (storedTheme === SYSTEM_MODE) {
|
| const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
| const handleSystemChange = () => {
|
| updateDisplayedMode();
|
| };
|
| mediaQuery.addEventListener("change", handleSystemChange);
|
| }
|
|
|
|
|
| const handleContentReplace = () => {
|
| const newTheme = getStoredTheme();
|
| mode = newTheme;
|
| updateDisplayedMode();
|
| };
|
|
|
|
|
| const win = window as WindowWithSwup;
|
| if (win.swup?.hooks) {
|
| win.swup.hooks.on("content:replace", handleContentReplace);
|
| } else {
|
| document.addEventListener("swup:enable", () => {
|
| const w = window as WindowWithSwup;
|
| if (w.swup?.hooks) {
|
| w.swup.hooks.on("content:replace", handleContentReplace);
|
| }
|
| });
|
| }
|
|
|
|
|
| const handleThemeChange = () => {
|
|
|
|
|
| if (mode !== SYSTEM_MODE) {
|
| const newTheme = getStoredTheme();
|
| mode = newTheme;
|
| updateDisplayedMode();
|
| } else {
|
|
|
| updateDisplayedMode();
|
| }
|
| };
|
|
|
| window.addEventListener("theme-change", handleThemeChange);
|
|
|
|
|
| return () => {
|
| window.removeEventListener("theme-change", handleThemeChange);
|
| };
|
| });
|
| </script>
|
|
|
| <div class="relative z-50">
|
| <button aria-label="Light/Dark Mode" aria-haspopup="menu" class="relative btn-plain scale-animation rounded-lg h-11 w-11 active:scale-90" id="scheme-switch">
|
| <div class="absolute inset-0 flex items-center justify-center" class:opacity-0={displayedMode !== LIGHT_MODE}>
|
| <Icon icon="material-symbols:wb-sunny-outline-rounded" class="text-[1.25rem]"></Icon>
|
| </div>
|
| <div class="absolute inset-0 flex items-center justify-center" class:opacity-0={displayedMode !== DARK_MODE}>
|
| <Icon icon="material-symbols:dark-mode-outline-rounded" class="text-[1.25rem]"></Icon>
|
| </div>
|
| </button>
|
| <div id="theme-mode-panel" class="absolute transition float-panel-closed top-11 -right-2 pt-5 z-50" role="menu" aria-labelledby="scheme-switch">
|
| <DropdownPanel>
|
| <DropdownItem
|
| role="menuitem"
|
| isActive={mode === LIGHT_MODE}
|
| isLast={false}
|
| onclick={() => switchScheme(LIGHT_MODE)}
|
| >
|
| <Icon icon="material-symbols:wb-sunny-outline-rounded" class="text-[1.25rem] mr-3"></Icon>
|
| {i18n(I18nKey.lightMode)}
|
| </DropdownItem>
|
| <DropdownItem
|
| role="menuitem"
|
| isActive={mode === DARK_MODE}
|
| isLast={false}
|
| onclick={() => switchScheme(DARK_MODE)}
|
| >
|
| <Icon icon="material-symbols:dark-mode-outline-rounded" class="text-[1.25rem] mr-3"></Icon>
|
| {i18n(I18nKey.darkMode)}
|
| </DropdownItem>
|
| <DropdownItem
|
| role="menuitem"
|
| isActive={mode === SYSTEM_MODE}
|
| isLast={true}
|
| onclick={() => switchScheme(SYSTEM_MODE)}
|
| >
|
| <Icon icon="material-symbols:brightness-auto-outline-rounded" class="text-[1.25rem] mr-3"></Icon>
|
| {i18n(I18nKey.systemMode)}
|
| </DropdownItem>
|
| </DropdownPanel>
|
| </div>
|
| </div> |