@schukai/monster 4.153.0 → 4.154.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/package.json +1 -1
- package/source/components/form/message-state-button.mjs +1 -1
- package/source/components/form/select-link.mjs +12 -1
- package/source/components/state/log.mjs +3 -7
- package/source/components/state/thread.mjs +3 -7
- package/source/components/time/day.mjs +0 -19
- package/source/components/time/month-calendar.mjs +5 -1
- package/source/data/buildmap.mjs +6 -5
- package/source/data/buildtree.mjs +0 -8
- package/source/data/datasource.mjs +3 -10
- package/source/data/diff.mjs +13 -12
- package/source/data/extend.mjs +23 -1
- package/source/data/pathfinder.mjs +228 -32
- package/source/data/transformer.mjs +2 -2
- package/source/dom/customcontrol.mjs +35 -9
- package/source/dom/customelement.mjs +293 -31
- package/source/dom/slotted.mjs +46 -31
- package/source/dom/template.mjs +4 -1
- package/source/dom/updater.mjs +57 -17
- package/source/types/base.mjs +7 -6
- package/source/types/basewithoptions.mjs +1 -8
- package/source/types/internal.mjs +4 -10
- package/source/types/node.mjs +1 -3
- package/source/types/observerlist.mjs +9 -17
- package/source/types/proxyobserver.mjs +51 -12
- package/source/types/tokenlist.mjs +13 -8
- package/source/util/clone.mjs +44 -14
- package/test/cases/browser-bundle.mjs +75 -0
- package/test/cases/components/time/month-calendar.mjs +115 -0
- package/test/cases/data/buildmap.mjs +14 -0
- package/test/cases/data/buildtree.mjs +49 -0
- package/test/cases/data/diff.mjs +86 -0
- package/test/cases/data/extend.mjs +52 -0
- package/test/cases/data/pathfinder-resolution-baseline.mjs +322 -0
- package/test/cases/data/pathfinder.mjs +163 -0
- package/test/cases/data/transformer.mjs +26 -0
- package/test/cases/dom/customcontrol.mjs +85 -0
- package/test/cases/dom/customelement-rebind-baseline.mjs +323 -0
- package/test/cases/dom/customelement-teardown.mjs +408 -0
- package/test/cases/dom/customelement.mjs +408 -0
- package/test/cases/dom/dimension.mjs +34 -10
- package/test/cases/dom/slotted-nodes.mjs +48 -1
- package/test/cases/dom/template.mjs +11 -0
- package/test/cases/dom/updater-remove.mjs +198 -0
- package/test/cases/dom/updater.mjs +118 -0
- package/test/cases/packaging.mjs +213 -0
- package/test/cases/types/base.mjs +50 -0
- package/test/cases/types/observerlist.mjs +103 -1
- package/test/cases/types/proxyobserver.mjs +189 -0
- package/test/cases/types/regex.mjs +4 -0
- package/test/cases/types/tokenlist.mjs +64 -1
- package/test/cases/util/clone.mjs +115 -0
|
@@ -377,6 +377,7 @@ function getInternal() {
|
|
|
377
377
|
}
|
|
378
378
|
|
|
379
379
|
const debounceValueSymbol = Symbol("debounceValue");
|
|
380
|
+
const pendingValueAttributeSymbol = Symbol("pendingValueAttribute");
|
|
380
381
|
|
|
381
382
|
/**
|
|
382
383
|
*
|
|
@@ -388,7 +389,17 @@ const debounceValueSymbol = Symbol("debounceValue");
|
|
|
388
389
|
*/
|
|
389
390
|
function initValueAttributeObserver() {
|
|
390
391
|
const self = this;
|
|
391
|
-
this[attributeObserverSymbol]["value"] = () => {
|
|
392
|
+
this[attributeObserverSymbol]["value"] = (newValue, oldValue) => {
|
|
393
|
+
if (!self[pendingValueAttributeSymbol]) {
|
|
394
|
+
self[pendingValueAttributeSymbol] = {
|
|
395
|
+
oldValue: normalizeAttributeValue(oldValue),
|
|
396
|
+
newValue: normalizeAttributeValue(newValue),
|
|
397
|
+
};
|
|
398
|
+
} else {
|
|
399
|
+
self[pendingValueAttributeSymbol].newValue =
|
|
400
|
+
normalizeAttributeValue(newValue);
|
|
401
|
+
}
|
|
402
|
+
|
|
392
403
|
if (self[debounceValueSymbol] instanceof DeadMansSwitch) {
|
|
393
404
|
try {
|
|
394
405
|
self[debounceValueSymbol].touch();
|
|
@@ -402,18 +413,33 @@ function initValueAttributeObserver() {
|
|
|
402
413
|
}
|
|
403
414
|
|
|
404
415
|
self[debounceValueSymbol] = new DeadMansSwitch(10, () => {
|
|
405
|
-
const
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
if (
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
416
|
+
const pending = self[pendingValueAttributeSymbol];
|
|
417
|
+
delete self[pendingValueAttributeSymbol];
|
|
418
|
+
delete self[debounceValueSymbol];
|
|
419
|
+
if (!pending) return;
|
|
420
|
+
|
|
421
|
+
setTimeout(() => {
|
|
422
|
+
const currentValue = normalizeAttributeValue(self.value);
|
|
423
|
+
if (
|
|
424
|
+
currentValue === pending.oldValue &&
|
|
425
|
+
currentValue !== pending.newValue
|
|
426
|
+
) {
|
|
427
|
+
self.value = pending.newValue;
|
|
428
|
+
}
|
|
429
|
+
}, 0);
|
|
413
430
|
});
|
|
414
431
|
};
|
|
415
432
|
}
|
|
416
433
|
|
|
434
|
+
/**
|
|
435
|
+
* @param {*} value
|
|
436
|
+
* @return {string}
|
|
437
|
+
* @private
|
|
438
|
+
*/
|
|
439
|
+
function normalizeAttributeValue(value) {
|
|
440
|
+
return value === null || value === undefined ? "" : String(value);
|
|
441
|
+
}
|
|
442
|
+
|
|
417
443
|
const disabledSyncSymbol = Symbol("disabledSync");
|
|
418
444
|
const disabledAttributeSyncSymbol = Symbol("disabledAttributeSync");
|
|
419
445
|
|
|
@@ -36,7 +36,11 @@ import {
|
|
|
36
36
|
validateObject,
|
|
37
37
|
} from "../types/validate.mjs";
|
|
38
38
|
import { clone } from "../util/clone.mjs";
|
|
39
|
-
import {
|
|
39
|
+
import {
|
|
40
|
+
getLinkedObjects,
|
|
41
|
+
hasObjectLink,
|
|
42
|
+
removeObjectLink,
|
|
43
|
+
} from "./attributes.mjs";
|
|
40
44
|
import {
|
|
41
45
|
ATTRIBUTE_DISABLED,
|
|
42
46
|
ATTRIBUTE_OPTIONS,
|
|
@@ -119,6 +123,8 @@ const updateCloneDataSymbol = Symbol("@schukai/monster/dom/@@updateCloneData");
|
|
|
119
123
|
const scriptHostElementSymbol = Symbol("scriptHostElement");
|
|
120
124
|
const optionDefaultsSymbol = Symbol("optionDefaults");
|
|
121
125
|
const managedShadowRootSymbol = Symbol("managedShadowRoot");
|
|
126
|
+
const assembledOnceSymbol = Symbol("assembledOnce");
|
|
127
|
+
const teardownCallbacksSymbol = Symbol("teardownCallbacks");
|
|
122
128
|
const visibilityStateSymbol = Symbol("visibilityState");
|
|
123
129
|
let hostVisibilityStyleSheet = null;
|
|
124
130
|
|
|
@@ -358,7 +364,7 @@ class CustomElement extends HTMLElement {
|
|
|
358
364
|
* @property {Function} templateFormatter.marker.open=null Specifies the opening marker for the templates.
|
|
359
365
|
* @property {Function} templateFormatter.marker.close=null Specifies the closing marker for the templates.
|
|
360
366
|
* @property {Boolean} templateFormatter.i18n=false Specifies whether the templates should be formatted with i18n.
|
|
361
|
-
* @property {Boolean} eventProcessing=false Specifies whether the control processes events.
|
|
367
|
+
* @property {Boolean} eventProcessing=false Specifies whether the control processes events. A bound input writes its value into the updater snapshot, not into the options. The synchronisation is one way, options to snapshot, so getOption() does not observe user input; read it with getInternalUpdateCloneData().
|
|
362
368
|
* @property {Object} updater Specifies updater options.
|
|
363
369
|
* @property {Boolean} updater.batchUpdates=false Batches updater content/attribute updates per diff.
|
|
364
370
|
* @since 1.8.0
|
|
@@ -540,16 +546,9 @@ class CustomElement extends HTMLElement {
|
|
|
540
546
|
* @since 1.10.0
|
|
541
547
|
*/
|
|
542
548
|
getOption(path, defaultValue = undefined) {
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
value = new Pathfinder(
|
|
547
|
-
this[internalSymbol].getRealSubject()["options"],
|
|
548
|
-
).getVia(path);
|
|
549
|
-
} catch (e) {}
|
|
550
|
-
|
|
551
|
-
if (value === undefined) return defaultValue;
|
|
552
|
-
return value;
|
|
549
|
+
return new Pathfinder(
|
|
550
|
+
this[internalSymbol].getRealSubject()["options"],
|
|
551
|
+
).getVia(path, defaultValue);
|
|
553
552
|
}
|
|
554
553
|
|
|
555
554
|
/**
|
|
@@ -699,6 +698,7 @@ class CustomElement extends HTMLElement {
|
|
|
699
698
|
[assembleMethodSymbol]() {
|
|
700
699
|
let elements;
|
|
701
700
|
let nodeList;
|
|
701
|
+
|
|
702
702
|
// Extract options from attributes and set them
|
|
703
703
|
const AttributeOptions = getOptionsFromAttributes.call(this);
|
|
704
704
|
if (
|
|
@@ -734,7 +734,15 @@ class CustomElement extends HTMLElement {
|
|
|
734
734
|
// Initialize the custom elements inside the shadow root and the slotted elements
|
|
735
735
|
initFromCallbackHost.call(this);
|
|
736
736
|
try {
|
|
737
|
-
nodeList = new Set([
|
|
737
|
+
nodeList = new Set([
|
|
738
|
+
...elements,
|
|
739
|
+
...getSlottedElements.call(
|
|
740
|
+
this,
|
|
741
|
+
undefined,
|
|
742
|
+
undefined,
|
|
743
|
+
getManagedShadowRoot.call(this),
|
|
744
|
+
),
|
|
745
|
+
]);
|
|
738
746
|
} catch (e) {
|
|
739
747
|
nodeList = elements;
|
|
740
748
|
}
|
|
@@ -761,13 +769,40 @@ class CustomElement extends HTMLElement {
|
|
|
761
769
|
);
|
|
762
770
|
// Attach a mutation observer to observe changes to the attributes of the element
|
|
763
771
|
attachAttributeChangeMutationObserver.call(this);
|
|
772
|
+
attachChildMutationObserver.call(this);
|
|
764
773
|
|
|
765
774
|
if (isFunction(this[internalSymbol]?.syncDisabledState)) {
|
|
766
|
-
this[internalSymbol].syncDisabledState();
|
|
775
|
+
this[internalSymbol].syncDisabledState(true);
|
|
767
776
|
}
|
|
768
777
|
|
|
769
778
|
syncVisibilityState.call(this);
|
|
770
779
|
|
|
780
|
+
this[assembledOnceSymbol] = true;
|
|
781
|
+
|
|
782
|
+
return this;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
/**
|
|
786
|
+
* Registers a callback that runs when the element is disconnected.
|
|
787
|
+
*
|
|
788
|
+
* The registration is consumed by the first teardown, so a callback that is
|
|
789
|
+
* needed again after a reconnect has to be registered again. A callback that
|
|
790
|
+
* throws does not stop the remaining teardown; the error is reported through
|
|
791
|
+
* the error attribute.
|
|
792
|
+
*
|
|
793
|
+
* @param {function} callback
|
|
794
|
+
* @return {CustomElement}
|
|
795
|
+
* @throws {TypeError} value is not a function
|
|
796
|
+
* @since 4.154.0
|
|
797
|
+
*/
|
|
798
|
+
registerTeardown(callback) {
|
|
799
|
+
validateFunction(callback);
|
|
800
|
+
|
|
801
|
+
if (!isArray(this[teardownCallbacksSymbol])) {
|
|
802
|
+
this[teardownCallbacksSymbol] = [];
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
this[teardownCallbacksSymbol].push(callback);
|
|
771
806
|
return this;
|
|
772
807
|
}
|
|
773
808
|
|
|
@@ -775,7 +810,15 @@ class CustomElement extends HTMLElement {
|
|
|
775
810
|
* You know what you are doing? This function is only for advanced users.
|
|
776
811
|
* The result is a clone of the internal data.
|
|
777
812
|
*
|
|
813
|
+
* This snapshot is the object the updaters are bound to. It is the only
|
|
814
|
+
* place where values typed into a `data-monster-bind` input end up when
|
|
815
|
+
* `eventProcessing` is enabled, because the synchronisation runs one way
|
|
816
|
+
* from the options into the snapshot and never back. A reconnect rebinds to
|
|
817
|
+
* the same snapshot, so that state survives.
|
|
818
|
+
*
|
|
778
819
|
* @return {*}
|
|
820
|
+
* @see Monster.DOM.CustomElement#getOption
|
|
821
|
+
* @since 3.56.0
|
|
779
822
|
*/
|
|
780
823
|
getInternalUpdateCloneData() {
|
|
781
824
|
return clone(this[updateCloneDataSymbol]);
|
|
@@ -790,19 +833,31 @@ class CustomElement extends HTMLElement {
|
|
|
790
833
|
* @see https://developer.mozilla.org/en-US/docs/Web/API/Element/connectedCallback
|
|
791
834
|
*/
|
|
792
835
|
connectedCallback() {
|
|
793
|
-
//
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
836
|
+
// Already bound, nothing to do. Moving a connected element inside its own
|
|
837
|
+
// parent fires disconnect and connect, so this runs often.
|
|
838
|
+
if (hasObjectLink(this, customElementUpdaterLinkSymbol)) {
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
try {
|
|
843
|
+
initOptionsFromAttributes(
|
|
844
|
+
this,
|
|
845
|
+
this[internalSymbol].getSubject()?.["options"],
|
|
846
|
+
);
|
|
847
|
+
} catch (e) {
|
|
848
|
+
addErrorAttribute(this, e);
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
// The assemble method is a one shot: derived classes override it and
|
|
852
|
+
// rebuild their own state in there, for example state/log.mjs resets its
|
|
853
|
+
// entries array and its entry maps. After a disconnect the rendered DOM
|
|
854
|
+
// and that state are still intact, so a reconnect only relinks.
|
|
855
|
+
if (this[assembledOnceSymbol] === true) {
|
|
856
|
+
rebindAfterReconnect.call(this);
|
|
857
|
+
return;
|
|
805
858
|
}
|
|
859
|
+
|
|
860
|
+
this[assembleMethodSymbol]();
|
|
806
861
|
}
|
|
807
862
|
|
|
808
863
|
/**
|
|
@@ -811,7 +866,16 @@ class CustomElement extends HTMLElement {
|
|
|
811
866
|
* @return {void}
|
|
812
867
|
* @since 1.7.0
|
|
813
868
|
*/
|
|
814
|
-
disconnectedCallback() {
|
|
869
|
+
disconnectedCallback() {
|
|
870
|
+
runTeardownCallbacks.call(this);
|
|
871
|
+
disposeLinkedUpdaters.call(this);
|
|
872
|
+
detachAttributeChangeMutationObserver.call(this);
|
|
873
|
+
detachChildMutationObserver.call(this);
|
|
874
|
+
|
|
875
|
+
if (isFunction(this[internalSymbol]?.disconnectDisabledObservers)) {
|
|
876
|
+
this[internalSymbol].disconnectDisabledObservers();
|
|
877
|
+
}
|
|
878
|
+
}
|
|
815
879
|
|
|
816
880
|
/**
|
|
817
881
|
* The custom element has been moved into a new document (e.g. someone called document.adoptNode(el)).
|
|
@@ -967,6 +1031,168 @@ function initFromCallbackHost() {
|
|
|
967
1031
|
callControlCallback.call(this, callBackFunctionName);
|
|
968
1032
|
}
|
|
969
1033
|
|
|
1034
|
+
/**
|
|
1035
|
+
* Relinks a previously assembled element after it was reconnected.
|
|
1036
|
+
*
|
|
1037
|
+
* Deliberately does not touch the shadow root, the rendered markup, the options
|
|
1038
|
+
* snapshot or anything a derived assemble method built. It only recreates what
|
|
1039
|
+
* the teardown released: the updaters and the observers.
|
|
1040
|
+
*
|
|
1041
|
+
* @private
|
|
1042
|
+
* @this CustomElement
|
|
1043
|
+
*/
|
|
1044
|
+
function rebindAfterReconnect() {
|
|
1045
|
+
let elements = getManagedShadowRoot.call(this)?.childNodes;
|
|
1046
|
+
if (!(elements instanceof NodeList)) {
|
|
1047
|
+
elements = this.childNodes;
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
let nodeList;
|
|
1051
|
+
try {
|
|
1052
|
+
nodeList = new Set([
|
|
1053
|
+
...elements,
|
|
1054
|
+
...getSlottedElements.call(
|
|
1055
|
+
this,
|
|
1056
|
+
undefined,
|
|
1057
|
+
undefined,
|
|
1058
|
+
getManagedShadowRoot.call(this),
|
|
1059
|
+
),
|
|
1060
|
+
]);
|
|
1061
|
+
} catch {
|
|
1062
|
+
nodeList = elements;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
const cfg = {};
|
|
1066
|
+
if (this.getOption("eventProcessing") === true) {
|
|
1067
|
+
cfg.eventProcessing = true;
|
|
1068
|
+
}
|
|
1069
|
+
if (this.getOption("updater.batchUpdates") === true) {
|
|
1070
|
+
cfg.batchUpdates = true;
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
// Binds to the same snapshot object on purpose: it carries the runtime state
|
|
1074
|
+
// the updaters produced, including values written back by event processing,
|
|
1075
|
+
// which the component options never receive.
|
|
1076
|
+
addObjectWithUpdaterToElement.call(
|
|
1077
|
+
this,
|
|
1078
|
+
nodeList,
|
|
1079
|
+
customElementUpdaterLinkSymbol,
|
|
1080
|
+
this[updateCloneDataSymbol],
|
|
1081
|
+
cfg,
|
|
1082
|
+
);
|
|
1083
|
+
|
|
1084
|
+
attachAttributeChangeMutationObserver.call(this);
|
|
1085
|
+
attachChildMutationObserver.call(this);
|
|
1086
|
+
|
|
1087
|
+
if (isFunction(this[internalSymbol]?.syncDisabledState)) {
|
|
1088
|
+
this[internalSymbol].syncDisabledState(true);
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
syncVisibilityState.call(this);
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
/**
|
|
1095
|
+
* @private
|
|
1096
|
+
* @this CustomElement
|
|
1097
|
+
*/
|
|
1098
|
+
function runTeardownCallbacks() {
|
|
1099
|
+
const callbacks = this[teardownCallbacksSymbol];
|
|
1100
|
+
if (!isArray(callbacks) || callbacks.length === 0) {
|
|
1101
|
+
return;
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
// taken first, so a callback that re-registers during teardown does not run
|
|
1105
|
+
// in the same pass
|
|
1106
|
+
this[teardownCallbacksSymbol] = [];
|
|
1107
|
+
|
|
1108
|
+
for (const callback of callbacks) {
|
|
1109
|
+
try {
|
|
1110
|
+
callback.call(this);
|
|
1111
|
+
} catch (e) {
|
|
1112
|
+
addErrorAttribute(this, e);
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
/**
|
|
1118
|
+
* @private
|
|
1119
|
+
* @this CustomElement
|
|
1120
|
+
*/
|
|
1121
|
+
function disposeLinkedUpdaters() {
|
|
1122
|
+
if (!hasObjectLink(this, customElementUpdaterLinkSymbol)) {
|
|
1123
|
+
return;
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
try {
|
|
1127
|
+
for (const group of getLinkedObjects(
|
|
1128
|
+
this,
|
|
1129
|
+
customElementUpdaterLinkSymbol,
|
|
1130
|
+
)) {
|
|
1131
|
+
if (!(group instanceof Set)) {
|
|
1132
|
+
continue;
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
for (const updater of group) {
|
|
1136
|
+
if (typeof updater?.dispose !== "function") {
|
|
1137
|
+
continue;
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
try {
|
|
1141
|
+
updater.dispose();
|
|
1142
|
+
} catch (e) {
|
|
1143
|
+
addErrorAttribute(this, e);
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
removeObjectLink(this, customElementUpdaterLinkSymbol);
|
|
1149
|
+
} catch (e) {
|
|
1150
|
+
addErrorAttribute(this, e);
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
/**
|
|
1155
|
+
* The symbol is deleted, not only disconnected: the attach guard treats a
|
|
1156
|
+
* present symbol as an active observer and would otherwise never replace it.
|
|
1157
|
+
*
|
|
1158
|
+
* @private
|
|
1159
|
+
* @this CustomElement
|
|
1160
|
+
*/
|
|
1161
|
+
function detachAttributeChangeMutationObserver() {
|
|
1162
|
+
const observer = this[attributeMutationObserverSymbol];
|
|
1163
|
+
if (observer) {
|
|
1164
|
+
try {
|
|
1165
|
+
observer.disconnect();
|
|
1166
|
+
} catch (e) {
|
|
1167
|
+
addErrorAttribute(this, e);
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
delete this[attributeMutationObserverSymbol];
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
/**
|
|
1175
|
+
* @private
|
|
1176
|
+
* @this CustomElement
|
|
1177
|
+
*/
|
|
1178
|
+
function detachChildMutationObserver() {
|
|
1179
|
+
const observer = this[childMutationObserverSymbol];
|
|
1180
|
+
if (observer) {
|
|
1181
|
+
try {
|
|
1182
|
+
observer.disconnect();
|
|
1183
|
+
} catch (e) {
|
|
1184
|
+
addErrorAttribute(this, e);
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
delete this[childMutationObserverSymbol];
|
|
1189
|
+
|
|
1190
|
+
if (this[childMutationTimerSymbol]) {
|
|
1191
|
+
clearTimeout(this[childMutationTimerSymbol]);
|
|
1192
|
+
this[childMutationTimerSymbol] = null;
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
|
|
970
1196
|
/**
|
|
971
1197
|
* This method is called when the element is first created.
|
|
972
1198
|
*
|
|
@@ -1095,12 +1321,21 @@ function containChildNode(node) {
|
|
|
1095
1321
|
return true;
|
|
1096
1322
|
}
|
|
1097
1323
|
|
|
1098
|
-
|
|
1099
|
-
|
|
1324
|
+
// Node.contains() already covers the whole light DOM subtree, so the only
|
|
1325
|
+
// thing left to search is the shadow root of nested elements, which no
|
|
1326
|
+
// contains() call can see.
|
|
1327
|
+
for (const child of this.childNodes) {
|
|
1328
|
+
const shadowRoot = child?.shadowRoot;
|
|
1329
|
+
if (
|
|
1330
|
+
shadowRoot instanceof ShadowRoot &&
|
|
1331
|
+
containChildNode.call(shadowRoot, node)
|
|
1332
|
+
) {
|
|
1100
1333
|
return true;
|
|
1101
1334
|
}
|
|
1102
1335
|
|
|
1103
|
-
containChildNode.call(
|
|
1336
|
+
if (child.childNodes?.length && containChildNode.call(child, node)) {
|
|
1337
|
+
return true;
|
|
1338
|
+
}
|
|
1104
1339
|
}
|
|
1105
1340
|
|
|
1106
1341
|
return false;
|
|
@@ -1133,7 +1368,12 @@ function initOptionObserver() {
|
|
|
1133
1368
|
targets.add(element);
|
|
1134
1369
|
}
|
|
1135
1370
|
}
|
|
1136
|
-
for (const element of getSlottedElements.call(
|
|
1371
|
+
for (const element of getSlottedElements.call(
|
|
1372
|
+
self,
|
|
1373
|
+
disabledQuery,
|
|
1374
|
+
undefined,
|
|
1375
|
+
getManagedShadowRoot.call(self),
|
|
1376
|
+
)) {
|
|
1137
1377
|
targets.add(element);
|
|
1138
1378
|
}
|
|
1139
1379
|
} catch (e) {}
|
|
@@ -1220,6 +1460,8 @@ function initOptionObserver() {
|
|
|
1220
1460
|
self.attachObserver(new Observer(syncDisabledState));
|
|
1221
1461
|
|
|
1222
1462
|
self[internalSymbol].syncDisabledState = syncDisabledState;
|
|
1463
|
+
self[internalSymbol].disconnectDisabledObservers =
|
|
1464
|
+
disconnectDisabledObservers;
|
|
1223
1465
|
|
|
1224
1466
|
let updaterSyncRevision = 0;
|
|
1225
1467
|
self.attachObserver(
|
|
@@ -1293,6 +1535,17 @@ function syncUpdaterSubject(target, source) {
|
|
|
1293
1535
|
return;
|
|
1294
1536
|
}
|
|
1295
1537
|
|
|
1538
|
+
// Empty option objects are also used as refresh markers by editable
|
|
1539
|
+
// datasource components. Keep their established merge behavior while
|
|
1540
|
+
// allowing non-empty replacement objects to remove stale keys.
|
|
1541
|
+
if (Object.keys(source).length > 0) {
|
|
1542
|
+
for (const key of Object.keys(target)) {
|
|
1543
|
+
if (!Object.hasOwn(source, key)) {
|
|
1544
|
+
delete target[key];
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1296
1549
|
for (const [key, value] of Object.entries(source)) {
|
|
1297
1550
|
syncUpdaterValue(target, key, value);
|
|
1298
1551
|
}
|
|
@@ -1679,6 +1932,15 @@ function initShadowRoot() {
|
|
|
1679
1932
|
let template;
|
|
1680
1933
|
let html;
|
|
1681
1934
|
|
|
1935
|
+
// A rebind keeps the rendered shadow DOM. Calling attachShadow() again would
|
|
1936
|
+
// throw, after which the main template used to be appended into the light
|
|
1937
|
+
// DOM and a second updater set was bound to the same host.
|
|
1938
|
+
const existing = getManagedShadowRoot.call(this);
|
|
1939
|
+
if (existing instanceof ShadowRoot) {
|
|
1940
|
+
this[managedShadowRootSymbol] = existing;
|
|
1941
|
+
return this;
|
|
1942
|
+
}
|
|
1943
|
+
|
|
1682
1944
|
try {
|
|
1683
1945
|
template = findDocumentTemplate(this.constructor.getTag());
|
|
1684
1946
|
} catch (e) {
|
package/source/dom/slotted.mjs
CHANGED
|
@@ -27,37 +27,33 @@ export { getSlottedElements, getSlottedNodes };
|
|
|
27
27
|
* @since 3.33.0
|
|
28
28
|
* @throws {Error} query must be a string
|
|
29
29
|
*/
|
|
30
|
-
function getSlottedNodes(query, name) {
|
|
30
|
+
function getSlottedNodes(query, name, managedShadowRoot) {
|
|
31
31
|
const result = new Set();
|
|
32
|
+
const shadowRoot = getShadowRoot(this, managedShadowRoot);
|
|
32
33
|
|
|
33
|
-
if (!
|
|
34
|
+
if (!(shadowRoot instanceof ShadowRoot)) {
|
|
34
35
|
return result;
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
if (name !== undefined) {
|
|
39
|
-
if (name === null) {
|
|
40
|
-
selector += ":not([name])";
|
|
41
|
-
} else {
|
|
42
|
-
selector += `[name=${validateString(name)}]`;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const slots = this.shadowRoot.querySelectorAll(selector);
|
|
38
|
+
const slots = getSlotsByName(shadowRoot, name);
|
|
47
39
|
|
|
48
|
-
for (const
|
|
40
|
+
for (const slot of slots) {
|
|
49
41
|
slot.assignedNodes().forEach(function (node) {
|
|
50
42
|
if (node === null || node === undefined) {
|
|
51
43
|
return;
|
|
52
44
|
}
|
|
53
45
|
|
|
54
46
|
if (isString(query)) {
|
|
55
|
-
|
|
56
|
-
result.add(n);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
if (node.matches(query)) {
|
|
47
|
+
if (query.length === 0) {
|
|
60
48
|
result.add(node);
|
|
49
|
+
} else if (node instanceof Element) {
|
|
50
|
+
node.querySelectorAll(query).forEach(function (n) {
|
|
51
|
+
result.add(n);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
if (node.matches(query)) {
|
|
55
|
+
result.add(node);
|
|
56
|
+
}
|
|
61
57
|
}
|
|
62
58
|
} else if (query !== undefined) {
|
|
63
59
|
throw new Error("query must be a string");
|
|
@@ -80,25 +76,17 @@ function getSlottedNodes(query, name) {
|
|
|
80
76
|
* @since 1.23.0
|
|
81
77
|
* @throws {Error} query must be a string
|
|
82
78
|
*/
|
|
83
|
-
function getSlottedElements(query, name) {
|
|
79
|
+
function getSlottedElements(query, name, managedShadowRoot) {
|
|
84
80
|
const result = new Set();
|
|
81
|
+
const shadowRoot = getShadowRoot(this, managedShadowRoot);
|
|
85
82
|
|
|
86
|
-
if (!(
|
|
83
|
+
if (!(shadowRoot instanceof ShadowRoot)) {
|
|
87
84
|
return result;
|
|
88
85
|
}
|
|
89
86
|
|
|
90
|
-
|
|
91
|
-
if (name !== undefined) {
|
|
92
|
-
if (name === null) {
|
|
93
|
-
selector += ":not([name])";
|
|
94
|
-
} else {
|
|
95
|
-
selector += `[name=${validateString(name)}]`;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const slots = this.shadowRoot.querySelectorAll(selector);
|
|
87
|
+
const slots = getSlotsByName(shadowRoot, name);
|
|
100
88
|
|
|
101
|
-
for (const
|
|
89
|
+
for (const slot of slots) {
|
|
102
90
|
slot.assignedElements().forEach(function (node) {
|
|
103
91
|
if (
|
|
104
92
|
!(node instanceof HTMLElement) &&
|
|
@@ -129,3 +117,30 @@ function getSlottedElements(query, name) {
|
|
|
129
117
|
|
|
130
118
|
return result;
|
|
131
119
|
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* @param {*} host
|
|
123
|
+
* @param {*} managedShadowRoot
|
|
124
|
+
* @return {ShadowRoot|null}
|
|
125
|
+
* @private
|
|
126
|
+
*/
|
|
127
|
+
function getShadowRoot(host, managedShadowRoot) {
|
|
128
|
+
if (managedShadowRoot instanceof ShadowRoot) return managedShadowRoot;
|
|
129
|
+
return host?.shadowRoot instanceof ShadowRoot ? host.shadowRoot : null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Filter slots by their DOM name instead of interpolating an unsafe selector.
|
|
134
|
+
* @param {ShadowRoot} shadowRoot
|
|
135
|
+
* @param {string|undefined|null} name
|
|
136
|
+
* @return {HTMLSlotElement[]}
|
|
137
|
+
* @private
|
|
138
|
+
*/
|
|
139
|
+
function getSlotsByName(shadowRoot, name) {
|
|
140
|
+
if (name !== undefined && name !== null) validateString(name);
|
|
141
|
+
return Array.from(shadowRoot.querySelectorAll("slot")).filter((slot) => {
|
|
142
|
+
if (name === undefined) return true;
|
|
143
|
+
if (name === null) return slot.name === "";
|
|
144
|
+
return slot.name === name;
|
|
145
|
+
});
|
|
146
|
+
}
|
package/source/dom/template.mjs
CHANGED
|
@@ -156,7 +156,10 @@ export function findDocumentTemplate(id, currentNode) {
|
|
|
156
156
|
)
|
|
157
157
|
) {
|
|
158
158
|
if (currentNode instanceof Node) {
|
|
159
|
-
if (
|
|
159
|
+
if (
|
|
160
|
+
currentNode instanceof Element &&
|
|
161
|
+
currentNode.hasAttribute(ATTRIBUTE_TEMPLATE_PREFIX)
|
|
162
|
+
) {
|
|
160
163
|
prefixID = currentNode.getAttribute(ATTRIBUTE_TEMPLATE_PREFIX);
|
|
161
164
|
}
|
|
162
165
|
|