UNPKG

@digital-blueprint/lunchlottery-app

Version:

[GitHub Repository](https://github.com/digital-blueprint/lunchlottery-app) | [npmjs package](https://www.npmjs.com/package/@digital-blueprint/lunchlottery-app) | [Unpkg CDN](https://unpkg.com/browse/@digital-blueprint/lunchlottery-app/)

271 lines (235 loc) 9.01 kB
import {createInstance, setOverridesByGlobalCache} from './i18n.js'; import {html, css} from 'lit'; import {ScopedElementsMixin, LangMixin} from '@dbp-toolkit/common'; import {AdapterLitElement, Icon} from '@dbp-toolkit/common'; import * as commonStyles from '@dbp-toolkit/common/styles'; import {classMap} from 'lit/directives/class-map.js'; export class ThemeSwitcher extends LangMixin( ScopedElementsMixin(AdapterLitElement), createInstance, ) { constructor() { super(); this.themes = []; this.boundCloseAdditionalMenuHandler = this.hideModeMenu.bind(this); this.detectBrowserDarkMode = false; this.darkModeClass = 'dark-theme'; this.langDir = ''; this.dropdownRight = false; this.darkModeThemeOverride = null; } static get properties() { return { ...super.properties, themes: {type: Array, attribute: 'themes'}, darkModeThemeOverride: {type: String, attribute: 'dark-mode-theme-override'}, dropdownRight: {type: Boolean, attribute: 'dropdown-right'}, langDir: {type: String, attribute: 'lang-dir'}, }; } static get scopedElements() { return { 'dbp-icon': Icon, }; } connectedCallback() { super.connectedCallback(); this.updateComplete.then(() => { if (this.darkModeThemeOverride === null) { this.detectBrowserDarkMode = true; } else if (this.darkModeThemeOverride === '') { this.detectBrowserDarkMode = false; } else { this.detectBrowserDarkMode = true; this.darkModeClass = this.darkModeThemeOverride; } this.loadTheme('light-theme'); this.detectInitialMode(); }); if (this.langDir) { setOverridesByGlobalCache(this._i18n, this); } } detectInitialMode() { //look for saved modes let prefMode = localStorage.getItem('prefered-color-mode'); if (prefMode) { // search for prefered mode const theme = this.themes.find((theme) => theme.class === prefMode); if (theme) { this.loadTheme(theme.class); } return; } if (this.detectBrowserDarkMode) { //look for browser mode const useDark = window.matchMedia('(prefers-color-scheme: dark)'); if (useDark.matches) { // search for dark mode const theme = this.themes.find((theme) => theme.class === this.darkModeClass); if (theme) { this.loadTheme(theme.class); } } } } toggleModeMenu() { const button = this.shadowRoot.querySelector('.mode-button'); if (!button) { return; } if (button.classList.contains('active')) button.classList.remove('active'); else button.classList.add('active'); const menu = this.shadowRoot.querySelector('ul.extended-menu'); const menuStart = this.shadowRoot.querySelector('.mode-button'); if (menu === null || menuStart === null) { return; } menu.classList.toggle('hidden'); if (!menu.classList.contains('hidden')) { // add event listener for clicking outside of menu document.addEventListener('click', this.boundCloseAdditionalMenuHandler); this.initateOpenAdditionalMenu = true; } else { document.removeEventListener('click', this.boundCloseAdditionalMenuHandler); } } hideModeMenu() { if (this.initateOpenAdditionalMenu) { this.initateOpenAdditionalMenu = false; return; } const menu = this.shadowRoot.querySelector('ul.extended-menu'); if (menu && !menu.classList.contains('hidden')) this.toggleModeMenu(); } loadTheme(themeName) { const button = this.shadowRoot.querySelector('.button-' + themeName); const otherButtons = this.shadowRoot.querySelectorAll('.button-theme'); const body = this.shadowRoot.host.getRootNode({composed: true}).body; if (button === null || otherButtons.length === 0 || body === null) { return; } otherButtons.forEach((button) => button.classList.remove('active')); button.classList.add('active'); if (!body.classList.contains(themeName)) { this.themes.forEach((theme) => { body.classList.remove(theme.class); }); body.classList.add(themeName); } } saveTheme(themeName) { //set active state const browserModeDark = window.matchMedia('(prefers-color-scheme: dark)'); const browserModeLight = window.matchMedia('(prefers-color-scheme: light)'); if (themeName === 'light-theme' && browserModeLight.matches) { localStorage.removeItem('prefered-color-mode'); } else if (themeName === this.darkModeClass && browserModeDark.matches) { localStorage.removeItem('prefered-color-mode'); } else { localStorage.setItem('prefered-color-mode', themeName); } } static get styles() { return css` ${commonStyles.getThemeCSS()} ${commonStyles.getGeneralCSS()} ${commonStyles.getButtonCSS()} mode-button, button.button { border: none; } .active, .extended-menu li a.active dbp-icon { color: var(--dbp-accent); } .active { font-weight: bolder; } a:hover:not(.active), .extended-menu li a:hover:not(.active) { color: var(--dbp-hover-color, var(--dbp-content)); background-color: var(--dbp-hover-background-color); transition: none; } a { padding: 0.3em; display: inline-block; text-decoration: none; transition: background-color 0.15s, color 0.15s; color: var(--dbp-content); } .extended-menu { list-style: none; border: var(--dbp-border); position: absolute; background-color: var(--dbp-background); z-index: 1000; border-radius: var(--dbp-border-radius); } .extended-menu li { text-align: left; min-width: 160px; } .extended-menu li a { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; padding: 12px 15px; width: 100%; box-sizing: border-box; text-align: left; color: var(--dbp-content); background: none; display: block; } .icon { margin-right: 10px; } #theme-menu { position: relative; } .ul-right { right: 0px; } `; } render() { const i18n = this._i18n; return html` <div id="theme-menu" class="${classMap({hidden: this.themes.length <= 1})}"> <a href="#" class="mode-button" title="${i18n.t('color-mode')}" @click="${(e) => { this.toggleModeMenu(); e.preventDefault(); }}"> <dbp-icon name="contrast"></dbp-icon> </a> <ul class="extended-menu hidden ${classMap({'ul-right': this.dropdownRight})}"> ${this.themes.map( (theme) => html` <li class="" id="${theme.class}"> <a href="#" class="button-theme button-${theme.class}" @click="${(e) => { this.loadTheme(theme.class); this.saveTheme(theme.class); e.preventDefault(); }}" title="${theme.name}"> <dbp-icon class="icon" name="${theme.icon}"></dbp-icon> ${theme.name} </a> </li> `, )} </ul> </div> `; } }