diff --git a/packages/app-shell/src/app-shell.js b/packages/app-shell/src/app-shell.js index 10f0b66481bdd2a3dbe7f0904b95a54b1369967a..c0878047d7ecdea2307335387a78d50b4914e4d3 100644 --- a/packages/app-shell/src/app-shell.js +++ b/packages/app-shell/src/app-shell.js @@ -5,8 +5,8 @@ import {LanguageSelect} from '@dbp-toolkit/language-select'; import {Icon} from '@dbp-toolkit/common'; import {AuthKeycloak} from '@dbp-toolkit/auth'; import {AuthMenuButton} from './auth-menu-button.js'; -import {ColorMode} from './color-mode.js'; import {Notification} from '@dbp-toolkit/notification'; +import {ThemeSwitcher} from '@dbp-toolkit/theme-switcher'; import * as commonStyles from '@dbp-toolkit/common/styles'; import {classMap} from 'lit/directives/class-map.js'; import {Router} from './router.js'; @@ -74,7 +74,6 @@ export class AppShell extends ScopedElementsMixin(DBPLitElement) { this.auth = {}; - this.themes = ""; } static get scopedElements() { @@ -83,7 +82,7 @@ export class AppShell extends ScopedElementsMixin(DBPLitElement) { 'dbp-build-info': BuildInfo, 'dbp-auth-keycloak': AuthKeycloak, 'dbp-auth-menu-button': AuthMenuButton, - 'dbp-color-mode-button': ColorMode, + 'dbp-theme-switcher': ThemeSwitcher, 'dbp-notification': Notification, 'dbp-icon': Icon, 'dbp-matomo': MatomoElement, @@ -260,8 +259,6 @@ export class AppShell extends ScopedElementsMixin(DBPLitElement) { buildTime: { type: String, attribute: "build-time" }, env: { type: String }, auth: { type: Object }, - themes: { type: String, attribute: "themes" }, - darkModeThemeOverride: {type: String, attribute: "dark-mode-theme-override"} }; } @@ -911,10 +908,6 @@ export class AppShell extends ScopedElementsMixin(DBPLitElement) { menuTemplates.push(html`<li><a @click="${(e) => this.onMenuItemClick(e)}" href="${this.router.getPathname({component: routingName})}" data-nav class="${getSelectClasses(routingName)}" title="${this.metaDataText(routingName, "description")}">${this.metaDataText(routingName, "short_name")}</a></li>`); } - const colorModeButton = this.darkModeThemeOverride !== undefined ? - html`<dbp-color-mode-button themes="${this.themes}" dark-mode-theme-override=${this.darkModeThemeOverride} lang="${this.lang}"></dbp-color-mode-button>` : - html`<dbp-color-mode-button themes="${this.themes}" lang="${this.lang}"></dbp-color-mode-button>`; - const kc = this.keycloakConfig; return html` <slot class="${slotClassMap}"></slot> @@ -926,7 +919,7 @@ export class AppShell extends ScopedElementsMixin(DBPLitElement) { <header> <slot name="header"> <div class="hd1-left"> - ${colorModeButton} + <dbp-theme-switcher subscribe="themes,dark-mode-theme-override" lang="${this.lang}"></dbp-theme-switcher> <dbp-language-select id="lang-select" lang="${this.lang}"></dbp-language-select> </div> <div class="hd1-middle"> diff --git a/packages/app-shell/src/color-mode.js b/packages/app-shell/src/color-mode.js deleted file mode 100644 index 89e02999cb8356f517c956b7c0881381d8de433b..0000000000000000000000000000000000000000 --- a/packages/app-shell/src/color-mode.js +++ /dev/null @@ -1,254 +0,0 @@ -import {html, css} from 'lit'; -import * as commonStyles from '@dbp-toolkit/common/styles'; -import {createInstance} from './i18n.js'; -import DBPLitElement from '@dbp-toolkit/common/dbp-lit-element'; -import {ScopedElementsMixin} from '@open-wc/scoped-elements'; -import {Icon} from '@dbp-toolkit/common'; -import {classMap} from 'lit/directives/class-map.js'; - - -export class ColorMode extends ScopedElementsMixin(DBPLitElement) { - constructor() { - super(); - - this._i18n = createInstance(); - this.lang = this._i18n.language; - this.themes = []; - this.boundCloseAdditionalMenuHandler = this.hideModeMenu.bind(this); - this.detectBrowserDarkMode = false; - this.darkModeClass = "dark-theme"; - } - - static get properties() { - return { - ...super.properties, - lang: { type: String }, - themes: { type: Array, attribute: "themes" }, - darkModeThemeOverride: {type: String, attribute: "dark-mode-theme-override"} - }; - } - - static get scopedElements() { - return { - 'dbp-icon': Icon - }; - } - - - update(changedProperties) { - changedProperties.forEach((oldValue, propName) => { - if (propName === "lang") { - this._i18n.changeLanguage(this.lang); - } - }); - super.update(changedProperties); - } - - connectedCallback() { - super.connectedCallback(); - this.updateComplete.then(() => { - console.log("------", this.darkModeThemeOverride); - if (typeof this.darkModeThemeOverride === "undefined") { - this.detectBrowserDarkMode = true; - console.log("darkMode on"); - } else if ( this.darkModeThemeOverride === "") { - this.detectBrowserDarkMode = false; - console.log("darkMode off"); - } else { - this.detectBrowserDarkMode = true; - this.darkModeClass = this.darkModeThemeOverride; - } - this.loadTheme("light-theme"); - this.detectInitialMode(); - }); - - } - - 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-text); - background-color: var(--dbp-hover-base); - transition: none; - } - - a { - padding: 0.3em; - display: inline-block; - text-decoration: none; - transition: background-color 0.15s, color 0.15s; - color: var(--dbp-text); - } - - .extended-menu { - list-style: none; - border: var(--dbp-border); - position: absolute; - background-color: var(--dbp-base); - 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; - w1idth: 100%; - box-sizing: border-box; - text-align: left; - color: var(--dbp-text); - background: none; - display: block - } - - .icon { - margin-right: 10px; - } - - `; - } - - render() { - const i18n = this._i18n; - - return html` - <div class="${classMap({hidden: this.themes.length <= 1})}"> - <a class="mode-button" title="${i18n.t('color-mode')}" - @click="${() => {this.toggleModeMenu();}}"><dbp-icon name="contrast"></dbp-icon></a> - <ul class='extended-menu hidden'> - ${this.themes.map(theme => html` - <li class="" id="${theme.class}"> - <a class="button-theme button-${theme.class}" @click="${() => {this.loadTheme(theme.class); this.saveTheme(theme.class);}}" title="${theme.name}"> - <dbp-icon class="icon" name="${theme.icon}"></dbp-icon> ${theme.name} - </a> - </li> - `)} - </ul> - </div> - `; - } -} \ No newline at end of file