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/)

344 lines (326 loc) 12 kB
import {createInstance} from './i18n.js'; import {html, css} from 'lit'; import {ScopedElementsMixin} from '@dbp-toolkit/common'; import {AdapterLitElement, Icon, LangMixin} from '@dbp-toolkit/common'; import {classMap} from 'lit/directives/class-map.js'; export class LayoutSwitcher extends LangMixin( ScopedElementsMixin(AdapterLitElement), createInstance, ) { /** * constructor function of LayoutSwitcher class for creating and initializing objects instance of this class */ constructor() { super(); /** @type {string} */ this.langDir = ''; /** @type {boolean} */ this.isDisabled = false; /** @type {string} */ this.appName = ''; /** @type {Array} */ this.layouts = [{name: 'wide'}, {name: 'standard'}]; /** @type {string} */ this.defaultLayout = null; /** @type {string} */ this.layout = ''; /** @type {boolean} */ this.isDefaultLayout = this.layout === 'standard'; /** @type {string} */ this.disabledLayout = ''; /** @type {string} */ this.defaultModeClass = 'wide-layout'; /** @type {string} */ this.alternateModeClass = 'standard-layout'; /** @type {boolean} */ this.dropdown = false; this.boundCloseAdditionalMenuHandler = this.hideLayoutMenu.bind(this); this.initateOpenLayoutMenu = false; } /** * Defines reactive properties for the component */ static get properties() { return { ...super.properties, appName: {type: String, attribute: 'app-name'}, langDir: {type: String, attribute: 'lang-dir'}, isDisabled: {type: Boolean, attribute: 'disabled', reflect: true}, layout: {type: String}, layouts: {type: Array}, defaultLayout: {type: String, attribute: 'default-layout'}, disabledLayout: {type: String, attribute: 'disabled-layout'}, isDefaultLayout: {type: Boolean}, dropdown: {type: Boolean}, }; } /** * scopedElements Registers custom elements used within the component */ static get scopedElements() { return { 'dbp-icon': Icon, 'dbp-layout-switcher': LayoutSwitcher, }; } update(changedProperties) { changedProperties.forEach((oldValue, propName) => { if (propName === 'defaultLayout') { /* Set default layout based on: */ if (this._getStoredLayout()) { // 1. Stored layout value this.layout = this._getStoredLayout(); } else { // 2. Default layout from attribute // 3. Fallback to 'standard' this.layout = this.defaultLayout || 'standard'; this._setStoredLayout(this.layout); } } if (propName === 'layout') { this.dispatchEvent(new CustomEvent('layout-changed', {detail: this.layout})); if (this.layout === 'standard') { this.loadDefaultLayout(); } else { this.loadAlternateLayout(); } } }); super.update(changedProperties); } /** * connectedCallback lifecycle function is executed whenever a custom element is inserted into the DOM */ connectedCallback() { super.connectedCallback(); this.updateLayoutBasedOnWindowSize(); window.addEventListener('resize', this.updateLayoutBasedOnWindowSize.bind(this)); this.updateComplete.then(() => { if (this.disabledLayout) { /** Disable layout switcher if disabledLayout is set and only one layout is available */ this.isDisabled = true; this.layout = this.layouts.filter( (layout) => layout.name !== this.disabledLayout, )[0].name; this._setStoredLayout(this.layout); } if (this.layout === 'standard') { this.loadDefaultLayout(); } else { this.loadAlternateLayout(); } }); } /** * disconnectedCallback lifecycle function used for cleanup when the element is removed from the DOM */ disconnectedCallback() { super.disconnectedCallback(); window.removeEventListener('resize', this.updateLayoutBasedOnWindowSize.bind(this)); } /** * Updates the layout based on the window size */ updateLayoutBasedOnWindowSize() { if (window.innerWidth < 871) { this.layout = 'wide'; } else { this.layout = this._getStoredLayout() || this.defaultLayout || 'standard'; } this.requestUpdate(); } loadDefaultLayout() { this.isDefaultLayout = true; } loadAlternateLayout() { this.isDefaultLayout = false; } /** * Gets the stored layout value with namespace * @returns {string} * @private */ _getStoredLayout() { if (!this.appName) return ''; const key = `${this.appName}:layout`; return localStorage.getItem(key); } /** * Sets the stored layout value with namespace * @param {string} value * @private */ _setStoredLayout(value) { if (!this.appName) return false; const key = `${this.appName}:layout`; localStorage.setItem(key, value); } toggleLayout(newLayout) { /* Update the layout property of the component with the new layout. */ this.layout = newLayout; this._setStoredLayout(this.layout); /* Dispatch a custom event to inform other parts of the application of the layout change.The new layout state is passed as a detail in the event for use by event listeners. */ this.dispatchEvent(new CustomEvent('layout-changed', {detail: this.layout})); this.loadLayout(); } loadLayout() { this.isDefaultLayout = this.layout === 'standard'; } /** * toggleLayoutMenu to control the visibility of a dropdown menu by toggling its state based on user interactions * @param {object} e * @returns {void} */ toggleLayoutMenu(e) { this.dropdown = !this.dropdown; if (this.dropdown) { document.addEventListener('click', this.boundCloseAdditionalMenuHandler); this.initateOpenLayoutMenu = true; } else { document.removeEventListener('click', this.boundCloseAdditionalMenuHandler); } } /** * hideLayoutMenu Hides the layout menu if a click occurs outside of it. * @param {object} event * @returns {void} */ hideLayoutMenu(event) { if (this.initateOpenLayoutMenu) { this.initateOpenLayoutMenu = false; return; } const menu = this.shadowRoot.querySelector('.extended-menu'); if (!menu.classList.contains('hidden')) { this.dropdown = false; document.removeEventListener('click', this.boundCloseAdditionalMenuHandler); this.requestUpdate(); } } static get styles() { return css` :host { display: block; } .layout-button, button.button { cursor: pointer; border: none; display: none; } @media screen and (min-width: 871px) { .layout-button { display: inline-block; } } .layout-button.active dbp-icon { color: var(--dbp-accent); } .active { font-weight: bold; } a.active, .extended-menu li a.active { color: var(--dbp-accent); } a.active dbp-icon { color: var(--dbp-accent); } 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); margin: 0px; padding: 0px; } .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; color: var(--dbp-content); background: none; display: block; text-decoration: none; } .icon { margin-right: 10px; } #layout-menu { position: relative; } .ul-right { right: 0px; } .hidden { display: none; } `; } render() { const i18n = this._i18n; if (!this.isDisabled) { return html` <div id="layout-menu"> <a href="#" class=${classMap({'layout-button': true, active: this.dropdown})} title="${i18n.t('switch-layout')}" @click="${this.toggleLayoutMenu}"> <dbp-icon name="layout"></dbp-icon> </a> <ul class="extended-menu ${classMap({hidden: !this.dropdown})}"> ${this.layouts.map( (layout) => html` <li> <!-- Title for each layout option --> <a href="#" class="${this.layout === layout.name ? 'active' : ''}" title="${layout.name === 'wide' ? i18n.t('switch-to-wide-layout-label') : i18n.t('switch-to-standard-layout-label')}" @click="${() => this.toggleLayout(layout.name)}"> <!-- Icon based on layout--> <dbp-icon class="icon" name="${layout.name === 'wide' ? 'wide' : 'standard'}"></dbp-icon> ${layout.name === 'wide' ? i18n.t('wide-layout-name') : i18n.t('standard-layout-name')} </a> </li> `, )} </ul> </div> `; } } }