Skip to content
Snippets Groups Projects
Select Git revision
  • 6ab6abde0ec8d9ab1fd636dc4a8bada6c5ae500b
  • master default protected
  • 1.31
  • 4.24.2
  • 4.24.1
  • 4.24.0
  • 4.23.6
  • 4.23.5
  • 4.23.4
  • 4.23.3
  • 4.23.2
  • 4.23.1
  • 4.23.0
  • 4.22.3
  • 4.22.2
  • 4.22.1
  • 4.22.0
  • 4.21.0
  • 4.20.1
  • 4.20.0
  • 4.19.0
  • 4.18.0
  • 4.17.0
23 results

namespace.mjs

Blame
  • message-state-button.mjs 9.88 KiB
    /**
     * 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 { ATTRIBUTE_ROLE } from "../../dom/constants.mjs";
    import {
    	assembleMethodSymbol,
    	registerCustomElement,
    } from "../../dom/customelement.mjs";
    import { isArray, isString } from "../../types/is.mjs";
    import { validateString } from "../../types/validate.mjs";
    import { Popper } from "./popper.mjs";
    import { MessageStateButtonStyleSheet } from "./stylesheet/message-state-button.mjs";
    import { StateButtonStyleSheet } from "./stylesheet/state-button.mjs";
    import "./state-button.mjs";
    import { isFunction } from "../../types/is.mjs";
    
    export { MessageStateButton };
    
    /**
     * @private
     * @type {symbol}
     */
    const buttonElementSymbol = Symbol("buttonElement");
    
    /**
     * A select control that can be used to select one or more options from a list.
     *
     * @fragments /fragments/components/form/message-state-button/
     *
     * @example /examples/components/form/message-state-button-simple
     *
     * @since 2.11.0
     * @copyright schukai GmbH
     * @summary A beautiful select control that can make your life easier and also looks good.
     * @fires monster-options-set
     * @fires monster-selected
     * @fires monster-change
     * @fires monster-changed
     */
    class MessageStateButton extends Popper {
    	/**
    	 * This method is called by the `instanceof` operator.
    	 * @return {symbol}
    	 */
    	static get [instanceSymbol]() {
    		return Symbol.for(
    			"@schukai/monster/components/form/message-state-button@@instance",
    		);
    	}
    
    	/**
    	 *
    	 * @param {string} state
    	 * @param {number} timeout
    	 * @return {Monster.Components.Form.MessageStateButton}
    	 * @throws {TypeError} value is not a string
    	 * @throws {TypeError} value is not an instance
    	 */
    	setState(state, timeout) {
    		return this[buttonElementSymbol].setState(state, timeout);
    	}
    
    	/**
    	 *
    	 * @return {Monster.Components.Form.MessageStateButton}
    	 */
    	removeState() {
    		return this[buttonElementSymbol].removeState();
    	}
    
    	/**
    	 * @return {Monster.Components.Form.Types.State|undefined}
    	 */
    	getState() {
    		return this[buttonElementSymbol].getState();
    	}
    
    	/**
    	 * 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} labels Label definitions
    	 * @property {string} labels.button Button label
    	 * @property {Object} mode Mode definitions (manual, submit)
    	 */
    	get defaults() {
    		return Object.assign({}, super.defaults, {
    			message: {
    				title: undefined,
    				content: undefined,
    				icon: undefined,
    			},
    			templates: {
    				main: getTemplate(),
    			},
    			mode: "manual",
    			labels: {
    				button: "<slot></slot>",
    			},
    			classes: {
    				button: "monster-button-primary",
    			},
    			actions: {
    				click: (e) => {
    					throw new Error("the click action is not defined");
    				},
    			},
    			features: {
    				disableButton: false,
    			},
    		});
    	}
    
    	/**
    	 */
    	[assembleMethodSymbol]() {
    		super[assembleMethodSymbol]();
    		initControlReferences.call(this);
    
    		let modes = null;
    		const modeOption = this.getOption("mode");
    		if (typeof modeOption === "string") {
    			modes = modeOption.split(" ");
    		}
    
    		if (
    			modes === null ||
    			modes === undefined ||
    			isArray(modes) === false ||
    			modes.length === 0
    		) {
    			modes = ["manual"];
    		}
    
    		for (const [, mode] of Object.entries(modes)) {
    			initEventHandlerByMode.call(this, mode);
    		}
    
    		return this;
    	}
    
    	/**
    	 * Sets the message
    	 *
    	 * @param {string|HTMLElement}message
    	 * @param {string} title
    	 * @param {string} icon
    	 * @return {Monster.Components.Form.MessageStateButton}
    	 */
    	setMessage(message, title, icon) {
    		if (isString(message)) {
    			if (message === "") {
    				throw new TypeError("message must not be empty");
    			}
    
    			const containerDiv = document.createElement("div");
    			const messageDiv = document.createElement("div");
    			const titleDiv = document.createElement("div");
    			titleDiv.setAttribute("data-monster-role", "message-title-box");
    
    			let titleElement, iconElement;
    			if (title !== undefined) {
    				title = validateString(title);
    				titleElement = document.createElement("div");
    				titleElement.setAttribute("class", "");
    				titleElement.innerHTML = title;
    				titleElement.setAttribute("data-monster-role", "message-title");
    				titleDiv.appendChild(titleElement);
    			}
    
    			if (icon !== undefined) {
    				icon = validateString(icon);
    				iconElement = document.createElement("div");
    				iconElement.setAttribute("class", "");
    				iconElement.innerHTML = icon;
    				iconElement.setAttribute("data-monster-role", "message-icon");
    				titleDiv.appendChild(iconElement);
    			}
    
    			messageDiv.innerHTML = message;
    			containerDiv.appendChild(titleDiv);
    			containerDiv.appendChild(messageDiv);
    
    			this.setOption("message.content", containerDiv);
    		} else if (message instanceof HTMLElement) {
    			this.setOption("message.content", message);
    		} else {
    			throw new TypeError(
    				"message must be a string or an instance of HTMLElement",
    			);
    		}
    
    		return this;
    	}
    
    	/**
    	 * clears the Message
    	 *
    	 * @return {Monster.Components.Form.MessageStateButton}
    	 */
    	clearMessage() {
    		this.setOption("message.title", undefined);
    		this.setOption("message.content", undefined);
    		this.setOption("message.icon", undefined);
    		return this;
    	}
    
    	/**
    	 * With this method you can show the popper with timeout feature.
    	 *
    	 * @param {number} timeout
    	 * @return {MessageStateButton}
    	 */
    	showMessage(timeout) {
    		this.showDialog.call(this);
    
    		if (timeout !== undefined) {
    			setTimeout(() => {
    				super.hideDialog();
    			}, timeout);
    		}
    
    		return this;
    	}
    
    	/**
    	 * With this method you can show the popper.
    	 *
    	 * @return {MessageStateButton}
    	 */
    	showDialog() {
    		if (this.getOption("message.content") === undefined) {
    			return;
    		}
    		super.showDialog();
    		return this;
    	}
    
    	/**
    	 *
    	 * @return {Monster.Components.Form.MessageStateButton}
    	 */
    	hideMessage() {
    		super.hideDialog();
    		return this;
    	}
    
    	/**
    	 *
    	 * @return {Monster.Components.Form.MessageStateButton}
    	 */
    	toggleMessage() {
    		super.toggleDialog();
    		return this;
    	}
    
    	/**
    	 *
    	 * @return {Object}
    	 */
    	getMessage() {
    		return this.getOption("message");
    	}
    
    	/**
    	 *
    	 * @return {string}
    	 */
    	static getTag() {
    		return "monster-message-state-button";
    	}
    
    	/**
    	 *
    	 * @return {Array<CSSStyleSheet>}
    	 */
    	static getCSSStyleSheet() {
    		const styles = Popper.getCSSStyleSheet();
    		styles.push(StateButtonStyleSheet);
    		styles.push(MessageStateButtonStyleSheet);
    		return styles;
    	}
    
    	/**
    	 * The Button.click() method simulates a click on the internal button element.
    	 *
    	 * @since 3.27.0
    	 * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/click}
    	 */
    	click() {
    		if (this.getOption("disabled") === true) {
    			return;
    		}
    
    		if (
    			this[buttonElementSymbol] &&
    			isFunction(this[buttonElementSymbol].click)
    		) {
    			this[buttonElementSymbol].click();
    		}
    	}
    
    	/**
    	 * The Button.focus() method sets focus on the internal button element.
    	 *
    	 * @since 3.27.0
    	 * @param {Object} options
    	 * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus}
    	 */
    	focus(options) {
    		if (this.getOption("disabled") === true) {
    			return;
    		}
    
    		if (
    			this[buttonElementSymbol] &&
    			isFunction(this[buttonElementSymbol].focus)
    		) {
    			this[buttonElementSymbol].focus(options);
    		}
    	}
    
    	/**
    	 * The Button.blur() method removes focus from the internal button element.
    	 */
    	blur() {
    		if (
    			this[buttonElementSymbol] &&
    			isFunction(this[buttonElementSymbol].blur)
    		) {
    			this[buttonElementSymbol].blur();
    		}
    	}
    }
    
    function initEventHandlerByMode(mode) {
    	switch (mode) {
    		case "manual":
    			this[buttonElementSymbol].setOption("actions.click", (e) => {
    				const callback = this.getOption("actions.click");
    				if (isFunction(callback)) {
    					callback(e);
    				}
    			});
    
    			break;
    		case "submit":
    			this[buttonElementSymbol].setOption("actions.click", (e) => {
    				const form = this.form;
    
    				if (form instanceof HTMLFormElement) {
    					form.requestSubmit();
    				}
    			});
    
    			break;
    	}
    }
    
    /**
     * @private
     * @return {Select}
     */
    function initControlReferences() {
    	this[buttonElementSymbol] = this.shadowRoot.querySelector(
    		`[${ATTRIBUTE_ROLE}=button]`,
    	);
    }
    
    /**
     * @private
     * @return {string}
     */
    function getTemplate() {
    	// language=HTML
    	return `
            <div data-monster-role="control" part="control">
    
                <monster-state-button exportparts="button:button-button,control:button-control"
                                      data-monster-attributes="data-monster-option-classes-button path:classes.button, disabled path:features.disableButton | if:true"
                                      part="button"
                                      name="button"
                                      data-monster-role="button">
                    <span data-monster-replace="path:labels.button"></span>
                </monster-state-button>
    
    
                <div data-monster-role="popper" part="popper" tabindex="-1" class="monster-color-primary-1">
                    <div data-monster-role="arrow"></div>
                    <div data-monster-role="message" part="message" class="flex"
                         data-monster-replace="path:message.content"></div>
                </div>
            </div>
            </div>
        `;
    }
    
    registerCustomElement(MessageStateButton);