UNPKG

@schukai/monster

Version:

Monster is a simple library for creating fast, robust and lightweight websites.

338 lines (296 loc) 8.15 kB
/** * Copyright © schukai GmbH and all contributing authors, {{copyRightYear}}. All rights reserved. * Node module: @schukai/monster * * This source code is licensed under the GNU Affero General Public License version 3 (AGPLv3). * The full text of the license can be found at: https://www.gnu.org/licenses/agpl-3.0.en.html * * For those who do not wish to adhere to the AGPLv3, a commercial license is available. * Acquiring a commercial license allows you to use this software without complying with the AGPLv3 terms. * For more information about purchasing a commercial license, please contact schukai GmbH. * * SPDX-License-Identifier: AGPL-3.0 */ import { instanceSymbol } from "../../constants.mjs"; import { assembleMethodSymbol, CustomElement, registerCustomElement, } from "../../dom/customelement.mjs"; import { findTargetElementFromEvent } from "../../dom/events.mjs"; import { clone } from "../../util/clone.mjs"; import { ColumnBarStyleSheet } from "./stylesheet/column-bar.mjs"; import { createPopper } from "@popperjs/core"; import { getLocaleOfDocument } from "../../dom/locale.mjs"; export { ColumnBar }; /** * @private * @type {symbol} */ const settingsButtonElementSymbol = Symbol("settingButtonElement"); /** * @private * @type {symbol} */ const settingsButtonEventHandlerSymbol = Symbol("settingsButtonEventHandler"); /** * @private * @type {symbol} */ const settingsLayerElementSymbol = Symbol("settingsLayerElement"); /** * @private * @type {symbol} */ const dotsContainerElementSymbol = Symbol("dotsContainerElement"); /** * @private * @type {symbol} */ const popperInstanceSymbol = Symbol("popperInstance"); /** * A column bar for a datatable * * @fragments /fragments/components/datatable/datatable/ * * @example /examples/components/datatable/empty * * @copyright schukai GmbH * @summary The ColumnBar component is used to show and configure the columns of a datatable. **/ class ColumnBar extends CustomElement { /** * This method is called by the `instanceof` operator. * @return {symbol} */ static get [instanceSymbol]() { return Symbol.for("@schukai/monster/components/column-bar"); } /** * To set the options via the HTML tag, the attribute `data-monster-options` must be used. * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control} * * The individual configuration values can be found in the table. * * @property {Object} templates Template definitions * @property {string} templates.main Main template * @property {object} datasource The datasource * @property {boolean} autoLoad If true, the datasource is called immediately after the control is created. */ get defaults() { const obj = Object.assign({}, super.defaults, { templates: { main: getTemplate(), }, locale: getTranslations(), columns: [], }); return obj; } /** * * @return {string} */ static getTag() { return "monster-column-bar"; } /** * * @return {void} */ [assembleMethodSymbol]() { super[assembleMethodSymbol](); initControlReferences.call(this); initEventHandler.call(this); } /** * @return {CSSStyleSheet[]} */ static getCSSStyleSheet() { return [ColumnBarStyleSheet]; } } /** * @private * @returns {{settings: string}} */ function getTranslations() { const locale = getLocaleOfDocument(); switch (locale.language) { case "de": return { settings: "Einstellungen", }; case "fr": return { settings: "Paramètres", }; case "sp": return { settings: "Configuración", }; case "it": return { settings: "Impostazioni", }; case "pl": return { settings: "Ustawienia", }; case "no": return { settings: "Innstillinger", }; case "dk": return { settings: "Indstillinger", }; case "sw": return { settings: "Inställningar", }; default: case "en": return { settings: "Settings", }; } } /** * @private * @return {ColumnBar} */ function initControlReferences() { if (!this.shadowRoot) { throw new Error("no shadow-root is defined"); } this[settingsButtonElementSymbol] = this.shadowRoot.querySelector( "[data-monster-role=settings-button]", ); this[settingsLayerElementSymbol] = this.shadowRoot.querySelector( "[data-monster-role=settings-layer]", ); this[dotsContainerElementSymbol] = this.shadowRoot.querySelector( "[data-monster-role=dots]", ); return this; } /** * @private */ function initEventHandler() { const self = this; self[popperInstanceSymbol] = createPopper( self[settingsButtonElementSymbol], self[settingsLayerElementSymbol], { placement: "auto", modifiers: [ { name: "offset", options: { offset: [10, 10], }, }, ], }, ); self[dotsContainerElementSymbol].addEventListener("click", function (event) { const element = findTargetElementFromEvent( event, "data-monster-role", "column", ); if (element) { const index = element.getAttribute("data-monster-index"); event.preventDefault(); const columns = clone(self.getOption("columns")); const column = columns.find((col) => { return parseInt(col.index) === parseInt(index); }); column.visible = !column.visible; self.setOption("columns", columns); } }); self[settingsButtonEventHandlerSymbol] = (event) => { const clickTarget = event.composedPath()?.[0]; if ( self[settingsLayerElementSymbol] === clickTarget || self[settingsLayerElementSymbol].contains(clickTarget) ) { return; } document.body.removeEventListener( "click", self[settingsButtonEventHandlerSymbol], ); }; self[settingsButtonElementSymbol].addEventListener("click", function (event) { const element = findTargetElementFromEvent( event, "data-monster-role", "settings-button", ); if (element) { self[settingsLayerElementSymbol].classList.toggle("visible"); event.preventDefault(); if (self[settingsLayerElementSymbol].classList.contains("visible")) { self[popperInstanceSymbol].update(); queueMicrotask(() => { document.body.addEventListener( "click", self[settingsButtonEventHandlerSymbol], ); }); } } }); self[settingsLayerElementSymbol].addEventListener("change", function (event) { const control = event.target; const index = control.getAttribute("data-monster-index"); const columns = clone(self.getOption("columns")); const column = columns.find((col) => { return parseInt(col.index) === parseInt(index); }); column.visible = control.checked; self.setOption("columns", columns); }); } /** * @private * @return {string} */ function getTemplate() { // language=HTML return ` <template id="column"> <div data-monster-role="column"> <label><input type="checkbox" data-monster-attributes=" data-monster-index path:column.index, checked path:column.visible | ?:checked:"><span data-monster-replace="path:column.name" ></span></label> </div> </template> <template id="dots"> <li data-monster-insert=""> <a href="#" data-monster-role="column" data-monster-attributes=" class path:dots.visible | ?:is-hidden:is-visible, title path:dots.name, data-monster-index path:dots.index"> </a> </li> </template> <div data-monster-role="control" part="control" data-monster-select-this="true" data-monster-attributes="class path:columns | has-entries | ?::hidden"> <ul data-monster-insert="dots path:columns" data-monster-role="dots"></ul> <a href="#" data-monster-role="settings-button" data-monster-replace="path:locale.settings">Settings</a> <div data-monster-role="settings-layer"> <div data-monster-insert="column path:columns" data-monster-role="settings-popup-list"> </div> </div> </div> `; } registerCustomElement(ColumnBar);