@schukai/monster 4.75.0 → 4.77.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,25 @@
2
2
 
3
3
 
4
4
 
5
+ ## [4.77.0] - 2026-01-05
6
+
7
+ ### Add Features
8
+
9
+ - Add documentation and enhance waitForCustomElement function
10
+ ### Changes
11
+
12
+ - update project
13
+
14
+
15
+
16
+ ## [4.76.0] - 2026-01-05
17
+
18
+ ### Add Features
19
+
20
+ - Add tab control feature for issue [#362](https://gitlab.schukai.com/oss/libraries/javascript/monster/issues/362)
21
+
22
+
23
+
5
24
  ## [4.75.0] - 2026-01-04
6
25
 
7
26
  ### Add Features
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.75.0"}
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.77.0"}
@@ -38,37 +38,37 @@ export { FilterSelect, getSummaryTemplate };
38
38
 
39
39
  */
40
40
  class FilterSelect extends Select {
41
- /**
42
- * To set the options via the HTML tag, the attribute `data-monster-options` must be used.
43
- * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
44
- *
45
- * The individual configuration values can be found in the table.
46
- */
47
- get defaults() {
48
- return Object.assign({}, super.defaults, {
49
- type: "checkbox",
50
- });
51
- }
41
+ /**
42
+ * To set the options via the HTML tag, the attribute `data-monster-options` must be used.
43
+ * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
44
+ *
45
+ * The individual configuration values can be found in the table.
46
+ */
47
+ get defaults() {
48
+ return Object.assign({}, super.defaults, {
49
+ type: "checkbox",
50
+ });
51
+ }
52
52
 
53
- /**
54
- *
55
- * @return {string}
56
- */
57
- static getTag() {
58
- return "monster-filter-select";
59
- }
53
+ /**
54
+ *
55
+ * @return {string}
56
+ */
57
+ static getTag() {
58
+ return "monster-filter-select";
59
+ }
60
60
 
61
- /**
62
- * @return {CSSStyleSheet[]}
63
- */
64
- static getCSSStyleSheet() {
65
- const styleSheet = super.getCSSStyleSheet();
66
- return [
67
- ...styleSheet,
68
- FilterControlsDefaultsStyleSheet,
69
- FilterSelectStyleSheet,
70
- ];
71
- }
61
+ /**
62
+ * @return {CSSStyleSheet[]}
63
+ */
64
+ static getCSSStyleSheet() {
65
+ const styleSheet = super.getCSSStyleSheet();
66
+ return [
67
+ ...styleSheet,
68
+ FilterControlsDefaultsStyleSheet,
69
+ FilterSelectStyleSheet,
70
+ ];
71
+ }
72
72
  }
73
73
 
74
74
  registerCustomElement(FilterSelect);
@@ -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,80 @@ 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, readyCheck = null } = {},
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 isReady = () => {
371
+ if (method && typeof element[method] !== "function") {
372
+ return false;
373
+ }
374
+ if (typeof readyCheck === "function") {
375
+ return readyCheck(element) === true;
376
+ }
377
+ return true;
378
+ };
379
+
380
+ const check = () => {
381
+ if (isReady()) {
382
+ resolve(element);
383
+ return;
384
+ }
385
+
386
+ if (
387
+ typeof timeout === "number" &&
388
+ timeout >= 0 &&
389
+ Date.now() - start > timeout
390
+ ) {
391
+ reject(
392
+ new Error(`Timed out waiting for "${method}" on <${name}>.`),
393
+ );
394
+ return;
395
+ }
396
+
397
+ window.requestAnimationFrame(check);
398
+ };
399
+
400
+ window.requestAnimationFrame(check);
401
+ }),
402
+ );
403
+ }
@@ -101,6 +101,7 @@ export * from "./components/datatable/filter/date-presets.mjs";
101
101
  export * from "./components/datatable/filter/date.mjs";
102
102
  export * from "./components/datatable/filter/date-range.mjs";
103
103
  export * from "./components/datatable/filter/abstract-base.mjs";
104
+ export * from "./components/datatable/filter/select.mjs";
104
105
  export * from "./components/datatable/filter/settings.mjs";
105
106
  export * from "./components/datatable/filter/date-time.mjs";
106
107
  export * from "./components/datatable/filter/range.mjs";
@@ -156,7 +156,7 @@ function getMonsterVersion() {
156
156
  }
157
157
 
158
158
  /** don't touch, replaced by make with package.json version */
159
- monsterVersion = new Version("4.70.1");
159
+ monsterVersion = new Version("4.76.0");
160
160
 
161
161
  return monsterVersion;
162
162
  }
@@ -1,5 +1,8 @@
1
1
  import {
2
- getDocument, getWindow, getDocumentFragmentFromString
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,51 @@ 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
+
139
+ it('should wait for a readyCheck condition', async () => {
140
+ const tagName = `test-ready-${Date.now()}`;
141
+ const element = document.createElement(tagName);
142
+ document.body.appendChild(element);
143
+
144
+ const promise = waitForCustomElement(element, {
145
+ readyCheck: (el) => el.hasAttribute('data-ready'),
146
+ });
147
+
148
+ class TestReady extends HTMLElement {
149
+ connectedCallback() {
150
+ setTimeout(() => {
151
+ this.setAttribute('data-ready', 'true');
152
+ }, 0);
153
+ }
154
+ }
155
+
156
+ customElements.define(tagName, TestReady);
157
+
158
+ const ready = await promise;
159
+ expect(ready).to.equal(element);
160
+ expect(ready.getAttribute('data-ready')).to.equal('true');
161
+ });
162
+ });
114
163
 
115
- });
164
+ });
@@ -7,7 +7,7 @@ describe('Monster', function () {
7
7
  let monsterVersion
8
8
 
9
9
  /** don´t touch, replaced by make with package.json version */
10
- monsterVersion = new Version("4.70.1")
10
+ monsterVersion = new Version("4.76.0")
11
11
 
12
12
  let m = getMonsterVersion();
13
13
 
@@ -9,8 +9,8 @@
9
9
  </head>
10
10
  <body>
11
11
  <div id="headline" style="display: flex;align-items: center;justify-content: center;flex-direction: column;">
12
- <h1 style='margin-bottom: 0.1em;'>Monster 4.70.1</h1>
13
- <div id="lastupdate" style='font-size:0.7em'>last update Sa 3. Jan 13:33:23 CET 2026</div>
12
+ <h1 style='margin-bottom: 0.1em;'>Monster 4.76.0</h1>
13
+ <div id="lastupdate" style='font-size:0.7em'>last update Mo 5. Jan 16:52:49 CET 2026</div>
14
14
  </div>
15
15
  <div id="mocha-errors"
16
16
  style="color: red;font-weight: bold;display: flex;align-items: center;justify-content: center;flex-direction: column;margin:20px;"></div>