UNPKG

@schukai/monster

Version:

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

207 lines (182 loc) 5.1 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 { instanceSymbol } from "../../constants.mjs"; import { assembleMethodSymbol, CustomElement, registerCustomElement, } from "../../dom/customelement.mjs"; import { CallButtonStyleSheet } from "./stylesheet/call-button.mjs"; import { isArray, isObject, isFunction } from "../../types/is.mjs"; import { findElementWithSelectorUpwards } from "../../dom/util.mjs"; import { ATTRIBUTE_PREFIX } from "../../dom/constants.mjs"; export { CallButton }; /** * @private * @type {symbol} */ const callButtonElementSymbol = Symbol("callButtonElement"); /** * @type {string} */ const ATTRIBUTE_REFERENCE = `${ATTRIBUTE_PREFIX}reference`; /** * @type {string} */ const ATTRIBUTE_CALL = `${ATTRIBUTE_PREFIX}call`; /** * The call button component is used to call a method of another element. * * @fragments /fragments/components/host/call-button/ * * @example /examples/components/host/call-button-simple Call button * * @copyright Volker Schukai * @summary A call button component that can call a method of another element. */ class CallButton extends CustomElement { /** * This method is called by the `instanceof` operator. * @return {symbol} */ static get [instanceSymbol]() { return Symbol.for("@schukai/component-host/call-button@@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 */ get defaults() { return Object.assign( {}, super.defaults, { templates: { main: getTemplate(), }, references: { callableSelector: undefined, }, call: undefined, labels: { button: "<slot>Toggle</slot>", }, }, initOptionsFromArguments.call(this), ); } /** * * @return {string} */ static getTag() { return "monster-call-button"; } /** * @return {CallButton} */ [assembleMethodSymbol]() { super[assembleMethodSymbol](); initControlReferences.call(this); initEventHandler.call(this); } /** * @return {Array} */ static getCSSStyleSheet() { return [CallButtonStyleSheet]; } } /** * @private * @return {CallButton} */ function initControlReferences() { if (!this.shadowRoot) { throw new Error("no shadow-root is defined"); } this[callButtonElementSymbol] = this.shadowRoot.querySelector( "[data-monster-role=control]", ); return this; } /** * @private * @return {object} * @throws {TypeError} incorrect arguments passed for the datasource * @throws {Error} the datasource could not be initialized */ function initOptionsFromArguments() { const options = {}; const value = this.getAttribute(ATTRIBUTE_REFERENCE); if (value) { if (!isObject(options.references)) { options.references = {}; } const selectors = value.split(","); if (isArray(selectors) && selectors.length === 0) { throw new TypeError("incorrect arguments passed for the datasource"); } options.references.callableSelector = selectors; } const call = this.getAttribute(ATTRIBUTE_CALL); if (call) { options.call = call; } return options; } /** * @private * @throws {Error} The option references.callableSelector must be an array */ function initEventHandler() { this[callButtonElementSymbol].addEventListener("click", (event) => { event.preventDefault(); const selectors = this.getOption("references.callableSelector"); if (!isArray(selectors)) { throw new Error( "The option references.callableSelector must be an array", ); } const call = this.getOption("call"); if (!call) { throw new Error("The option call must be defined"); } for (const selector of selectors) { const element = findElementWithSelectorUpwards(this, selector); if (element instanceof HTMLElement && isFunction(element?.[call])) { element[call](); } } }); } /** * @private * @return {string} */ function getTemplate() { // language=HTML return ` <div data-monster-role="control" part="control" data-monster-attributes="class path:references.callableSelector | ?::hidden"> <a href="#" data-monster-role="call-button" data-monster-replace="path:labels.button"></a> </div> `; } registerCustomElement(CallButton);