Skip to content
Snippets Groups Projects
Select Git revision
  • 57deb525c6852f46d7ee5a22c98a7e24b6438599
  • master default protected
  • 1.31
  • 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
  • 4.28.0
  • 4.27.0
  • 4.26.0
  • 4.25.5
  • 4.25.4
  • 4.25.3
  • 4.25.2
  • 4.25.1
23 results

Monster.Logging.Logger.html

Blame
  • message.mjs 8.09 KiB
    /**
     * Copyright schukai GmbH and contributors 2022. All Rights Reserved.
     * Node module: @schukai/monster
     * This file is licensed under the AGPLv3 License.
     * License text available at https://www.gnu.org/licenses/agpl-3.0.en.html
     */
    
    import {
    	ATTRIBUTE_ERRORMESSAGE,
    	ATTRIBUTE_PREFIX,
    	ATTRIBUTE_ROLE,
    } from "../../dom/constants.mjs";
    import {
    	assembleMethodSymbol,
    	CustomElement,
    	initMethodSymbol,
    	registerCustomElement,
    } from "../../dom/customelement.mjs";
    import { findTargetElementFromEvent } from "../../dom/events.mjs";
    import { isString } from "../../types/is.mjs";
    import { MessageStyleSheet } from "./stylesheet/message.mjs";
    
    export { Message };
    
    /**
     * @private
     * @type {symbol}
     */
    const controlElementSymbol = Symbol("controlElement");
    
    /**
     * @private
     * @type {symbol}
     */
    const removeElementSymbol = Symbol("removeElement");
    
    /**
     * @private
     * @type {symbol}
     */
    const timerSymbol = Symbol("timer");
    
    /**
     * @private
     * @type {symbol}
     */
    const mouseenterEventHandlerSymbol = Symbol("mouseenterEventHandler");
    
    /**
     * @private
     * @type {symbol}
     */
    const mouseleaveEventHandlerSymbol = Symbol("mouseleaveEventHandler");
    
    /**
     * @private
     * @type {symbol}
     */
    const removeEventHandlerSymbol = Symbol("removeEventHandler");
    
    /**
     * This CustomControl creates a notification element with a variety of options.
     *
     * <img src="./images/message.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-notify-message />` directly in the HTML
     *
     * ```html
     * <monster-notify-message></monster-notify-message>
     * ```
     *
     * or using Javascript via the `document.createElement('monster-notify');` method.
     *
     * ```javascript
     * import '@schukai/monster/source/components/notify/message.js';
     * document.createElement('monster-notify-message');
     * ```
     *
     * @externalExample ../../../example/components/notify/message.mjs
     * @startuml message.png
     * skinparam monochrome true
     * skinparam shadowing false
     * HTMLElement <|-- CustomElement
     * CustomElement <|-- Message
     * @enduml
     * @since 1.0.0
     * @copyright schukai GmbH
     * @memberOf Monster.Components.Notify
     * @summary A highly configurable select control
     * @fires Monster.Components.Notify.event:monster-xxxx
     */
    class Message extends CustomElement {
    	/**
    	 * The defaults can be set either directly in the object or via an attribute in the HTML tag.
    	 * The value of the attribute `data-monster-options` in the HTML tag must be a JSON string.
    	 *
    	 * ```
    	 * <monster-message data-monster-options="{}"></monster-message>
    	 * ```
    	 *
    	 * Since 1.18.0 the JSON can be specified as a DataURI.
    	 *
    	 * ```
    	 * new Monster.Types.DataUrl(btoa(JSON.stringify({
    	 *        timeout: 3000,
    	 *        features: {
    	 *          clear: true,
    	 *          disappear: true
    	 *        }
    	 *    })),'application/json',true).toString()
    	 * ```
    	 *
    	 * @property {string} templates Template definitions
    	 * @property {Object} templates Template definitions
    	 * @property {integer} timeout time in milliseconds until the message should be removed. The timeout can be disabled via the feature `disappear`.
    	 * @property {Object} features
    	 * @property {boolean} features.clear show clear button
    	 * @property {boolean} features.disappear automatically remove the message after the timeout
    	 * @property {string} templates.main Main template
    	 *
    	 */
    	get defaults() {
    		return Object.assign(
    			{},
    			super.defaults,
    			{
    				timeout: 6000,
    				features: {
    					clear: true,
    					disappear: true,
    				},
    				content: "<slot></slot>",
    				templates: {
    					main: getTemplate(),
    				},
    			},
    			initOptionsFromArguments.call(this),
    		);
    	}
    
    	/**
    	 *
    	 * @return {Monster.Components.Notify.Message}
    	 */
    	[assembleMethodSymbol]() {
    		super[assembleMethodSymbol]();
    		initControlReferences.call(this);
    		initEventhandler.call(this);
    		return this;
    	}
    
    	/**
    	 *
    	 * @return {string}
    	 */
    	static getTag() {
    		return "monster-notify-message";
    	}
    
    	/**
    	 *
    	 * @return {CSSStyleSheet[]}
    	 */
    	static getCSSStyleSheet() {
    		return [MessageStyleSheet];
    	}
    
    	/**
    	 *
    	 */
    	[initMethodSymbol]() {
    		super[initMethodSymbol]();
    	}
    
    	/**
    	 * @return {void}
    	 */
    	connectedCallback() {
    		super.connectedCallback();
    
    		if (this.getOption("features.disappear") === true) {
    			startFadeout.call(this);
    			this.addEventListener("mouseenter", this[mouseenterEventHandlerSymbol]);
    		}
    	}
    
    	/**
    	 * @return {void}
    	 */
    	disconnectedCallback() {
    		super.disconnectedCallback();
    		stopFadeout.call(this);
    
    		if (this.getOption("features.disappear") === true) {
    			this.removeEventListener(
    				"mouseenter",
    				this[mouseenterEventHandlerSymbol],
    			);
    			this.removeEventListener(
    				"mouseenter",
    				this[mouseleaveEventHandlerSymbol],
    			);
    		}
    	}
    }
    
    /**
     * @private
     */
    function startFadeout() {
    	if (!this?.[timerSymbol]) {
    		this[timerSymbol] = setTimeout(() => {
    			removeSelf.call(this);
    		}, this.getOption("timeout", 1000));
    	}
    }
    
    function removeSelf() {
    
    	stopFadeout();
    	this.classList.add("fadeout");
    
    	setTimeout(() => {
    		this.remove();
    	}, 200);
    }
    
    /**
     * @private
     */
    function stopFadeout() {
    	if (this?.[timerSymbol]) {
    		clearTimeout(this[timerSymbol]);
    		this[timerSymbol] = undefined;
    	}
    }
    
    /**
     * This attribute can be used to pass a URL to this select.
     *
     * @private
     * @return {object}
     */
    function initOptionsFromArguments() {
    	const options = {};
    
    	const timeout = this.getAttribute(ATTRIBUTE_PREFIX + "timeout");
    	if (isString(timeout)) {
    		try {
    			options["timeout"] = parseInt(timeout, 10);
    		} catch (e) {
    			this.setAttribute(
    				ATTRIBUTE_ERRORMESSAGE,
    				this.getAttribute(ATTRIBUTE_ERRORMESSAGE + ", " + e.toString()),
    			);
    		}
    	}
    
    	return options;
    }
    
    /**
     * @private
     * @return {Message}
     * @throws {Error} no shadow-root is defined
     */
    function initControlReferences() {
    
    	if (!this.shadowRoot) {
    		throw new Error("no shadow-root is defined");
    	}
    
    	this[controlElementSymbol] = this.shadowRoot.querySelector(
    		"[" + ATTRIBUTE_ROLE + "=control]",
    	);
    	this[removeElementSymbol] = this.shadowRoot.querySelector(
    		"[" + ATTRIBUTE_ROLE + "=remove]",
    	);
    }
    
    /**
     * @private
     */
    function initEventhandler() {
    
    	/**
    	 * @param {Event} event
    	 */
    	this[mouseenterEventHandlerSymbol] = (event) => {
    		const element = findTargetElementFromEvent(
    			event,
    			ATTRIBUTE_ROLE,
    			"control",
    		);
    
    		if (element instanceof HTMLElement) {
    			this.removeEventListener(
    				"mouseenter",
    				this[mouseenterEventHandlerSymbol],
    			);
    			this.addEventListener("mouseleave", this[mouseleaveEventHandlerSymbol]);
    			stopFadeout.call(this);
    		}
    	};
    
    	/**
    	 * @param {Event} event
    	 */
    	this[mouseleaveEventHandlerSymbol] = (event) => {
    		const element = findTargetElementFromEvent(
    			event,
    			ATTRIBUTE_ROLE,
    			"control",
    		);
    		if (element instanceof HTMLElement) {
    			this.removeEventListener(
    				"mouseleave",
    				this[mouseleaveEventHandlerSymbol],
    			);
    			this.addEventListener("mouseenter", this[mouseenterEventHandlerSymbol]);
    			startFadeout.call(this);
    		}
    	};
    
    	/**
    	 * @param {Event} event
    	 */
    	this[removeEventHandlerSymbol] = (event) => {
    		const element = findTargetElementFromEvent(event, ATTRIBUTE_ROLE, "remove");
    
    		if (element instanceof HTMLElement) {
    			removeSelf.call(this);
    		}
    	};
    
    	if (this.getOption("features.close") === true) {
    		this[removeElementSymbol].addEventListener(
    			"click",
    			this[removeEventHandlerSymbol],
    		);
    		this[removeElementSymbol].addEventListener(
    			"touch",
    			this[removeEventHandlerSymbol],
    		);
    	}
    
    	return this;
    }
    
    /**
     * @private
     * @return {string}
     */
    function getTemplate() {
    	// language=HTML
    	return `
            <div data-monster-role="control" part="control" class="center">
                <div data-monster-role="message" part="message"
                     data-monster-attributes="data-monster-orientation path:orientation">
                    <div data-monster-replace="path:content" part="content"
                         data-monster-role="content">
    
                    </div>
                    <div part="remove"
                         data-monster-attributes="class path:features.clear | ?::hidden "
                         data-monster-role="close" tabindex="-1"></div>
                </div>
            </div>
        `;
    }
    
    registerCustomElement(Message);