@schukai/monster 3.91.0 → 3.92.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,55 +12,55 @@
12
12
  * SPDX-License-Identifier: AGPL-3.0
13
13
  */
14
14
 
15
- import { instanceSymbol } from "../../constants.mjs";
16
- import { createPopper } from "@popperjs/core";
17
- import { extend } from "../../data/extend.mjs";
18
- import { Pathfinder } from "../../data/pathfinder.mjs";
15
+ import {instanceSymbol} from "../../constants.mjs";
16
+ import {createPopper} from "@popperjs/core";
17
+ import {extend} from "../../data/extend.mjs";
18
+ import {Pathfinder} from "../../data/pathfinder.mjs";
19
19
  import {
20
- addAttributeToken,
21
- addToObjectLink,
22
- hasObjectLink,
20
+ addAttributeToken,
21
+ addToObjectLink,
22
+ hasObjectLink,
23
23
  } from "../../dom/attributes.mjs";
24
24
  import {
25
- ATTRIBUTE_ERRORMESSAGE,
26
- ATTRIBUTE_PREFIX,
27
- ATTRIBUTE_ROLE,
25
+ ATTRIBUTE_ERRORMESSAGE,
26
+ ATTRIBUTE_PREFIX,
27
+ ATTRIBUTE_ROLE,
28
28
  } from "../../dom/constants.mjs";
29
29
  import {
30
- assembleMethodSymbol,
31
- CustomElement,
32
- getSlottedElements,
33
- registerCustomElement,
30
+ assembleMethodSymbol,
31
+ CustomElement,
32
+ getSlottedElements,
33
+ registerCustomElement,
34
34
  } from "../../dom/customelement.mjs";
35
35
  import {
36
- findTargetElementFromEvent,
37
- fireCustomEvent,
36
+ findTargetElementFromEvent,
37
+ fireCustomEvent,
38
38
  } from "../../dom/events.mjs";
39
- import { getDocument } from "../../dom/util.mjs";
40
- import { random } from "../../math/random.mjs";
41
- import { getGlobal } from "../../types/global.mjs";
42
- import { ID } from "../../types/id.mjs";
43
- import { isArray, isString } from "../../types/is.mjs";
44
- import { TokenList } from "../../types/tokenlist.mjs";
45
- import { clone } from "../../util/clone.mjs";
46
- import { DeadMansSwitch } from "../../util/deadmansswitch.mjs";
47
- import { Processing } from "../../util/processing.mjs";
39
+ import {getDocument} from "../../dom/util.mjs";
40
+ import {random} from "../../math/random.mjs";
41
+ import {getGlobal} from "../../types/global.mjs";
42
+ import {ID} from "../../types/id.mjs";
43
+ import {isArray, isString} from "../../types/is.mjs";
44
+ import {TokenList} from "../../types/tokenlist.mjs";
45
+ import {clone} from "../../util/clone.mjs";
46
+ import {DeadMansSwitch} from "../../util/deadmansswitch.mjs";
47
+ import {Processing} from "../../util/processing.mjs";
48
48
  import {
49
- ATTRIBUTE_BUTTON_LABEL,
50
- ATTRIBUTE_FORM_RELOAD,
51
- ATTRIBUTE_FORM_URL,
52
- STYLE_DISPLAY_MODE_BLOCK,
49
+ ATTRIBUTE_BUTTON_LABEL,
50
+ ATTRIBUTE_FORM_RELOAD,
51
+ ATTRIBUTE_FORM_URL,
52
+ STYLE_DISPLAY_MODE_BLOCK,
53
53
  } from "../form/constants.mjs";
54
54
 
55
- import { TabsStyleSheet } from "./stylesheet/tabs.mjs";
56
- import { loadAndAssignContent } from "../form/util/fetch.mjs";
57
- import { ThemeStyleSheet } from "../stylesheet/theme.mjs";
55
+ import {TabsStyleSheet} from "./stylesheet/tabs.mjs";
56
+ import {loadAndAssignContent} from "../form/util/fetch.mjs";
57
+ import {ThemeStyleSheet} from "../stylesheet/theme.mjs";
58
58
  import {
59
- popperInstanceSymbol,
60
- setEventListenersModifiers,
59
+ popperInstanceSymbol,
60
+ setEventListenersModifiers,
61
61
  } from "../form/util/popper.mjs";
62
62
 
63
- export { Tabs };
63
+ export {Tabs};
64
64
 
65
65
  /**
66
66
  * @private
@@ -146,408 +146,414 @@ const resizeObserverSymbol = Symbol("resizeObserver");
146
146
  * @fragments /fragments/components/layout/tabs/
147
147
  *
148
148
  * @example /examples/components/layout/tabs-simple
149
+ * @example /examples/components/layout/tabs-active
150
+ * @example /examples/components/layout/tabs-removable
151
+ *
152
+ * @issue https://localhost.alvine.dev:8443/development/issues/closed/268.html
149
153
  *
150
154
  * @since 3.74.0
151
155
  * @copyright schukai GmbH
152
156
  * @summary This CustomControl creates a tab element with a variety of options.
153
157
  */
154
158
  class Tabs extends CustomElement {
155
- /**
156
- * This method is called by the `instanceof` operator.
157
- * @return {symbol}
158
- */
159
- static get [instanceSymbol]() {
160
- return Symbol.for("@schukai/monster/components/layout/tabs");
161
- }
162
-
163
- /**
164
- * To set the options via the HTML tag, the attribute `data-monster-options` must be used.
165
- * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
166
- *
167
- * The individual configuration values can be found in the table.
168
- *
169
- * @property {Object} templates Template definitions
170
- * @property {string} templates.main Main template
171
- * @property {Object} labels
172
- * @property {string} labels.new-tab-label="New Tab"
173
- * @property {Object} features
174
- * @property {number} features.openDelay=500 Open delay in milliseconds
175
- * @property {Object} fetch Fetch [see Using Fetch mozilla.org](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch)
176
- * @property {String} fetch.redirect=error
177
- * @property {String} fetch.method=GET
178
- * @property {String} fetch.mode=same-origin
179
- * @property {String} fetch.credentials=same-origin
180
- * @property {Object} fetch.headers={"accept":"text/html"}}
181
- * @property {Object} popper [PopperJS Options](https://popper.js.org/docs/v2/)
182
- * @property {string} popper.placement=bottom PopperJS placement
183
- * @property {Object[]} modifiers={name:offset} PopperJS placement
184
- */
185
- get defaults() {
186
- return Object.assign({}, super.defaults, {
187
- templates: {
188
- main: getTemplate(),
189
- },
190
- labels: {
191
- "new-tab-label": "New Tab",
192
- },
193
- buttons: {
194
- standard: [],
195
- popper: [],
196
- },
197
- fetch: {
198
- redirect: "error",
199
- method: "GET",
200
- mode: "same-origin",
201
- credentials: "same-origin",
202
- headers: {
203
- accept: "text/html",
204
- },
205
- },
206
-
207
- features: {
208
- openDelay: null,
209
- },
210
-
211
- classes: {
212
- button: "monster-theme-primary-1",
213
- popper: "monster-theme-primary-1",
214
- navigation: "monster-theme-primary-1",
215
- },
216
-
217
- popper: {
218
- placement: "bottom",
219
- modifiers: [
220
- {
221
- name: "offset",
222
- options: {
223
- offset: [0, 2],
224
- },
225
- },
226
-
227
- {
228
- name: "eventListeners",
229
- enabled: false,
230
- },
231
- ],
232
- },
233
- });
234
- }
235
-
236
- /**
237
- * This method is called internal and should not be called directly.
238
- */
239
- [assembleMethodSymbol]() {
240
- super[assembleMethodSymbol]();
241
-
242
- initControlReferences.call(this);
243
-
244
- this[dimensionsSymbol] = new Pathfinder({ data: {} });
245
-
246
- initEventHandler.call(this);
247
-
248
- // setup structure
249
- initTabButtons.call(this).then(() => {
250
- initPopperSwitch.call(this);
251
- initPopper.call(this);
252
- attachResizeObserver.call(this);
253
- attachTabChangeObserver.call(this);
254
- });
255
- }
256
-
257
- /**
258
- * This method is called internal and should not be called directly.
259
- *
260
- * @return {CSSStyleSheet[]}
261
- */
262
- static getCSSStyleSheet() {
263
- return [TabsStyleSheet];
264
- }
265
-
266
- /**
267
- * This method is called internal and should not be called directly.
268
- *
269
- * @return {string}
270
- */
271
- static getTag() {
272
- return "monster-tabs";
273
- }
274
-
275
- /**
276
- * A function that activates a tab based on the provided name.
277
- *
278
- * The tabs have to be named with the `data-monster-name` attribute.
279
- *
280
- * @param {type} idOrName - the name or id of the tab to activate
281
- * @return {Tabs} - The current instance
282
- */
283
- activeTab(idOrName) {
284
- let found = false;
285
-
286
- getSlottedElements.call(this).forEach((node) => {
287
- if (found === true) {
288
- return;
289
- }
290
-
291
- if (node.getAttribute("data-monster-name") === idOrName) {
292
- this.shadowRoot
293
- .querySelector(
294
- `[data-monster-tab-reference="${node.getAttribute("id")}"]`,
295
- )
296
- .click();
297
- found = true;
298
- }
299
-
300
- if (node.getAttribute("id") === idOrName) {
301
- this.shadowRoot
302
- .querySelector(
303
- `[data-monster-tab-reference="${node.getAttribute("id")}"]`,
304
- )
305
- .click();
306
- found = true;
307
- }
308
- });
309
-
310
- return this;
311
- }
312
-
313
- /**
314
- * A function that returns the name or id of the currently active tab.
315
- *
316
- * The tabs have to be named with the `data-monster-name` attribute.
317
- *
318
- * @return {string|null}
319
- */
320
- getActiveTab() {
321
- const nodes = getSlottedElements.call(this);
322
- for (const node of nodes) {
323
- if (node.matches(".active") === true) {
324
- if (node.hasAttribute("data-monster-name")) {
325
- return node.getAttribute("data-monster-name");
326
- }
327
-
328
- return node.getAttribute("id");
329
- }
330
- }
331
- return null;
332
- }
333
-
334
- /**
335
- * This method is called by the dom and should not be called directly.
336
- *
337
- * @return {void}
338
- */
339
- connectedCallback() {
340
- super.connectedCallback();
341
-
342
- const document = getDocument();
343
-
344
- for (const [, type] of Object.entries(["click", "touch"])) {
345
- // close on outside ui-events
346
- document.addEventListener(type, this[closeEventHandler]);
347
- }
348
- }
349
-
350
- /**
351
- * This method is called by the dom and should not be called directly.
352
- *
353
- * @return {void}
354
- */
355
- disconnectedCallback() {
356
- super.disconnectedCallback();
357
-
358
- const document = getDocument();
359
-
360
- // close on outside ui-events
361
- for (const [, type] of Object.entries(["click", "touch"])) {
362
- document.removeEventListener(type, this[closeEventHandler]);
363
- }
364
- }
159
+ /**
160
+ * This method is called by the `instanceof` operator.
161
+ * @return {symbol}
162
+ */
163
+ static get [instanceSymbol]() {
164
+ return Symbol.for("@schukai/monster/components/layout/tabs");
165
+ }
166
+
167
+ /**
168
+ * To set the options via the HTML tag, the attribute `data-monster-options` must be used.
169
+ * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
170
+ *
171
+ * The individual configuration values can be found in the table.
172
+ *
173
+ * @property {Object} templates Template definitions
174
+ * @property {string} templates.main Main template
175
+ * @property {Object} labels
176
+ * @property {string} labels.new-tab-label="New Tab"
177
+ * @property {Object} features
178
+ * @property {number} features.openDelay=500 Open delay in milliseconds
179
+ * @property {string} features.removeBehavior="auto" Remove behavior, auto (default), next, previous and none
180
+ * @property {Object} fetch Fetch [see Using Fetch mozilla.org](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch)
181
+ * @property {String} fetch.redirect=error
182
+ * @property {String} fetch.method=GET
183
+ * @property {String} fetch.mode=same-origin
184
+ * @property {String} fetch.credentials=same-origin
185
+ * @property {Object} fetch.headers={"accept":"text/html"}}
186
+ * @property {Object} popper [PopperJS Options](https://popper.js.org/docs/v2/)
187
+ * @property {string} popper.placement=bottom PopperJS placement
188
+ * @property {Object[]} modifiers={name:offset} PopperJS placement
189
+ */
190
+ get defaults() {
191
+ return Object.assign({}, super.defaults, {
192
+ templates: {
193
+ main: getTemplate(),
194
+ },
195
+ labels: {
196
+ "new-tab-label": "New Tab",
197
+ },
198
+ buttons: {
199
+ standard: [],
200
+ popper: [],
201
+ },
202
+ fetch: {
203
+ redirect: "error",
204
+ method: "GET",
205
+ mode: "same-origin",
206
+ credentials: "same-origin",
207
+ headers: {
208
+ accept: "text/html",
209
+ },
210
+ },
211
+
212
+ features: {
213
+ openDelay: null,
214
+ removeBehavior: "auto",
215
+ },
216
+
217
+ classes: {
218
+ button: "monster-theme-primary-1",
219
+ popper: "monster-theme-primary-1",
220
+ navigation: "monster-theme-primary-1",
221
+ },
222
+
223
+ popper: {
224
+ placement: "bottom",
225
+ modifiers: [
226
+ {
227
+ name: "offset",
228
+ options: {
229
+ offset: [0, 2],
230
+ },
231
+ },
232
+
233
+ {
234
+ name: "eventListeners",
235
+ enabled: false,
236
+ },
237
+ ],
238
+ },
239
+ });
240
+ }
241
+
242
+ /**
243
+ * This method is called internal and should not be called directly.
244
+ */
245
+ [assembleMethodSymbol]() {
246
+ super[assembleMethodSymbol]();
247
+
248
+ initControlReferences.call(this);
249
+
250
+ this[dimensionsSymbol] = new Pathfinder({data: {}});
251
+
252
+ initEventHandler.call(this);
253
+
254
+ // setup structure
255
+ initTabButtons.call(this).then(() => {
256
+ initPopperSwitch.call(this);
257
+ initPopper.call(this);
258
+ attachResizeObserver.call(this);
259
+ attachTabChangeObserver.call(this);
260
+ });
261
+ }
262
+
263
+ /**
264
+ * This method is called internal and should not be called directly.
265
+ *
266
+ * @return {CSSStyleSheet[]}
267
+ */
268
+ static getCSSStyleSheet() {
269
+ return [TabsStyleSheet];
270
+ }
271
+
272
+ /**
273
+ * This method is called internal and should not be called directly.
274
+ *
275
+ * @return {string}
276
+ */
277
+ static getTag() {
278
+ return "monster-tabs";
279
+ }
280
+
281
+ /**
282
+ * A function that activates a tab based on the provided name.
283
+ *
284
+ * The tabs have to be named with the `data-monster-name` attribute.
285
+ *
286
+ * @param {type} idOrName - the name or id of the tab to activate
287
+ * @return {Tabs} - The current instance
288
+ */
289
+ activeTab(idOrName) {
290
+ let found = false;
291
+
292
+ getSlottedElements.call(this).forEach((node) => {
293
+ if (found === true) {
294
+ return;
295
+ }
296
+
297
+ if (node.getAttribute("data-monster-name") === idOrName) {
298
+ this.shadowRoot
299
+ .querySelector(
300
+ `[data-monster-tab-reference="${node.getAttribute("id")}"]`,
301
+ )
302
+ .click();
303
+ found = true;
304
+ }
305
+
306
+ if (node.getAttribute("id") === idOrName) {
307
+ this.shadowRoot
308
+ .querySelector(
309
+ `[data-monster-tab-reference="${node.getAttribute("id")}"]`,
310
+ )
311
+ .click();
312
+ found = true;
313
+ }
314
+ });
315
+
316
+ return this;
317
+ }
318
+
319
+ /**
320
+ * A function that returns the name or id of the currently active tab.
321
+ *
322
+ * The tabs have to be named with the `data-monster-name` attribute.
323
+ *
324
+ * @return {string|null}
325
+ */
326
+ getActiveTab() {
327
+ const nodes = getSlottedElements.call(this);
328
+ for (const node of nodes) {
329
+ if (node.matches(".active") === true) {
330
+ if (node.hasAttribute("data-monster-name")) {
331
+ return node.getAttribute("data-monster-name");
332
+ }
333
+
334
+ return node.getAttribute("id");
335
+ }
336
+ }
337
+ return null;
338
+ }
339
+
340
+ /**
341
+ * This method is called by the dom and should not be called directly.
342
+ *
343
+ * @return {void}
344
+ */
345
+ connectedCallback() {
346
+ super.connectedCallback();
347
+
348
+ const document = getDocument();
349
+
350
+ for (const [, type] of Object.entries(["click", "touch"])) {
351
+ // close on outside ui-events
352
+ document.addEventListener(type, this[closeEventHandler]);
353
+ }
354
+ }
355
+
356
+ /**
357
+ * This method is called by the dom and should not be called directly.
358
+ *
359
+ * @return {void}
360
+ */
361
+ disconnectedCallback() {
362
+ super.disconnectedCallback();
363
+
364
+ const document = getDocument();
365
+
366
+ // close on outside ui-events
367
+ for (const [, type] of Object.entries(["click", "touch"])) {
368
+ document.removeEventListener(type, this[closeEventHandler]);
369
+ }
370
+ }
365
371
  }
366
372
 
367
373
  /**
368
374
  * @private
369
375
  */
370
376
  function initPopperSwitch() {
371
- const nodes = getSlottedElements.call(this, `[${ATTRIBUTE_ROLE}="switch"]`); // null ↦ only unnamed slots
372
- let switchButton;
373
- if (nodes.size === 0) {
374
- switchButton = document.createElement("button");
375
- switchButton.setAttribute(ATTRIBUTE_ROLE, "switch");
376
- switchButton.setAttribute("part", "switch");
377
- switchButton.classList.add("hidden");
378
- const classList = this.getOption("classes.button");
379
- if (classList) {
380
- switchButton.classList.add(classList);
381
- }
382
- switchButton.innerHTML =
383
- '<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>';
384
- this[navElementSymbol].prepend(switchButton);
385
- } else {
386
- switchButton = nodes.next();
387
- }
388
-
389
- /**
390
- * @param {Event} event
391
- */
392
- this[popperSwitchEventHandler] = (event) => {
393
- const element = findTargetElementFromEvent(event, ATTRIBUTE_ROLE, "switch");
394
-
395
- if (element instanceof HTMLButtonElement) {
396
- togglePopper.call(this);
397
- }
398
- };
399
-
400
- for (const type of ["click", "touch"]) {
401
- switchButton.addEventListener(type, this[popperSwitchEventHandler]);
402
- }
403
-
404
- this[switchElementSymbol] = switchButton;
377
+ const nodes = getSlottedElements.call(this, `[${ATTRIBUTE_ROLE}="switch"]`); // null ↦ only unnamed slots
378
+ let switchButton;
379
+ if (nodes.size === 0) {
380
+ switchButton = document.createElement("button");
381
+ switchButton.setAttribute(ATTRIBUTE_ROLE, "switch");
382
+ switchButton.setAttribute("part", "switch");
383
+ switchButton.classList.add("hidden");
384
+ const classList = this.getOption("classes.button");
385
+ if (classList) {
386
+ switchButton.classList.add(classList);
387
+ }
388
+ switchButton.innerHTML =
389
+ '<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>';
390
+ this[navElementSymbol].prepend(switchButton);
391
+ } else {
392
+ switchButton = nodes.next();
393
+ }
394
+
395
+ /**
396
+ * @param {Event} event
397
+ */
398
+ this[popperSwitchEventHandler] = (event) => {
399
+ const element = findTargetElementFromEvent(event, ATTRIBUTE_ROLE, "switch");
400
+
401
+ if (element instanceof HTMLButtonElement) {
402
+ togglePopper.call(this);
403
+ }
404
+ };
405
+
406
+ for (const type of ["click", "touch"]) {
407
+ switchButton.addEventListener(type, this[popperSwitchEventHandler]);
408
+ }
409
+
410
+ this[switchElementSymbol] = switchButton;
405
411
  }
406
412
 
407
413
  /**
408
414
  * @private
409
415
  */
410
416
  function hidePopper() {
411
- if (!this[popperInstanceSymbol]) {
412
- return;
413
- }
417
+ if (!this[popperInstanceSymbol]) {
418
+ return;
419
+ }
414
420
 
415
- this[popperElementSymbol].style.display = "none";
416
- // performance https://popper.js.org/docs/v2/tutorial/#performance
417
- setEventListenersModifiers.call(this, false);
421
+ this[popperElementSymbol].style.display = "none";
422
+ // performance https://popper.js.org/docs/v2/tutorial/#performance
423
+ setEventListenersModifiers.call(this, false);
418
424
  }
419
425
 
420
426
  /**
421
427
  * @private
422
428
  */
423
429
  function showPopper() {
424
- if (this[popperElementSymbol].style.display === STYLE_DISPLAY_MODE_BLOCK) {
425
- return;
426
- }
427
-
428
- this[popperElementSymbol].style.visibility = "hidden";
429
- this[popperElementSymbol].style.display = STYLE_DISPLAY_MODE_BLOCK;
430
- // performance https://popper.js.org/docs/v2/tutorial/#performance
431
- setEventListenersModifiers.call(this, true);
432
-
433
- this[popperInstanceSymbol].update();
434
-
435
- new Processing(() => {
436
- this[popperElementSymbol].style.removeProperty("visibility");
437
- })
438
- .run(undefined)
439
- .then(() => {})
440
- .catch((e) => {
441
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
442
- });
430
+ if (this[popperElementSymbol].style.display === STYLE_DISPLAY_MODE_BLOCK) {
431
+ return;
432
+ }
433
+
434
+ this[popperElementSymbol].style.visibility = "hidden";
435
+ this[popperElementSymbol].style.display = STYLE_DISPLAY_MODE_BLOCK;
436
+ // performance https://popper.js.org/docs/v2/tutorial/#performance
437
+ setEventListenersModifiers.call(this, true);
438
+
439
+ this[popperInstanceSymbol].update();
440
+
441
+ new Processing(() => {
442
+ this[popperElementSymbol].style.removeProperty("visibility");
443
+ })
444
+ .run(undefined)
445
+ .then(() => {
446
+ })
447
+ .catch((e) => {
448
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
449
+ });
443
450
  }
444
451
 
445
452
  /**
446
453
  * @private
447
454
  */
448
455
  function togglePopper() {
449
- if (this[popperElementSymbol].style.display === STYLE_DISPLAY_MODE_BLOCK) {
450
- hidePopper.call(this);
451
- } else {
452
- showPopper.call(this);
453
- }
456
+ if (this[popperElementSymbol].style.display === STYLE_DISPLAY_MODE_BLOCK) {
457
+ hidePopper.call(this);
458
+ } else {
459
+ showPopper.call(this);
460
+ }
454
461
  }
455
462
 
456
463
  /**
457
464
  * @private
458
465
  */
459
466
  function attachResizeObserver() {
460
- // against flickering
461
- this[resizeObserverSymbol] = new ResizeObserver((entries) => {
462
- if (this[timerCallbackSymbol] instanceof DeadMansSwitch) {
463
- try {
464
- this[timerCallbackSymbol].touch();
465
- return;
466
- } catch (e) {
467
- delete this[timerCallbackSymbol];
468
- }
469
- }
470
-
471
- this[timerCallbackSymbol] = new DeadMansSwitch(200, () => {
472
- this[dimensionsSymbol].setVia("data.calculated", false);
473
- checkAndRearrangeButtons.call(this);
474
- });
475
- });
476
-
477
- this[resizeObserverSymbol].observe(this[navElementSymbol]);
467
+ // against flickering
468
+ this[resizeObserverSymbol] = new ResizeObserver((entries) => {
469
+ if (this[timerCallbackSymbol] instanceof DeadMansSwitch) {
470
+ try {
471
+ this[timerCallbackSymbol].touch();
472
+ return;
473
+ } catch (e) {
474
+ delete this[timerCallbackSymbol];
475
+ }
476
+ }
477
+
478
+ this[timerCallbackSymbol] = new DeadMansSwitch(200, () => {
479
+ this[dimensionsSymbol].setVia("data.calculated", false);
480
+ checkAndRearrangeButtons.call(this);
481
+ });
482
+ });
483
+
484
+ this[resizeObserverSymbol].observe(this[navElementSymbol]);
478
485
  }
479
486
 
480
487
  /**
481
488
  * @private
482
489
  */
483
490
  function attachTabChangeObserver() {
484
- // against flickering
485
- new MutationObserver((mutations) => {
486
- let runUpdate = false;
487
-
488
- for (const mutation of mutations) {
489
- if (mutation.type === "childList") {
490
- if (
491
- mutation.addedNodes.length > 0 ||
492
- mutation.removedNodes.length > 0
493
- ) {
494
- runUpdate = true;
495
- break;
496
- }
497
- }
498
- }
499
-
500
- if (runUpdate === true) {
501
- this[dimensionsSymbol].setVia("data.calculated", false);
502
- initTabButtons.call(this);
503
- }
504
- }).observe(this, {
505
- childList: true,
506
- });
491
+ // against flickering
492
+ new MutationObserver((mutations) => {
493
+ let runUpdate = false;
494
+
495
+ for (const mutation of mutations) {
496
+ if (mutation.type === "childList") {
497
+ if (
498
+ mutation.addedNodes.length > 0 ||
499
+ mutation.removedNodes.length > 0
500
+ ) {
501
+ runUpdate = true;
502
+ break;
503
+ }
504
+ }
505
+ }
506
+
507
+ if (runUpdate === true) {
508
+ this[dimensionsSymbol].setVia("data.calculated", false);
509
+ initTabButtons.call(this);
510
+ }
511
+ }).observe(this, {
512
+ childList: true,
513
+ });
507
514
  }
508
515
 
509
516
  /**
510
517
  * @private
511
518
  * @return {Select}
512
519
  * @external "external:createPopper"
513
- * @see {@link Plugins}
514
520
  */
515
521
  function initPopper() {
516
- const self = this;
517
-
518
- const options = extend({}, self.getOption("popper"));
519
-
520
- self[popperInstanceSymbol] = createPopper(
521
- self[switchElementSymbol],
522
- self[popperElementSymbol],
523
- options,
524
- );
525
-
526
- const observer1 = new MutationObserver(function (mutations) {
527
- let runUpdate = false;
528
- for (const mutation of mutations) {
529
- if (mutation.type === "childList") {
530
- if (
531
- mutation.addedNodes.length > 0 ||
532
- mutation.removedNodes.length > 0
533
- ) {
534
- runUpdate = true;
535
- break;
536
- }
537
- }
538
- }
539
-
540
- if (runUpdate === true) {
541
- self[popperInstanceSymbol].update();
542
- }
543
- });
544
-
545
- observer1.observe(self[popperNavElementSymbol], {
546
- childList: true,
547
- subtree: true,
548
- });
549
-
550
- return self;
522
+ const self = this;
523
+
524
+ const options = extend({}, self.getOption("popper"));
525
+
526
+ self[popperInstanceSymbol] = createPopper(
527
+ self[switchElementSymbol],
528
+ self[popperElementSymbol],
529
+ options,
530
+ );
531
+
532
+ const observer1 = new MutationObserver(function (mutations) {
533
+ let runUpdate = false;
534
+ for (const mutation of mutations) {
535
+ if (mutation.type === "childList") {
536
+ if (
537
+ mutation.addedNodes.length > 0 ||
538
+ mutation.removedNodes.length > 0
539
+ ) {
540
+ runUpdate = true;
541
+ break;
542
+ }
543
+ }
544
+ }
545
+
546
+ if (runUpdate === true) {
547
+ self[popperInstanceSymbol].update();
548
+ }
549
+ });
550
+
551
+ observer1.observe(self[popperNavElementSymbol], {
552
+ childList: true,
553
+ subtree: true,
554
+ });
555
+
556
+ return self;
551
557
  }
552
558
 
553
559
  /**
@@ -555,172 +561,229 @@ function initPopper() {
555
561
  * @param {HTMLElement} element
556
562
  */
557
563
  function show(element) {
558
- if (!this.shadowRoot) {
559
- throw new Error("no shadow-root is defined");
560
- }
561
-
562
- const reference = element.getAttribute(`${ATTRIBUTE_PREFIX}tab-reference`);
563
-
564
- const nodes = getSlottedElements.call(this);
565
- for (const node of nodes) {
566
- const id = node.getAttribute("id");
567
-
568
- if (id === reference) {
569
- node.classList.add("active");
570
-
571
- const openDelay = parseInt(this.getOption("features.openDelay"), 10);
572
-
573
- if (!isNaN(openDelay) && openDelay > 0) {
574
- node.style.visibility = "hidden";
575
-
576
- setTimeout(() => {
577
- node.style.visibility = "visible";
578
- }, openDelay);
579
- }
580
-
581
- // get all data- from button and filter out data-monster-attributes and data-monster-insert
582
- const data = {};
583
- const mask = [
584
- "data-monster-attributes",
585
- "data-monster-insert-reference",
586
- "data-monster-state",
587
- "data-monster-button-label",
588
- "data-monster-objectlink",
589
- "data-monster-role",
590
- ];
591
-
592
- for (const [, attr] of Object.entries(node.attributes)) {
593
- if (attr.name.startsWith("data-") && mask.indexOf(attr.name) === -1) {
594
- data[attr.name] = attr.value;
595
- }
596
- }
597
-
598
- if (node.hasAttribute(ATTRIBUTE_FORM_URL)) {
599
- const url = node.getAttribute(ATTRIBUTE_FORM_URL);
600
-
601
- if (
602
- !node.hasAttribute(ATTRIBUTE_FORM_RELOAD) ||
603
- node.getAttribute(ATTRIBUTE_FORM_RELOAD).toLowerCase() === "onshow"
604
- ) {
605
- node.removeAttribute(ATTRIBUTE_FORM_URL);
606
- }
607
-
608
- const options = this.getOption("fetch", {});
609
- const filter = undefined;
610
- loadAndAssignContent(node, url, options, filter)
611
- .then(() => {
612
- fireCustomEvent(this, "monster-tab-changed", {
613
- reference,
614
- });
615
- })
616
- .catch((e) => {
617
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
618
- });
619
- } else {
620
- fireCustomEvent(this, "monster-tab-changed", {
621
- reference,
622
- data,
623
- });
624
- }
625
- } else {
626
- node.classList.remove("active");
627
- }
628
- }
629
-
630
- const standardButtons = this.getOption("buttons.standard");
631
- for (const index in standardButtons) {
632
- const button = standardButtons[index];
633
- const state = button["reference"] === reference ? "active" : "inactive";
634
- this.setOption(`buttons.standard.${index}.state`, state);
635
- }
636
-
637
- const popperButton = this.getOption("buttons.popper");
638
- for (const index in popperButton) {
639
- const button = popperButton[index];
640
- const state = button["reference"] === reference ? "active" : "inactive";
641
- this.setOption(`buttons.popper.${index}.state`, state);
642
- }
643
-
644
- hidePopper.call(this);
564
+ if (!this.shadowRoot) {
565
+ throw new Error("no shadow-root is defined");
566
+ }
567
+
568
+ const reference = element.getAttribute(`${ATTRIBUTE_PREFIX}tab-reference`);
569
+
570
+ const nodes = getSlottedElements.call(this);
571
+ for (const node of nodes) {
572
+ const id = node.getAttribute("id");
573
+
574
+ if (id === reference) {
575
+ node.classList.add("active");
576
+
577
+ const openDelay = parseInt(this.getOption("features.openDelay"), 10);
578
+
579
+ if (!isNaN(openDelay) && openDelay > 0) {
580
+ node.style.visibility = "hidden";
581
+
582
+ setTimeout(() => {
583
+ node.style.visibility = "visible";
584
+ }, openDelay);
585
+ }
586
+
587
+ // get all data- from button and filter out data-monster-attributes and data-monster-insert
588
+ const data = {};
589
+ const mask = [
590
+ "data-monster-attributes",
591
+ "data-monster-insert-reference",
592
+ "data-monster-state",
593
+ "data-monster-button-label",
594
+ "data-monster-objectlink",
595
+ "data-monster-role",
596
+ ];
597
+
598
+ for (const [, attr] of Object.entries(node.attributes)) {
599
+ if (attr.name.startsWith("data-") && mask.indexOf(attr.name) === -1) {
600
+ data[attr.name] = attr.value;
601
+ }
602
+ }
603
+
604
+ if (node.hasAttribute(ATTRIBUTE_FORM_URL)) {
605
+ const url = node.getAttribute(ATTRIBUTE_FORM_URL);
606
+
607
+ if (
608
+ !node.hasAttribute(ATTRIBUTE_FORM_RELOAD) ||
609
+ node.getAttribute(ATTRIBUTE_FORM_RELOAD).toLowerCase() === "onshow"
610
+ ) {
611
+ node.removeAttribute(ATTRIBUTE_FORM_URL);
612
+ }
613
+
614
+ const options = this.getOption("fetch", {});
615
+ const filter = undefined;
616
+ loadAndAssignContent(node, url, options, filter)
617
+ .then(() => {
618
+ fireCustomEvent(this, "monster-tab-changed", {
619
+ reference,
620
+ });
621
+ })
622
+ .catch((e) => {
623
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
624
+ });
625
+ } else {
626
+ fireCustomEvent(this, "monster-tab-changed", {
627
+ reference,
628
+ data,
629
+ });
630
+ }
631
+ } else {
632
+ node.classList.remove("active");
633
+ }
634
+ }
635
+
636
+ const standardButtons = this.getOption("buttons.standard");
637
+ for (const index in standardButtons) {
638
+ const button = standardButtons[index];
639
+ const state = button["reference"] === reference ? "active" : "inactive";
640
+ this.setOption(`buttons.standard.${index}.state`, state);
641
+ }
642
+
643
+ const popperButton = this.getOption("buttons.popper");
644
+ for (const index in popperButton) {
645
+ const button = popperButton[index];
646
+ const state = button["reference"] === reference ? "active" : "inactive";
647
+ this.setOption(`buttons.popper.${index}.state`, state);
648
+ }
649
+
650
+ hidePopper.call(this);
645
651
  }
646
652
 
647
653
  /**
648
654
  * @private
649
655
  */
650
656
  function initEventHandler() {
651
- if (!this.shadowRoot) {
652
- throw new Error("no shadow-root is defined");
653
- }
654
-
655
- /**
656
- * @param {Event} event
657
- */
658
- this[changeTabEventHandler] = (event) => {
659
- const element = findTargetElementFromEvent(event, ATTRIBUTE_ROLE, "button");
660
-
661
- if (element instanceof HTMLButtonElement && element.disabled !== true) {
662
- show.call(this, element);
663
- }
664
- };
665
-
666
- /**
667
- * @param {Event} event
668
- * @fires monster-tab-remove
669
- */
670
- this[removeTabEventHandler] = (event) => {
671
- const element = findTargetElementFromEvent(
672
- event,
673
- ATTRIBUTE_ROLE,
674
- "remove-tab",
675
- );
676
-
677
- if (element instanceof HTMLElement) {
678
- const button = findTargetElementFromEvent(
679
- event,
680
- ATTRIBUTE_ROLE,
681
- "button",
682
- );
683
-
684
- if (button instanceof HTMLButtonElement && button.disabled !== true) {
685
- const reference = button.getAttribute(
686
- `${ATTRIBUTE_PREFIX}tab-reference`,
687
- );
688
- if (reference) {
689
- const container = this.querySelector(`[id=${reference}]`);
690
- if (container instanceof HTMLElement) {
691
- container.remove();
692
- initTabButtons.call(this);
693
- fireCustomEvent(this, "monster-tab-remove", {
694
- reference,
695
- });
696
- }
697
- }
698
- }
699
- }
700
- };
701
-
702
- this[navElementSymbol].addEventListener("touch", this[changeTabEventHandler]);
703
- this[navElementSymbol].addEventListener("click", this[changeTabEventHandler]);
704
-
705
- this[navElementSymbol].addEventListener("touch", this[removeTabEventHandler]);
706
- this[navElementSymbol].addEventListener("click", this[removeTabEventHandler]);
707
-
708
- /**
709
- * @param {Event} event
710
- */
711
- this[closeEventHandler] = (event) => {
712
- const path = event.composedPath();
713
-
714
- for (const [, element] of Object.entries(path)) {
715
- if (element === this) {
716
- return;
717
- }
718
- }
719
-
720
- hidePopper.call(this);
721
- };
722
-
723
- return this;
657
+ const self = this;
658
+
659
+ if (!this.shadowRoot) {
660
+ throw new Error("no shadow-root is defined");
661
+ }
662
+
663
+ /**
664
+ * @param {Event} event
665
+ * @fires monster-tab-remove
666
+ */
667
+ this[removeTabEventHandler] = (event) => {
668
+
669
+ const element = findTargetElementFromEvent(
670
+ event,
671
+ ATTRIBUTE_ROLE,
672
+ "remove-tab",
673
+ );
674
+
675
+ if (element instanceof HTMLElement) {
676
+ const button = findTargetElementFromEvent(
677
+ event,
678
+ ATTRIBUTE_ROLE,
679
+ "button",
680
+ );
681
+
682
+ if (button instanceof HTMLButtonElement && button.disabled !== true) {
683
+ const reference = button.getAttribute(
684
+ `${ATTRIBUTE_PREFIX}tab-reference`,
685
+ );
686
+
687
+ let doChange = false;
688
+ let nextName = null
689
+ let previousName = null;
690
+
691
+ const btn = this.getOption("buttons")
692
+ for (let i = 0; i < btn.standard.length; i++) {
693
+ if (btn.standard[i].reference === reference) {
694
+ if (btn.standard[i].state === "active") {
695
+ doChange = i
696
+ if (i < btn.standard.length - 1) {
697
+ nextName = btn.standard[i + 1]?.reference
698
+ }
699
+ if (i > 0) {
700
+ previousName = btn.standard[i - 1]?.reference
701
+ }
702
+ }
703
+ break;
704
+ }
705
+ }
706
+
707
+ if (reference) {
708
+ const container = this.querySelector(`[id=${reference}]`);
709
+ if (container instanceof HTMLElement) {
710
+
711
+ if (doChange) {
712
+
713
+ switch (this.getOption("features.removeBehavior")) {
714
+ case "auto":
715
+ if (nextName !== null) {
716
+ self.activeTab(nextName);
717
+ } else {
718
+ if (previousName !== null) {
719
+ self.activeTab(previousName);
720
+ }
721
+ }
722
+ break;
723
+ case "next":
724
+ if (nextName !== null) {
725
+ self.activeTab(nextName);
726
+ }
727
+ break;
728
+ case "previous":
729
+ if (previousName !== null) {
730
+ self.activeTab(previousName);
731
+ }
732
+ break;
733
+
734
+ default: // and "none"
735
+ break;
736
+ }
737
+ }
738
+
739
+ container.remove();
740
+ initTabButtons.call(this);
741
+ fireCustomEvent(this, "monster-tab-remove", {
742
+ reference,
743
+ });
744
+ }
745
+ }
746
+
747
+ }
748
+ }
749
+ };
750
+
751
+ /**
752
+ * @param {Event} event
753
+ */
754
+ this[changeTabEventHandler] = (event) => {
755
+
756
+ const element = findTargetElementFromEvent(event, ATTRIBUTE_ROLE, "button");
757
+
758
+ if (element instanceof HTMLButtonElement && element.disabled !== true) {
759
+ show.call(this, element);
760
+ }
761
+ };
762
+
763
+ /**
764
+ * @param {Event} event
765
+ */
766
+ this[closeEventHandler] = (event) => {
767
+ const path = event.composedPath();
768
+
769
+ for (const [, element] of Object.entries(path)) {
770
+ if (element === this) {
771
+ return;
772
+ }
773
+ }
774
+
775
+ hidePopper.call(this);
776
+ };
777
+
778
+
779
+ // the order is important, because the remove must be before the change
780
+ this[navElementSymbol].addEventListener("touch", this[removeTabEventHandler]);
781
+ this[navElementSymbol].addEventListener("click", this[removeTabEventHandler]);
782
+
783
+ this[navElementSymbol].addEventListener("touch", this[changeTabEventHandler]);
784
+ this[navElementSymbol].addEventListener("click", this[changeTabEventHandler]);
785
+
786
+ return this;
724
787
  }
725
788
 
726
789
  /**
@@ -728,37 +791,37 @@ function initEventHandler() {
728
791
  * @param observedNode
729
792
  */
730
793
  function attachTabMutationObserver(observedNode) {
731
- const self = this;
732
-
733
- if (hasObjectLink(observedNode, mutationObserverSymbol)) {
734
- return;
735
- }
736
-
737
- /**
738
- * this construct monitors a node whether it is disabled or modified
739
- * @type {MutationObserver}
740
- */
741
- const observer = new MutationObserver(function (mutations) {
742
- if (isArray(mutations)) {
743
- const mutation = mutations.pop();
744
- if (mutation instanceof MutationRecord) {
745
- initTabButtons.call(self);
746
- }
747
- }
748
- });
749
-
750
- observer.observe(observedNode, {
751
- childList: false,
752
- attributes: true,
753
- subtree: false,
754
- attributeFilter: [
755
- "disabled",
756
- ATTRIBUTE_BUTTON_LABEL,
757
- `${ATTRIBUTE_PREFIX}button-icon`,
758
- ],
759
- });
760
-
761
- addToObjectLink(observedNode, mutationObserverSymbol, observer);
794
+ const self = this;
795
+
796
+ if (hasObjectLink(observedNode, mutationObserverSymbol)) {
797
+ return;
798
+ }
799
+
800
+ /**
801
+ * this construct monitors a node whether it is disabled or modified
802
+ * @type {MutationObserver}
803
+ */
804
+ const observer = new MutationObserver(function (mutations) {
805
+ if (isArray(mutations)) {
806
+ const mutation = mutations.pop();
807
+ if (mutation instanceof MutationRecord) {
808
+ initTabButtons.call(self);
809
+ }
810
+ }
811
+ });
812
+
813
+ observer.observe(observedNode, {
814
+ childList: false,
815
+ attributes: true,
816
+ subtree: false,
817
+ attributeFilter: [
818
+ "disabled",
819
+ ATTRIBUTE_BUTTON_LABEL,
820
+ `${ATTRIBUTE_PREFIX}button-icon`,
821
+ ],
822
+ });
823
+
824
+ addToObjectLink(observedNode, mutationObserverSymbol, observer);
762
825
  }
763
826
 
764
827
  /**
@@ -767,22 +830,22 @@ function attachTabMutationObserver(observedNode) {
767
830
  * @throws {Error} no shadow-root is defined
768
831
  */
769
832
  function initControlReferences() {
770
- if (!this.shadowRoot) {
771
- throw new Error("no shadow-root is defined");
772
- }
773
-
774
- this[controlElementSymbol] = this.shadowRoot.querySelector(
775
- `[${ATTRIBUTE_ROLE}=control]`,
776
- );
777
- this[navElementSymbol] = this.shadowRoot.querySelector(
778
- `nav[${ATTRIBUTE_ROLE}=nav]`,
779
- );
780
- this[popperElementSymbol] = this.shadowRoot.querySelector(
781
- `[${ATTRIBUTE_ROLE}=popper]`,
782
- );
783
- this[popperNavElementSymbol] = this.shadowRoot.querySelector(
784
- `[${ATTRIBUTE_ROLE}=popper-nav]`,
785
- );
833
+ if (!this.shadowRoot) {
834
+ throw new Error("no shadow-root is defined");
835
+ }
836
+
837
+ this[controlElementSymbol] = this.shadowRoot.querySelector(
838
+ `[${ATTRIBUTE_ROLE}=control]`,
839
+ );
840
+ this[navElementSymbol] = this.shadowRoot.querySelector(
841
+ `nav[${ATTRIBUTE_ROLE}=nav]`,
842
+ );
843
+ this[popperElementSymbol] = this.shadowRoot.querySelector(
844
+ `[${ATTRIBUTE_ROLE}=popper]`,
845
+ );
846
+ this[popperNavElementSymbol] = this.shadowRoot.querySelector(
847
+ `[${ATTRIBUTE_ROLE}=popper-nav]`,
848
+ );
786
849
  }
787
850
 
788
851
  /**
@@ -792,101 +855,102 @@ function initControlReferences() {
792
855
  *
793
856
  */
794
857
  function initTabButtons() {
795
- if (!this.shadowRoot) {
796
- throw new Error("no shadow-root is defined");
797
- }
798
-
799
- let activeReference;
800
-
801
- const dimensionsCalculated = this[dimensionsSymbol].getVia(
802
- "data.calculated",
803
- false,
804
- );
805
-
806
- const buttons = [];
807
- const nodes = getSlottedElements.call(this, undefined, null); // null ↦ only unnamed slots
808
-
809
- for (const node of nodes) {
810
- if (!(node instanceof HTMLElement)) continue;
811
- let label = getButtonLabel.call(this, node);
812
-
813
- let reference;
814
- if (node.hasAttribute("id")) {
815
- reference = node.getAttribute("id");
816
- }
817
-
818
- let disabled;
819
- if (node.hasAttribute("disabled") || node.disabled === true) {
820
- disabled = true;
821
- }
822
-
823
- if (!reference) {
824
- reference = new ID("tab").toString();
825
- node.setAttribute("id", reference);
826
- }
827
-
828
- if (node.hasAttribute(`${ATTRIBUTE_PREFIX}button-icon`)) {
829
- label = `<span part="label">${label}</span><img part="icon" src="${node.getAttribute(
830
- `${ATTRIBUTE_PREFIX}button-icon`,
831
- )}">`;
832
- }
833
-
834
- let remove = false;
835
- if (node.hasAttribute(`${ATTRIBUTE_PREFIX}removable`)) {
836
- remove = true;
837
- }
838
-
839
- if (node.matches(".active") === true && disabled !== true) {
840
- node.classList.remove("active");
841
- activeReference = reference;
842
- }
843
-
844
- const state = "";
845
- const classes = dimensionsCalculated ? "" : "invisible";
846
-
847
- buttons.push({
848
- reference,
849
- label,
850
- state,
851
- class: classes,
852
- disabled,
853
- remove,
854
- });
855
-
856
- attachTabMutationObserver.call(this, node);
857
- }
858
-
859
- this.setOption("buttons.standard", clone(buttons));
860
- this.setOption("buttons.popper", []);
861
- this.setOption("marker", random());
862
-
863
- return adjustButtonVisibility.call(this).then(() => {
864
- if (activeReference) {
865
- return new Processing(() => {
866
- const button = this.shadowRoot.querySelector(
867
- `[${ATTRIBUTE_PREFIX}tab-reference="${activeReference}"]`,
868
- );
869
- if (button instanceof HTMLButtonElement && button.disabled !== true) {
870
- show.call(this, button);
871
- }
872
- })
873
- .run(undefined)
874
- .then(() => {})
875
- .catch((e) => {
876
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
877
- });
878
- }
879
-
880
- return Promise.resolve();
881
- });
858
+ if (!this.shadowRoot) {
859
+ throw new Error("no shadow-root is defined");
860
+ }
861
+
862
+ let activeReference;
863
+
864
+ const dimensionsCalculated = this[dimensionsSymbol].getVia(
865
+ "data.calculated",
866
+ false,
867
+ );
868
+
869
+ const buttons = [];
870
+ const nodes = getSlottedElements.call(this, undefined, null); // null ↦ only unnamed slots
871
+
872
+ for (const node of nodes) {
873
+ if (!(node instanceof HTMLElement)) continue;
874
+ let label = getButtonLabel.call(this, node);
875
+
876
+ let reference;
877
+ if (node.hasAttribute("id")) {
878
+ reference = node.getAttribute("id");
879
+ }
880
+
881
+ let disabled;
882
+ if (node.hasAttribute("disabled") || node.disabled === true) {
883
+ disabled = true;
884
+ }
885
+
886
+ if (!reference) {
887
+ reference = new ID("tab").toString();
888
+ node.setAttribute("id", reference);
889
+ }
890
+
891
+ if (node.hasAttribute(`${ATTRIBUTE_PREFIX}button-icon`)) {
892
+ label = `<span part="label">${label}</span><img part="icon" alt="this is an icon" src="${node.getAttribute(
893
+ `${ATTRIBUTE_PREFIX}button-icon`,
894
+ )}">`;
895
+ }
896
+
897
+ let remove = false;
898
+ if (node.hasAttribute(`${ATTRIBUTE_PREFIX}removable`)) {
899
+ remove = true;
900
+ }
901
+
902
+ if (node.matches(".active") === true && disabled !== true) {
903
+ node.classList.remove("active");
904
+ activeReference = reference;
905
+ }
906
+
907
+ const state = "";
908
+ const classes = dimensionsCalculated ? "" : "invisible";
909
+
910
+ buttons.push({
911
+ reference,
912
+ label,
913
+ state,
914
+ class: classes,
915
+ disabled,
916
+ remove,
917
+ });
918
+
919
+ attachTabMutationObserver.call(this, node);
920
+ }
921
+
922
+ this.setOption("buttons.standard", clone(buttons));
923
+ this.setOption("buttons.popper", []);
924
+ this.setOption("marker", random());
925
+
926
+ return adjustButtonVisibility.call(this).then(() => {
927
+ if (activeReference) {
928
+ return new Processing(() => {
929
+ const button = this.shadowRoot.querySelector(
930
+ `[${ATTRIBUTE_PREFIX}tab-reference="${activeReference}"]`,
931
+ );
932
+ if (button instanceof HTMLButtonElement && button.disabled !== true) {
933
+ show.call(this, button);
934
+ }
935
+ })
936
+ .run(undefined)
937
+ .then(() => {
938
+ })
939
+ .catch((e) => {
940
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
941
+ });
942
+ }
943
+
944
+ return Promise.resolve();
945
+ });
882
946
  }
883
947
 
884
948
  function checkAndRearrangeButtons() {
885
- if (this[dimensionsSymbol].getVia("data.calculated", false) !== true) {
886
- calculateNavigationButtonsDimensions.call(this);
887
- }
949
+ if (this[dimensionsSymbol].getVia("data.calculated", false) !== true) {
950
+ calculateNavigationButtonsDimensions.call(this);
951
+ }
888
952
 
889
- rearrangeButtons.call(this);
953
+ rearrangeButtons.call(this);
890
954
  }
891
955
 
892
956
  /**
@@ -894,29 +958,29 @@ function checkAndRearrangeButtons() {
894
958
  * @return {Promise<unknown>}
895
959
  */
896
960
  function adjustButtonVisibility() {
897
- const self = this;
961
+ const self = this;
898
962
 
899
- return new Promise((resolve) => {
900
- const observer = new MutationObserver(function (mutations) {
901
- const defCount = self.getOption("buttons.standard").length;
902
- const domCount = self[navElementSymbol].querySelectorAll(
903
- 'button[data-monster-role="button"]',
904
- ).length;
963
+ return new Promise((resolve) => {
964
+ const observer = new MutationObserver(function (mutations) {
965
+ const defCount = self.getOption("buttons.standard").length;
966
+ const domCount = self[navElementSymbol].querySelectorAll(
967
+ 'button[data-monster-role="button"]',
968
+ ).length;
905
969
 
906
- // in drawing
907
- if (defCount !== domCount) return;
970
+ // in drawing
971
+ if (defCount !== domCount) return;
908
972
 
909
- observer.disconnect();
973
+ observer.disconnect();
910
974
 
911
- checkAndRearrangeButtons.call(self);
975
+ checkAndRearrangeButtons.call(self);
912
976
 
913
- resolve();
914
- });
977
+ resolve();
978
+ });
915
979
 
916
- observer.observe(self[navElementSymbol], {
917
- attributes: true,
918
- });
919
- });
980
+ observer.observe(self[navElementSymbol], {
981
+ attributes: true,
982
+ });
983
+ });
920
984
  }
921
985
 
922
986
  /**
@@ -925,17 +989,17 @@ function adjustButtonVisibility() {
925
989
  * @return {number}
926
990
  */
927
991
  function getDimValue(value) {
928
- if ([undefined, null].indexOf(value) !== -1) {
929
- return 0;
930
- }
992
+ if ([undefined, null].indexOf(value) !== -1) {
993
+ return 0;
994
+ }
931
995
 
932
- const valueAsInt = parseInt(value, 10);
996
+ const valueAsInt = parseInt(value, 10);
933
997
 
934
- if (isNaN(valueAsInt)) {
935
- return 0;
936
- }
998
+ if (isNaN(valueAsInt)) {
999
+ return 0;
1000
+ }
937
1001
 
938
- return valueAsInt;
1002
+ return valueAsInt;
939
1003
  }
940
1004
 
941
1005
  /**
@@ -944,18 +1008,18 @@ function getDimValue(value) {
944
1008
  * @return {number}
945
1009
  */
946
1010
  function calcBoxWidth(node) {
947
- const dim = getGlobal("window").getComputedStyle(node);
948
- const bounding = node.getBoundingClientRect();
949
-
950
- return (
951
- getDimValue(dim["border-left-width"]) +
952
- getDimValue(dim["padding-left"]) +
953
- getDimValue(dim["margin-left"]) +
954
- getDimValue(bounding["width"]) +
955
- getDimValue(dim["border-right-width"]) +
956
- getDimValue(dim["margin-right"]) +
957
- getDimValue(dim["padding-left"])
958
- );
1011
+ const dim = getGlobal("window").getComputedStyle(node);
1012
+ const bounding = node.getBoundingClientRect();
1013
+
1014
+ return (
1015
+ getDimValue(dim["border-left-width"]) +
1016
+ getDimValue(dim["padding-left"]) +
1017
+ getDimValue(dim["margin-left"]) +
1018
+ getDimValue(bounding["width"]) +
1019
+ getDimValue(dim["border-right-width"]) +
1020
+ getDimValue(dim["margin-right"]) +
1021
+ getDimValue(dim["padding-left"])
1022
+ );
959
1023
  }
960
1024
 
961
1025
  /**
@@ -963,35 +1027,35 @@ function calcBoxWidth(node) {
963
1027
  * @return {Object}
964
1028
  */
965
1029
  function rearrangeButtons() {
966
- const standardButtons = [];
967
- const popperButtons = [];
968
-
969
- let sum = 0;
970
- const space = this[dimensionsSymbol].getVia("data.space");
971
-
972
- const buttons = this.getOption("buttons.standard");
973
- for (const [, button] of buttons.entries()) {
974
- const ref = button?.reference;
975
-
976
- sum += this[dimensionsSymbol].getVia(`data.button.${ref}`);
977
-
978
- if (sum > space) {
979
- popperButtons.push(clone(button));
980
- } else {
981
- standardButtons.push(clone(button));
982
- }
983
- }
984
-
985
- this.setOption("buttons.standard", standardButtons);
986
- this.setOption("buttons.popper", popperButtons);
987
-
988
- if (this[switchElementSymbol]) {
989
- if (popperButtons.length > 0) {
990
- this[switchElementSymbol].classList.remove("hidden");
991
- } else {
992
- this[switchElementSymbol].classList.add("hidden");
993
- }
994
- }
1030
+ const standardButtons = [];
1031
+ const popperButtons = [];
1032
+
1033
+ let sum = 0;
1034
+ const space = this[dimensionsSymbol].getVia("data.space");
1035
+
1036
+ const buttons = this.getOption("buttons.standard");
1037
+ for (const [, button] of buttons.entries()) {
1038
+ const ref = button?.reference;
1039
+
1040
+ sum += this[dimensionsSymbol].getVia(`data.button.${ref}`);
1041
+
1042
+ if (sum > space) {
1043
+ popperButtons.push(clone(button));
1044
+ } else {
1045
+ standardButtons.push(clone(button));
1046
+ }
1047
+ }
1048
+
1049
+ this.setOption("buttons.standard", standardButtons);
1050
+ this.setOption("buttons.popper", popperButtons);
1051
+
1052
+ if (this[switchElementSymbol]) {
1053
+ if (popperButtons.length > 0) {
1054
+ this[switchElementSymbol].classList.remove("hidden");
1055
+ } else {
1056
+ this[switchElementSymbol].classList.add("hidden");
1057
+ }
1058
+ }
995
1059
  }
996
1060
 
997
1061
  /**
@@ -999,50 +1063,50 @@ function rearrangeButtons() {
999
1063
  * @return {Object}
1000
1064
  */
1001
1065
  function calculateNavigationButtonsDimensions() {
1002
- const width = this[navElementSymbol].getBoundingClientRect().width;
1003
-
1004
- let startEndWidth = 0;
1005
-
1006
- getSlottedElements.call(this, undefined, "start").forEach((node) => {
1007
- startEndWidth += calcBoxWidth.call(this, node);
1008
- });
1009
-
1010
- getSlottedElements.call(this, undefined, "end").forEach((node) => {
1011
- startEndWidth += calcBoxWidth.call(this, node);
1012
- });
1013
-
1014
- this[dimensionsSymbol].setVia("data.space", width - startEndWidth - 2);
1015
- this[dimensionsSymbol].setVia("data.visible", !(width === 0));
1016
-
1017
- const buttons = this.getOption("buttons.standard").concat(
1018
- this.getOption("buttons.popper"),
1019
- );
1020
-
1021
- for (const [i, button] of buttons.entries()) {
1022
- const ref = button?.reference;
1023
- const element = this[navElementSymbol].querySelector(
1024
- `:scope > [${ATTRIBUTE_PREFIX}tab-reference="${ref}"]`,
1025
- );
1026
- if (!(element instanceof HTMLButtonElement)) continue;
1027
-
1028
- this[dimensionsSymbol].setVia(
1029
- `data.button.${ref}`,
1030
- calcBoxWidth.call(this, element),
1031
- );
1032
- button["class"] = new TokenList(button["class"])
1033
- .remove("invisible")
1034
- .toString();
1035
- }
1036
-
1037
- const slots = this[controlElementSymbol].querySelectorAll(
1038
- `nav[${ATTRIBUTE_PREFIX}role=nav] > slot.invisible, slot[${ATTRIBUTE_PREFIX}role=slot].invisible`,
1039
- );
1040
- for (const [, slot] of slots.entries()) {
1041
- slot.classList.remove("invisible");
1042
- }
1043
-
1044
- this[dimensionsSymbol].setVia("data.calculated", true);
1045
- this.setOption("buttons.standard", clone(buttons));
1066
+ const width = this[navElementSymbol].getBoundingClientRect().width;
1067
+
1068
+ let startEndWidth = 0;
1069
+
1070
+ getSlottedElements.call(this, undefined, "start").forEach((node) => {
1071
+ startEndWidth += calcBoxWidth.call(this, node);
1072
+ });
1073
+
1074
+ getSlottedElements.call(this, undefined, "end").forEach((node) => {
1075
+ startEndWidth += calcBoxWidth.call(this, node);
1076
+ });
1077
+
1078
+ this[dimensionsSymbol].setVia("data.space", width - startEndWidth - 2);
1079
+ this[dimensionsSymbol].setVia("data.visible", !(width === 0));
1080
+
1081
+ const buttons = this.getOption("buttons.standard").concat(
1082
+ this.getOption("buttons.popper"),
1083
+ );
1084
+
1085
+ for (const [i, button] of buttons.entries()) {
1086
+ const ref = button?.reference;
1087
+ const element = this[navElementSymbol].querySelector(
1088
+ `:scope > [${ATTRIBUTE_PREFIX}tab-reference="${ref}"]`,
1089
+ );
1090
+ if (!(element instanceof HTMLButtonElement)) continue;
1091
+
1092
+ this[dimensionsSymbol].setVia(
1093
+ `data.button.${ref}`,
1094
+ calcBoxWidth.call(this, element),
1095
+ );
1096
+ button["class"] = new TokenList(button["class"])
1097
+ .remove("invisible")
1098
+ .toString();
1099
+ }
1100
+
1101
+ const slots = this[controlElementSymbol].querySelectorAll(
1102
+ `nav[${ATTRIBUTE_PREFIX}role=nav] > slot.invisible, slot[${ATTRIBUTE_PREFIX}role=slot].invisible`,
1103
+ );
1104
+ for (const [, slot] of slots.entries()) {
1105
+ slot.classList.remove("invisible");
1106
+ }
1107
+
1108
+ this[dimensionsSymbol].setVia("data.calculated", true);
1109
+ this.setOption("buttons.standard", clone(buttons));
1046
1110
  }
1047
1111
 
1048
1112
  /**
@@ -1051,34 +1115,34 @@ function calculateNavigationButtonsDimensions() {
1051
1115
  * @return {string}
1052
1116
  */
1053
1117
  function getButtonLabel(node) {
1054
- let label;
1055
- let setLabel = false;
1056
- if (node.hasAttribute(ATTRIBUTE_BUTTON_LABEL)) {
1057
- label = node.getAttribute(ATTRIBUTE_BUTTON_LABEL);
1058
- } else {
1059
- label = node.innerText;
1060
- setLabel = true;
1061
- }
1062
-
1063
- if (!isString(label)) {
1064
- label = "";
1065
- }
1066
-
1067
- label = label.trim();
1068
-
1069
- if (label === "") {
1070
- label = this.getOption("labels.new-tab-label", "New Tab");
1071
- }
1072
-
1073
- if (label.length > 100) {
1074
- label = `${label.substring(0, 99)}…`;
1075
- }
1076
-
1077
- if (setLabel === true) {
1078
- node.setAttribute(ATTRIBUTE_BUTTON_LABEL, label);
1079
- }
1080
-
1081
- return label;
1118
+ let label;
1119
+ let setLabel = false;
1120
+ if (node.hasAttribute(ATTRIBUTE_BUTTON_LABEL)) {
1121
+ label = node.getAttribute(ATTRIBUTE_BUTTON_LABEL);
1122
+ } else {
1123
+ label = node.innerText;
1124
+ setLabel = true;
1125
+ }
1126
+
1127
+ if (!isString(label)) {
1128
+ label = "";
1129
+ }
1130
+
1131
+ label = label.trim();
1132
+
1133
+ if (label === "") {
1134
+ label = this.getOption("labels.new-tab-label", "New Tab");
1135
+ }
1136
+
1137
+ if (label.length > 100) {
1138
+ label = `${label.substring(0, 99)}…`;
1139
+ }
1140
+
1141
+ if (setLabel === true) {
1142
+ node.setAttribute(ATTRIBUTE_BUTTON_LABEL, label);
1143
+ }
1144
+
1145
+ return label;
1082
1146
  }
1083
1147
 
1084
1148
  /**
@@ -1086,8 +1150,8 @@ function getButtonLabel(node) {
1086
1150
  * @return {string}
1087
1151
  */
1088
1152
  function getTemplate() {
1089
- // language=HTML
1090
- return `
1153
+ // language=HTML
1154
+ return `
1091
1155
  <template id="buttons">
1092
1156
  <button part="button"
1093
1157
  data-monster-role="button"
@@ -1107,7 +1171,7 @@ function getTemplate() {
1107
1171
  data-monster-insert="buttons path:buttons.standard">
1108
1172
  <slot name="start" class="invisible"></slot>
1109
1173
  <div data-monster-role="popper" part="popper" tabindex="-1"
1110
- data-monster-attributes="class path:classes.popper">
1174
+ data-monster-attributes="class path:classes.popper">
1111
1175
  <div data-popper-arrow></div>
1112
1176
 
1113
1177