@schukai/monster 4.52.0 → 4.54.0
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.
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
|
|
5
|
+
## [4.54.0] - 2025-12-28
|
|
6
|
+
|
|
7
|
+
### Add Features
|
|
8
|
+
|
|
9
|
+
- Update project references and add issue handling for button bar
|
|
10
|
+
### Bug Fixes
|
|
11
|
+
|
|
12
|
+
- revert to jsdom 26.1
|
|
13
|
+
### Changes
|
|
14
|
+
|
|
15
|
+
- close issue [#347](https://gitlab.schukai.com/oss/libraries/javascript/monster/issues/347)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
## [4.53.0] - 2025-12-28
|
|
20
|
+
|
|
21
|
+
### Add Features
|
|
22
|
+
|
|
23
|
+
- Improve button visibility handling and optimize element width calculations
|
|
24
|
+
### Bug Fixes
|
|
25
|
+
|
|
26
|
+
- Preserve scroll position when updating hash for tabs
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
5
30
|
## [4.52.0] - 2025-12-28
|
|
6
31
|
|
|
7
32
|
### Add Features
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"author":"schukai GmbH","dependencies":{"@floating-ui/dom":"^1.7.4","@popperjs/core":"^2.11.8"},"description":"Monster is a simple library for creating fast, robust and lightweight websites.","homepage":"https://monsterjs.org/","keywords":["framework","web","dom","css","sass","mobile-first","app","front-end","templates","schukai","core","shopcloud","alvine","monster","buildmap","stack","observer","observable","uuid","node","nodelist","css-in-js","logger","log","theme"],"license":"AGPL 3.0","main":"source/monster.mjs","module":"source/monster.mjs","name":"@schukai/monster","repository":{"type":"git","url":"https://gitlab.schukai.com/oss/libraries/javascript/monster.git"},"type":"module","version":"4.
|
|
1
|
+
{"author":"schukai GmbH","dependencies":{"@floating-ui/dom":"^1.7.4","@popperjs/core":"^2.11.8"},"description":"Monster is a simple library for creating fast, robust and lightweight websites.","homepage":"https://monsterjs.org/","keywords":["framework","web","dom","css","sass","mobile-first","app","front-end","templates","schukai","core","shopcloud","alvine","monster","buildmap","stack","observer","observable","uuid","node","nodelist","css-in-js","logger","log","theme"],"license":"AGPL 3.0","main":"source/monster.mjs","module":"source/monster.mjs","name":"@schukai/monster","repository":{"type":"git","url":"https://gitlab.schukai.com/oss/libraries/javascript/monster.git"},"type":"module","version":"4.54.0"}
|
|
@@ -112,6 +112,12 @@ const popperSwitchEventHandler = Symbol("popperSwitchEventHandler");
|
|
|
112
112
|
*/
|
|
113
113
|
const popperNavElementSymbol = Symbol("popperNavElement");
|
|
114
114
|
|
|
115
|
+
/**
|
|
116
|
+
* @private
|
|
117
|
+
* @type {symbol}
|
|
118
|
+
*/
|
|
119
|
+
const mutationObserverSymbol = Symbol("mutationObserver");
|
|
120
|
+
|
|
115
121
|
/**
|
|
116
122
|
* @private
|
|
117
123
|
* @type {symbol}
|
|
@@ -257,6 +263,9 @@ class ButtonBar extends CustomElement {
|
|
|
257
263
|
}
|
|
258
264
|
|
|
259
265
|
disconnectResizeObserver.call(this);
|
|
266
|
+
if (this[mutationObserverSymbol]) {
|
|
267
|
+
this[mutationObserverSymbol].disconnect();
|
|
268
|
+
}
|
|
260
269
|
}
|
|
261
270
|
|
|
262
271
|
/**
|
|
@@ -300,72 +309,100 @@ function initDefaultsFromAttributes(obj) {
|
|
|
300
309
|
return obj;
|
|
301
310
|
}
|
|
302
311
|
|
|
312
|
+
/**
|
|
313
|
+
* @private
|
|
314
|
+
* @param {HTMLElement} element
|
|
315
|
+
* @return {boolean}
|
|
316
|
+
*/
|
|
317
|
+
function isElementTrulyVisible(element) {
|
|
318
|
+
if (!(element instanceof HTMLElement)) {
|
|
319
|
+
return false;
|
|
320
|
+
}
|
|
321
|
+
const computedStyle = getComputedStyle(element);
|
|
322
|
+
return computedStyle.display !== 'none' && computedStyle.visibility !== 'hidden' && computedStyle.opacity !== '0' && element.offsetWidth > 0 && element.offsetHeight > 0;
|
|
323
|
+
}
|
|
324
|
+
|
|
303
325
|
/**
|
|
304
326
|
* @private
|
|
305
327
|
*/
|
|
306
328
|
function initEventHandler() {
|
|
307
329
|
const self = this;
|
|
330
|
+
|
|
331
|
+
const triggerRecalculation = () => {
|
|
332
|
+
if (self[timerCallbackSymbol] instanceof DeadMansSwitch) {
|
|
333
|
+
try {
|
|
334
|
+
self[timerCallbackSymbol].touch();
|
|
335
|
+
return;
|
|
336
|
+
} catch (e) {
|
|
337
|
+
if (e.message !== "has already run") throw e;
|
|
338
|
+
delete self[timerCallbackSymbol];
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
self[timerCallbackSymbol] = new DeadMansSwitch(50, () => {
|
|
343
|
+
requestAnimationFrame(() => {
|
|
344
|
+
updatePopper.call(self);
|
|
345
|
+
self[dimensionsSymbol].setVia("data.calculated", false);
|
|
346
|
+
try {
|
|
347
|
+
checkAndRearrangeButtons.call(self);
|
|
348
|
+
} catch (error) {
|
|
349
|
+
addErrorAttribute(
|
|
350
|
+
self,
|
|
351
|
+
error?.message || "An error occurred while rearranging the buttons",
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
});
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
const mutationCallback = (mutationList) => {
|
|
359
|
+
let needsRecalc = false;
|
|
360
|
+
for (const mutation of mutationList) {
|
|
361
|
+
if (mutation.type === "attributes") {
|
|
362
|
+
const target = mutation.target;
|
|
363
|
+
if (target instanceof HTMLElement) {
|
|
364
|
+
const ref = target.getAttribute("data-monster-reference");
|
|
365
|
+
if (ref && !isElementTrulyVisible(target)) {
|
|
366
|
+
self[dimensionsSymbol].setVia(`data.button.${ref}`, 0);
|
|
367
|
+
needsRecalc = true;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
if (needsRecalc) {
|
|
373
|
+
triggerRecalculation();
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
|
|
308
377
|
/**
|
|
309
378
|
* @param {Event} event
|
|
310
379
|
*/
|
|
311
380
|
self[closeEventHandler] = (event) => {
|
|
312
381
|
const path = event.composedPath();
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
if (element === self) {
|
|
316
|
-
return;
|
|
317
|
-
}
|
|
382
|
+
for (const element of path) {
|
|
383
|
+
if (element === self) return;
|
|
318
384
|
}
|
|
319
|
-
|
|
320
385
|
hide.call(self);
|
|
321
386
|
};
|
|
322
387
|
|
|
323
388
|
if (self[buttonBarSlotElementSymbol]) {
|
|
324
|
-
self[buttonBarSlotElementSymbol].addEventListener("slotchange", (
|
|
389
|
+
self[buttonBarSlotElementSymbol].addEventListener("slotchange", () => {
|
|
325
390
|
checkAndRearrangeButtons.call(self);
|
|
326
391
|
});
|
|
327
392
|
}
|
|
328
393
|
|
|
329
394
|
if (self[popperElementSymbol]) {
|
|
330
|
-
self[popperElementSymbol].addEventListener("slotchange", (
|
|
395
|
+
self[popperElementSymbol].addEventListener("slotchange", () => {
|
|
331
396
|
checkAndRearrangeButtons.call(self);
|
|
332
397
|
});
|
|
333
398
|
}
|
|
334
399
|
|
|
335
|
-
// data-monster-options
|
|
336
400
|
self[attributeObserverSymbol][ATTRIBUTE_POPPER_POSITION] = function (value) {
|
|
337
401
|
self.setOption("classes.button", value);
|
|
338
402
|
};
|
|
339
403
|
|
|
340
|
-
self[resizeObserverSymbol] = new ResizeObserver(
|
|
341
|
-
|
|
342
|
-
try {
|
|
343
|
-
self[timerCallbackSymbol].touch();
|
|
344
|
-
return;
|
|
345
|
-
} catch (e) {
|
|
346
|
-
// catch Error("has already run");
|
|
347
|
-
if (e.message !== "has already run") {
|
|
348
|
-
throw e;
|
|
349
|
-
}
|
|
350
|
-
delete self[timerCallbackSymbol];
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
self[timerCallbackSymbol] = new DeadMansSwitch(200, () => {
|
|
355
|
-
requestAnimationFrame(() => {
|
|
356
|
-
updatePopper.call(self);
|
|
357
|
-
self[dimensionsSymbol].setVia("data.calculated", false);
|
|
358
|
-
try {
|
|
359
|
-
checkAndRearrangeButtons.call(self);
|
|
360
|
-
} catch (error) {
|
|
361
|
-
addErrorAttribute(
|
|
362
|
-
this,
|
|
363
|
-
error?.message || "An error occurred while rearranging the buttons",
|
|
364
|
-
);
|
|
365
|
-
}
|
|
366
|
-
});
|
|
367
|
-
});
|
|
368
|
-
});
|
|
404
|
+
self[resizeObserverSymbol] = new ResizeObserver(triggerRecalculation);
|
|
405
|
+
self[mutationObserverSymbol] = new MutationObserver(mutationCallback);
|
|
369
406
|
|
|
370
407
|
initSlotChangedHandler.call(self);
|
|
371
408
|
}
|
|
@@ -389,16 +426,21 @@ function checkAndRearrangeButtons() {
|
|
|
389
426
|
* @return {Object}
|
|
390
427
|
*/
|
|
391
428
|
function rearrangeButtons() {
|
|
392
|
-
let sum =
|
|
429
|
+
let sum = 0;
|
|
430
|
+
// Only add the popper switch's width if it's currently visible.
|
|
431
|
+
if (isElementTrulyVisible(this[switchElementSymbol])) {
|
|
432
|
+
sum += this[switchElementSymbol].offsetWidth;
|
|
433
|
+
}
|
|
393
434
|
const space = this[dimensionsSymbol].getVia("data.space");
|
|
394
435
|
|
|
395
436
|
const buttonReferences = this[dimensionsSymbol].getVia(
|
|
396
437
|
"data.buttonReferences",
|
|
397
438
|
);
|
|
398
439
|
|
|
399
|
-
|
|
400
|
-
|
|
440
|
+
const visibleButtonsInMainSlot = [];
|
|
441
|
+
const buttonsToMoveToPopper = [];
|
|
401
442
|
|
|
443
|
+
for (const ref of buttonReferences) {
|
|
402
444
|
let elements = getSlottedElements.call(
|
|
403
445
|
this,
|
|
404
446
|
'[data-monster-reference="' + ref + '"]',
|
|
@@ -422,15 +464,53 @@ function rearrangeButtons() {
|
|
|
422
464
|
continue;
|
|
423
465
|
}
|
|
424
466
|
|
|
425
|
-
|
|
426
|
-
|
|
467
|
+
let buttonWidth = 0;
|
|
468
|
+
try {
|
|
469
|
+
buttonWidth = this[dimensionsSymbol].getVia(`data.button.${ref}`);
|
|
470
|
+
} catch (e) {
|
|
471
|
+
// If the path does not exist, pathfinder throws an error.
|
|
472
|
+
// In this case, we assume the width is 0.
|
|
473
|
+
// This can happen for buttons that have never been visible.
|
|
474
|
+
}
|
|
475
|
+
const style = getComputedStyle(element);
|
|
476
|
+
|
|
477
|
+
// A user-hidden button should not participate in layout calculations.
|
|
478
|
+
// We will assign it to the popper to keep it out of the flow.
|
|
479
|
+
if (style.display === "none") {
|
|
480
|
+
// This button is not visible. It could be user-hidden, or it could be
|
|
481
|
+
// in the popper which is currently hidden.
|
|
482
|
+
// We should only count it towards the popper if it has a non-zero width,
|
|
483
|
+
// which implies it's a genuinely overflowing button, not one that is
|
|
484
|
+
// user-hidden from the start.
|
|
485
|
+
if (buttonWidth > 0) {
|
|
486
|
+
buttonsToMoveToPopper.push(element);
|
|
487
|
+
} else {
|
|
488
|
+
// It has 0 width. Treat it as a truly hidden element.
|
|
489
|
+
// Put it in the main slot, where it will be invisible and take no space.
|
|
490
|
+
visibleButtonsInMainSlot.push(element);
|
|
491
|
+
}
|
|
492
|
+
continue;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
const switchWidth = this[dimensionsSymbol].getVia("data.switchWidth") || 2;
|
|
496
|
+
if (sum + buttonWidth > space - switchWidth) { buttonsToMoveToPopper.push(element);
|
|
427
497
|
} else {
|
|
428
|
-
|
|
498
|
+
sum += buttonWidth;
|
|
499
|
+
visibleButtonsInMainSlot.push(element);
|
|
429
500
|
}
|
|
430
501
|
}
|
|
431
502
|
|
|
432
|
-
const
|
|
433
|
-
|
|
503
|
+
for (const button of buttonsToMoveToPopper) {
|
|
504
|
+
button.setAttribute("slot", "popper");
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
for (const button of visibleButtonsInMainSlot) {
|
|
508
|
+
button.removeAttribute("slot");
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
const buttonsInPopper = getSlottedElements.call(this, ":scope", "popper");
|
|
512
|
+
|
|
513
|
+
if (buttonsInPopper.size > 0) {
|
|
434
514
|
this[switchElementSymbol].classList.remove("hidden");
|
|
435
515
|
} else {
|
|
436
516
|
this[switchElementSymbol].classList.add("hidden");
|
|
@@ -483,9 +563,8 @@ function calculateButtonBarDimensions() {
|
|
|
483
563
|
try {
|
|
484
564
|
pixel = convertToPixels(width);
|
|
485
565
|
} catch (e) {
|
|
486
|
-
|
|
566
|
+
addErrorAttribute(
|
|
487
567
|
this,
|
|
488
|
-
ATTRIBUTE_ERRORMESSAGE,
|
|
489
568
|
e?.message || "An error occurred while calculating the dimensions",
|
|
490
569
|
);
|
|
491
570
|
}
|
|
@@ -503,9 +582,8 @@ function calculateButtonBarDimensions() {
|
|
|
503
582
|
try {
|
|
504
583
|
borderWidthWithoutUnit = convertToPixels(borderWidth);
|
|
505
584
|
} catch (e) {
|
|
506
|
-
|
|
585
|
+
addErrorAttribute(
|
|
507
586
|
this,
|
|
508
|
-
ATTRIBUTE_ERRORMESSAGE,
|
|
509
587
|
e?.message || "An error occurred while calculating the dimensions",
|
|
510
588
|
);
|
|
511
589
|
}
|
|
@@ -521,35 +599,44 @@ function calculateButtonBarDimensions() {
|
|
|
521
599
|
|
|
522
600
|
const buttonReferences = [];
|
|
523
601
|
|
|
524
|
-
|
|
602
|
+
// Get all buttons, regardless of their current slot
|
|
603
|
+
const allButtons = Array.from(getSlottedElements.call(this, ":scope", null));
|
|
604
|
+
const popperButtons = Array.from(getSlottedElements.call(this, ":scope", "popper"));
|
|
605
|
+
|
|
606
|
+
const combinedButtons = [...allButtons, ...popperButtons].filter((button, index, self) => {
|
|
607
|
+
// Filter out duplicates based on data-monster-reference if present, or element itself
|
|
608
|
+
return self.findIndex(b => b.dataset.monsterReference === button.dataset.monsterReference || b === button) === index;
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
for (const button of combinedButtons) {
|
|
612
|
+
if (!(button instanceof HTMLElement)) {
|
|
613
|
+
continue;
|
|
614
|
+
}
|
|
525
615
|
|
|
526
|
-
for (const [, button] of visibleButtons.entries()) {
|
|
527
616
|
if (!button.hasAttribute("data-monster-reference")) {
|
|
528
|
-
button.setAttribute("data-monster-reference", new ID("
|
|
617
|
+
button.setAttribute("data-monster-reference", new ID("btn").toString());
|
|
529
618
|
}
|
|
530
619
|
|
|
531
620
|
const ref = button.getAttribute("data-monster-reference");
|
|
532
621
|
if (ref === null) continue;
|
|
533
622
|
|
|
534
623
|
buttonReferences.push(ref);
|
|
535
|
-
this[dimensionsSymbol].setVia(
|
|
536
|
-
`data.button.${ref}`,
|
|
537
|
-
calcBoxWidth.call(this, button),
|
|
538
|
-
);
|
|
539
|
-
}
|
|
540
624
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
if (
|
|
544
|
-
|
|
625
|
+
// Only calculate width for visible buttons. Assume invisible ones
|
|
626
|
+
// (e.g. in popper) have their width calculated previously and stored.
|
|
627
|
+
if (isElementTrulyVisible(button)) {
|
|
628
|
+
this[dimensionsSymbol].setVia(
|
|
629
|
+
`data.button.${ref}`,
|
|
630
|
+
calcBoxWidth.call(this, button),
|
|
631
|
+
);
|
|
545
632
|
}
|
|
633
|
+
}
|
|
546
634
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
}
|
|
635
|
+
if (this[switchElementSymbol]) {
|
|
636
|
+
this[dimensionsSymbol].setVia(
|
|
637
|
+
"data.switchWidth",
|
|
638
|
+
this[switchElementSymbol].offsetWidth,
|
|
639
|
+
);
|
|
553
640
|
}
|
|
554
641
|
|
|
555
642
|
this[dimensionsSymbol].setVia("data.calculated", true);
|
|
@@ -561,10 +648,19 @@ function calculateButtonBarDimensions() {
|
|
|
561
648
|
*/
|
|
562
649
|
function updateResizeObserverObservation() {
|
|
563
650
|
this[resizeObserverSymbol].disconnect();
|
|
651
|
+
if (this[mutationObserverSymbol]) {
|
|
652
|
+
this[mutationObserverSymbol].disconnect();
|
|
653
|
+
}
|
|
564
654
|
|
|
565
655
|
const slottedNodes = getSlottedElements.call(this);
|
|
566
656
|
slottedNodes.forEach((node) => {
|
|
567
657
|
this[resizeObserverSymbol].observe(node);
|
|
658
|
+
if (this[mutationObserverSymbol]) {
|
|
659
|
+
this[mutationObserverSymbol].observe(node, {
|
|
660
|
+
attributes: true,
|
|
661
|
+
attributeFilter: ["style", "class"],
|
|
662
|
+
});
|
|
663
|
+
}
|
|
568
664
|
});
|
|
569
665
|
|
|
570
666
|
requestAnimationFrame(() => {
|
|
@@ -17,9 +17,9 @@ import "../form/field-set.mjs";
|
|
|
17
17
|
import { DeadMansSwitch } from "../../util/deadmansswitch.mjs";
|
|
18
18
|
import { DataSet } from "../datatable/dataset.mjs";
|
|
19
19
|
import {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
assembleMethodSymbol,
|
|
21
|
+
registerCustomElement,
|
|
22
|
+
getSlottedElements,
|
|
23
23
|
} from "../../dom/customelement.mjs";
|
|
24
24
|
import { datasourceLinkedElementSymbol } from "../datatable/util.mjs";
|
|
25
25
|
import { FormStyleSheet } from "./stylesheet/form.mjs";
|
|
@@ -60,116 +60,116 @@ const debounceBindSymbol = Symbol("debounceBind");
|
|
|
60
60
|
* @fires monster-changed
|
|
61
61
|
*/
|
|
62
62
|
class Form extends DataSet {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
63
|
+
/**
|
|
64
|
+
* @property {Object} templates Template definitions
|
|
65
|
+
* @property {string} templates.main Main template
|
|
66
|
+
* @property {Object} classes Class definitions
|
|
67
|
+
* @property {string} classes.form Form class
|
|
68
|
+
* @property {Object} writeBack Write back definitions
|
|
69
|
+
* @property {string[]} writeBack.events Write back events
|
|
70
|
+
* @property {Object} bind Bind definitions
|
|
71
|
+
* @property {Object} reportValidity Report validity definitions
|
|
72
|
+
* @property {string} reportValidity.selector Report validity selector
|
|
73
|
+
* @property {boolean} features.mutationObserver Mutation observer feature
|
|
74
|
+
* @property {boolean} features.writeBack Write back feature
|
|
75
|
+
* @property {boolean} features.bind Bind feature
|
|
76
|
+
*/
|
|
77
|
+
get defaults() {
|
|
78
|
+
const obj = Object.assign({}, super.defaults, {
|
|
79
|
+
templates: {
|
|
80
|
+
main: getTemplate(),
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
classes: {
|
|
84
|
+
form: "",
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
writeBack: {
|
|
88
|
+
events: ["keyup", "click", "change", "drop", "touchend", "input"],
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
reportValidity: {
|
|
92
|
+
selector:
|
|
93
|
+
"input,select,textarea,monster-select,monster-toggle-switch,monster-password",
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
eventProcessing: true,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
obj["features"]["mutationObserver"] = false;
|
|
100
|
+
obj["features"]["writeBack"] = true;
|
|
101
|
+
|
|
102
|
+
return obj;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
*
|
|
107
|
+
* @return {string}
|
|
108
|
+
*/
|
|
109
|
+
static getTag() {
|
|
110
|
+
return "monster-form";
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @return {CSSStyleSheet[]}
|
|
115
|
+
*/
|
|
116
|
+
static getCSSStyleSheet() {
|
|
117
|
+
return [FormStyleSheet, InvalidStyleSheet];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
*
|
|
122
|
+
*/
|
|
123
|
+
[assembleMethodSymbol]() {
|
|
124
|
+
const selector = this.getOption("datasource.selector");
|
|
125
|
+
|
|
126
|
+
if (!selector) {
|
|
127
|
+
this[datasourceLinkedElementSymbol] = getDocument().createElement(
|
|
128
|
+
"monster-datasource-dom",
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
super[assembleMethodSymbol]();
|
|
133
|
+
|
|
134
|
+
initControlReferences.call(this);
|
|
135
|
+
initEventHandler.call(this);
|
|
136
|
+
initDataSourceHandler.call(this);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* This method is called when the component is created.
|
|
141
|
+
* @since 3.70.0
|
|
142
|
+
* @return {Promise}
|
|
143
|
+
*/
|
|
144
|
+
refresh() {
|
|
145
|
+
return this.write().then(() => {
|
|
146
|
+
super.refresh();
|
|
147
|
+
return this;
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Run reportValidation on all child html form controls.
|
|
153
|
+
*
|
|
154
|
+
* @since 2.10.0
|
|
155
|
+
* @return {boolean}
|
|
156
|
+
*/
|
|
157
|
+
reportValidity() {
|
|
158
|
+
let valid = true;
|
|
159
|
+
|
|
160
|
+
const selector = this.getOption("reportValidity.selector");
|
|
161
|
+
const nodes = getSlottedElements.call(this, selector);
|
|
162
|
+
|
|
163
|
+
nodes.forEach((node) => {
|
|
164
|
+
if (typeof node.reportValidity === "function") {
|
|
165
|
+
if (node.reportValidity() === false) {
|
|
166
|
+
valid = false;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
return valid;
|
|
172
|
+
}
|
|
173
173
|
}
|
|
174
174
|
|
|
175
175
|
function initDataSourceHandler() {}
|
|
@@ -179,47 +179,47 @@ function initDataSourceHandler() {}
|
|
|
179
179
|
* @return {initEventHandler}
|
|
180
180
|
*/
|
|
181
181
|
function initEventHandler() {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
182
|
+
this[debounceBindSymbol] = {};
|
|
183
|
+
|
|
184
|
+
if (this.getOption("features.writeBack") === true) {
|
|
185
|
+
setTimeout(() => {
|
|
186
|
+
const events = this.getOption("writeBack.events");
|
|
187
|
+
for (const event of events) {
|
|
188
|
+
this.addEventListener(event, (e) => {
|
|
189
|
+
if (!this.reportValidity()) {
|
|
190
|
+
this.classList.add("invalid");
|
|
191
|
+
setTimeout(() => {
|
|
192
|
+
this.classList.remove("invalid");
|
|
193
|
+
}, 1000);
|
|
194
|
+
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (this[debounceWriteBackSymbol] instanceof DeadMansSwitch) {
|
|
199
|
+
try {
|
|
200
|
+
this[debounceWriteBackSymbol].touch();
|
|
201
|
+
return;
|
|
202
|
+
} catch (e) {
|
|
203
|
+
if (e.message !== "has already run") {
|
|
204
|
+
throw e;
|
|
205
|
+
}
|
|
206
|
+
delete this[debounceWriteBackSymbol];
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
this[debounceWriteBackSymbol] = new DeadMansSwitch(200, () => {
|
|
211
|
+
setTimeout(() => {
|
|
212
|
+
this.write().catch((e) => {
|
|
213
|
+
addAttributeToken(this, "error", e.message || `${e}`);
|
|
214
|
+
});
|
|
215
|
+
}, 0);
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
}, 0);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return this;
|
|
223
223
|
}
|
|
224
224
|
|
|
225
225
|
/**
|
|
@@ -227,10 +227,10 @@ function initEventHandler() {
|
|
|
227
227
|
* @return {FilterButton}
|
|
228
228
|
*/
|
|
229
229
|
function initControlReferences() {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
230
|
+
if (!this.shadowRoot) {
|
|
231
|
+
throw new Error("no shadow-root is defined");
|
|
232
|
+
}
|
|
233
|
+
return this;
|
|
234
234
|
}
|
|
235
235
|
|
|
236
236
|
/**
|
|
@@ -238,8 +238,8 @@ function initControlReferences() {
|
|
|
238
238
|
* @return {string}
|
|
239
239
|
*/
|
|
240
240
|
function getTemplate() {
|
|
241
|
-
|
|
242
|
-
|
|
241
|
+
// language=HTML
|
|
242
|
+
return `
|
|
243
243
|
<div data-monster-role="control" part="control">
|
|
244
244
|
<form data-monster-attributes="disabled path:disabled | if:true, class path:classes.form"
|
|
245
245
|
data-monster-role="form"
|
|
@@ -108,7 +108,10 @@ export function attachTabsHashSync(
|
|
|
108
108
|
|
|
109
109
|
const newHash = createBracketedKeyValueHash(hashObj);
|
|
110
110
|
if (location.hash !== newHash) {
|
|
111
|
+
const scrollX = window.scrollX;
|
|
112
|
+
const scrollY = window.scrollY;
|
|
111
113
|
history.replaceState(null, "", newHash);
|
|
114
|
+
window.scrollTo(scrollX, scrollY);
|
|
112
115
|
}
|
|
113
116
|
lastKnownActiveTabId = activeTabId;
|
|
114
117
|
lastKnownAllTabIds = allTabIds;
|