@schukai/monster 4.127.1 → 4.128.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 +20 -1
- package/package.json +1 -1
- package/source/components/form/message-state-button.mjs +23 -3
- package/source/components/layout/popper.mjs +37 -6
- package/source/dom/constants.mjs +6 -0
- package/source/dom/customelement.mjs +26 -11
- package/source/dom/events.mjs +6 -1
- package/source/dom/updater.mjs +472 -15
- package/test/cases/components/form/message-state-button.mjs +85 -0
- package/test/cases/dom/customelement.mjs +78 -8
- package/test/cases/dom/updater.mjs +848 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
## [Unreleased]
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
- **updater:** add explicit disposal semantics for observer, queue, and event-processing teardown
|
|
9
|
+
- **updater:** dispose linked stateful subtrees before destructive replace/remove operations
|
|
10
|
+
- **updater:** add `data-monster-patch` as a lifecycle-safe alternative to destructive replace rendering
|
|
11
|
+
- **updater:** extend `data-monster-patch` with `DocumentFragment` and unkeyed array support
|
|
12
|
+
- **updater:** add initial `data-monster-patch-key` support for keyed array reorder and removal
|
|
13
|
+
- **updater:** add `data-monster-patch-render` for keyed object arrays with explicit single-node item rendering
|
|
14
|
+
- **message-state-button:** render `message.content` through `data-monster-patch` to preserve rich and stateful message content
|
|
15
|
+
- **customelement:** skip mutation-observer updater reruns for disconnected or disposed instances
|
|
16
|
+
- **message-state-button:** clear auto-hide timers on disconnect and ignore delayed hides after removal
|
|
17
|
+
- **popper:** guard show/hide/update flows against disconnected hosts and missing internal elements
|
|
18
|
+
|
|
19
|
+
### Changes
|
|
20
|
+
|
|
21
|
+
- document lifecycle ownership rules for Updater-driven and stateful custom element implementations
|
|
22
|
+
|
|
23
|
+
|
|
4
24
|
|
|
5
25
|
## [4.127.1] - 2026-03-16
|
|
6
26
|
|
|
@@ -4658,4 +4678,3 @@
|
|
|
4658
4678
|
## 1.8.0 - 2021-08-15
|
|
4659
4679
|
|
|
4660
4680
|
- Initial release
|
|
4661
|
-
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"author":"Volker Schukai","dependencies":{"@floating-ui/dom":"^1.7.
|
|
1
|
+
{"author":"Volker Schukai","dependencies":{"@floating-ui/dom":"^1.7.6","@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.128.0"}
|
|
@@ -39,6 +39,7 @@ const innerDisabledObserverSymbol = Symbol("innerDisabledObserver");
|
|
|
39
39
|
const popperElementSymbol = Symbol("popperElement");
|
|
40
40
|
const messageElementSymbol = Symbol("messageElement");
|
|
41
41
|
const measurementPopperSymbol = Symbol("measurementPopper");
|
|
42
|
+
const autoHideTimerSymbol = Symbol("autoHideTimer");
|
|
42
43
|
|
|
43
44
|
/**
|
|
44
45
|
* A specialized button component that combines state management with message display capabilities.
|
|
@@ -245,6 +246,7 @@ class MessageStateButton extends Popper {
|
|
|
245
246
|
this.setOption("message.title", undefined);
|
|
246
247
|
this.setOption("message.content", undefined);
|
|
247
248
|
this.setOption("message.icon", undefined);
|
|
249
|
+
clearAutoHideTimer.call(this);
|
|
248
250
|
return this;
|
|
249
251
|
}
|
|
250
252
|
|
|
@@ -255,12 +257,17 @@ class MessageStateButton extends Popper {
|
|
|
255
257
|
* @return {MessageStateButton} Returns the button instance for chaining
|
|
256
258
|
*/
|
|
257
259
|
showMessage(timeout) {
|
|
260
|
+
clearAutoHideTimer.call(this);
|
|
258
261
|
applyMeasuredMessageWidth.call(this);
|
|
259
262
|
this.showDialog.call(this);
|
|
260
263
|
|
|
261
264
|
if (timeout !== undefined) {
|
|
262
|
-
setTimeout(() => {
|
|
263
|
-
|
|
265
|
+
this[autoHideTimerSymbol] = setTimeout(() => {
|
|
266
|
+
this[autoHideTimerSymbol] = undefined;
|
|
267
|
+
if (!this.isConnected) {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
this.hideMessage();
|
|
264
271
|
}, timeout);
|
|
265
272
|
}
|
|
266
273
|
|
|
@@ -285,10 +292,16 @@ class MessageStateButton extends Popper {
|
|
|
285
292
|
* @return {MessageStateButton}
|
|
286
293
|
*/
|
|
287
294
|
hideMessage() {
|
|
295
|
+
clearAutoHideTimer.call(this);
|
|
288
296
|
super.hideDialog();
|
|
289
297
|
return this;
|
|
290
298
|
}
|
|
291
299
|
|
|
300
|
+
disconnectedCallback() {
|
|
301
|
+
clearAutoHideTimer.call(this);
|
|
302
|
+
super.disconnectedCallback();
|
|
303
|
+
}
|
|
304
|
+
|
|
292
305
|
/**
|
|
293
306
|
*
|
|
294
307
|
* @return {MessageStateButton}
|
|
@@ -407,6 +420,13 @@ function initEventHandlerByMode(mode) {
|
|
|
407
420
|
}
|
|
408
421
|
}
|
|
409
422
|
|
|
423
|
+
function clearAutoHideTimer() {
|
|
424
|
+
if (this[autoHideTimerSymbol] !== undefined) {
|
|
425
|
+
clearTimeout(this[autoHideTimerSymbol]);
|
|
426
|
+
this[autoHideTimerSymbol] = undefined;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
410
430
|
/**
|
|
411
431
|
* @private
|
|
412
432
|
* @return {Select}
|
|
@@ -515,7 +535,7 @@ function getTemplate() {
|
|
|
515
535
|
<div data-monster-role="popper" part="popper" tabindex="-1" class="monster-color-primary-1">
|
|
516
536
|
<div data-monster-role="arrow"></div>
|
|
517
537
|
<div data-monster-role="message" part="message" class="flex"
|
|
518
|
-
data-monster-
|
|
538
|
+
data-monster-patch="path:message.content"></div>
|
|
519
539
|
</div>
|
|
520
540
|
</div>
|
|
521
541
|
</div>
|
|
@@ -427,13 +427,26 @@ function disconnectResizeObserver() {
|
|
|
427
427
|
*/
|
|
428
428
|
function hide() {
|
|
429
429
|
const self = this;
|
|
430
|
+
const popperElement = self[popperElementSymbol];
|
|
431
|
+
const controlElement = self[controlElementSymbol];
|
|
432
|
+
|
|
433
|
+
if (!self.isConnected) {
|
|
434
|
+
unregisterFromHost.call(self);
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
430
437
|
|
|
431
438
|
fireCustomEvent(self, "monster-popper-hide", {
|
|
432
439
|
self,
|
|
433
440
|
});
|
|
434
441
|
|
|
435
|
-
|
|
436
|
-
|
|
442
|
+
if (popperElement instanceof HTMLElement) {
|
|
443
|
+
popperElement.style.display = "none";
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if (controlElement instanceof HTMLElement) {
|
|
447
|
+
removeAttributeToken(controlElement, "class", "open");
|
|
448
|
+
}
|
|
449
|
+
|
|
437
450
|
unregisterFromHost.call(self);
|
|
438
451
|
|
|
439
452
|
setTimeout(() => {
|
|
@@ -449,12 +462,22 @@ function hide() {
|
|
|
449
462
|
*/
|
|
450
463
|
function show() {
|
|
451
464
|
const self = this;
|
|
465
|
+
const popperElement = self[popperElementSymbol];
|
|
466
|
+
const controlElement = self[controlElementSymbol];
|
|
452
467
|
|
|
453
468
|
if (self.getOption("disabled", false) === true) {
|
|
454
469
|
return;
|
|
455
470
|
}
|
|
456
471
|
|
|
457
|
-
if (
|
|
472
|
+
if (
|
|
473
|
+
!self.isConnected ||
|
|
474
|
+
!(popperElement instanceof HTMLElement) ||
|
|
475
|
+
!(controlElement instanceof HTMLElement)
|
|
476
|
+
) {
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
if (popperElement.style.display === STYLE_DISPLAY_MODE_BLOCK) {
|
|
458
481
|
return;
|
|
459
482
|
}
|
|
460
483
|
|
|
@@ -462,10 +485,10 @@ function show() {
|
|
|
462
485
|
self,
|
|
463
486
|
});
|
|
464
487
|
|
|
465
|
-
|
|
466
|
-
|
|
488
|
+
popperElement.style.visibility = "hidden";
|
|
489
|
+
popperElement.style.display = STYLE_DISPLAY_MODE_BLOCK;
|
|
467
490
|
|
|
468
|
-
addAttributeToken(
|
|
491
|
+
addAttributeToken(controlElement, "class", "open");
|
|
469
492
|
registerWithHost.call(self);
|
|
470
493
|
updatePopper.call(self);
|
|
471
494
|
|
|
@@ -481,6 +504,14 @@ function show() {
|
|
|
481
504
|
* @private
|
|
482
505
|
*/
|
|
483
506
|
function updatePopper() {
|
|
507
|
+
if (
|
|
508
|
+
!this.isConnected ||
|
|
509
|
+
!(this[controlElementSymbol] instanceof HTMLElement) ||
|
|
510
|
+
!(this[popperElementSymbol] instanceof HTMLElement)
|
|
511
|
+
) {
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
|
|
484
515
|
if (this[popperElementSymbol].style.display !== STYLE_DISPLAY_MODE_BLOCK) {
|
|
485
516
|
return;
|
|
486
517
|
}
|
package/source/dom/constants.mjs
CHANGED
|
@@ -23,6 +23,9 @@ export {
|
|
|
23
23
|
ATTRIBUTE_UPDATER_PROPERTIES,
|
|
24
24
|
ATTRIBUTE_UPDATER_SELECT_THIS,
|
|
25
25
|
ATTRIBUTE_UPDATER_REPLACE,
|
|
26
|
+
ATTRIBUTE_UPDATER_PATCH,
|
|
27
|
+
ATTRIBUTE_UPDATER_PATCH_KEY,
|
|
28
|
+
ATTRIBUTE_UPDATER_PATCH_RENDER,
|
|
26
29
|
ATTRIBUTE_UPDATER_INSERT,
|
|
27
30
|
ATTRIBUTE_UPDATER_INSERT_REFERENCE,
|
|
28
31
|
ATTRIBUTE_UPDATER_REMOVE,
|
|
@@ -172,6 +175,9 @@ const ATTRIBUTE_UPDATER_SELECT_THIS = `${ATTRIBUTE_PREFIX}select-this`;
|
|
|
172
175
|
* @since 1.8.0
|
|
173
176
|
*/
|
|
174
177
|
const ATTRIBUTE_UPDATER_REPLACE = `${ATTRIBUTE_PREFIX}replace`;
|
|
178
|
+
const ATTRIBUTE_UPDATER_PATCH = `${ATTRIBUTE_PREFIX}patch`;
|
|
179
|
+
const ATTRIBUTE_UPDATER_PATCH_KEY = `${ATTRIBUTE_PREFIX}patch-key`;
|
|
180
|
+
const ATTRIBUTE_UPDATER_PATCH_RENDER = `${ATTRIBUTE_PREFIX}patch-render`;
|
|
175
181
|
|
|
176
182
|
/**
|
|
177
183
|
* @type {string}
|
|
@@ -932,18 +932,33 @@ function attachChildMutationObserver() {
|
|
|
932
932
|
return;
|
|
933
933
|
}
|
|
934
934
|
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
935
|
+
self[childMutationTimerSymbol] = setTimeout(() => {
|
|
936
|
+
self[childMutationTimerSymbol] = null;
|
|
937
|
+
if (!self.isConnected) {
|
|
938
|
+
return;
|
|
939
|
+
}
|
|
940
940
|
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
941
|
+
if (!hasObjectLink(self, customElementUpdaterLinkSymbol)) {
|
|
942
|
+
return;
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
const updaters = getLinkedObjects(self, customElementUpdaterLinkSymbol);
|
|
946
|
+
for (const list of updaters) {
|
|
947
|
+
for (const updater of list) {
|
|
948
|
+
if (
|
|
949
|
+
typeof updater?.isDisposed === "function" &&
|
|
950
|
+
updater.isDisposed() === true
|
|
951
|
+
) {
|
|
952
|
+
continue;
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
if (!updater?.[internalSymbol]?.element?.isConnected) {
|
|
956
|
+
continue;
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
updater.run().catch((e) => {
|
|
960
|
+
addErrorAttribute(self, e);
|
|
961
|
+
});
|
|
947
962
|
}
|
|
948
963
|
}
|
|
949
964
|
}, 50);
|
package/source/dom/events.mjs
CHANGED
|
@@ -72,7 +72,12 @@ function fireEvent(element, type) {
|
|
|
72
72
|
* @summary Construct and send and event
|
|
73
73
|
*/
|
|
74
74
|
function fireCustomEvent(element, type, detail) {
|
|
75
|
-
if (
|
|
75
|
+
if (
|
|
76
|
+
element instanceof HTMLElement ||
|
|
77
|
+
(element &&
|
|
78
|
+
typeof element === "object" &&
|
|
79
|
+
typeof element.dispatchEvent === "function")
|
|
80
|
+
) {
|
|
76
81
|
if (!isObject(detail)) {
|
|
77
82
|
detail = { detail };
|
|
78
83
|
}
|