diff --git a/development/issues/closed/268.html b/development/issues/closed/268.html index 36a5b82c995dd286a90eee9d116785cbce456876..af7910c45ae4c261bd4c65b14ae5841bf5d81902 100644 --- a/development/issues/closed/268.html +++ b/development/issues/closed/268.html @@ -16,11 +16,11 @@ <main> <monster-tabs data-monster-option-features-removeBehavior="auto"> - <div data-monster-button-label="A1" data-monster-removable>test</div> - <div data-monster-button-label="A2" data-monster-removable>test</div> - <div data-monster-button-label="A3" class="active" data-monster-removable>test</div> - <div data-monster-button-label="A4" data-monster-removable>test</div> - <div data-monster-button-label="A5" data-monster-removable>test</div> + <div data-monster-button-label="A1" data-monster-removable>test1</div> + <div data-monster-button-label="A2" data-monster-removable>test2</div> + <div data-monster-button-label="A3" class="active" data-monster-removable>test3</div> + <div data-monster-button-label="A4" data-monster-removable>test44</div> + <div data-monster-button-label="A5" data-monster-removable>test55</div> </monster-tabs> </main> diff --git a/source/components/layout/tabs.mjs b/source/components/layout/tabs.mjs index 6958a1535ce0b25a6b3dc61685d7aca545ef7ca9..27f39d44c39e0e21aceb48cd240b340839995347 100644 --- a/source/components/layout/tabs.mjs +++ b/source/components/layout/tabs.mjs @@ -12,55 +12,55 @@ * SPDX-License-Identifier: AGPL-3.0 */ -import { instanceSymbol } from "../../constants.mjs"; -import { createPopper } from "@popperjs/core"; -import { extend } from "../../data/extend.mjs"; -import { Pathfinder } from "../../data/pathfinder.mjs"; +import {instanceSymbol} from "../../constants.mjs"; +import {createPopper} from "@popperjs/core"; +import {extend} from "../../data/extend.mjs"; +import {Pathfinder} from "../../data/pathfinder.mjs"; import { - addAttributeToken, - addToObjectLink, - hasObjectLink, + addAttributeToken, + addToObjectLink, + hasObjectLink, } from "../../dom/attributes.mjs"; import { - ATTRIBUTE_ERRORMESSAGE, - ATTRIBUTE_PREFIX, - ATTRIBUTE_ROLE, + ATTRIBUTE_ERRORMESSAGE, + ATTRIBUTE_PREFIX, + ATTRIBUTE_ROLE, } from "../../dom/constants.mjs"; import { - assembleMethodSymbol, - CustomElement, - getSlottedElements, - registerCustomElement, + assembleMethodSymbol, + CustomElement, + getSlottedElements, + registerCustomElement, } from "../../dom/customelement.mjs"; import { - findTargetElementFromEvent, - fireCustomEvent, + findTargetElementFromEvent, + fireCustomEvent, } from "../../dom/events.mjs"; -import { getDocument } from "../../dom/util.mjs"; -import { random } from "../../math/random.mjs"; -import { getGlobal } from "../../types/global.mjs"; -import { ID } from "../../types/id.mjs"; -import { isArray, isString } from "../../types/is.mjs"; -import { TokenList } from "../../types/tokenlist.mjs"; -import { clone } from "../../util/clone.mjs"; -import { DeadMansSwitch } from "../../util/deadmansswitch.mjs"; -import { Processing } from "../../util/processing.mjs"; +import {getDocument} from "../../dom/util.mjs"; +import {random} from "../../math/random.mjs"; +import {getGlobal} from "../../types/global.mjs"; +import {ID} from "../../types/id.mjs"; +import {isArray, isString} from "../../types/is.mjs"; +import {TokenList} from "../../types/tokenlist.mjs"; +import {clone} from "../../util/clone.mjs"; +import {DeadMansSwitch} from "../../util/deadmansswitch.mjs"; +import {Processing} from "../../util/processing.mjs"; import { - ATTRIBUTE_BUTTON_LABEL, - ATTRIBUTE_FORM_RELOAD, - ATTRIBUTE_FORM_URL, - STYLE_DISPLAY_MODE_BLOCK, + ATTRIBUTE_BUTTON_LABEL, + ATTRIBUTE_FORM_RELOAD, + ATTRIBUTE_FORM_URL, + STYLE_DISPLAY_MODE_BLOCK, } from "../form/constants.mjs"; -import { TabsStyleSheet } from "./stylesheet/tabs.mjs"; -import { loadAndAssignContent } from "../form/util/fetch.mjs"; -import { ThemeStyleSheet } from "../stylesheet/theme.mjs"; +import {TabsStyleSheet} from "./stylesheet/tabs.mjs"; +import {loadAndAssignContent} from "../form/util/fetch.mjs"; +import {ThemeStyleSheet} from "../stylesheet/theme.mjs"; import { - popperInstanceSymbol, - setEventListenersModifiers, + popperInstanceSymbol, + setEventListenersModifiers, } from "../form/util/popper.mjs"; -export { Tabs }; +export {Tabs}; /** * @private @@ -156,360 +156,361 @@ const resizeObserverSymbol = Symbol("resizeObserver"); * @summary This CustomControl creates a tab element with a variety of options. */ class Tabs extends CustomElement { - /** - * This method is called by the `instanceof` operator. - * @return {symbol} - */ - static get [instanceSymbol]() { - return Symbol.for("@schukai/monster/components/layout/tabs"); - } - - /** - * 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 - * @property {string} labels.new-tab-label="New Tab" - * @property {Object} features - * @property {number} features.openDelay=500 Open delay in milliseconds - * @property {string} features.removeBehavior="auto" Remove behavior, auto (default), next, previous and none - * @property {Object} fetch Fetch [see Using Fetch mozilla.org](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) - * @property {String} fetch.redirect=error - * @property {String} fetch.method=GET - * @property {String} fetch.mode=same-origin - * @property {String} fetch.credentials=same-origin - * @property {Object} fetch.headers={"accept":"text/html"}} - * @property {Object} popper [PopperJS Options](https://popper.js.org/docs/v2/) - * @property {string} popper.placement=bottom PopperJS placement - * @property {Object[]} modifiers={name:offset} PopperJS placement - */ - get defaults() { - return Object.assign({}, super.defaults, { - templates: { - main: getTemplate(), - }, - labels: { - "new-tab-label": "New Tab", - }, - buttons: { - standard: [], - popper: [], - }, - fetch: { - redirect: "error", - method: "GET", - mode: "same-origin", - credentials: "same-origin", - headers: { - accept: "text/html", - }, - }, - - features: { - openDelay: null, - removeBehavior: "auto", - }, - - classes: { - button: "monster-theme-primary-1", - popper: "monster-theme-primary-1", - navigation: "monster-theme-primary-1", - }, - - popper: { - placement: "bottom", - modifiers: [ - { - name: "offset", - options: { - offset: [0, 2], - }, - }, - - { - name: "eventListeners", - enabled: false, - }, - ], - }, - }); - } - - /** - * This method is called internal and should not be called directly. - */ - [assembleMethodSymbol]() { - super[assembleMethodSymbol](); - - initControlReferences.call(this); - - this[dimensionsSymbol] = new Pathfinder({ data: {} }); - - initEventHandler.call(this); - - // setup structure - initTabButtons.call(this).then(() => { - initPopperSwitch.call(this); - initPopper.call(this); - attachResizeObserver.call(this); - attachTabChangeObserver.call(this); - }); - } - - /** - * This method is called internal and should not be called directly. - * - * @return {CSSStyleSheet[]} - */ - static getCSSStyleSheet() { - return [TabsStyleSheet]; - } - - /** - * This method is called internal and should not be called directly. - * - * @return {string} - */ - static getTag() { - return "monster-tabs"; - } - - /** - * A function that activates a tab based on the provided name. - * - * The tabs have to be named with the `data-monster-name` attribute. - * - * @param {type} idOrName - the name or id of the tab to activate - * @return {Tabs} - The current instance - */ - activeTab(idOrName) { - let found = false; - - getSlottedElements.call(this).forEach((node) => { - if (found === true) { - return; - } - - if (node.getAttribute("data-monster-name") === idOrName) { - this.shadowRoot - .querySelector( - `[data-monster-tab-reference="${node.getAttribute("id")}"]`, - ) - .click(); - found = true; - } - - if (node.getAttribute("id") === idOrName) { - this.shadowRoot - .querySelector( - `[data-monster-tab-reference="${node.getAttribute("id")}"]`, - ) - .click(); - found = true; - } - }); - - return this; - } - - /** - * A function that returns the name or id of the currently active tab. - * - * The tabs have to be named with the `data-monster-name` attribute. - * - * @return {string|null} - */ - getActiveTab() { - const nodes = getSlottedElements.call(this); - for (const node of nodes) { - if (node.matches(".active") === true) { - if (node.hasAttribute("data-monster-name")) { - return node.getAttribute("data-monster-name"); - } - - return node.getAttribute("id"); - } - } - return null; - } - - /** - * This method is called by the dom and should not be called directly. - * - * @return {void} - */ - connectedCallback() { - super.connectedCallback(); - - const document = getDocument(); - - for (const [, type] of Object.entries(["click", "touch"])) { - // close on outside ui-events - document.addEventListener(type, this[closeEventHandler]); - } - } - - /** - * This method is called by the dom and should not be called directly. - * - * @return {void} - */ - disconnectedCallback() { - super.disconnectedCallback(); - - const document = getDocument(); - - // close on outside ui-events - for (const [, type] of Object.entries(["click", "touch"])) { - document.removeEventListener(type, this[closeEventHandler]); - } - } + /** + * This method is called by the `instanceof` operator. + * @return {symbol} + */ + static get [instanceSymbol]() { + return Symbol.for("@schukai/monster/components/layout/tabs"); + } + + /** + * 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 + * @property {string} labels.new-tab-label="New Tab" + * @property {Object} features + * @property {number} features.openDelay=500 Open delay in milliseconds + * @property {string} features.removeBehavior="auto" Remove behavior, auto (default), next, previous and none + * @property {Object} fetch Fetch [see Using Fetch mozilla.org](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) + * @property {String} fetch.redirect=error + * @property {String} fetch.method=GET + * @property {String} fetch.mode=same-origin + * @property {String} fetch.credentials=same-origin + * @property {Object} fetch.headers={"accept":"text/html"}} + * @property {Object} popper [PopperJS Options](https://popper.js.org/docs/v2/) + * @property {string} popper.placement=bottom PopperJS placement + * @property {Object[]} modifiers={name:offset} PopperJS placement + */ + get defaults() { + return Object.assign({}, super.defaults, { + templates: { + main: getTemplate(), + }, + labels: { + "new-tab-label": "New Tab", + }, + buttons: { + standard: [], + popper: [], + }, + fetch: { + redirect: "error", + method: "GET", + mode: "same-origin", + credentials: "same-origin", + headers: { + accept: "text/html", + }, + }, + + features: { + openDelay: null, + removeBehavior: "auto", + }, + + classes: { + button: "monster-theme-primary-1", + popper: "monster-theme-primary-1", + navigation: "monster-theme-primary-1", + }, + + popper: { + placement: "bottom", + modifiers: [ + { + name: "offset", + options: { + offset: [0, 2], + }, + }, + + { + name: "eventListeners", + enabled: false, + }, + ], + }, + }); + } + + /** + * This method is called internal and should not be called directly. + */ + [assembleMethodSymbol]() { + super[assembleMethodSymbol](); + + initControlReferences.call(this); + + this[dimensionsSymbol] = new Pathfinder({data: {}}); + + initEventHandler.call(this); + + // setup structure + initTabButtons.call(this).then(() => { + initPopperSwitch.call(this); + initPopper.call(this); + attachResizeObserver.call(this); + attachTabChangeObserver.call(this); + }); + } + + /** + * This method is called internal and should not be called directly. + * + * @return {CSSStyleSheet[]} + */ + static getCSSStyleSheet() { + return [TabsStyleSheet]; + } + + /** + * This method is called internal and should not be called directly. + * + * @return {string} + */ + static getTag() { + return "monster-tabs"; + } + + /** + * A function that activates a tab based on the provided name. + * + * The tabs have to be named with the `data-monster-name` attribute. + * + * @param {type} idOrName - the name or id of the tab to activate + * @return {Tabs} - The current instance + */ + activeTab(idOrName) { + let found = false; + + getSlottedElements.call(this).forEach((node) => { + if (found === true) { + return; + } + + if (node.getAttribute("data-monster-name") === idOrName) { + this.shadowRoot + .querySelector( + `[data-monster-tab-reference="${node.getAttribute("id")}"]`, + ) + .click(); + found = true; + } + + if (node.getAttribute("id") === idOrName) { + this.shadowRoot + .querySelector( + `[data-monster-tab-reference="${node.getAttribute("id")}"]`, + ) + .click(); + found = true; + } + }); + + return this; + } + + /** + * A function that returns the name or id of the currently active tab. + * + * The tabs have to be named with the `data-monster-name` attribute. + * + * @return {string|null} + */ + getActiveTab() { + const nodes = getSlottedElements.call(this); + for (const node of nodes) { + if (node.matches(".active") === true) { + if (node.hasAttribute("data-monster-name")) { + return node.getAttribute("data-monster-name"); + } + + return node.getAttribute("id"); + } + } + return null; + } + + /** + * This method is called by the dom and should not be called directly. + * + * @return {void} + */ + connectedCallback() { + super.connectedCallback(); + + const document = getDocument(); + + for (const [, type] of Object.entries(["click", "touch"])) { + // close on outside ui-events + document.addEventListener(type, this[closeEventHandler]); + } + } + + /** + * This method is called by the dom and should not be called directly. + * + * @return {void} + */ + disconnectedCallback() { + super.disconnectedCallback(); + + const document = getDocument(); + + // close on outside ui-events + for (const [, type] of Object.entries(["click", "touch"])) { + document.removeEventListener(type, this[closeEventHandler]); + } + } } /** * @private */ function initPopperSwitch() { - const nodes = getSlottedElements.call(this, `[${ATTRIBUTE_ROLE}="switch"]`); // null ↦ only unnamed slots - let switchButton; - if (nodes.size === 0) { - switchButton = document.createElement("button"); - switchButton.setAttribute(ATTRIBUTE_ROLE, "switch"); - switchButton.setAttribute("part", "switch"); - switchButton.classList.add("hidden"); - const classList = this.getOption("classes.button"); - if (classList) { - switchButton.classList.add(classList); - } - switchButton.innerHTML = - '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16"><path d="M9.5 13a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0zm0-5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0zm0-5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0z"/></svg>'; - this[navElementSymbol].prepend(switchButton); - } else { - switchButton = nodes.next(); - } - - /** - * @param {Event} event - */ - this[popperSwitchEventHandler] = (event) => { - const element = findTargetElementFromEvent(event, ATTRIBUTE_ROLE, "switch"); - - if (element instanceof HTMLButtonElement) { - togglePopper.call(this); - } - }; - - for (const type of ["click", "touch"]) { - switchButton.addEventListener(type, this[popperSwitchEventHandler]); - } - - this[switchElementSymbol] = switchButton; + const nodes = getSlottedElements.call(this, `[${ATTRIBUTE_ROLE}="switch"]`); // null ↦ only unnamed slots + let switchButton; + if (nodes.size === 0) { + switchButton = document.createElement("button"); + switchButton.setAttribute(ATTRIBUTE_ROLE, "switch"); + switchButton.setAttribute("part", "switch"); + switchButton.classList.add("hidden"); + const classList = this.getOption("classes.button"); + if (classList) { + switchButton.classList.add(classList); + } + switchButton.innerHTML = + '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16"><path d="M9.5 13a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0zm0-5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0zm0-5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0z"/></svg>'; + this[navElementSymbol].prepend(switchButton); + } else { + switchButton = nodes.next(); + } + + /** + * @param {Event} event + */ + this[popperSwitchEventHandler] = (event) => { + const element = findTargetElementFromEvent(event, ATTRIBUTE_ROLE, "switch"); + + if (element instanceof HTMLButtonElement) { + togglePopper.call(this); + } + }; + + for (const type of ["click", "touch"]) { + switchButton.addEventListener(type, this[popperSwitchEventHandler]); + } + + this[switchElementSymbol] = switchButton; } /** * @private */ function hidePopper() { - if (!this[popperInstanceSymbol]) { - return; - } + if (!this[popperInstanceSymbol]) { + return; + } - this[popperElementSymbol].style.display = "none"; - // performance https://popper.js.org/docs/v2/tutorial/#performance - setEventListenersModifiers.call(this, false); + this[popperElementSymbol].style.display = "none"; + // performance https://popper.js.org/docs/v2/tutorial/#performance + setEventListenersModifiers.call(this, false); } /** * @private */ function showPopper() { - if (this[popperElementSymbol].style.display === STYLE_DISPLAY_MODE_BLOCK) { - return; - } - - this[popperElementSymbol].style.visibility = "hidden"; - this[popperElementSymbol].style.display = STYLE_DISPLAY_MODE_BLOCK; - // performance https://popper.js.org/docs/v2/tutorial/#performance - setEventListenersModifiers.call(this, true); - - this[popperInstanceSymbol].update(); - - new Processing(() => { - this[popperElementSymbol].style.removeProperty("visibility"); - }) - .run(undefined) - .then(() => {}) - .catch((e) => { - addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message); - }); + if (this[popperElementSymbol].style.display === STYLE_DISPLAY_MODE_BLOCK) { + return; + } + + this[popperElementSymbol].style.visibility = "hidden"; + this[popperElementSymbol].style.display = STYLE_DISPLAY_MODE_BLOCK; + // performance https://popper.js.org/docs/v2/tutorial/#performance + setEventListenersModifiers.call(this, true); + + this[popperInstanceSymbol].update(); + + new Processing(() => { + this[popperElementSymbol].style.removeProperty("visibility"); + }) + .run(undefined) + .then(() => { + }) + .catch((e) => { + addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message); + }); } /** * @private */ function togglePopper() { - if (this[popperElementSymbol].style.display === STYLE_DISPLAY_MODE_BLOCK) { - hidePopper.call(this); - } else { - showPopper.call(this); - } + if (this[popperElementSymbol].style.display === STYLE_DISPLAY_MODE_BLOCK) { + hidePopper.call(this); + } else { + showPopper.call(this); + } } /** * @private */ function attachResizeObserver() { - // against flickering - this[resizeObserverSymbol] = new ResizeObserver((entries) => { - if (this[timerCallbackSymbol] instanceof DeadMansSwitch) { - try { - this[timerCallbackSymbol].touch(); - return; - } catch (e) { - delete this[timerCallbackSymbol]; - } - } - - this[timerCallbackSymbol] = new DeadMansSwitch(200, () => { - this[dimensionsSymbol].setVia("data.calculated", false); - checkAndRearrangeButtons.call(this); - }); - }); - - this[resizeObserverSymbol].observe(this[navElementSymbol]); + // against flickering + this[resizeObserverSymbol] = new ResizeObserver((entries) => { + if (this[timerCallbackSymbol] instanceof DeadMansSwitch) { + try { + this[timerCallbackSymbol].touch(); + return; + } catch (e) { + delete this[timerCallbackSymbol]; + } + } + + this[timerCallbackSymbol] = new DeadMansSwitch(200, () => { + this[dimensionsSymbol].setVia("data.calculated", false); + checkAndRearrangeButtons.call(this); + }); + }); + + this[resizeObserverSymbol].observe(this[navElementSymbol]); } /** * @private */ function attachTabChangeObserver() { - // against flickering - new MutationObserver((mutations) => { - let runUpdate = false; - - for (const mutation of mutations) { - if (mutation.type === "childList") { - if ( - mutation.addedNodes.length > 0 || - mutation.removedNodes.length > 0 - ) { - runUpdate = true; - break; - } - } - } - - if (runUpdate === true) { - this[dimensionsSymbol].setVia("data.calculated", false); - initTabButtons.call(this); - } - }).observe(this, { - childList: true, - }); + // against flickering + new MutationObserver((mutations) => { + let runUpdate = false; + + for (const mutation of mutations) { + if (mutation.type === "childList") { + if ( + mutation.addedNodes.length > 0 || + mutation.removedNodes.length > 0 + ) { + runUpdate = true; + break; + } + } + } + + if (runUpdate === true) { + this[dimensionsSymbol].setVia("data.calculated", false); + initTabButtons.call(this); + } + }).observe(this, { + childList: true, + }); } /** @@ -518,41 +519,41 @@ function attachTabChangeObserver() { * @external "external:createPopper" */ function initPopper() { - const self = this; - - const options = extend({}, self.getOption("popper")); - - self[popperInstanceSymbol] = createPopper( - self[switchElementSymbol], - self[popperElementSymbol], - options, - ); - - const observer1 = new MutationObserver(function (mutations) { - let runUpdate = false; - for (const mutation of mutations) { - if (mutation.type === "childList") { - if ( - mutation.addedNodes.length > 0 || - mutation.removedNodes.length > 0 - ) { - runUpdate = true; - break; - } - } - } - - if (runUpdate === true) { - self[popperInstanceSymbol].update(); - } - }); - - observer1.observe(self[popperNavElementSymbol], { - childList: true, - subtree: true, - }); - - return self; + const self = this; + + const options = extend({}, self.getOption("popper")); + + self[popperInstanceSymbol] = createPopper( + self[switchElementSymbol], + self[popperElementSymbol], + options, + ); + + const observer1 = new MutationObserver(function (mutations) { + let runUpdate = false; + for (const mutation of mutations) { + if (mutation.type === "childList") { + if ( + mutation.addedNodes.length > 0 || + mutation.removedNodes.length > 0 + ) { + runUpdate = true; + break; + } + } + } + + if (runUpdate === true) { + self[popperInstanceSymbol].update(); + } + }); + + observer1.observe(self[popperNavElementSymbol], { + childList: true, + subtree: true, + }); + + return self; } /** @@ -560,202 +561,229 @@ function initPopper() { * @param {HTMLElement} element */ function show(element) { - if (!this.shadowRoot) { - throw new Error("no shadow-root is defined"); - } - - const reference = element.getAttribute(`${ATTRIBUTE_PREFIX}tab-reference`); - - const nodes = getSlottedElements.call(this); - for (const node of nodes) { - const id = node.getAttribute("id"); - - if (id === reference) { - node.classList.add("active"); - - const openDelay = parseInt(this.getOption("features.openDelay"), 10); - - if (!isNaN(openDelay) && openDelay > 0) { - node.style.visibility = "hidden"; - - setTimeout(() => { - node.style.visibility = "visible"; - }, openDelay); - } - - // get all data- from button and filter out data-monster-attributes and data-monster-insert - const data = {}; - const mask = [ - "data-monster-attributes", - "data-monster-insert-reference", - "data-monster-state", - "data-monster-button-label", - "data-monster-objectlink", - "data-monster-role", - ]; - - for (const [, attr] of Object.entries(node.attributes)) { - if (attr.name.startsWith("data-") && mask.indexOf(attr.name) === -1) { - data[attr.name] = attr.value; - } - } - - if (node.hasAttribute(ATTRIBUTE_FORM_URL)) { - const url = node.getAttribute(ATTRIBUTE_FORM_URL); - - if ( - !node.hasAttribute(ATTRIBUTE_FORM_RELOAD) || - node.getAttribute(ATTRIBUTE_FORM_RELOAD).toLowerCase() === "onshow" - ) { - node.removeAttribute(ATTRIBUTE_FORM_URL); - } - - const options = this.getOption("fetch", {}); - const filter = undefined; - loadAndAssignContent(node, url, options, filter) - .then(() => { - fireCustomEvent(this, "monster-tab-changed", { - reference, - }); - }) - .catch((e) => { - addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message); - }); - } else { - fireCustomEvent(this, "monster-tab-changed", { - reference, - data, - }); - } - } else { - node.classList.remove("active"); - } - } - - const standardButtons = this.getOption("buttons.standard"); - for (const index in standardButtons) { - const button = standardButtons[index]; - const state = button["reference"] === reference ? "active" : "inactive"; - this.setOption(`buttons.standard.${index}.state`, state); - } - - const popperButton = this.getOption("buttons.popper"); - for (const index in popperButton) { - const button = popperButton[index]; - const state = button["reference"] === reference ? "active" : "inactive"; - this.setOption(`buttons.popper.${index}.state`, state); - } - - hidePopper.call(this); + if (!this.shadowRoot) { + throw new Error("no shadow-root is defined"); + } + + const reference = element.getAttribute(`${ATTRIBUTE_PREFIX}tab-reference`); + + const nodes = getSlottedElements.call(this); + for (const node of nodes) { + const id = node.getAttribute("id"); + + if (id === reference) { + node.classList.add("active"); + + const openDelay = parseInt(this.getOption("features.openDelay"), 10); + + if (!isNaN(openDelay) && openDelay > 0) { + node.style.visibility = "hidden"; + + setTimeout(() => { + node.style.visibility = "visible"; + }, openDelay); + } + + // get all data- from button and filter out data-monster-attributes and data-monster-insert + const data = {}; + const mask = [ + "data-monster-attributes", + "data-monster-insert-reference", + "data-monster-state", + "data-monster-button-label", + "data-monster-objectlink", + "data-monster-role", + ]; + + for (const [, attr] of Object.entries(node.attributes)) { + if (attr.name.startsWith("data-") && mask.indexOf(attr.name) === -1) { + data[attr.name] = attr.value; + } + } + + if (node.hasAttribute(ATTRIBUTE_FORM_URL)) { + const url = node.getAttribute(ATTRIBUTE_FORM_URL); + + if ( + !node.hasAttribute(ATTRIBUTE_FORM_RELOAD) || + node.getAttribute(ATTRIBUTE_FORM_RELOAD).toLowerCase() === "onshow" + ) { + node.removeAttribute(ATTRIBUTE_FORM_URL); + } + + const options = this.getOption("fetch", {}); + const filter = undefined; + loadAndAssignContent(node, url, options, filter) + .then(() => { + fireCustomEvent(this, "monster-tab-changed", { + reference, + }); + }) + .catch((e) => { + addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message); + }); + } else { + fireCustomEvent(this, "monster-tab-changed", { + reference, + data, + }); + } + } else { + node.classList.remove("active"); + } + } + + const standardButtons = this.getOption("buttons.standard"); + for (const index in standardButtons) { + const button = standardButtons[index]; + const state = button["reference"] === reference ? "active" : "inactive"; + this.setOption(`buttons.standard.${index}.state`, state); + } + + const popperButton = this.getOption("buttons.popper"); + for (const index in popperButton) { + const button = popperButton[index]; + const state = button["reference"] === reference ? "active" : "inactive"; + this.setOption(`buttons.popper.${index}.state`, state); + } + + hidePopper.call(this); } /** * @private */ function initEventHandler() { - if (!this.shadowRoot) { - throw new Error("no shadow-root is defined"); - } - - /** - * @param {Event} event - */ - this[changeTabEventHandler] = (event) => { - const element = findTargetElementFromEvent(event, ATTRIBUTE_ROLE, "button"); - - if (element instanceof HTMLButtonElement && element.disabled !== true) { - show.call(this, element); - } - }; - - /** - * @param {Event} event - * @fires monster-tab-remove - */ - this[removeTabEventHandler] = (event) => { - const element = findTargetElementFromEvent( - event, - ATTRIBUTE_ROLE, - "remove-tab", - ); - - if (element instanceof HTMLElement) { - const button = findTargetElementFromEvent( - event, - ATTRIBUTE_ROLE, - "button", - ); - - if (button instanceof HTMLButtonElement && button.disabled !== true) { - const reference = button.getAttribute( - `${ATTRIBUTE_PREFIX}tab-reference`, - ); - - const previous = button.previousElementSibling; - const next = button.nextElementSibling; - - if (reference) { - const container = this.querySelector(`[id=${reference}]`); - if (container instanceof HTMLElement) { - container.remove(); - initTabButtons.call(this); - fireCustomEvent(this, "monster-tab-remove", { - reference, - }); - } - } - - switch (this.getOption("features.removeBehavior")) { - case "auto": - if (next instanceof HTMLButtonElement) { - next.click(); - } else { - // get previous button - if (previous instanceof HTMLButtonElement) { - previous.click(); - } - } - break; - case "next": - if (next instanceof HTMLButtonElement) { - next.click(); - } - break; - case "previous": - if (previous instanceof HTMLButtonElement) { - previous.click(); - } - break; - - default: // and "none" - break; - } - } - } - }; - - this[navElementSymbol].addEventListener("touch", this[changeTabEventHandler]); - this[navElementSymbol].addEventListener("click", this[changeTabEventHandler]); - - this[navElementSymbol].addEventListener("touch", this[removeTabEventHandler]); - this[navElementSymbol].addEventListener("click", this[removeTabEventHandler]); - - /** - * @param {Event} event - */ - this[closeEventHandler] = (event) => { - const path = event.composedPath(); - - for (const [, element] of Object.entries(path)) { - if (element === this) { - return; - } - } - - hidePopper.call(this); - }; - - return this; + const self = this; + + if (!this.shadowRoot) { + throw new Error("no shadow-root is defined"); + } + + /** + * @param {Event} event + * @fires monster-tab-remove + */ + this[removeTabEventHandler] = (event) => { + + const element = findTargetElementFromEvent( + event, + ATTRIBUTE_ROLE, + "remove-tab", + ); + + if (element instanceof HTMLElement) { + const button = findTargetElementFromEvent( + event, + ATTRIBUTE_ROLE, + "button", + ); + + if (button instanceof HTMLButtonElement && button.disabled !== true) { + const reference = button.getAttribute( + `${ATTRIBUTE_PREFIX}tab-reference`, + ); + + let doChange = false; + let nextName = null + let previousName = null; + + const btn = this.getOption("buttons") + for (let i = 0; i < btn.standard.length; i++) { + if (btn.standard[i].reference === reference) { + if (btn.standard[i].state === "active") { + doChange = i + if (i < btn.standard.length - 1) { + nextName = btn.standard[i + 1]?.reference + } + if (i > 0) { + previousName = btn.standard[i - 1]?.reference + } + } + break; + } + } + + if (reference) { + const container = this.querySelector(`[id=${reference}]`); + if (container instanceof HTMLElement) { + + if (doChange) { + + switch (this.getOption("features.removeBehavior")) { + case "auto": + if (nextName !== null) { + self.activeTab(nextName); + } else { + if (previousName !== null) { + self.activeTab(previousName); + } + } + break; + case "next": + if (nextName !== null) { + self.activeTab(nextName); + } + break; + case "previous": + if (previousName !== null) { + self.activeTab(previousName); + } + break; + + default: // and "none" + break; + } + } + + container.remove(); + initTabButtons.call(this); + fireCustomEvent(this, "monster-tab-remove", { + reference, + }); + } + } + + } + } + }; + + /** + * @param {Event} event + */ + this[changeTabEventHandler] = (event) => { + + const element = findTargetElementFromEvent(event, ATTRIBUTE_ROLE, "button"); + + if (element instanceof HTMLButtonElement && element.disabled !== true) { + show.call(this, element); + } + }; + + /** + * @param {Event} event + */ + this[closeEventHandler] = (event) => { + const path = event.composedPath(); + + for (const [, element] of Object.entries(path)) { + if (element === this) { + return; + } + } + + hidePopper.call(this); + }; + + + // the order is important, because the remove must be before the change + this[navElementSymbol].addEventListener("touch", this[removeTabEventHandler]); + this[navElementSymbol].addEventListener("click", this[removeTabEventHandler]); + + this[navElementSymbol].addEventListener("touch", this[changeTabEventHandler]); + this[navElementSymbol].addEventListener("click", this[changeTabEventHandler]); + + return this; } /** @@ -763,37 +791,37 @@ function initEventHandler() { * @param observedNode */ function attachTabMutationObserver(observedNode) { - const self = this; - - if (hasObjectLink(observedNode, mutationObserverSymbol)) { - return; - } - - /** - * this construct monitors a node whether it is disabled or modified - * @type {MutationObserver} - */ - const observer = new MutationObserver(function (mutations) { - if (isArray(mutations)) { - const mutation = mutations.pop(); - if (mutation instanceof MutationRecord) { - initTabButtons.call(self); - } - } - }); - - observer.observe(observedNode, { - childList: false, - attributes: true, - subtree: false, - attributeFilter: [ - "disabled", - ATTRIBUTE_BUTTON_LABEL, - `${ATTRIBUTE_PREFIX}button-icon`, - ], - }); - - addToObjectLink(observedNode, mutationObserverSymbol, observer); + const self = this; + + if (hasObjectLink(observedNode, mutationObserverSymbol)) { + return; + } + + /** + * this construct monitors a node whether it is disabled or modified + * @type {MutationObserver} + */ + const observer = new MutationObserver(function (mutations) { + if (isArray(mutations)) { + const mutation = mutations.pop(); + if (mutation instanceof MutationRecord) { + initTabButtons.call(self); + } + } + }); + + observer.observe(observedNode, { + childList: false, + attributes: true, + subtree: false, + attributeFilter: [ + "disabled", + ATTRIBUTE_BUTTON_LABEL, + `${ATTRIBUTE_PREFIX}button-icon`, + ], + }); + + addToObjectLink(observedNode, mutationObserverSymbol, observer); } /** @@ -802,22 +830,22 @@ function attachTabMutationObserver(observedNode) { * @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[navElementSymbol] = this.shadowRoot.querySelector( - `nav[${ATTRIBUTE_ROLE}=nav]`, - ); - this[popperElementSymbol] = this.shadowRoot.querySelector( - `[${ATTRIBUTE_ROLE}=popper]`, - ); - this[popperNavElementSymbol] = this.shadowRoot.querySelector( - `[${ATTRIBUTE_ROLE}=popper-nav]`, - ); + if (!this.shadowRoot) { + throw new Error("no shadow-root is defined"); + } + + this[controlElementSymbol] = this.shadowRoot.querySelector( + `[${ATTRIBUTE_ROLE}=control]`, + ); + this[navElementSymbol] = this.shadowRoot.querySelector( + `nav[${ATTRIBUTE_ROLE}=nav]`, + ); + this[popperElementSymbol] = this.shadowRoot.querySelector( + `[${ATTRIBUTE_ROLE}=popper]`, + ); + this[popperNavElementSymbol] = this.shadowRoot.querySelector( + `[${ATTRIBUTE_ROLE}=popper-nav]`, + ); } /** @@ -827,101 +855,102 @@ function initControlReferences() { * */ function initTabButtons() { - if (!this.shadowRoot) { - throw new Error("no shadow-root is defined"); - } - - let activeReference; - - const dimensionsCalculated = this[dimensionsSymbol].getVia( - "data.calculated", - false, - ); - - const buttons = []; - const nodes = getSlottedElements.call(this, undefined, null); // null ↦ only unnamed slots - - for (const node of nodes) { - if (!(node instanceof HTMLElement)) continue; - let label = getButtonLabel.call(this, node); - - let reference; - if (node.hasAttribute("id")) { - reference = node.getAttribute("id"); - } - - let disabled; - if (node.hasAttribute("disabled") || node.disabled === true) { - disabled = true; - } - - if (!reference) { - reference = new ID("tab").toString(); - node.setAttribute("id", reference); - } - - if (node.hasAttribute(`${ATTRIBUTE_PREFIX}button-icon`)) { - label = `<span part="label">${label}</span><img part="icon" alt="this is an icon" src="${node.getAttribute( - `${ATTRIBUTE_PREFIX}button-icon`, - )}">`; - } - - let remove = false; - if (node.hasAttribute(`${ATTRIBUTE_PREFIX}removable`)) { - remove = true; - } - - if (node.matches(".active") === true && disabled !== true) { - node.classList.remove("active"); - activeReference = reference; - } - - const state = ""; - const classes = dimensionsCalculated ? "" : "invisible"; - - buttons.push({ - reference, - label, - state, - class: classes, - disabled, - remove, - }); - - attachTabMutationObserver.call(this, node); - } - - this.setOption("buttons.standard", clone(buttons)); - this.setOption("buttons.popper", []); - this.setOption("marker", random()); - - return adjustButtonVisibility.call(this).then(() => { - if (activeReference) { - return new Processing(() => { - const button = this.shadowRoot.querySelector( - `[${ATTRIBUTE_PREFIX}tab-reference="${activeReference}"]`, - ); - if (button instanceof HTMLButtonElement && button.disabled !== true) { - show.call(this, button); - } - }) - .run(undefined) - .then(() => {}) - .catch((e) => { - addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message); - }); - } - - return Promise.resolve(); - }); + if (!this.shadowRoot) { + throw new Error("no shadow-root is defined"); + } + + let activeReference; + + const dimensionsCalculated = this[dimensionsSymbol].getVia( + "data.calculated", + false, + ); + + const buttons = []; + const nodes = getSlottedElements.call(this, undefined, null); // null ↦ only unnamed slots + + for (const node of nodes) { + if (!(node instanceof HTMLElement)) continue; + let label = getButtonLabel.call(this, node); + + let reference; + if (node.hasAttribute("id")) { + reference = node.getAttribute("id"); + } + + let disabled; + if (node.hasAttribute("disabled") || node.disabled === true) { + disabled = true; + } + + if (!reference) { + reference = new ID("tab").toString(); + node.setAttribute("id", reference); + } + + if (node.hasAttribute(`${ATTRIBUTE_PREFIX}button-icon`)) { + label = `<span part="label">${label}</span><img part="icon" alt="this is an icon" src="${node.getAttribute( + `${ATTRIBUTE_PREFIX}button-icon`, + )}">`; + } + + let remove = false; + if (node.hasAttribute(`${ATTRIBUTE_PREFIX}removable`)) { + remove = true; + } + + if (node.matches(".active") === true && disabled !== true) { + node.classList.remove("active"); + activeReference = reference; + } + + const state = ""; + const classes = dimensionsCalculated ? "" : "invisible"; + + buttons.push({ + reference, + label, + state, + class: classes, + disabled, + remove, + }); + + attachTabMutationObserver.call(this, node); + } + + this.setOption("buttons.standard", clone(buttons)); + this.setOption("buttons.popper", []); + this.setOption("marker", random()); + + return adjustButtonVisibility.call(this).then(() => { + if (activeReference) { + return new Processing(() => { + const button = this.shadowRoot.querySelector( + `[${ATTRIBUTE_PREFIX}tab-reference="${activeReference}"]`, + ); + if (button instanceof HTMLButtonElement && button.disabled !== true) { + show.call(this, button); + } + }) + .run(undefined) + .then(() => { + }) + .catch((e) => { + addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message); + }); + } + + return Promise.resolve(); + }); } function checkAndRearrangeButtons() { - if (this[dimensionsSymbol].getVia("data.calculated", false) !== true) { - calculateNavigationButtonsDimensions.call(this); - } + if (this[dimensionsSymbol].getVia("data.calculated", false) !== true) { + calculateNavigationButtonsDimensions.call(this); + } - rearrangeButtons.call(this); + rearrangeButtons.call(this); } /** @@ -929,29 +958,29 @@ function checkAndRearrangeButtons() { * @return {Promise<unknown>} */ function adjustButtonVisibility() { - const self = this; + const self = this; - return new Promise((resolve) => { - const observer = new MutationObserver(function (mutations) { - const defCount = self.getOption("buttons.standard").length; - const domCount = self[navElementSymbol].querySelectorAll( - 'button[data-monster-role="button"]', - ).length; + return new Promise((resolve) => { + const observer = new MutationObserver(function (mutations) { + const defCount = self.getOption("buttons.standard").length; + const domCount = self[navElementSymbol].querySelectorAll( + 'button[data-monster-role="button"]', + ).length; - // in drawing - if (defCount !== domCount) return; + // in drawing + if (defCount !== domCount) return; - observer.disconnect(); + observer.disconnect(); - checkAndRearrangeButtons.call(self); + checkAndRearrangeButtons.call(self); - resolve(); - }); + resolve(); + }); - observer.observe(self[navElementSymbol], { - attributes: true, - }); - }); + observer.observe(self[navElementSymbol], { + attributes: true, + }); + }); } /** @@ -960,17 +989,17 @@ function adjustButtonVisibility() { * @return {number} */ function getDimValue(value) { - if ([undefined, null].indexOf(value) !== -1) { - return 0; - } + if ([undefined, null].indexOf(value) !== -1) { + return 0; + } - const valueAsInt = parseInt(value, 10); + const valueAsInt = parseInt(value, 10); - if (isNaN(valueAsInt)) { - return 0; - } + if (isNaN(valueAsInt)) { + return 0; + } - return valueAsInt; + return valueAsInt; } /** @@ -979,18 +1008,18 @@ function getDimValue(value) { * @return {number} */ function calcBoxWidth(node) { - const dim = getGlobal("window").getComputedStyle(node); - const bounding = node.getBoundingClientRect(); - - return ( - getDimValue(dim["border-left-width"]) + - getDimValue(dim["padding-left"]) + - getDimValue(dim["margin-left"]) + - getDimValue(bounding["width"]) + - getDimValue(dim["border-right-width"]) + - getDimValue(dim["margin-right"]) + - getDimValue(dim["padding-left"]) - ); + const dim = getGlobal("window").getComputedStyle(node); + const bounding = node.getBoundingClientRect(); + + return ( + getDimValue(dim["border-left-width"]) + + getDimValue(dim["padding-left"]) + + getDimValue(dim["margin-left"]) + + getDimValue(bounding["width"]) + + getDimValue(dim["border-right-width"]) + + getDimValue(dim["margin-right"]) + + getDimValue(dim["padding-left"]) + ); } /** @@ -998,35 +1027,35 @@ function calcBoxWidth(node) { * @return {Object} */ function rearrangeButtons() { - const standardButtons = []; - const popperButtons = []; - - let sum = 0; - const space = this[dimensionsSymbol].getVia("data.space"); - - const buttons = this.getOption("buttons.standard"); - for (const [, button] of buttons.entries()) { - const ref = button?.reference; - - sum += this[dimensionsSymbol].getVia(`data.button.${ref}`); - - if (sum > space) { - popperButtons.push(clone(button)); - } else { - standardButtons.push(clone(button)); - } - } - - this.setOption("buttons.standard", standardButtons); - this.setOption("buttons.popper", popperButtons); - - if (this[switchElementSymbol]) { - if (popperButtons.length > 0) { - this[switchElementSymbol].classList.remove("hidden"); - } else { - this[switchElementSymbol].classList.add("hidden"); - } - } + const standardButtons = []; + const popperButtons = []; + + let sum = 0; + const space = this[dimensionsSymbol].getVia("data.space"); + + const buttons = this.getOption("buttons.standard"); + for (const [, button] of buttons.entries()) { + const ref = button?.reference; + + sum += this[dimensionsSymbol].getVia(`data.button.${ref}`); + + if (sum > space) { + popperButtons.push(clone(button)); + } else { + standardButtons.push(clone(button)); + } + } + + this.setOption("buttons.standard", standardButtons); + this.setOption("buttons.popper", popperButtons); + + if (this[switchElementSymbol]) { + if (popperButtons.length > 0) { + this[switchElementSymbol].classList.remove("hidden"); + } else { + this[switchElementSymbol].classList.add("hidden"); + } + } } /** @@ -1034,50 +1063,50 @@ function rearrangeButtons() { * @return {Object} */ function calculateNavigationButtonsDimensions() { - const width = this[navElementSymbol].getBoundingClientRect().width; - - let startEndWidth = 0; - - getSlottedElements.call(this, undefined, "start").forEach((node) => { - startEndWidth += calcBoxWidth.call(this, node); - }); - - getSlottedElements.call(this, undefined, "end").forEach((node) => { - startEndWidth += calcBoxWidth.call(this, node); - }); - - this[dimensionsSymbol].setVia("data.space", width - startEndWidth - 2); - this[dimensionsSymbol].setVia("data.visible", !(width === 0)); - - const buttons = this.getOption("buttons.standard").concat( - this.getOption("buttons.popper"), - ); - - for (const [i, button] of buttons.entries()) { - const ref = button?.reference; - const element = this[navElementSymbol].querySelector( - `:scope > [${ATTRIBUTE_PREFIX}tab-reference="${ref}"]`, - ); - if (!(element instanceof HTMLButtonElement)) continue; - - this[dimensionsSymbol].setVia( - `data.button.${ref}`, - calcBoxWidth.call(this, element), - ); - button["class"] = new TokenList(button["class"]) - .remove("invisible") - .toString(); - } - - const slots = this[controlElementSymbol].querySelectorAll( - `nav[${ATTRIBUTE_PREFIX}role=nav] > slot.invisible, slot[${ATTRIBUTE_PREFIX}role=slot].invisible`, - ); - for (const [, slot] of slots.entries()) { - slot.classList.remove("invisible"); - } - - this[dimensionsSymbol].setVia("data.calculated", true); - this.setOption("buttons.standard", clone(buttons)); + const width = this[navElementSymbol].getBoundingClientRect().width; + + let startEndWidth = 0; + + getSlottedElements.call(this, undefined, "start").forEach((node) => { + startEndWidth += calcBoxWidth.call(this, node); + }); + + getSlottedElements.call(this, undefined, "end").forEach((node) => { + startEndWidth += calcBoxWidth.call(this, node); + }); + + this[dimensionsSymbol].setVia("data.space", width - startEndWidth - 2); + this[dimensionsSymbol].setVia("data.visible", !(width === 0)); + + const buttons = this.getOption("buttons.standard").concat( + this.getOption("buttons.popper"), + ); + + for (const [i, button] of buttons.entries()) { + const ref = button?.reference; + const element = this[navElementSymbol].querySelector( + `:scope > [${ATTRIBUTE_PREFIX}tab-reference="${ref}"]`, + ); + if (!(element instanceof HTMLButtonElement)) continue; + + this[dimensionsSymbol].setVia( + `data.button.${ref}`, + calcBoxWidth.call(this, element), + ); + button["class"] = new TokenList(button["class"]) + .remove("invisible") + .toString(); + } + + const slots = this[controlElementSymbol].querySelectorAll( + `nav[${ATTRIBUTE_PREFIX}role=nav] > slot.invisible, slot[${ATTRIBUTE_PREFIX}role=slot].invisible`, + ); + for (const [, slot] of slots.entries()) { + slot.classList.remove("invisible"); + } + + this[dimensionsSymbol].setVia("data.calculated", true); + this.setOption("buttons.standard", clone(buttons)); } /** @@ -1086,34 +1115,34 @@ function calculateNavigationButtonsDimensions() { * @return {string} */ function getButtonLabel(node) { - let label; - let setLabel = false; - if (node.hasAttribute(ATTRIBUTE_BUTTON_LABEL)) { - label = node.getAttribute(ATTRIBUTE_BUTTON_LABEL); - } else { - label = node.innerText; - setLabel = true; - } - - if (!isString(label)) { - label = ""; - } - - label = label.trim(); - - if (label === "") { - label = this.getOption("labels.new-tab-label", "New Tab"); - } - - if (label.length > 100) { - label = `${label.substring(0, 99)}…`; - } - - if (setLabel === true) { - node.setAttribute(ATTRIBUTE_BUTTON_LABEL, label); - } - - return label; + let label; + let setLabel = false; + if (node.hasAttribute(ATTRIBUTE_BUTTON_LABEL)) { + label = node.getAttribute(ATTRIBUTE_BUTTON_LABEL); + } else { + label = node.innerText; + setLabel = true; + } + + if (!isString(label)) { + label = ""; + } + + label = label.trim(); + + if (label === "") { + label = this.getOption("labels.new-tab-label", "New Tab"); + } + + if (label.length > 100) { + label = `${label.substring(0, 99)}…`; + } + + if (setLabel === true) { + node.setAttribute(ATTRIBUTE_BUTTON_LABEL, label); + } + + return label; } /** @@ -1121,8 +1150,8 @@ function getButtonLabel(node) { * @return {string} */ function getTemplate() { - // language=HTML - return ` + // language=HTML + return ` <template id="buttons"> <button part="button" data-monster-role="button"