UNPKG

@schukai/monster

Version:

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

290 lines (257 loc) 6.71 kB
/** * Copyright © Volker Schukai 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 Volker Schukai. * * SPDX-License-Identifier: AGPL-3.0 */ import { assembleMethodSymbol, registerCustomElement, } from "../../dom/customelement.mjs"; import { DetailsStyleSheet } from "./stylesheet/details.mjs"; import { ATTRIBUTE_BUTTON_LABEL } from "../host/constants.mjs"; import { isString } from "../../types/is.mjs"; import { generateUniqueConfigKey } from "../host/util.mjs"; import { Collapse, nameSymbol } from "./collapse.mjs"; import { instanceSymbol } from "../../constants.mjs"; import { getLocaleOfDocument } from "../../dom/locale.mjs"; export { Details }; /** * @private * @type {symbol} */ const buttonElementSymbol = Symbol("buttonElement"); /** * @private * @type {symbol} */ const buttonEventHandlerSymbol = Symbol("buttonEventHandler"); /** * A Details component * * @fragments /fragments/components/layout/details/ * * @example /examples/components/layout/details-simple * @example /examples/components/layout/details-with-label * @example /examples/components/layout/details-as-accordion * @example /examples/components/layout/details-with-your-own-design * * @since 3.74.0 * @copyright Volker Schukai * @summary A simple but cool detail component. This is based on the collapse component and extends it with a button. * @summary You can also easily build an accordion from the component. */ class Details extends Collapse { /** * This method is called by the `instanceof` operator. * @return {symbol} */ static get [instanceSymbol]() { return Symbol.for("@schukai/monster/components/layout/details@@instance"); } /** * */ constructor() { super(); // the name is only used for the host config and the event name this[nameSymbol] = "details"; } /** * 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} button Button configuration * @property {string} button.label Button label */ get defaults() { return Object.assign({}, super.defaults, { templates: { main: getTemplate(), }, labels: getTranslations(), }); } /** * @return {void} */ [assembleMethodSymbol]() { super[assembleMethodSymbol](); initButtonLabel.call(this); initControlReferences.call(this); initEventHandler.call(this); } /** * @return {void} */ connectedCallback() { super.connectedCallback(); const containDocument = this.shadowRoot; if (containDocument !== null) { const previousElement = this.previousElementSibling; if (previousElement && previousElement.tagName === "MONSTER-DETAILS") { this[buttonElementSymbol].style.borderTop = "0"; } } } /** * @return {string} */ static getTag() { return "monster-details"; } /** * @return {CSSStyleSheet[]} */ static getCSSStyleSheet() { const css = super.getCSSStyleSheet(); css.push(DetailsStyleSheet); return css; } } /** * @private * @returns {object} */ function getTranslations() { const locale = getLocaleOfDocument(); switch (locale.language) { case "de": return { button: "Details", }; case "fr": return { button: "Détails", }; case "sp": return { button: "Detalles", }; case "it": return { button: "Dettagli", }; case "pl": return { button: "Szczegóły", }; case "no": return { button: "Detaljer", }; case "dk": return { button: "Detaljer", }; case "sw": return { button: "Detaljer", }; default: case "en": return { button: "Details", }; } } /** * @private * @return {Select} * @throws {Error} no shadow-root is defined */ function initControlReferences() { if (!this.shadowRoot) { throw new Error("no shadow-root is defined"); } this[buttonElementSymbol] = this.shadowRoot.querySelector( "[data-monster-role=button]", ); } /** * @private */ function initEventHandler() { if (!this.shadowRoot) { throw new Error("no shadow-root is defined"); } this[buttonEventHandlerSymbol] = (event) => { this.toggle(); }; this[buttonElementSymbol].addEventListener( "click", this[buttonEventHandlerSymbol], ); return this; } /** * @private * @return {string} */ function initButtonLabel() { let label; const setLabel = false; if (this.hasAttribute(ATTRIBUTE_BUTTON_LABEL)) { label = this.getAttribute(ATTRIBUTE_BUTTON_LABEL); } else { label = this.getOption("labels.button", "Details"); } if (!isString(label)) { label = ""; } if (label === "") { label = this.innerText; } label = label.trim(); if (label === "") { label = this.getOption("labels.button", "Details"); } if (label.length > 100) { label = `${label.substring(0, 99)}…`; } this.setAttribute(ATTRIBUTE_BUTTON_LABEL, label); this.setOption("labels.button", label); return label; } /** * @private * @return {string} */ function getConfigKey() { return generateUniqueConfigKey("details", this.id, "state"); } /** * @private * @return {string} */ function getTemplate() { // language=HTML return ` <div data-monster-role="control" part="control" class="overflow-hidden"> <div data-monster-role="summary" part="summary"> <button part="button" data-monster-attributes="class path:classes.button" data-monster-role="button" data-monster-replace="path:labels.button | default:click me">click me </button> </div> <div data-monster-role="detail"> <div data-monster-attributes="class path:classes.container" part="container" data-monster-role="container"> <slot part="slot"></slot> </div> <div part="deco-line" class="deco-line" data-monster-role="deco"></div> </div> </div>`; } registerCustomElement(Details);