vira
Version:
A simple and highly versatile design system using element-vir.
105 lines (100 loc) • 3.69 kB
JavaScript
import { mapEnumToObject } from '@augment-vir/common';
import { classMap, css, html, listen } from 'element-vir';
import { AutoTheme24Icon, Moon24Icon, Sun24Icon } from '../icons/index.js';
import { viraFormCssVars } from '../styles/form-styles.js';
import { noNativeFormStyles, noNativeSpacing, viraTheme } from '../styles/index.js';
import { defineViraElement } from '../util/define-vira-element.js';
import { ViraThemeClient, ViraThemeSelection } from '../util/vira-theme-client.js';
import { ViraIcon } from './vira-icon.element.js';
const themeIcons = mapEnumToObject(ViraThemeSelection, (theme) => {
const map = {
[ViraThemeSelection.Light]: Sun24Icon,
[ViraThemeSelection.Dark]: Moon24Icon,
[ViraThemeSelection.Auto]: AutoTheme24Icon,
};
return map[theme];
});
const defaultThemeLabels = {
[ViraThemeSelection.Light]: 'Light',
[ViraThemeSelection.Dark]: 'Dark',
[ViraThemeSelection.Auto]: 'Auto',
};
/**
* A row of buttons for selecting a {@link ViraThemeSelection} (light, dark, or auto). Fires a
* `themeSelect` event when the user picks an option; the consumer is responsible for applying the
* resulting theme.
*
* @category Elements
*/
export const ViraThemeSwitcher = defineViraElement()({
tagName: 'vira-theme-switcher',
styles: css `
:host {
display: inline-flex;
align-items: center;
box-sizing: border-box;
gap: 4px;
}
button {
${noNativeSpacing};
${noNativeFormStyles};
display: flex;
align-items: center;
justify-content: center;
border: 1px solid transparent;
border-radius: ${viraFormCssVars['vira-form-radius'].value};
padding: 2px;
cursor: pointer;
color: ${viraTheme.colors['vira-grey-foreground-placeholder'].foreground.value};
&:hover {
color: ${viraFormCssVars['vira-form-accent-primary-hover-color'].value};
border-color: currentColor;
}
&.selected {
pointer-events: none;
color: ${viraFormCssVars['vira-form-accent-primary-color'].value};
border-color: ${viraFormCssVars['vira-form-accent-primary-color'].value};
}
}
${ViraIcon} {
width: 20px;
aspect-ratio: 1;
}
`,
state() {
return {
internalThemeClient: undefined,
currentTheme: ViraThemeSelection.Auto,
};
},
render({ inputs, state, updateState }) {
const themeClient = inputs.themeClient || state.internalThemeClient || new ViraThemeClient();
updateState({
internalThemeClient: themeClient,
currentTheme: themeClient.currentTheme,
});
const labels = inputs.labels || defaultThemeLabels;
return Object.values(ViraThemeSelection).map((theme) => {
return html `
<button
class=${classMap({
selected: themeClient.currentTheme === theme,
})}
title=${labels[theme]}
${listen('click', (event) => {
event.stopPropagation();
themeClient.setSelectedTheme(theme);
updateState({
currentTheme: themeClient.currentTheme,
});
})}
>
<${ViraIcon.assign({
icon: themeIcons[theme],
fitContainer: true,
})}></${ViraIcon}>
</button>
`;
});
},
});