Files
website/node_modules/@astrojs/starlight/components/ThemeSelect.astro
2024-05-06 17:15:30 -04:00

78 lines
2.5 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
import Select from './Select.astro';
import type { Props } from '../props';
const { labels } = Astro.props;
---
<starlight-theme-select>
{/* TODO: Can we give this select a width that works well for each languages strings? */}
<Select
icon="laptop"
label={labels['themeSelect.accessibleLabel']}
value="auto"
options={[
{ label: labels['themeSelect.dark'], selected: false, value: 'dark' },
{ label: labels['themeSelect.light'], selected: false, value: 'light' },
{ label: labels['themeSelect.auto'], selected: true, value: 'auto' },
]}
width="6.25em"
/>
</starlight-theme-select>
{/* Inlined to avoid FOUC. Uses global scope from `ThemeProvider.astro` */}
<script is:inline>
StarlightThemeProvider.updatePickers();
</script>
<script>
type Theme = 'auto' | 'dark' | 'light';
/** Key in `localStorage` to store color theme preference at. */
const storageKey = 'starlight-theme';
/** Get a typesafe theme string from any JS value (unknown values are coerced to `'auto'`). */
const parseTheme = (theme: unknown): Theme =>
theme === 'auto' || theme === 'dark' || theme === 'light' ? theme : 'auto';
/** Load the users preference from `localStorage`. */
const loadTheme = (): Theme =>
parseTheme(typeof localStorage !== 'undefined' && localStorage.getItem(storageKey));
/** Store the users preference in `localStorage`. */
function storeTheme(theme: Theme): void {
if (typeof localStorage !== 'undefined') {
localStorage.setItem(storageKey, theme === 'light' || theme === 'dark' ? theme : '');
}
}
/** Get the preferred system color scheme. */
const getPreferredColorScheme = (): Theme =>
matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
/** Update select menu UI, document theme, and local storage state. */
function onThemeChange(theme: Theme): void {
StarlightThemeProvider.updatePickers(theme);
document.documentElement.dataset.theme = theme === 'auto' ? getPreferredColorScheme() : theme;
storeTheme(theme);
}
// React to changes in system color scheme.
matchMedia(`(prefers-color-scheme: light)`).addEventListener('change', () => {
if (loadTheme() === 'auto') onThemeChange('auto');
});
class StarlightThemeSelect extends HTMLElement {
constructor() {
super();
onThemeChange(loadTheme());
this.querySelector('select')?.addEventListener('change', (e) => {
if (e.currentTarget instanceof HTMLSelectElement) {
onThemeChange(parseTheme(e.currentTarget.value));
}
});
}
}
customElements.define('starlight-theme-select', StarlightThemeSelect);
</script>