@schukai/monster 4.46.5 → 4.46.6
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 +8 -0
- package/package.json +1 -1
- package/source/dom/updater.mjs +75 -61
package/CHANGELOG.md
CHANGED
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.46.
|
|
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.46.6"}
|
package/source/dom/updater.mjs
CHANGED
|
@@ -72,6 +72,12 @@ const pendingDiffsSymbol = Symbol("pendingDiffs");
|
|
|
72
72
|
*/
|
|
73
73
|
const processingSymbol = Symbol("processing");
|
|
74
74
|
|
|
75
|
+
/**
|
|
76
|
+
* @private
|
|
77
|
+
* Performance optimization: static Set for boolean checks
|
|
78
|
+
*/
|
|
79
|
+
const TRUE_VALUES = new Set(["true", "1", "on"]);
|
|
80
|
+
|
|
75
81
|
/**
|
|
76
82
|
* The updater class connects an object with the DOM. In this way, structures and contents in the DOM can be
|
|
77
83
|
* programmatically adapted via attributes.
|
|
@@ -194,7 +200,7 @@ class Updater extends Base {
|
|
|
194
200
|
*
|
|
195
201
|
* ```js
|
|
196
202
|
* updater.run().then(() => {
|
|
197
|
-
*
|
|
203
|
+
* updater.enableEventProcessing();
|
|
198
204
|
* });
|
|
199
205
|
* ```
|
|
200
206
|
*
|
|
@@ -243,7 +249,7 @@ class Updater extends Base {
|
|
|
243
249
|
*
|
|
244
250
|
* ```js
|
|
245
251
|
* updater.run().then(() => {
|
|
246
|
-
*
|
|
252
|
+
* updater.enableEventProcessing();
|
|
247
253
|
* });
|
|
248
254
|
* ```
|
|
249
255
|
*
|
|
@@ -308,8 +314,9 @@ function getCheckStateCallback() {
|
|
|
308
314
|
return function (current) {
|
|
309
315
|
// this is a reference to the current object (therefore no array function here)
|
|
310
316
|
if (this instanceof HTMLInputElement) {
|
|
311
|
-
if (["radio", "checkbox"].
|
|
312
|
-
|
|
317
|
+
if (["radio", "checkbox"].includes(this.type)) {
|
|
318
|
+
if (current == null) return undefined;
|
|
319
|
+
return String(this.value) === String(current) ? "true" : undefined;
|
|
313
320
|
}
|
|
314
321
|
} else if (this instanceof HTMLOptionElement) {
|
|
315
322
|
if (isArray(current) && current.indexOf(this.value) !== -1) {
|
|
@@ -447,18 +454,21 @@ function retrieveAndSetValue(element) {
|
|
|
447
454
|
case "int":
|
|
448
455
|
case "float":
|
|
449
456
|
case "integer":
|
|
450
|
-
|
|
451
|
-
if (
|
|
452
|
-
value =
|
|
457
|
+
const num = Number(value);
|
|
458
|
+
if (value === "" || value === null || value === undefined) {
|
|
459
|
+
value = undefined;
|
|
460
|
+
} else if (Number.isNaN(num)) {
|
|
461
|
+
value = undefined;
|
|
462
|
+
} else {
|
|
463
|
+
value = num;
|
|
453
464
|
}
|
|
454
465
|
break;
|
|
455
466
|
case "boolean":
|
|
456
467
|
case "bool":
|
|
457
468
|
case "checkbox":
|
|
458
|
-
|
|
459
|
-
|
|
469
|
+
// Use static set (Performance fix)
|
|
470
|
+
value = TRUE_VALUES.has(String(value).toLowerCase()) || value === true;
|
|
460
471
|
break;
|
|
461
|
-
|
|
462
472
|
case "string[]":
|
|
463
473
|
case "[]string":
|
|
464
474
|
if (isString(value)) {
|
|
@@ -509,11 +519,14 @@ function retrieveAndSetValue(element) {
|
|
|
509
519
|
case "object":
|
|
510
520
|
case "json":
|
|
511
521
|
if (isString(value)) {
|
|
512
|
-
|
|
522
|
+
try {
|
|
523
|
+
value = JSON.parse(value);
|
|
524
|
+
} catch (e) {
|
|
525
|
+
throw new Error("unsupported value for object");
|
|
526
|
+
}
|
|
513
527
|
} else {
|
|
514
528
|
throw new Error("unsupported value for object");
|
|
515
529
|
}
|
|
516
|
-
|
|
517
530
|
break;
|
|
518
531
|
default:
|
|
519
532
|
break;
|
|
@@ -535,21 +548,11 @@ function retrieveAndSetValue(element) {
|
|
|
535
548
|
* @private
|
|
536
549
|
*/
|
|
537
550
|
function parseIntArray(val) {
|
|
538
|
-
if (
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
.map((v) => parseInt(v, 10))
|
|
544
|
-
.filter((v) => !isNaN(v));
|
|
545
|
-
} else if (isInteger(val)) {
|
|
546
|
-
return [val];
|
|
547
|
-
} else if (val === undefined || val === null) {
|
|
548
|
-
return [];
|
|
549
|
-
} else if (isArray(val)) {
|
|
550
|
-
return val.map((v) => parseInt(v, 10)).filter((v) => !isNaN(v));
|
|
551
|
-
}
|
|
552
|
-
throw new Error("unsupported value for int array");
|
|
551
|
+
if (val === undefined || val === null) return [];
|
|
552
|
+
|
|
553
|
+
const list = isArray(val) ? val : String(val).split(",");
|
|
554
|
+
|
|
555
|
+
return list.map((v) => parseInt(v, 10)).filter((v) => !Number.isNaN(v));
|
|
553
556
|
}
|
|
554
557
|
|
|
555
558
|
/**
|
|
@@ -830,18 +833,11 @@ function runUpdateContent(container, parts, subject) {
|
|
|
830
833
|
|
|
831
834
|
// Unfortunately, static data is always changed as well, since it is not possible to react to changes here.
|
|
832
835
|
const query = `[${ATTRIBUTE_UPDATER_REPLACE}^="path:${current}"], [${ATTRIBUTE_UPDATER_REPLACE}^="static:"], [${ATTRIBUTE_UPDATER_REPLACE}^="i18n:"]`;
|
|
833
|
-
const e = container.querySelectorAll(`${query}`);
|
|
834
|
-
|
|
835
|
-
const iterator = new Set([...e]);
|
|
836
836
|
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
}
|
|
837
|
+
// Performance optimization: avoid new Set([...NodeList])
|
|
838
|
+
const elements = container.querySelectorAll(`${query}`);
|
|
840
839
|
|
|
841
|
-
|
|
842
|
-
* @type {HTMLElement}
|
|
843
|
-
*/
|
|
844
|
-
for (const [element] of iterator.entries()) {
|
|
840
|
+
const process = (element) => {
|
|
845
841
|
if (mem.has(element)) return;
|
|
846
842
|
mem.add(element);
|
|
847
843
|
|
|
@@ -874,6 +870,16 @@ function runUpdateContent(container, parts, subject) {
|
|
|
874
870
|
} else {
|
|
875
871
|
element.innerHTML = value;
|
|
876
872
|
}
|
|
873
|
+
};
|
|
874
|
+
|
|
875
|
+
// Iterate NodeList directly
|
|
876
|
+
for (const element of elements) {
|
|
877
|
+
process(element);
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
// Check container
|
|
881
|
+
if (container.matches(query)) {
|
|
882
|
+
process(container);
|
|
877
883
|
}
|
|
878
884
|
}
|
|
879
885
|
}
|
|
@@ -908,27 +914,18 @@ function runUpdateAttributes(container, parts, subject) {
|
|
|
908
914
|
const current = parts.join(".");
|
|
909
915
|
parts.pop();
|
|
910
916
|
|
|
911
|
-
let iterator = new Set();
|
|
912
|
-
|
|
913
917
|
const query = `[${ATTRIBUTE_UPDATER_SELECT_THIS}][${ATTRIBUTE_UPDATER_ATTRIBUTES}], [${ATTRIBUTE_UPDATER_ATTRIBUTES}*="path:${current}"], [${ATTRIBUTE_UPDATER_ATTRIBUTES}^="static:"], [${ATTRIBUTE_UPDATER_ATTRIBUTES}^="i18n:"]`;
|
|
914
918
|
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
if (e.length > 0) {
|
|
918
|
-
iterator = new Set([...e]);
|
|
919
|
-
}
|
|
920
|
-
|
|
921
|
-
if (container.matches(query)) {
|
|
922
|
-
iterator.add(container);
|
|
923
|
-
}
|
|
919
|
+
// Performance optimization: avoid new Set([...NodeList])
|
|
920
|
+
const elements = container.querySelectorAll(query);
|
|
924
921
|
|
|
925
|
-
|
|
922
|
+
const process = (element) => {
|
|
926
923
|
if (mem.has(element)) return;
|
|
927
924
|
mem.add(element);
|
|
928
925
|
|
|
929
926
|
// this case occurs when the ATTRIBUTE_UPDATER_SELECT_THIS attribute is set
|
|
930
927
|
if (!element.hasAttribute(ATTRIBUTE_UPDATER_ATTRIBUTES)) {
|
|
931
|
-
|
|
928
|
+
return;
|
|
932
929
|
}
|
|
933
930
|
|
|
934
931
|
const attributes = element.getAttribute(ATTRIBUTE_UPDATER_ATTRIBUTES);
|
|
@@ -967,6 +964,16 @@ function runUpdateAttributes(container, parts, subject) {
|
|
|
967
964
|
}
|
|
968
965
|
handleInputControlAttributeUpdate.call(this, element, name, value);
|
|
969
966
|
}
|
|
967
|
+
};
|
|
968
|
+
|
|
969
|
+
// Iterate NodeList directly
|
|
970
|
+
for (const element of elements) {
|
|
971
|
+
process(element);
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
// Check container
|
|
975
|
+
if (container.matches(query)) {
|
|
976
|
+
process(container);
|
|
970
977
|
}
|
|
971
978
|
}
|
|
972
979
|
}
|
|
@@ -979,52 +986,59 @@ function runUpdateAttributes(container, parts, subject) {
|
|
|
979
986
|
* @return {void}
|
|
980
987
|
* @this Updater
|
|
981
988
|
*/
|
|
982
|
-
|
|
983
989
|
function handleInputControlAttributeUpdate(element, name, value) {
|
|
990
|
+
// Prevent NaN warnings by normalizing invalid numbers to undefined
|
|
991
|
+
if (typeof value === "number" && isNaN(value)) {
|
|
992
|
+
value = undefined;
|
|
993
|
+
}
|
|
994
|
+
|
|
984
995
|
if (element instanceof HTMLSelectElement) {
|
|
985
996
|
switch (element.type) {
|
|
986
997
|
case "select-multiple":
|
|
987
|
-
|
|
988
|
-
|
|
998
|
+
// Ensure value is an array before calling indexOf to avoid errors
|
|
999
|
+
if (Array.isArray(value) || typeof value === "string") {
|
|
1000
|
+
for (const [index, opt] of Object.entries(element.options)) {
|
|
1001
|
+
opt.selected = value.indexOf(opt.value) !== -1;
|
|
1002
|
+
}
|
|
989
1003
|
}
|
|
990
|
-
|
|
991
1004
|
break;
|
|
992
1005
|
case "select-one":
|
|
993
1006
|
// Only one value may be selected
|
|
994
|
-
|
|
995
1007
|
for (const [index, opt] of Object.entries(element.options)) {
|
|
996
|
-
if (opt.value
|
|
1008
|
+
if (opt.value == value) {
|
|
1009
|
+
// Loose equality to match string numbers
|
|
997
1010
|
element.selectedIndex = index;
|
|
998
1011
|
break;
|
|
999
1012
|
}
|
|
1000
1013
|
}
|
|
1001
|
-
|
|
1002
1014
|
break;
|
|
1003
1015
|
}
|
|
1004
1016
|
} else if (element instanceof HTMLInputElement) {
|
|
1005
1017
|
switch (element.type) {
|
|
1006
1018
|
case "radio":
|
|
1007
1019
|
if (name === "checked") {
|
|
1008
|
-
element.checked = value !== undefined;
|
|
1020
|
+
element.checked = value !== undefined && value !== null;
|
|
1009
1021
|
}
|
|
1010
1022
|
break;
|
|
1011
1023
|
|
|
1012
1024
|
case "checkbox":
|
|
1013
1025
|
if (name === "checked") {
|
|
1014
|
-
element.checked = value !== undefined;
|
|
1026
|
+
element.checked = value !== undefined && value !== null;
|
|
1015
1027
|
}
|
|
1016
1028
|
break;
|
|
1017
1029
|
|
|
1018
1030
|
case "text":
|
|
1019
1031
|
default:
|
|
1020
1032
|
if (name === "value") {
|
|
1021
|
-
|
|
1033
|
+
// Check for undefined, null, or NaN
|
|
1034
|
+
element.value = value === undefined || value === null ? "" : value;
|
|
1022
1035
|
}
|
|
1023
1036
|
break;
|
|
1024
1037
|
}
|
|
1025
1038
|
} else if (element instanceof HTMLTextAreaElement) {
|
|
1026
1039
|
if (name === "value") {
|
|
1027
|
-
|
|
1040
|
+
// Check for undefined, null, or NaN
|
|
1041
|
+
element.value = value === undefined || value === null ? "" : value;
|
|
1028
1042
|
}
|
|
1029
1043
|
}
|
|
1030
1044
|
}
|