Improve theme settings interface

This commit is contained in:
2025-06-11 21:26:56 -07:00
parent 1ebb927c66
commit 40bf1f6169

View File

@ -3,6 +3,8 @@
import { themes } from "../script/theme.ts";
import { setCookie, getCookie } from "../script/cookie.ts";
let panelRef = $state() as HTMLElement;
let lightButtonIcon = $state() as HTMLElement;
let darkButtonIcon = $state() as HTMLElement;
let autoButtonIcon = $state() as HTMLElement;
@ -13,32 +15,28 @@
let { themeOption = $bindable() } = $props();
let expanded = $state(false);
let currentIcon = $state("bi-circle-half");
let darkTheme: CallableFunction = getContext("darkTheme");
function setThemeOption(newThemeOption: string) {
expanded = false;
setCookie("theme", newThemeOption);
themeOption = newThemeOption;
switch (newThemeOption) {
case themes.LIGHT:
themeOption = themes.LIGHT;
lightButtonIcon.classList.replace("bi-sun", "bi-sun-fill");
darkButtonIcon.classList.replace("bi-moon-fill", "bi-moon");
autoButtonIcon.classList.replace("bi-display-fill", "bi-display");
currentIcon = "bi-sun-fill";
break;
case themes.DARK:
themeOption = themes.DARK;
lightButtonIcon.classList.replace("bi-sun-fill", "bi-sun");
darkButtonIcon.classList.replace("bi-moon", "bi-moon-fill");
autoButtonIcon.classList.replace("bi-display-fill", "bi-display");
currentIcon = "bi-moon-fill";
break;
case themes.AUTO:
themeOption = themes.AUTO;
lightButtonIcon.classList.replace("bi-sun-fill", "bi-sun");
darkButtonIcon.classList.replace("bi-moon-fill", "bi-moon");
autoButtonIcon.classList.replace("bi-display", "bi-display-fill");
currentIcon = "bi-circle-half";
break;
default:
console.error("setThemeOption was passed a value that is not a theme");
console.error("Invalid theme option");
}
}
@ -47,59 +45,113 @@
if (Object.values(themes).includes(themeCookie)) {
setThemeOption(themeCookie);
}
function handleClickOutside(event: MouseEvent) {
if (panelRef && !panelRef.contains(event.target as HTMLElement)) {
expanded = false;
}
}
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
});
let darkTheme: CallableFunction = getContext("darkTheme");
function togglePanel() {
expanded = !expanded;
}
</script>
<div class="panel settings {darkTheme() ? 'dark-theme' : ''}">
<div
bind:this={panelRef}
class="theme-toggle panel {darkTheme() ? 'dark-theme' : ''}"
>
<button
aria-label="Dark Theme"
bind:this={darkButton}
onclick={() => {
setThemeOption(themes.DARK);
}}
class="toggle-button"
onclick={togglePanel}
aria-label="Toggle Theme Selector"
>
<i bind:this={darkButtonIcon} class="bi bi-moon"></i>
</button>
<button
aria-label="Light Theme"
bind:this={lightButton}
onclick={() => {
setThemeOption(themes.LIGHT);
}}
>
<i bind:this={lightButtonIcon} class="bi bi-sun"></i>
</button>
<button
aria-label="Auto Theme"
bind:this={autoButton}
onclick={() => {
setThemeOption(themes.AUTO);
}}
>
<i bind:this={autoButtonIcon} class="bi bi-display-fill"></i>
<i class={`bi ${currentIcon}`}></i>
</button>
<!-- Unfortunately, we have to hard-code 72px because CSS won't animate `auto` or `min-content` -->
<div class="button-group" style:height={expanded ? "68px" : "0px"}>
<!-- Don't show the button if it is currently selected (it will be shown as the dropdown icon) -->
{#if themeOption !== themes.DARK}
<button
aria-label="Dark Theme"
bind:this={darkButton}
onclick={() => setThemeOption(themes.DARK)}
>
<i bind:this={darkButtonIcon} class="bi bi-moon"></i>
</button>
{/if}
{#if themeOption !== themes.LIGHT}
<button
aria-label="Light Theme"
bind:this={lightButton}
onclick={() => setThemeOption(themes.LIGHT)}
>
<i bind:this={lightButtonIcon} class="bi bi-sun"></i>
</button>
{/if}
{#if themeOption !== themes.AUTO}
<button
aria-label="Auto Theme"
bind:this={autoButton}
onclick={() => setThemeOption(themes.AUTO)}
>
<i bind:this={autoButtonIcon} class="bi bi-circle-half"></i>
</button>
{/if}
</div>
</div>
<style lang="scss">
@use "../style/global.scss";
div.panel.settings {
padding: 6px 8px;
div.theme-toggle {
padding: 4px;
position: fixed;
bottom: 20px;
right: 20px;
display: flex;
flex-direction: column-reverse;
align-items: flex-end;
}
div.panel.settings button {
color: global.$text-color;
font-size: 110%;
cursor: pointer;
// Reset buton style
button.toggle-button {
font-size: 1.3rem;
background: transparent;
border: none;
background-color: #00000000;
border-radius: 0;
cursor: pointer;
color: global.$text-color;
aspect-ratio: 1 / 1;
}
div.button-group {
overflow: hidden;
transition: height 0.3s ease;
display: flex;
flex-direction: column;
}
div.button-group button {
margin: 2px 0;
font-size: 1.3rem;
background: transparent;
border: none;
color: global.$text-color;
cursor: pointer;
}
@media screen and (max-width: global.$mobile-width) {
div.button-group button {
margin: 4px 0;
}
}
</style>