/** * 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 { addAttributeToken } from "../../dom/attributes.mjs"; import { ATTRIBUTE_ERRORMESSAGE, ATTRIBUTE_ROLE, } from "../../dom/constants.mjs"; import { CustomControl } from "../../dom/customcontrol.mjs"; import { assembleMethodSymbol, getSlottedElements, registerCustomElement, } from "../../dom/customelement.mjs"; import { isFunction } from "../../types/is.mjs"; import { FieldSetStyleSheet } from "./stylesheet/field-set.mjs"; import "../layout/collapse.mjs"; import "./toggle-switch.mjs"; export { FieldSet }; /** * @private * @type {symbol} */ const fieldSetElementSymbol = Symbol("fieldSetElement"); /** * @private * @type {symbol} */ const collapseElementSymbol = Symbol("collapseElement"); /** * @private * @type {symbol} */ const extendedSwitchSymbol = Symbol("extendedSwitch"); /** * @private * @type {symbol} */ const headerElementSymbol = Symbol("headerElement"); /** * @private * @type {symbol} */ const toggleSwitchElementSymbol = Symbol("toggleSwitchElement"); /** * @private * @type {symbol} */ const extendedSwitchElementSymbol = Symbol("extendedSwitchElement"); /** * A field set control that can be used to group form elements. * * @fragments /fragments/components/form/field-set/ * * @example /examples/components/form/field-set-simple * * @since 3.65.0 * @copyright schukai GmbH * @summary A field set control */ class FieldSet extends CustomControl { /** * This method is called by the `instanceof` operator. * @returns {symbol} */ static get [instanceSymbol]() { return Symbol.for("@schukai/monster/components/form/fieldset@@instance"); } /** * @return {Components.Form.FieldSet */ [assembleMethodSymbol]() { super[assembleMethodSymbol](); initControlReferences.call(this); initEventHandler.call(this); updateExtendedFields.call(this); updateColumns.call(this); return this; } /** * 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 {Object} actions Callbacks * @property {string} actions.click="throw Error" Callback when clicked * @property {Object} features Features * @property {boolean} features.multipleColumns=true Multiple columns * @property {Object} classes CSS classes * @property {boolean} disabled=false Disabled state */ get defaults() { return Object.assign({}, super.defaults, { templates: { main: getTemplate(), }, labels: { toggleSwitchOn: "✔", toggleSwitchOff: "✖", toggleSwitchLabel: "Expand", title: "", }, classes: {}, disabled: false, features: { multipleColumns: true, }, actions: { click: () => { throw new Error("the click action is not defined"); }, }, value: null, }); } /** * * @return {string} */ static getTag() { return "monster-field-set"; } /** * * @return {CSSStyleSheet[]} */ static getCSSStyleSheet() { return [FieldSetStyleSheet]; } /** * The FieldSet.click() method simulates a click on the internal element. * * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/click} */ click() { if (this.getOption("disabled") === true) { return; } if ( this[fieldSetElementSymbol] && isFunction(this[fieldSetElementSymbol].click) ) { this[fieldSetElementSymbol].click(); } } /** * The Button.focus() method sets focus on the internal element. * * @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[fieldSetElementSymbol] && isFunction(this[fieldSetElementSymbol].focus) ) { this[fieldSetElementSymbol].focus(options); } } /** * The Button.blur() method removes focus from the internal element. */ blur() { if ( this[fieldSetElementSymbol] && isFunction(this[fieldSetElementSymbol].blur) ) { this[fieldSetElementSymbol].blur(); } } /** * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/attachInternals} * @return {boolean} */ static get formAssociated() { return true; } /** * The current value of the form control. * * ```js * e = document.querySelector('monster-field-set'); * console.log(e.value) * ``` * * @property {string} */ get value() { return this.getOption("value"); } /** * Set the value of the form control. * * ``` * e = document.querySelector('monster-field-set'); * e.value=1 * ``` * * @property {string} value * @throws {Error} unsupported type */ set value(value) { this.setOption("value", value); try { this?.setFormValue(this.value); } catch (e) { addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message); } } } /** * @private */ function updateExtendedFields() { const nodes = getSlottedElements.call(this, "", "extended"); if (nodes.size > 0) { this[extendedSwitchSymbol].classList.remove("hidden"); } else { this[extendedSwitchSymbol].classList.add("hidden"); } } /** * @private */ function updateColumns() { if (this.getOption("features.multipleColumns") !== true) { this[fieldSetElementSymbol].classList.remove("multiple-columns"); return; } this[fieldSetElementSymbol].classList.add("multiple-columns"); } /** * @private * @return {initEventHandler} * @fires event:monster-field-set-clicked */ function initEventHandler() { this[toggleSwitchElementSymbol].setOption( "labels.toggleSwitchOn", this.getOption("labels.toggleSwitchOn"), ); this[toggleSwitchElementSymbol].setOption( "labels.toggleSwitchOff", this.getOption("labels.toggleSwitchOff"), ); this[toggleSwitchElementSymbol].setOption("actions.on", () => { this[collapseElementSymbol].open(); }); this[toggleSwitchElementSymbol].setOption("actions.off", () => { this[collapseElementSymbol].close(); }); return this; } /** * @private */ function initControlReferences() { this[fieldSetElementSymbol] = this.shadowRoot.querySelector( `[${ATTRIBUTE_ROLE}="control"]`, ); this[extendedSwitchElementSymbol] = this.shadowRoot.querySelector( `[${ATTRIBUTE_ROLE}="extended-switch"]`, ); this[collapseElementSymbol] = this.shadowRoot.querySelector( `[${ATTRIBUTE_ROLE}="collapse"]`, ); this[headerElementSymbol] = this.shadowRoot.querySelector( `[${ATTRIBUTE_ROLE}="header"]`, ); this[extendedSwitchSymbol] = this.shadowRoot.querySelector( `[${ATTRIBUTE_ROLE}="extended-switch"]`, ); this[toggleSwitchElementSymbol] = this.shadowRoot.querySelector( `monster-toggle-switch`, ); } /** * @private * @return {string} */ function getTemplate() { // language=HTML return ` <div data-monster-role="control" part="control"> <div data-monster-role="header"> <div data-monster-replace="path:labels.title" data-monster-role="title"></div> <div data-monster-role="extended-switch"> <label data-monster-replace="path:labels.toggle-switch-label"></label> <monster-toggle-switch></monster-toggle-switch> </div> </div> <div data-monster-role="container"> <div class="collapse-alignment"> <slot part="content"></slot> </div> <monster-collapse data-monster-role="collapse"> <slot name="extended" part="extended"></slot> </monster-collapse> </div> </div>`; } registerCustomElement(FieldSet);