Skip to content
Snippets Groups Projects
Select Git revision
  • f68f71f94d9f1be5143fe12dd572080090e63d8c
  • master default protected
  • 1.31
  • 4.38.2
  • 4.38.1
  • 4.38.0
  • 4.37.2
  • 4.37.1
  • 4.37.0
  • 4.36.0
  • 4.35.0
  • 4.34.1
  • 4.34.0
  • 4.33.1
  • 4.33.0
  • 4.32.2
  • 4.32.1
  • 4.32.0
  • 4.31.0
  • 4.30.1
  • 4.30.0
  • 4.29.1
  • 4.29.0
23 results

call-button.mjs

Blame
  • Volker Schukai's avatar
    Volker Schukai authored
    f68f71f9
    History
    call-button.mjs 5.25 KiB
    /**
     * Copyright 2023 schukai GmbH
     * 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 { getDocument } from "../../dom/util.mjs";
    import { ATTRIBUTE_PREFIX } from "../../dom/constants.mjs";
    
    export { CallButton };
    
    /**
     * @private
     * @type {symbol}
     */
    const callButtonElementSymbol = Symbol("callButtonElement");
    
    /**
     * @memberOf Monster.Components.Host
     * @type {string}
     */
    const ATTRIBUTE_REFERENCE = `${ATTRIBUTE_PREFIX}reference`;
    /**
     * @memberOf Monster.Components.Host
     * @type {string}
     */
    const ATTRIBUTE_CALL = `${ATTRIBUTE_PREFIX}call`;
    
    /**
     * The call button component is used to call a method of another element.
     *
     * <img src="./images/call-button.png">
     *
     * Dependencies: the system uses functions of the [monsterjs](https://monsterjs.org/) library
     *
     * You can create this control either by specifying the HTML tag <monster-call-button />` directly in the HTML or using
     * Javascript via the `document.createElement('monster-call-button');` method.
     *
     * ```html
     * <monster-call-button></monster-call-button>
     * ```
     *
     * Or you can create this CustomControl directly in Javascript:
     *
     * ```js
     * import '@schukai/component-host/source/filter.mjs';
     * document.createElement('monster-call-button');
     * ```
     *
     * The Body should have a class "hidden" to ensure that the styles are applied correctly.
     *
     * ```css
     * body.hidden {
     *    visibility: hidden;
     * }
     * ```
     *
     * @startuml call-button.png
     * skinparam monochrome true
     * skinparam shadowing false
     * HTMLElement <|-- CustomElement
     * CustomElement <|-- CallButton
     * @enduml
     *
     * @copyright schukai GmbH
     * @memberOf Monster.Components.Host
     * @summary A toggle button
     */
    class CallButton extends CustomElement {
    	/**
    	 * This method is called by the `instanceof` operator.
    	 * @returns {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() {
    		const obj = Object.assign(
    			{},
    			super.defaults,
    			{
    				templates: {
    					main: getTemplate(),
    				},
    				references: {
    					callableSelector: undefined,
    				},
    
    				call: undefined,
    
    				labels: {
    					button: "<slot>Toggle</slot>",
    				},
    			},
    			initOptionsFromArguments.call(this),
    		);
    
    		return obj;
    	}
    
    	/**
    	 *
    	 * @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() {
    
    	const doc = getDocument();
    
    	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 = doc.querySelector(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);