diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1eb1d023160489e80004d54a4cefa11cb8e92249..5807cec32022fbb74352c63b93ffd55e7cb48eca 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -64,7 +64,7 @@ release: when: on_success rules: - if: $DEPLOY_VERSION == null - - if: '$CI_COMMIT_BRANCH == "master"' + - if: $CI_COMMIT_BRANCH == "master" deploy: stage: deploy diff --git a/development/issues/open/192.html b/development/issues/closed/192.html similarity index 100% rename from development/issues/open/192.html rename to development/issues/closed/192.html diff --git a/development/issues/open/192.mjs b/development/issues/closed/192.mjs similarity index 88% rename from development/issues/open/192.mjs rename to development/issues/closed/192.mjs index 24dd34c847b35dc853e66309294436809db9d000..b2f7b0b1192319b352df1cd3ec60120c98d607e5 100644 --- a/development/issues/open/192.mjs +++ b/development/issues/closed/192.mjs @@ -8,5 +8,6 @@ import "../../../source/components/style/property.pcss"; import "../../../source/components/style/normalize.pcss"; import "../../../source/components/style/typography.pcss"; +import "../../../source/components/style/color.pcss"; import "../../../source/components/form/context-error.mjs"; diff --git a/development/issues/open/186.html b/development/issues/open/186.html index 57e56f6ed4339b0b7cef80030a633869f34b89ef..f9b00853be8d97e9c029f350cf2be7f66ed074ae 100644 --- a/development/issues/open/186.html +++ b/development/issues/open/186.html @@ -1,29 +1,29 @@ <!DOCTYPE html> <html lang="en"> <head> - <meta charset="UTF-8"> - <meta name="viewport" content="width=device-width, initial-scale=1.0"> - <title>add new field set for forms #186</title> - <script src="./186.mjs" type="module"></script> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>add new field set for forms #186</title> + <script src="./186.mjs" type="module"></script> </head> <body> - <h1>add new field set for forms #186</h1> - <p></p> - <ul> - <li><a href="https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/186">Issue #186</a></li> - <li><a href="/">Back to overview</a></li> - </ul> - <main> - <monster-field-set> - - <label>N1<input type="text" name="name1" id="name1" placeholder="Name1"></label> - <label>N2<input type="text" name="name2" id="name2" placeholder="Name2"></label> - <label>N3<input type="text" name="name3" id="name3" placeholder="Name3"></label> - <label>N4<input type="text" name="name4" id="name4" placeholder="Name4"></label> - <label>N5<input type="text" name="name5" id="name5" placeholder="Name5"></label> - - </monster-field-set> - </main> +<h1>add new field set for forms #186</h1> + +<ul> + <li><a href="https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/186">Issue #186</a></li> + <li><a href="/">Back to overview</a></li> +</ul> +<main> + <monster-field-set> +dsfasdfsdf + <label slot="extended">N1<input type="text" name="name1" id="name1" placeholder="Name1"></label> + <label slot="extended">N2<input type="text" name="name2" id="name2" placeholder="Name2"></label> + <label slot="extended">N3<input type="text" name="name3" id="name3" placeholder="Name3"></label> + <label>N4<input type="text" name="name4" id="name4" placeholder="Name4"></label> + <label>N5<input type="text" name="name5" id="name5" placeholder="Name5"></label> + + </monster-field-set> +</main> </body> </html> diff --git a/development/issues/open/186.mjs b/development/issues/open/186.mjs index 46f2c9f46e0ac12ab6f3cd2337ae21875bb83f42..c89cea4f5dc170866e3d99d1c7b4997e11a36f12 100644 --- a/development/issues/open/186.mjs +++ b/development/issues/open/186.mjs @@ -6,6 +6,7 @@ */ import "../../../source/components/style/property.pcss"; +import "../../../source/components/style/color.pcss"; import "../../../source/components/style/normalize.pcss"; import "../../../source/components/style/typography.pcss"; import "../../../source/components/form/field-set.mjs"; diff --git a/source/components/form/field-set.mjs b/source/components/form/field-set.mjs index adfa8a2bdb0ca42f299c10f6cfb51e3461d17d68..54aff9373d3ca85544433c2a08e63ce2c512931c 100644 --- a/source/components/form/field-set.mjs +++ b/source/components/form/field-set.mjs @@ -12,30 +12,49 @@ * SPDX-License-Identifier: AGPL-3.0 */ -import { instanceSymbol } from "../../constants.mjs"; -import { addAttributeToken } from "../../dom/attributes.mjs"; +import {instanceSymbol} from "../../constants.mjs"; +import {addAttributeToken} from "../../dom/attributes.mjs"; import { - ATTRIBUTE_ERRORMESSAGE, - ATTRIBUTE_ROLE, + ATTRIBUTE_ERRORMESSAGE, + ATTRIBUTE_ROLE, } from "../../dom/constants.mjs"; -import { CustomControl } from "../../dom/customcontrol.mjs"; +import {CustomControl} from "../../dom/customcontrol.mjs"; import { - assembleMethodSymbol, - registerCustomElement, + assembleMethodSymbol, + registerCustomElement, } from "../../dom/customelement.mjs"; -import { findTargetElementFromEvent } from "../../dom/events.mjs"; -import { isFunction } from "../../types/is.mjs"; -import { FieldSetStyleSheet } from "./stylesheet/field-set.mjs"; -import { fireCustomEvent } from "../../dom/events.mjs"; +import {findTargetElementFromEvent} from "../../dom/events.mjs"; +import {isFunction} from "../../types/is.mjs"; +import {FieldSetStyleSheet} from "./stylesheet/field-set.mjs"; +import {fireCustomEvent} from "../../dom/events.mjs"; import "../layout/collapse.mjs"; +import {ToggleSwitch} from "./toggle-switch.mjs"; -export { FieldSet }; +export {FieldSet}; /** * @private * @type {symbol} */ -export const fieldSetElementSymbol = Symbol("fieldSetElement"); +const fieldSetElementSymbol = Symbol("fieldSetElement"); + +/** + * @private + * @type {symbol} + */ +const collapseElementSymbol = Symbol("collapseElement"); + +/** + * @private + * @type {symbol} + */ +const toggleSwitchElementSymbol = Symbol("toggleSwitchElement"); + +/** + * @private + * @type {symbol} + */ +const extendedSwitchElementSymbol = Symbol("extendedSwitchElement"); /** * This CustomControl creates a FieldSet element with a variety of options. @@ -71,164 +90,174 @@ export const fieldSetElementSymbol = Symbol("fieldSetElement"); * @summary A simple FieldSet */ 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); - 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 {Object} classes CSS classes - * @property {boolean} disabled=false Disabled state - */ - get defaults() { - return Object.assign({}, super.defaults, { - templates: { - main: getTemplate(), - }, - labels: {}, - classes: {}, - disabled: false, - features: {}, - actions: { - click: () => { - throw new Error("the click action is not defined"); - }, - }, - value: null, - }); - } - - /** - * - * @return {string} - */ - static getTag() { - return "monster-field-set"; - } - - /** - * - * @return {Array<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 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); - } - } + /** + * 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); + 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 {Object} classes CSS classes + * @property {boolean} disabled=false Disabled state + */ + get defaults() { + return Object.assign({}, super.defaults, { + templates: { + main: getTemplate(), + }, + labels: {}, + classes: {}, + disabled: false, + features: {}, + 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 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); + } + } +} + +function updateExtendedFields() { + // check if slot has extended fields + getSlottedElements.call(this, "", "extended").forEach((node) => { + if (node.hasAttribute(ATTRIBUTE_EXTENDED)) { + this.hasExtended = true; + } + }) } /** @@ -237,45 +266,78 @@ class FieldSet extends CustomControl { * @fires Monster.Components.Form.event:monster-field-set-clicked */ function initEventHandler() { - const self = this; - const element = this[fieldSetElementSymbol]; - - const type = "click"; - - element.addEventListener(type, function (event) { - const callback = self.getOption("actions.click"); + // const self = this; + // const element = this[fieldSetElementSymbol]; + // + // const type = "click"; - fireCustomEvent(self, "monster-field-set-clicked", { - element: self, - }); + // element.addEventListener(type, function (event) { + // const callback = self.getOption("actions.click"); + // + // fireCustomEvent(self, "monster-field-set-clicked", { + // element: self, + // }); + // + // if (!isFunction(callback)) { + // return; + // } + // + // const element = findTargetElementFromEvent( + // event, + // ATTRIBUTE_ROLE, + // "control", + // ); + // + // if (!(element instanceof Node && self.hasNode(element))) { + // return; + // } + // + // callback.call(self, event); + // }); - if (!isFunction(callback)) { - return; - } - const element = findTargetElementFromEvent( - event, - ATTRIBUTE_ROLE, - "control", - ); + this[toggleSwitchElementSymbol].setOption("actions.on", () => { + console.log( this[collapseElementSymbol],"!!!") + this[collapseElementSymbol].open(); + }); - if (!(element instanceof Node && self.hasNode(element))) { - return; - } + this[toggleSwitchElementSymbol].setOption("actions.off", () => { + this[collapseElementSymbol].close(); + }); + + // this[extendedSwitchElementSymbol].addEventListener(type, function (event) { + // const element = findTargetElementFromEvent( + // event, + // ATTRIBUTE_ROLE, + // "extended-switch", + // ); + // + // + // + // }) - callback.call(self, event); - }); - - return this; + return this; } /** * @private */ function initControlReferences() { - this[fieldSetElementSymbol] = this.shadowRoot.querySelector( - `[${ATTRIBUTE_ROLE}="control"]`, - ); + 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[toggleSwitchElementSymbol] = this.shadowRoot.querySelector( + `monster-toggle-switch`, + ); } /** @@ -283,12 +345,20 @@ function initControlReferences() { * @return {string} */ function getTemplate() { - // language=HTML - return ` + // language=HTML + return ` <div data-monster-role="control" part="control"> - <monster-collapse> - <slot></slot> - </monster-collapse> + <div data-monster-role="header"> + <monster-toggle-switch class="hidden"></monster-toggle-switch> + </div> + <div> + <slot></slot> + <monster-collapse data-monster-role="collapse"> + <slot name="extended"></slot> + </monster-collapse> + </div> + + </div>`; } diff --git a/source/components/form/style/field-set.pcss b/source/components/form/style/field-set.pcss index 79ec0c20f54525175fd41f715899736ac3c75337..3684f709bb205ca740c07f93e8c4afb43d22a0a5 100644 --- a/source/components/form/style/field-set.pcss +++ b/source/components/form/style/field-set.pcss @@ -9,5 +9,12 @@ @import "../../style/floating-ui.pcss"; [data-monster-role=control] { - border: 1px solid red; -} \ No newline at end of file + +} + + +[data-monster-role=header] { + display: flex; + align-items: center; + justify-content: flex-end; +} diff --git a/source/components/form/stylesheet/field-set.mjs b/source/components/form/stylesheet/field-set.mjs index 69b45291ae8e7a5e90efda4aba1ca6b95591a512..ee4375a63cfca41f855d92930fc52d5e78d0dc84 100644 --- a/source/components/form/stylesheet/field-set.mjs +++ b/source/components/form/stylesheet/field-set.mjs @@ -1,5 +1,5 @@ /** - * Copyright © schukai GmbH and all contributing authors, {{copyRightYear}}. All rights reserved. + * Copyright © schukai GmbH and all contributing authors, 2024. All rights reserved. * Node module: @schukai/monster * * This source code is licensed under the GNU Affero General Public License version 3 (AGPLv3). @@ -8,14 +8,12 @@ * 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 { addAttributeToken } from "../../../dom/attributes.mjs"; -import { ATTRIBUTE_ERRORMESSAGE } from "../../../dom/constants.mjs"; +import {addAttributeToken} from "../../../dom/attributes.mjs"; +import {ATTRIBUTE_ERRORMESSAGE} from "../../../dom/constants.mjs"; -export { FieldSetStyleSheet }; +export {FieldSetStyleSheet} /** * @private @@ -24,17 +22,10 @@ export { FieldSetStyleSheet }; const FieldSetStyleSheet = new CSSStyleSheet(); try { - FieldSetStyleSheet.insertRule( - ` + FieldSetStyleSheet.insertRule(` @layer fieldset { -.block{display:block}.inline{display:inline}.inline-block{display:inline-block}.grid{display:grid}.inline-grid{display:inline-grid}.flex{display:flex}.inline-flex{display:inline-flex}.hidden,.hide,.none{display:none}.visible{visibility:visible}.invisible{visibility:hidden}.monster-border-primary-1,.monster-border-primary-2,.monster-border-primary-3,.monster-border-primary-4{border-radius:var(--monster-border-radius);border-style:var(--monster-border-style);border-width:var(--monster-border-width)}.monster-border-0{border-radius:0;border-style:none;border-width:0}.monster-border-primary-1{border-color:var(--monster-bg-color-primary-1)}.monster-border-primary-2{border-color:var(--monster-bg-color-primary-2)}.monster-border-primary-3{border-color:var(--monster-bg-color-primary-3)}.monster-border-primary-4{border-color:var(--monster-bg-color-primary-4)}.monster-border-secondary-1,.monster-border-secondary-2,.monster-border-secondary-3,.monster-border-secondary-4{border-radius:var(--monster-border-radius);border-style:var(--monster-border-style);border-width:var(--monster-border-width)}.monster-border-secondary-1{border-color:var(--monster-bg-color-secondary-1)}.monster-border-secondary-2{border-color:var(--monster-bg-color-secondary-2)}.monster-border-secondary-3{border-color:var(--monster-bg-color-secondary-3)}.monster-border-secondary-4{border-color:var(--monster-bg-color-secondary-4)}.monster-border-tertiary-1,.monster-border-tertiary-2,.monster-border-tertiary-3,.monster-border-tertiary-4{border-radius:var(--monster-border-radius);border-style:var(--monster-border-style);border-width:var(--monster-border-width)}.monster-border-tertiary-1{border-color:var(--monster-bg-color-tertiary-1)}.monster-border-tertiary-2{border-color:var(--monster-bg-color-tertiary-2)}.monster-border-tertiary-3{border-color:var(--monster-bg-color-tertiary-3)}.monster-border-tertiary-4{border-color:var(--monster-bg-color-tertiary-4)}[data-monster-role=control]{outline:none;width:100%}[data-monster-role=control].flex{align-items:center;display:flex;flex-direction:row}.monster-badge-primary{padding:.25em .4em}.monster-badge-primary,.monster-badge-primary-pill{background-color:var(--monster-bg-color-primary-4);border-radius:.25rem;color:var(--monster-color-primary-4);display:inline-block;font-size:75%;font-weight:700;line-height:1;text-align:center;text-decoration:none;vertical-align:baseline;white-space:nowrap}.monster-badge-primary-pill{border-radius:10rem;padding:.25em .6em}.monster-badge-secondary{padding:.25em .4em}.monster-badge-secondary,.monster-badge-secondary-pill{background-color:var(--monster-bg-color-secondary-3);border-radius:.25rem;color:var(--monster-color-secondary-3);display:inline-block;font-size:75%;font-weight:700;line-height:1;text-align:center;text-decoration:none;vertical-align:baseline;white-space:nowrap}.monster-badge-secondary-pill{border-radius:10rem;padding:.25em .6em}.monster-badge-tertiary{padding:.25em .4em}.monster-badge-tertiary,.monster-badge-tertiary-pill{background-color:var(--monster-bg-color-tertiary-3);border-radius:.25rem;color:var(--monster-color-tertiary-3);display:inline-block;font-size:75%;font-weight:700;line-height:1;text-align:center;text-decoration:none;vertical-align:baseline;white-space:nowrap}.monster-badge-tertiary-pill{border-radius:10rem;padding:.25em .6em}.monster-badge-destructive{padding:.25em .4em}.monster-badge-destructive,.monster-badge-destructive-pill{background-color:var(--monster-bg-color-destructive-1);border-radius:.25rem;color:var(--monster-color-destructive-1);display:inline-block;font-size:75%;font-weight:700;line-height:1;text-align:center;text-decoration:none;vertical-align:baseline;white-space:nowrap}.monster-badge-destructive-pill{border-radius:10rem;padding:.25em .6em}.monster-badge-success{padding:.25em .4em}.monster-badge-success,.monster-badge-success-pill{background-color:var(--monster-bg-color-success-1);border-radius:.25rem;color:var(--monster-color-success-1);display:inline-block;font-size:75%;font-weight:700;line-height:1;text-align:center;text-decoration:none;vertical-align:baseline;white-space:nowrap}.monster-badge-success-pill{border-radius:10rem;padding:.25em .6em}.monster-badge-warning{padding:.25em .4em}.monster-badge-warning,.monster-badge-warning-pill{background-color:var(--monster-bg-color-warning-1);border-radius:.25rem;color:var(--monster-color-warning-1);display:inline-block;font-size:75%;font-weight:700;line-height:1;text-align:center;text-decoration:none;vertical-align:baseline;white-space:nowrap}.monster-badge-warning-pill{border-radius:10rem;padding:.25em .6em}.monster-badge-error{padding:.25em .4em}.monster-badge-error,.monster-badge-error-pill{background-color:var(--monster-bg-color-error-1);border-radius:.25rem;color:var(--monster-color-error-1);display:inline-block;font-size:75%;font-weight:700;line-height:1;text-align:center;text-decoration:none;vertical-align:baseline;white-space:nowrap}.monster-badge-error-pill{border-radius:10rem;padding:.25em .6em}div[data-monster-role=popper]{align-content:center;background:var(--monster-bg-color-primary-1);border-color:var(--monster-bg-color-primary-4);border-radius:var(--monster-border-radius);border-style:var(--monster-border-style);border-width:var(--monster-border-width);box-shadow:var(--monster-box-shadow-1);box-sizing:border-box;color:var(--monster-color-primary-1);display:none;justify-content:space-between;left:0;padding:1.1em;position:absolute;top:0;width:-moz-max-content;width:max-content;z-index:var(--monster-z-index-modal)}div[data-monster-role=popper] div[data-monster-role=arrow]{background:var(--monster-bg-color-primary-1);height:calc(max(var(--monster-popper-witharrrow-distance), -1 * var(--monster-popper-witharrrow-distance))*2);pointer-events:none;position:absolute;width:calc(max(var(--monster-popper-witharrrow-distance), -1 * var(--monster-popper-witharrrow-distance))*2);z-index:-1}[data-monster-role=control]{border:1px solid red} -}`, - 0, - ); +.block{display:block}.inline{display:inline}.inline-block{display:inline-block}.grid{display:grid}.inline-grid{display:inline-grid}.flex{display:flex}.inline-flex{display:inline-flex}.hidden,.hide,.none{display:none}.visible{visibility:visible}.invisible{visibility:hidden}.monster-border-primary-1,.monster-border-primary-2,.monster-border-primary-3,.monster-border-primary-4{border-radius:var(--monster-border-radius);border-style:var(--monster-border-style);border-width:var(--monster-border-width)}.monster-border-0{border-radius:0;border-style:none;border-width:0}.monster-border-primary-1{border-color:var(--monster-bg-color-primary-1)}.monster-border-primary-2{border-color:var(--monster-bg-color-primary-2)}.monster-border-primary-3{border-color:var(--monster-bg-color-primary-3)}.monster-border-primary-4{border-color:var(--monster-bg-color-primary-4)}.monster-border-secondary-1,.monster-border-secondary-2,.monster-border-secondary-3,.monster-border-secondary-4{border-radius:var(--monster-border-radius);border-style:var(--monster-border-style);border-width:var(--monster-border-width)}.monster-border-secondary-1{border-color:var(--monster-bg-color-secondary-1)}.monster-border-secondary-2{border-color:var(--monster-bg-color-secondary-2)}.monster-border-secondary-3{border-color:var(--monster-bg-color-secondary-3)}.monster-border-secondary-4{border-color:var(--monster-bg-color-secondary-4)}.monster-border-tertiary-1,.monster-border-tertiary-2,.monster-border-tertiary-3,.monster-border-tertiary-4{border-radius:var(--monster-border-radius);border-style:var(--monster-border-style);border-width:var(--monster-border-width)}.monster-border-tertiary-1{border-color:var(--monster-bg-color-tertiary-1)}.monster-border-tertiary-2{border-color:var(--monster-bg-color-tertiary-2)}.monster-border-tertiary-3{border-color:var(--monster-bg-color-tertiary-3)}.monster-border-tertiary-4{border-color:var(--monster-bg-color-tertiary-4)}[data-monster-role=control]{outline:none;width:100%}[data-monster-role=control].flex{align-items:center;display:flex;flex-direction:row}.monster-badge-primary{padding:.25em .4em}.monster-badge-primary,.monster-badge-primary-pill{background-color:var(--monster-bg-color-primary-4);border-radius:.25rem;color:var(--monster-color-primary-4);display:inline-block;font-size:75%;font-weight:700;line-height:1;text-align:center;text-decoration:none;vertical-align:baseline;white-space:nowrap}.monster-badge-primary-pill{border-radius:10rem;padding:.25em .6em}.monster-badge-secondary{padding:.25em .4em}.monster-badge-secondary,.monster-badge-secondary-pill{background-color:var(--monster-bg-color-secondary-3);border-radius:.25rem;color:var(--monster-color-secondary-3);display:inline-block;font-size:75%;font-weight:700;line-height:1;text-align:center;text-decoration:none;vertical-align:baseline;white-space:nowrap}.monster-badge-secondary-pill{border-radius:10rem;padding:.25em .6em}.monster-badge-tertiary{padding:.25em .4em}.monster-badge-tertiary,.monster-badge-tertiary-pill{background-color:var(--monster-bg-color-tertiary-3);border-radius:.25rem;color:var(--monster-color-tertiary-3);display:inline-block;font-size:75%;font-weight:700;line-height:1;text-align:center;text-decoration:none;vertical-align:baseline;white-space:nowrap}.monster-badge-tertiary-pill{border-radius:10rem;padding:.25em .6em}.monster-badge-destructive{padding:.25em .4em}.monster-badge-destructive,.monster-badge-destructive-pill{background-color:var(--monster-bg-color-destructive-1);border-radius:.25rem;color:var(--monster-color-destructive-1);display:inline-block;font-size:75%;font-weight:700;line-height:1;text-align:center;text-decoration:none;vertical-align:baseline;white-space:nowrap}.monster-badge-destructive-pill{border-radius:10rem;padding:.25em .6em}.monster-badge-success{padding:.25em .4em}.monster-badge-success,.monster-badge-success-pill{background-color:var(--monster-bg-color-success-1);border-radius:.25rem;color:var(--monster-color-success-1);display:inline-block;font-size:75%;font-weight:700;line-height:1;text-align:center;text-decoration:none;vertical-align:baseline;white-space:nowrap}.monster-badge-success-pill{border-radius:10rem;padding:.25em .6em}.monster-badge-warning{padding:.25em .4em}.monster-badge-warning,.monster-badge-warning-pill{background-color:var(--monster-bg-color-warning-1);border-radius:.25rem;color:var(--monster-color-warning-1);display:inline-block;font-size:75%;font-weight:700;line-height:1;text-align:center;text-decoration:none;vertical-align:baseline;white-space:nowrap}.monster-badge-warning-pill{border-radius:10rem;padding:.25em .6em}.monster-badge-error{padding:.25em .4em}.monster-badge-error,.monster-badge-error-pill{background-color:var(--monster-bg-color-error-1);border-radius:.25rem;color:var(--monster-color-error-1);display:inline-block;font-size:75%;font-weight:700;line-height:1;text-align:center;text-decoration:none;vertical-align:baseline;white-space:nowrap}.monster-badge-error-pill{border-radius:10rem;padding:.25em .6em}div[data-monster-role=popper]{align-content:center;background:var(--monster-bg-color-primary-1);border-color:var(--monster-bg-color-primary-4);border-radius:var(--monster-border-radius);border-style:var(--monster-border-style);border-width:var(--monster-border-width);box-shadow:var(--monster-box-shadow-1);box-sizing:border-box;color:var(--monster-color-primary-1);display:none;justify-content:space-between;left:0;padding:1.1em;position:absolute;top:0;width:-moz-max-content;width:max-content;z-index:var(--monster-z-index-modal)}div[data-monster-role=popper] div[data-monster-role=arrow]{background:var(--monster-bg-color-primary-1);height:calc(max(var(--monster-popper-witharrrow-distance), -1 * var(--monster-popper-witharrrow-distance))*2);pointer-events:none;position:absolute;width:calc(max(var(--monster-popper-witharrrow-distance), -1 * var(--monster-popper-witharrrow-distance))*2);z-index:-1}[data-monster-role=header]{align-items:center;display:flex;justify-content:flex-end} +}`, 0); } catch (e) { - addAttributeToken( - document.getRootNode().querySelector("html"), - ATTRIBUTE_ERRORMESSAGE, - e + "", - ); + addAttributeToken(document.getRootNode().querySelector('html'), ATTRIBUTE_ERRORMESSAGE, e + ""); } diff --git a/source/components/form/toggle-switch.mjs b/source/components/form/toggle-switch.mjs index 032bc2bde95ebe44383f8ecf09b72f92b8e52c19..9d4c67576bdbe788142bb44e00fcbd9db5fe32e0 100644 --- a/source/components/form/toggle-switch.mjs +++ b/source/components/form/toggle-switch.mjs @@ -12,25 +12,26 @@ * SPDX-License-Identifier: AGPL-3.0 */ -import { instanceSymbol } from "../../constants.mjs"; -import { internalSymbol } from "../../constants.mjs"; -import { CustomControl } from "../../dom/customcontrol.mjs"; -import { Observer } from "../../types/observer.mjs"; -import { ProxyObserver } from "../../types/proxyobserver.mjs"; +import {instanceSymbol} from "../../constants.mjs"; +import {internalSymbol} from "../../constants.mjs"; +import {CustomControl} from "../../dom/customcontrol.mjs"; +import {Observer} from "../../types/observer.mjs"; +import {ProxyObserver} from "../../types/proxyobserver.mjs"; -import { addAttributeToken } from "../../dom/attributes.mjs"; +import {addAttributeToken} from "../../dom/attributes.mjs"; import { - assembleMethodSymbol, - registerCustomElement, - updaterTransformerMethodsSymbol, + assembleMethodSymbol, + registerCustomElement, + updaterTransformerMethodsSymbol, } from "../../dom/customelement.mjs"; -import { isObject } from "../../types/is.mjs"; -import { ToggleSwitchStyleSheet } from "./stylesheet/toggle-switch.mjs"; +import {isObject, isFunction} from "../../types/is.mjs"; +import {ToggleSwitchStyleSheet} from "./stylesheet/toggle-switch.mjs"; import { - ATTRIBUTE_ERRORMESSAGE, - ATTRIBUTE_ROLE, + ATTRIBUTE_ERRORMESSAGE, + ATTRIBUTE_ROLE, } from "../../dom/constants.mjs"; -export { ToggleSwitch }; + +export {ToggleSwitch}; /** * @private @@ -68,336 +69,344 @@ export const STATE_OFF = "off"; * @summary A simple toggle switch */ class ToggleSwitch extends CustomControl { - /** - * 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 {string} value=current value of the element - * @property {Boolean} disabled=disabled=false Disabled state - * @property {Object} classes - * @property {string} classes.on=specifies the class for the on state. - * @property {string} classes.off=specifies the class for the off state. - * @property {Object} values - * @property {string} values.off=specifies the value of the element if it is not selected - * @property {Object} labels - * @property {string} labels.on=specifies the label for the on state. - * @property {string} labels.off=specifies the label for the off state. - * @property {string} actions - * @property {string} actions.on=specifies the action for the on state. - * @property {string} actions.off=specifies the action for the off state. - * @property {Object} templates - * @property {string} templates.main=specifies the main template used by the control. - * - * @since 3.57.0 - */ - get defaults() { - return Object.assign({}, super.defaults, { - value: null, - disabled: false, - classes: { - on: "monster-theme-on", - off: "monster-theme-off", - handle: "monster-theme-primary-1", - }, - values: { - on: "on", - off: "off", - }, - labels: { - "toggle-switch-on": "ON", - "toggle-switch-off": "OFF", - }, - templates: { - main: getTemplate(), - }, - actions: { - on: () => { - throw new Error("the on action is not defined"); - }, - off: () => { - throw new Error("the off action is not defined"); - }, - } - }); - } - - /** - * @return {ToggleSwitch} - */ - [assembleMethodSymbol]() { - const self = this; - super[assembleMethodSymbol](); - initControlReferences.call(this); - initEventHandler.call(this); - - /** - * init value to off - * if the value was not defined before inserting it into the HTML - */ - if (self.getOption("value") === null) { - self.setOption("value", self.getOption("values.off")); - } - - /** - * value from attribute - */ - if (self.hasAttribute("value")) { - self.setOption("value", self.getAttribute("value")); - } - - /** - * validate value - */ - validateAndSetValue.call(self); - - if (this.state === STATE_ON) { - toggleClassOn.call(self); - } else { - toggleClassOff.call(self); - } - - /** - * is called when options changed - */ - self[internalSymbol].attachObserver( - new Observer(function () { - if (isObject(this) && this instanceof ProxyObserver) { - validateAndSetValue.call(self); - toggleClass.call(self); - } - }), - ); - - return this; - } - - /** - * updater transformer methods for pipe - * - * @return {function} - */ - [updaterTransformerMethodsSymbol]() { - return { - "state-callback": (Wert) => { - return this.state; - }, - }; - } - - /** - * @return [CSSStyleSheet] - */ - static getCSSStyleSheet() { - return [ToggleSwitchStyleSheet]; - } - - /** - * toggle switch - * - * ``` - * e = document.querySelector('monster-toggle-switch'); - * e.click() - * ``` - */ - click() { - toggleValues.call(this); - } - - /** - * toggle switch on/off - * - * ``` - * e = document.querySelector('monster-toggle-switch'); - * e.toggle() - * ``` - * - * @return {ToggleSwitch} - */ - toggle() { - this.click(); - return this; - } - - /** - * toggle switch on - * - * ``` - * e = document.querySelector('monster-toggle-switch'); - * e.toggleOn() - * ``` - * - * @return {ToggleSwitch} - */ - toggleOn() { - this.setOption("value", this.getOption("values.on")); - return this; - } - - /** - * toggle switch off - * - * ``` - * e = document.querySelector('monster-toggle-switch'); - * e.toggleOff() - * ``` - * - * @return {ToggleSwitch} - */ - toggleOff() { - this.setOption("value", this.getOption("values.off")); - return this; - } - - /** - * returns the status of the element - * - * ``` - * e = document.querySelector('monster-toggle-switch'); - * console.log(e.state) - * // ↦ off - * ``` - * - * @return {string} - */ - get state() { - return this.getOption("value") === this.getOption("values.on") - ? STATE_ON - : STATE_OFF; - } - - /** - * The current value of the Switch - * - * ``` - * e = document.querySelector('monster-toggle-switch'); - * console.log(e.value) - * // ↦ on - * ``` - * - * @return {string} - */ - get value() { - return this.state === STATE_ON - ? this.getOption("values.on") - : this.getOption("values.off"); - } - - /** - * Set value - * - * ``` - * e = document.querySelector('monster-toggle-switch'); - * e.value="on" - * ``` - * - * @property {string} value - */ - set value(value) { - this.setOption("value", value); - } - - /** - * This method is called by the `instanceof` operator. - * @returns {symbol} - * @since 2.1.0 - */ - static get [instanceSymbol]() { - return Symbol.for( - "@schukai/monster/components/form/toggle-switch@@instance", - ); - } - - static getTag() { - return "monster-toggle-switch"; - } + /** + * 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 {string} value=current value of the element + * @property {Boolean} disabled=disabled=false Disabled state + * @property {Object} classes + * @property {string} classes.on=specifies the class for the on state. + * @property {string} classes.off=specifies the class for the off state. + * @property {Object} values + * @property {string} values.off=specifies the value of the element if it is not selected + * @property {Object} labels + * @property {string} labels.on=specifies the label for the on state. + * @property {string} labels.off=specifies the label for the off state. + * @property {string} actions + * @property {string} actions.on=specifies the action for the on state. + * @property {string} actions.off=specifies the action for the off state. + * @property {Object} templates + * @property {string} templates.main=specifies the main template used by the control. + * + * @since 3.57.0 + */ + get defaults() { + return Object.assign({}, super.defaults, { + value: null, + disabled: false, + classes: { + on: "monster-theme-on", + off: "monster-theme-off", + handle: "monster-theme-primary-1", + }, + values: { + on: "on", + off: "off", + }, + labels: { + "toggle-switch-on": "ON", + "toggle-switch-off": "OFF", + }, + templates: { + main: getTemplate(), + }, + actions: { + on: () => { + throw new Error("the on action is not defined"); + }, + off: () => { + throw new Error("the off action is not defined"); + }, + } + }); + } + + /** + * @return {ToggleSwitch} + */ + [assembleMethodSymbol]() { + const self = this; + super[assembleMethodSymbol](); + initControlReferences.call(this); + initEventHandler.call(this); + + /** + * init value to off + * if the value was not defined before inserting it into the HTML + */ + if (self.getOption("value") === null) { + self.setOption("value", self.getOption("values.off")); + } + + /** + * value from attribute + */ + if (self.hasAttribute("value")) { + self.setOption("value", self.getAttribute("value")); + } + + /** + * validate value + */ + validateAndSetValue.call(self); + + if (this.state === STATE_ON) { + toggleClassOn.call(self); + } else { + toggleClassOff.call(self); + } + + /** + * is called when options changed + */ + self[internalSymbol].attachObserver( + new Observer(function () { + if (isObject(this) && this instanceof ProxyObserver) { + validateAndSetValue.call(self); + toggleClass.call(self); + } + }), + ); + + return this; + } + + /** + * updater transformer methods for pipe + * + * @return {function} + */ + [updaterTransformerMethodsSymbol]() { + return { + "state-callback": (Wert) => { + return this.state; + }, + }; + } + + /** + * @return [CSSStyleSheet] + */ + static getCSSStyleSheet() { + return [ToggleSwitchStyleSheet]; + } + + /** + * toggle switch + * + * ``` + * e = document.querySelector('monster-toggle-switch'); + * e.click() + * ``` + */ + click() { + toggleValues.call(this); + } + + /** + * toggle switch on/off + * + * ``` + * e = document.querySelector('monster-toggle-switch'); + * e.toggle() + * ``` + * + * @return {ToggleSwitch} + */ + toggle() { + this.click(); + return this; + } + + /** + * toggle switch on + * + * ``` + * e = document.querySelector('monster-toggle-switch'); + * e.toggleOn() + * ``` + * + * @return {ToggleSwitch} + */ + toggleOn() { + this.setOption("value", this.getOption("values.on")); + return this; + } + + /** + * toggle switch off + * + * ``` + * e = document.querySelector('monster-toggle-switch'); + * e.toggleOff() + * ``` + * + * @return {ToggleSwitch} + */ + toggleOff() { + this.setOption("value", this.getOption("values.off")); + return this; + } + + /** + * returns the status of the element + * + * ``` + * e = document.querySelector('monster-toggle-switch'); + * console.log(e.state) + * // ↦ off + * ``` + * + * @return {string} + */ + get state() { + return this.getOption("value") === this.getOption("values.on") + ? STATE_ON + : STATE_OFF; + } + + /** + * The current value of the Switch + * + * ``` + * e = document.querySelector('monster-toggle-switch'); + * console.log(e.value) + * // ↦ on + * ``` + * + * @return {string} + */ + get value() { + return this.state === STATE_ON + ? this.getOption("values.on") + : this.getOption("values.off"); + } + + /** + * Set value + * + * ``` + * e = document.querySelector('monster-toggle-switch'); + * e.value="on" + * ``` + * + * @property {string} value + */ + set value(value) { + this.setOption("value", value); + } + + /** + * This method is called by the `instanceof` operator. + * @returns {symbol} + * @since 2.1.0 + */ + static get [instanceSymbol]() { + return Symbol.for( + "@schukai/monster/components/form/toggle-switch@@instance", + ); + } + + static getTag() { + return "monster-toggle-switch"; + } } /** * @private */ function initControlReferences() { - this[switchElementSymbol] = this.shadowRoot.querySelector( - `[${ATTRIBUTE_ROLE}=switch]`, - ); + this[switchElementSymbol] = this.shadowRoot.querySelector( + `[${ATTRIBUTE_ROLE}=switch]`, + ); } /** * @private */ function toggleClassOn() { - this[switchElementSymbol].classList.remove(this.getOption("classes.off")); // change color - this[switchElementSymbol].classList.add(this.getOption("classes.on")); // change color + this[switchElementSymbol].classList.remove(this.getOption("classes.off")); // change color + this[switchElementSymbol].classList.add(this.getOption("classes.on")); // change color } /** * @private */ function toggleClassOff() { - this[switchElementSymbol].classList.remove(this.getOption("classes.on")); // change color - this[switchElementSymbol].classList.add(this.getOption("classes.off")); // change color + this[switchElementSymbol].classList.remove(this.getOption("classes.on")); // change color + this[switchElementSymbol].classList.add(this.getOption("classes.off")); // change color } /** * @private */ function toggleClass() { - if (this.getOption("value") === this.getOption("values.on")) { - toggleClassOn.call(this); - } else { - toggleClassOff.call(this); - } + if (this.getOption("value") === this.getOption("values.on")) { + toggleClassOn.call(this); + } else { + toggleClassOff.call(this); + } } /** * @private */ function toggleValues() { - if (this.getOption("disabled") === true) { - return; - } - - if (this.getOption("value") === this.getOption("values.on")) { - this.setOption("value", this.getOption("values.off")); - this?.setFormValue(this.getOption("value")); // set form value - this.getOption("actions.off")?.call(this); - } else { - this.setOption("value", this.getOption("values.on")); - this?.setFormValue(this.getOption("values.off")); // set form value - this.getOption("actions.on")?.call(this); - } - - this.setOption("state", this.state); + if (this.getOption("disabled") === true) { + return; + } + + let callback, value; + + if (this.getOption("value") === this.getOption("values.on")) { + value = this.getOption("values.off"); + callback = this.getOption("actions.off"); + } else { + value = this.getOption("values.on"); + callback = this.getOption("actions.on"); + } + + this.setOption("value", value); + this?.setFormValue(value); + + if (isFunction(callback)) { + callback.call(this)(); + } + + + this.setOption("state", this.state); } /** * @private */ function validateAndSetValue() { - const value = this.getOption("value"); - - const validatedValues = []; - validatedValues.push(this.getOption("values.on")); - validatedValues.push(this.getOption("values.off")); - - if (validatedValues.includes(value) === false) { - addAttributeToken( - this, - ATTRIBUTE_ERRORMESSAGE, - 'The value "' + - value + - '" must be "' + - this.getOption("values.on") + - '" or "' + - this.getOption("values.off"), - ); - this.setOption("disabled", true); - this.formDisabledCallback(true); - } else { - this.setOption("disabled", false); - this.formDisabledCallback(false); - } + const value = this.getOption("value"); + + const validatedValues = []; + validatedValues.push(this.getOption("values.on")); + validatedValues.push(this.getOption("values.off")); + + if (validatedValues.includes(value) === false) { + addAttributeToken( + this, + ATTRIBUTE_ERRORMESSAGE, + 'The value "' + + value + + '" must be "' + + this.getOption("values.on") + + '" or "' + + this.getOption("values.off"), + ); + this.setOption("disabled", true); + this.formDisabledCallback(true); + } else { + this.setOption("disabled", false); + this.formDisabledCallback(false); + } } /** @@ -405,16 +414,16 @@ function validateAndSetValue() { * @return {initEventHandler} */ function initEventHandler() { - const self = this; - self.addEventListener("keyup", function (event) { - if (event.code === "Space") { - self[switchElementSymbol].click(); - } - }); - self.addEventListener("click", function (event) { - toggleValues.call(self); - }); - return this; + const self = this; + self.addEventListener("keyup", function (event) { + if (event.code === "Space") { + self[switchElementSymbol].click(); + } + }); + self.addEventListener("click", function (event) { + toggleValues.call(self); + }); + return this; } /** @@ -422,10 +431,10 @@ function initEventHandler() { * @return {string} */ function getTemplate() { - // language=HTML - return ` + // language=HTML + return ` <div data-monster-role="control" part="control" tabindex="0"> - <div class="switch" data-monster-role="switch" + <div class="switch" data-monster-role="switch" data-monster-attributes="data-monster-state path:value | call:state-callback"> <div class="label on" data-monster-replace="path:labels.toggle-switch-on"></div> <div class="label off" data-monster-replace="path:labels.toggle-switch-off"></div> diff --git a/source/components/host/toggle-button.mjs b/source/components/host/toggle-button.mjs index a1c1c151d15f6ebc7b71fc3ec585018e21ffe0f7..92e5fd380f9637f0171e509a4af5d946d642c1ff 100644 --- a/source/components/host/toggle-button.mjs +++ b/source/components/host/toggle-button.mjs @@ -78,11 +78,9 @@ class ToggleButton extends CallButton { * @property {string} templates.main Main template */ get defaults() { - const obj = Object.assign({}, super.defaults, { + return Object.assign({}, super.defaults, { call: "toggle", }); - - return obj; } /** diff --git a/test/cases/components/form/toggle-switch.mjs b/test/cases/components/form/toggle-switch.mjs index bcd50923038d3a397e7cfffd3cca2a3a2f4340b1..22dbfafd59441888e4adfd19d9660da2a1e9125b 100644 --- a/test/cases/components/form/toggle-switch.mjs +++ b/test/cases/components/form/toggle-switch.mjs @@ -91,6 +91,8 @@ describe('ToggleSwitch', function () { it('toggle to on', function () { const toggleSwitch = document.createElement('monster-toggle-switch'); + toggleSwitch.setOption('actions.on', 'true'); + toggleSwitch.setOption('actions.off', 'false'); expect(toggleSwitch.value).is.equal('off'); expect(toggleSwitch.state).is.equal('off');