@schukai/monster 3.58.3 → 3.59.0

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