UNPKG

@schukai/monster

Version:

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

244 lines (218 loc) 6.42 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 { addAttributeToken } from "../../dom/attributes.mjs"; import { ATTRIBUTE_ERRORMESSAGE } from "../../dom/constants.mjs"; import { assembleMethodSymbol, CustomElement, registerCustomElement, } from "../../dom/customelement.mjs"; import { fireCustomEvent } from "../../dom/events.mjs"; import { Observer } from "../../types/observer.mjs"; import { WidthToggleStyleSheet } from "./stylesheet/width-toggle.mjs"; import { instanceSymbol } from "../../constants.mjs"; import { internalSymbol } from "../../constants.mjs"; export { WidthToggle, MODE_SMALL, MODE_WIDE }; /** * @private * @type {symbol} */ const widthToggleElementSymbol = Symbol("WidthToggleElement"); /** * @private * @type {symbol} */ const toggleElementSymbol = Symbol("toggleElement"); /** * @private * @type {symbol} */ const insideElementSymbol = Symbol("insideElement"); /** * @type {string} */ const MODE_SMALL = "small"; /** * @type {string} */ const MODE_WIDE = "wide"; /** * A WidthToggle Control * * @fragments /fragments/components/layout/width-toggle/ * * @example /examples/components/layout/width-toggle-simple Toggle Width * * @since 3.57.0 * @copyright Volker Schukai * @summary The WidthToggle component allows users to dynamically change the width of a panel by clicking a button. * @summary This feature improves readability and space utilization by allowing the panel width to be adjusted * @summary according to user preferences. */ class WidthToggle extends CustomElement { /** * This method is called by the `instanceof` operator. * @return {symbol} */ static get [instanceSymbol]() { return Symbol.for( "@schukai/monster/components/layout/width-toggle@@instance", ); } /** * 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 {string} width Dimension * @property {string} width.initial Initial dimension of the start panel * @property {string} width.small Minimum dimension of the start panel * @property {string} width.wide Maximum dimension of the start panel * @property {string} default Default dimension of the start panel */ get defaults() { return Object.assign({}, super.defaults, { templates: { main: getTemplate(), }, width: { small: "40%", wide: "95%", }, default: MODE_SMALL, }); } /** * @return {void} */ [assembleMethodSymbol]() { super[assembleMethodSymbol](); initControlReferences.call(this); initEventHandler.call(this); applyContainerWidth.call(this, this.getOption("default")); } /** * Check if the dimension is a percentage and within a valid range, then set the dimension option. * * @param {string} mode - The mode of the panel. Possible values are "wide" or "small". * @return {Object} - Returns the current object instance for chaining. * @throws {Error} - If the mode is not supported. */ /** * @param {string} mode * @return {WidthToggle} * @throws {Error} no shadow-root is defined */ setWidth(mode) { applyContainerWidth.call(this, mode); return this; } /** * * @return {string} */ static getTag() { return "monster-width-toggle"; } /** * @return {CSSStyleSheet[]} */ static getCSSStyleSheet() { return [WidthToggleStyleSheet]; } } /** * Set the dimensions of the panel based on the split type. * @param {string} mode * @throws {Error} no shadow-root is defined * @private */ function applyContainerWidth(mode) { const width = this.getOption("width." + mode); if (!width) { addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message); throw new Error("unsupported mode"); } switch (mode) { case MODE_SMALL: case MODE_WIDE: this[toggleElementSymbol].style.right = "calc( 50% - (" + width + " / 2) + 1rem)"; this[toggleElementSymbol].setAttribute("data-monster-state", mode); this[insideElementSymbol].style.width = width; fireCustomEvent(this, "monster-dimension-changed", { controller: this, dimension: width, }); break; default: const error = new Error("unsupported mode"); addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, error.message); throw error; } } /** * @private * @return {Select} * @throws {Error} no shadow-root is defined */ function initControlReferences() { if (!this.shadowRoot) { throw new Error("no shadow-root is defined"); } this[widthToggleElementSymbol] = this.shadowRoot.querySelector( "[data-monster-role=control]", ); this[toggleElementSymbol] = this.shadowRoot.querySelector( "[data-monster-role=toggle]", ); this[insideElementSymbol] = this.shadowRoot.querySelector( "[data-monster-role=inside]", ); } /** * @private */ function initEventHandler() { const self = this; this[toggleElementSymbol].addEventListener("click", function () { const mode = self[toggleElementSymbol].getAttribute("data-monster-state") === MODE_SMALL ? MODE_WIDE : MODE_SMALL; applyContainerWidth.call(self, mode); }); return this; } /** * @private * @return {string} */ function getTemplate() { // language=HTML return ` <div data-monster-role="control" part="control"> <div part="container" data-monster-role="container"> <div part="toggle" data-monster-role="toggle" data-monster-state="wide"></div> <div part="inside" data-monster-role="inside"> <slot></slot> </div> </div> </div>`; } registerCustomElement(WidthToggle);