@schukai/monster 4.74.3 → 4.76.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,22 @@
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
|
|
5
|
+
## [4.76.0] - 2026-01-05
|
|
6
|
+
|
|
7
|
+
### Add Features
|
|
8
|
+
|
|
9
|
+
- Add tab control feature for issue [#362](https://gitlab.schukai.com/oss/libraries/javascript/monster/issues/362)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
## [4.75.0] - 2026-01-04
|
|
14
|
+
|
|
15
|
+
### Add Features
|
|
16
|
+
|
|
17
|
+
- Add demo for tracking click events with enhanced datasource behavior
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
5
21
|
## [4.74.3] - 2026-01-03
|
|
6
22
|
|
|
7
23
|
### Bug Fixes
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"author":"Volker Schukai","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":"Volker Schukai","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.76.0"}
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import { instanceSymbol } from "../../constants.mjs";
|
|
16
|
+
import { diff } from "../../data/diff.mjs";
|
|
16
17
|
import { Pathfinder } from "../../data/pathfinder.mjs";
|
|
17
18
|
import {
|
|
18
19
|
assembleMethodSymbol,
|
|
@@ -121,6 +122,8 @@ class DataSet extends CustomElement {
|
|
|
121
122
|
* @type {boolean}
|
|
122
123
|
*/
|
|
123
124
|
refreshOnMutation: true,
|
|
125
|
+
// Feature flag: default-on no-op write suppression, can be rolled back.
|
|
126
|
+
skipNoopWrite: true,
|
|
124
127
|
},
|
|
125
128
|
|
|
126
129
|
/**
|
|
@@ -201,6 +204,22 @@ class DataSet extends CustomElement {
|
|
|
201
204
|
pathWithIndex = String(index);
|
|
202
205
|
}
|
|
203
206
|
|
|
207
|
+
if (this.getOption("features.skipNoopWrite") === true) {
|
|
208
|
+
const currentData = this[datasourceLinkedElementSymbol]?.data;
|
|
209
|
+
let currentValue;
|
|
210
|
+
if (currentData) {
|
|
211
|
+
try {
|
|
212
|
+
currentValue = new Pathfinder(currentData).getVia(pathWithIndex);
|
|
213
|
+
} catch (e) {
|
|
214
|
+
currentValue = undefined;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (diff(currentValue, internalData).length === 0) {
|
|
218
|
+
resolve();
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
204
223
|
if (this.getOption("logLevel") === "debug") {
|
|
205
224
|
console.log("monster-dataset: write", {
|
|
206
225
|
path: pathWithIndex,
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
CustomElement,
|
|
17
17
|
assembleMethodSymbol,
|
|
18
18
|
} from "../../dom/customelement.mjs";
|
|
19
|
+
import { diff } from "../../data/diff.mjs";
|
|
19
20
|
import { Datasource as DatasourceBase } from "../../data/datasource.mjs";
|
|
20
21
|
import { instanceSymbol } from "../../constants.mjs";
|
|
21
22
|
|
|
@@ -70,6 +71,10 @@ class Datasource extends CustomElement {
|
|
|
70
71
|
get defaults() {
|
|
71
72
|
return Object.assign({}, super.defaults, {
|
|
72
73
|
logLevel: "error",
|
|
74
|
+
features: {
|
|
75
|
+
// Feature flag: default-on no-op event suppression, can be rolled back.
|
|
76
|
+
skipNoopEvents: true,
|
|
77
|
+
},
|
|
73
78
|
});
|
|
74
79
|
}
|
|
75
80
|
|
|
@@ -98,6 +103,12 @@ class Datasource extends CustomElement {
|
|
|
98
103
|
data: data,
|
|
99
104
|
});
|
|
100
105
|
}
|
|
106
|
+
if (this.getOption("features.skipNoopEvents") === true) {
|
|
107
|
+
const current = this[dataSourceSymbol].get();
|
|
108
|
+
if (diff(current, data).length === 0) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
101
112
|
this[dataSourceSymbol].set(data);
|
|
102
113
|
}
|
|
103
114
|
|
|
@@ -19,6 +19,7 @@ import { parseDataURL } from "../types/dataurl.mjs";
|
|
|
19
19
|
import { isString } from "../types/is.mjs";
|
|
20
20
|
import { ProxyObserver } from "../types/proxyobserver.mjs";
|
|
21
21
|
import { validateObject } from "../types/validate.mjs";
|
|
22
|
+
import { diff } from "./diff.mjs";
|
|
22
23
|
import { extend } from "./extend.mjs";
|
|
23
24
|
import { Pathfinder } from "./pathfinder.mjs";
|
|
24
25
|
|
|
@@ -109,7 +110,12 @@ class Datasource extends Base {
|
|
|
109
110
|
* ```
|
|
110
111
|
*/
|
|
111
112
|
get defaults() {
|
|
112
|
-
return {
|
|
113
|
+
return {
|
|
114
|
+
features: {
|
|
115
|
+
// Feature flag: default-on no-op event suppression, can be rolled back.
|
|
116
|
+
skipNoopEvents: true,
|
|
117
|
+
},
|
|
118
|
+
};
|
|
113
119
|
}
|
|
114
120
|
|
|
115
121
|
/**
|
|
@@ -195,6 +201,12 @@ class Datasource extends Base {
|
|
|
195
201
|
* @return {Datasource}
|
|
196
202
|
*/
|
|
197
203
|
set(data) {
|
|
204
|
+
if (this.getOption("features.skipNoopEvents") === true) {
|
|
205
|
+
const current = this[internalDataSymbol].getRealSubject();
|
|
206
|
+
if (diff(current, data).length === 0) {
|
|
207
|
+
return this;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
198
210
|
this[internalDataSymbol].setSubject(data);
|
|
199
211
|
return this;
|
|
200
212
|
}
|
package/source/dom/util.mjs
CHANGED
|
@@ -23,6 +23,7 @@ export {
|
|
|
23
23
|
getContainingDocument,
|
|
24
24
|
getRegisteredCustomElements,
|
|
25
25
|
findElementWithSelectorUpwards,
|
|
26
|
+
waitForCustomElement,
|
|
26
27
|
};
|
|
27
28
|
|
|
28
29
|
/**
|
|
@@ -323,3 +324,72 @@ function getRegisteredCustomElements() {
|
|
|
323
324
|
|
|
324
325
|
return Array.from(new Set(customElementTags));
|
|
325
326
|
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Waits until a specific custom element instance is defined and upgraded.
|
|
330
|
+
*
|
|
331
|
+
* @param {HTMLElement} element
|
|
332
|
+
* @param {Object} [options]
|
|
333
|
+
* @param {string} [options.method] - Optional method to wait for on the instance.
|
|
334
|
+
* @param {string} [options.tagName] - Optional tag name override.
|
|
335
|
+
* @param {number|null} [options.timeout=2000] - Timeout in ms; set null to disable.
|
|
336
|
+
* @return {Promise<HTMLElement>}
|
|
337
|
+
* @since 4.1.0
|
|
338
|
+
*/
|
|
339
|
+
function waitForCustomElement(
|
|
340
|
+
element,
|
|
341
|
+
{ method = null, tagName = null, timeout = 2000 } = {},
|
|
342
|
+
) {
|
|
343
|
+
if (!(element instanceof HTMLElement)) {
|
|
344
|
+
return Promise.reject(
|
|
345
|
+
new Error("Invalid argument. Expected an HTMLElement."),
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const name = (tagName || element.tagName || "").toLowerCase();
|
|
350
|
+
if (!name.includes("-")) {
|
|
351
|
+
return Promise.reject(
|
|
352
|
+
new Error("Invalid argument. Expected a custom element tag name."),
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const window = getWindow();
|
|
357
|
+
const registry = window.customElements;
|
|
358
|
+
if (!registry) {
|
|
359
|
+
return Promise.reject(new Error("customElements is not supported."));
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
return registry.whenDefined(name).then(
|
|
363
|
+
() =>
|
|
364
|
+
new Promise((resolve, reject) => {
|
|
365
|
+
if (typeof registry.upgrade === "function") {
|
|
366
|
+
registry.upgrade(element);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const start = Date.now();
|
|
370
|
+
const check = () => {
|
|
371
|
+
if (!method || typeof element[method] === "function") {
|
|
372
|
+
resolve(element);
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (
|
|
377
|
+
typeof timeout === "number" &&
|
|
378
|
+
timeout >= 0 &&
|
|
379
|
+
Date.now() - start > timeout
|
|
380
|
+
) {
|
|
381
|
+
reject(
|
|
382
|
+
new Error(
|
|
383
|
+
`Timed out waiting for "${method}" on <${name}>.`,
|
|
384
|
+
),
|
|
385
|
+
);
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
window.requestAnimationFrame(check);
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
window.requestAnimationFrame(check);
|
|
393
|
+
}),
|
|
394
|
+
);
|
|
395
|
+
}
|
package/test/cases/dom/util.mjs
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
|
-
getDocument,
|
|
2
|
+
getDocument,
|
|
3
|
+
getWindow,
|
|
4
|
+
getDocumentFragmentFromString,
|
|
5
|
+
waitForCustomElement,
|
|
3
6
|
} from "../../../source/dom/util.mjs";
|
|
4
7
|
|
|
5
8
|
import {getContainingDocument} from "../../../source/dom/util.mjs";
|
|
@@ -111,5 +114,27 @@ describe('DOM', function () {
|
|
|
111
114
|
expect(containingDocument).to.be.null;
|
|
112
115
|
});
|
|
113
116
|
});
|
|
117
|
+
|
|
118
|
+
describe('waitForCustomElement()', () => {
|
|
119
|
+
it('should resolve for a specific instance once defined', async () => {
|
|
120
|
+
const tagName = `test-tabs-${Date.now()}`;
|
|
121
|
+
const element = document.createElement(tagName);
|
|
122
|
+
document.body.appendChild(element);
|
|
123
|
+
|
|
124
|
+
const promise = waitForCustomElement(element, { method: 'ping' });
|
|
125
|
+
|
|
126
|
+
class TestTabs extends HTMLElement {
|
|
127
|
+
ping() {
|
|
128
|
+
return 'pong';
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
customElements.define(tagName, TestTabs);
|
|
133
|
+
|
|
134
|
+
const ready = await promise;
|
|
135
|
+
expect(ready).to.equal(element);
|
|
136
|
+
expect(ready.ping()).to.equal('pong');
|
|
137
|
+
});
|
|
138
|
+
});
|
|
114
139
|
|
|
115
|
-
});
|
|
140
|
+
});
|