Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | 73x 73x 73x 292x 387x 387x 216x 353x 216x 216x 353x 353x 353x 353x 353x 353x 216x 216x 216x 353x 353x 216x 706x 706x | import { LitElement, unsafeCSS, html, PropertyValueMap } from "lit" import { customElement, property } from "lit/decorators.js" import theme from "./theme.scss?inline" /** * Supported theme names that can be applied by {@link ThemeProvider}. */ export type ThemeMode = "dark" | "light" | "glass" | "cartoon" /** * Detail payload for the {@link ThemeProvider | theme provider}'s `change` event. */ export interface ThemeChangeDetail { /** * Theme mode that is now active on the provider. */ readonly mode: ThemeMode /** * Theme mode that was active before the change. `null` when no previous theme existed. */ readonly previousMode: ThemeMode | null } const THEME_CLASS_PREFIX = "vg-theme-" const SUPPORTED_THEMES: readonly ThemeMode[] = ["dark", "light", "glass", "cartoon"] const SUPPORTED_THEME_SET = new Set<ThemeMode>(SUPPORTED_THEMES) const THEME_CLASSES = SUPPORTED_THEMES.map((mode) => `${THEME_CLASS_PREFIX}${mode}`) /** * Theme provider that exposes vg design tokens as CSS variables and toggles between predefined modes. * * @tag vg-theme-provider * @tagname vg-theme-provider * @summary The ThemeProvider component, used for managing application-wide theme and design tokens. * * @slot - Components that should inherit the active theme and design tokens * * @csspart provider - Allows you to style the theme provider container * * @fires {ThemeChangeDetail} vg-change - Emitted whenever the active theme mode changes * */ @customElement("vg-theme-provider") export class ThemeProvider extends LitElement { static styles = unsafeCSS(theme) /** * Theme mode that controls which token values are exposed. * The value is normalised to one of ThemeMode. Unsupported values fall back to "dark". */ @property({ type: String, reflect: true }) public mode: ThemeMode = "dark" protected willUpdate(changedProperties: PropertyValueMap<this>) { if (changedProperties.has("mode")) { const normalised = this.normaliseTheme(this.mode) Iif (Inormalised !== this.mode) { this.mode = normalised } } } protected updated(changedProperties: PropertyValueMap<this>) { if (changedProperties.has("mode")) { const previousValue = changedProperties.get("mode") as ThemeMode | undefined const previousMode = previousValue === undefined ? null : this.normaliseTheme(previousValue) const nextMode = this.normaliseTheme(this.mode) this.applyThemeClass(nextMode) this.setThemeAttributes(nextMode) if (previousMode !== nextMode) { this.dispatchEvent(new CustomEvent<ThemeChangeDetail>("vg-change", { detail: { mode: nextMode, previousMode, }, // bubbles: true, composed: true, })) } } } /** * @inheritdoc */ render() { return html`<slot></slot>` } private applyThemeClass(mode: ThemeMode) { this.classList.remove(...THEME_CLASSES) this.classList.add(`${THEME_CLASS_PREFIX}${mode}`) } private setThemeAttributes(mode: ThemeMode) { this.dataset.vgTheme = mode this.setAttribute("data-vg-theme", mode) } private normaliseTheme(value: ThemeMode | string | undefined): ThemeMode { const candidate = (value ?? "").toString().toLowerCase() as ThemeMode return SUPPORTED_THEME_SET.has(candidate) ? candidate : "dark" } } declare global { interface HTMLElementTagNameMap { 'vg-theme-provider': ThemeProvider } } |