Skip to content
Snippets Groups Projects
Select Git revision
  • 641704b5a0941adeb69a2eefcd217cf4d84802f3
  • 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

tutorial-dom-form-handling.html

Blame
  • details.mjs 6.89 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 {
    	assembleMethodSymbol,
    	registerCustomElement,
    } from "../../dom/customelement.mjs";
    import { DetailsStyleSheet } from "./stylesheet/details.mjs";
    import { ATTRIBUTE_BUTTON_LABEL } from "../host/constants.mjs";
    import { isString } from "../../types/is.mjs";
    import { generateUniqueConfigKey } from "../host/util.mjs";
    import { Collapse, nameSymbol } from "./collapse.mjs";
    import { instanceSymbol } from "../../constants.mjs";
    
    export { Details };
    
    /**
     * @private
     * @type {symbol}
     */
    const buttonElementSymbol = Symbol("buttonElement");
    
    /**
     * @private
     * @type {symbol}
     */
    const buttonEventHandlerSymbol = Symbol("buttonEventHandler");
    
    /**
     * The Details component is used to show a details.
     *
     * <img src="./images/details.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-details />` directly in the HTML or using
     * Javascript via the `document.createElement('monster-details');` method.
     *
     * ```html
     * <monster-details></monster-details>
     * ```
     *
     * Or you can create this CustomControl directly in Javascript:
     *
     * ```js
     * import '@schukai/component-state/source/details.mjs';
     * document.createElement('monster-details');
     * ```
     *
     * The Body should have a class "hidden" to ensure that the styles are applied correctly.
     *
     * ```css
     * body.hidden {
     *    visibility: hidden;
     * }
     * ```
     *
     * @startuml details.png
     * skinparam monochrome true
     * skinparam shadowing false
     * HTMLElement <|-- CustomElement
     * CustomElement <|-- Collapse
     * Collapse <|-- Details
     * @enduml
     *
     * @copyright schukai GmbH
     * @memberOf Monster.Components.Layout
     * @summary A simple details component
     * @fires Monster.Components.Layout.Details.event:monster-details-before-open
     * @fires Monster.Components.Layout.Details.event:monster-details-open
     * @fires Monster.Components.Layout.Details.event:monster-details-before-close
     * @fires Monster.Components.Layout.Details.event:monster-details-closed
     */
    class Details extends Collapse {
    	/**
    	 * This method is called by the `instanceof` operator.
    	 * @returns {symbol}
    	 */
    	static get [instanceSymbol]() {
    		return Symbol.for("@schukai/monster/components/layout/details@@instance");
    	}
    
    	/**
    	 *
    	 */
    	constructor() {
    		super();
    		// the name is only used for the host config and the event name
    		this[nameSymbol] = "details";
    	}
    
    	/**
    	 * 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} classes CSS classes
    	 * @property {string} classes.button CSS class for the button
    	 * @property {Object} button Button configuration
    	 * @property {string} button.label Button label
    	 * @property {Object} features Feature configuration
    	 * @property {boolean} features.accordion Enable accordion mode
    	 * @property {boolean} features.persistState Persist the state in the host configuration
    	 */
    	get defaults() {
    		return Object.assign({}, super.defaults, {
    			templates: {
    				main: getTemplate(),
    			},
    			labels: {
    				button: "Details",
    			},
    		});
    	}
    
    	/**
    	 *
    	 * @returns {Monster.Components.Layout.Details}
    	 */
    	[assembleMethodSymbol]() {
    		super[assembleMethodSymbol]();
    
    		initButtonLabel.call(this);
    		initControlReferences.call(this);
    		initEventHandler.call(this);
    	}
    
    	connectedCallback() {
    		super.connectedCallback();
    
    		const containDocument = this.shadowRoot;
    
    		if (containDocument !== null) {
    			const previousElement = this.previousElementSibling;
    			if (previousElement && previousElement.tagName === "MONSTER-DETAILS") {
    				this[buttonElementSymbol].style.borderTop = "0";
    			}
    		}
    	}
    
    	/**
    	 *
    	 * @return {string}
    	 */
    	static getTag() {
    		return "monster-details";
    	}
    
    	/**
    	 * @return {Array<CSSStyleSheet>}
    	 */
    	static getCSSStyleSheet() {
    		const css = super.getCSSStyleSheet();
    		css.push(DetailsStyleSheet);
    		return css;
    	}
    }
    
    /**
     * @private
     * @return {Select}
     * @throws {Error} no shadow-root is defined
     */
    function initControlReferences() {
    	if (!this.shadowRoot) {
    		throw new Error("no shadow-root is defined");
    	}
    
    	this[buttonElementSymbol] = this.shadowRoot.querySelector(
    		"[data-monster-role=button]",
    	);
    }
    
    /**
     * @private
     */
    function initEventHandler() {
    	if (!this.shadowRoot) {
    		throw new Error("no shadow-root is defined");
    	}
    
    	this[buttonEventHandlerSymbol] = (event) => {
    		this.toggle();
    	};
    
    	this[buttonElementSymbol].addEventListener(
    		"click",
    		this[buttonEventHandlerSymbol],
    	);
    
    	return this;
    }
    
    /**
     * @private
     * @return {string}
     */
    function initButtonLabel() {
    	let label;
    	const setLabel = false;
    	if (this.hasAttribute(ATTRIBUTE_BUTTON_LABEL)) {
    		label = this.getAttribute(ATTRIBUTE_BUTTON_LABEL);
    	} else {
    		label = this.innerText;
    	}
    
    	if (!isString(label)) {
    		label = "";
    	}
    
    	label = label.trim();
    
    	if (label === "") {
    		label = this.getOption("labels.button", "Details");
    	}
    
    	if (label.length > 100) {
    		label = `${label.substring(0, 99)}…`;
    	}
    
    	this.setAttribute(ATTRIBUTE_BUTTON_LABEL, label);
    	this.setOption("labels.button", label);
    
    	return label;
    }
    
    /**
     * @private
     * @returns {string}
     */
    function getConfigKey() {
    	return generateUniqueConfigKey("details", this.id, "state");
    }
    
    /**
     * @private
     * @return {string}
     */
    function getTemplate() {
    	// language=HTML
    	return `
            <div data-monster-role="control" part="control" class="overflow-hidden">
                <div data-monster-role="summary" part="summary">
                    <button part="button" data-monster-attributes="class path:classes.button"
                            data-monster-role="button"
                            data-monster-replace="path:labels.button | default:click me">click me
                    </button>
                </div>
                <div data-monster-role="detail">
                    <div data-monster-attributes="class path:classes.container" part="container"
                         data-monster-role="container">
                        <slot></slot>
                    </div>
                    <div class="deco-line" data-monster-role="deco"></div>
                </div>
            </div>`;
    }
    
    registerCustomElement(Details);