@schukai/monster 3.73.3 → 3.73.4

Sign up to get free protection for your applications and to get access to all the features.
package/CHANGELOG.md CHANGED
@@ -2,33 +2,38 @@
2
2
 
3
3
 
4
4
 
5
- ## [3.73.3] - 2024-07-01
5
+ ## [3.73.4] - 2024-07-02
6
6
 
7
7
  ### Bug Fixes
8
8
 
9
- - debouncing form handling
9
+ - eventprocessing is now only active in selected controls: form, filter. [#224](https://gitlab.schukai.com/oss/libraries/javascript/monster/issues/224)
10
10
 
11
11
 
12
12
 
13
- ## [3.73.2] - 2024-07-01
13
+ ## [3.73.3] - 2024-07-01
14
14
 
15
15
  ### Bug Fixes
16
16
 
17
- - id not defined
17
+ - debouncing form handling
18
18
 
19
+ ## [3.73.2] - 2024-07-01
19
20
 
21
+ ### Bug Fixes
22
+
23
+ - id not defined
20
24
 
21
25
  ## [3.73.1] - 2024-06-30
22
26
 
23
27
  ### Bug Fixes
24
28
 
25
- - update deadman switch assignment
29
+ - update dead man switch assignment
30
+
26
31
  ### Changes
27
32
 
28
33
  - update issues
29
34
  - remove node debug flag
30
35
  - tidy changelog
31
- - new task create documentation fragments and optimize create class task
36
+ - new task creates documentation fragments and optimizes creates a class task
32
37
  - cleanup code
33
38
 
34
39
  ## [3.73.0] - 2024-06-28
package/package.json CHANGED
@@ -1 +1 @@
1
- {"author":"schukai GmbH","dependencies":{"@floating-ui/dom":"^1.6.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":"3.73.3"}
1
+ {"author":"schukai GmbH","dependencies":{"@floating-ui/dom":"^1.6.7","@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":"3.73.4"}
@@ -210,7 +210,12 @@ class DataSet extends CustomElement {
210
210
  pathWithIndex = String(index);
211
211
  }
212
212
 
213
- const data = this[datasourceLinkedElementSymbol].data;
213
+ const data = this[datasourceLinkedElementSymbol]?.data;
214
+ if (!data) {
215
+ reject(new Error("No data"));
216
+ return;
217
+ }
218
+
214
219
  const unref = JSON.stringify(data);
215
220
  const ref = JSON.parse(unref);
216
221
 
@@ -244,24 +249,26 @@ class DataSet extends CustomElement {
244
249
 
245
250
  initEventHandler.call(this);
246
251
 
247
- const selector = this.getOption("datasource.selector");
252
+ if (!this[datasourceLinkedElementSymbol]) {
253
+ const selector = this.getOption("datasource.selector");
248
254
 
249
- if (isString(selector)) {
250
- const element = findElementWithSelectorUpwards(this, selector);
251
- if (element === null) {
252
- throw new Error("the selector must match exactly one element");
253
- }
255
+ if (isString(selector)) {
256
+ const element = findElementWithSelectorUpwards(this, selector);
257
+ if (element === null) {
258
+ throw new Error("the selector must match exactly one element");
259
+ }
254
260
 
255
- if (!(element instanceof Datasource)) {
256
- throw new TypeError("the element must be a datasource");
257
- }
261
+ if (!(element instanceof Datasource)) {
262
+ throw new TypeError("the element must be a datasource");
263
+ }
258
264
 
259
- this[datasourceLinkedElementSymbol] = element;
260
- element.datasource.attachObserver(
261
- new Observer(handleDataSourceChanges.bind(this)),
262
- );
263
- } else {
264
- throw new Error("the selector must be a string");
265
+ this[datasourceLinkedElementSymbol] = element;
266
+ element.datasource.attachObserver(
267
+ new Observer(handleDataSourceChanges.bind(this)),
268
+ );
269
+ } else {
270
+ throw new Error("the selector must be a string");
271
+ }
265
272
  }
266
273
 
267
274
  if (
@@ -274,6 +274,7 @@ class Filter extends CustomElement {
274
274
 
275
275
  query: undefined,
276
276
  defaultQuery: "",
277
+ eventProcessing: true,
277
278
  });
278
279
  }
279
280
 
@@ -201,7 +201,6 @@ class ContextError extends Popper {
201
201
  }
202
202
 
203
203
  if (c === "<slot></slot>") {
204
-
205
204
  const sr = this.shadowRoot;
206
205
  if (!sr) {
207
206
  return false;
@@ -12,28 +12,18 @@
12
12
  * SPDX-License-Identifier: AGPL-3.0
13
13
  */
14
14
 
15
- import {internalSymbol} from "../../constants.mjs";
16
- import {Pathfinder} from "../../data/pathfinder.mjs";
15
+ import { Datasource } from "../../data/datasource.mjs";
16
+ import { DeadMansSwitch } from "../../util/deadmansswitch.mjs";
17
+ import { DataSet } from "../datatable/dataset.mjs";
17
18
  import {
18
- ATTRIBUTE_FORM_BIND,
19
- ATTRIBUTE_FORM_BIND_TYPE,
20
- ATTRIBUTE_UPDATER_BIND,
21
- } from "../../dom/constants.mjs";
22
- import {findTargetElementFromEvent} from "../../dom/events.mjs";
23
- import {ID} from "../../types/id.mjs";
24
- import {clone} from "../../util/clone.mjs";
25
- import {DeadMansSwitch} from "../../util/deadmansswitch.mjs";
26
- import {DataSet} from "../datatable/dataset.mjs";
27
- import {
28
- assembleMethodSymbol,
29
- registerCustomElement,
30
- getSlottedElements,
19
+ assembleMethodSymbol,
20
+ registerCustomElement,
21
+ getSlottedElements,
31
22
  } from "../../dom/customelement.mjs";
32
- import {FormStyleSheet} from "./stylesheet/form.mjs";
33
- import {diff} from "../../data/diff.mjs";
34
- import {isString} from "../../types/is.mjs";
23
+ import { datasourceLinkedElementSymbol } from "../datatable/util.mjs";
24
+ import { FormStyleSheet } from "./stylesheet/form.mjs";
35
25
 
36
- export {Form};
26
+ export { Form };
37
27
 
38
28
  /**
39
29
  * @private
@@ -48,198 +38,159 @@ const debounceWriteBackSymbol = Symbol("debounceWriteBack");
48
38
  const debounceBindSymbol = Symbol("debounceBind");
49
39
 
50
40
  class Form extends DataSet {
51
- /**
52
- *
53
- * @property {Object} templates Template definitions
54
- * @property {string} templates.main Main template
55
- * @property {Object} classes Class definitions
56
- * @property {string} classes.form Form class
57
- * @property {Object} writeBack Write back definitions
58
- * @property {string[]} writeBack.events Write back events
59
- * @property {Object} bind Bind definitions
60
- * @property {string[]} bind.events Bind events
61
- * @property {Object} reportValidity Report validity definitions
62
- * @property {string} reportValidity.selector Report validity selector
63
- * @property {boolean} features.mutationObserver Mutation observer feature
64
- * @property {boolean} features.writeBack Write back feature
65
- * @property {boolean} features.bind Bind feature
66
- */
67
- get defaults() {
68
- const obj = Object.assign({}, super.defaults, {
69
- templates: {
70
- main: getTemplate(),
71
- },
72
-
73
- classes: {
74
- form: "",
75
- },
76
-
77
- writeBack: {
78
- events: ["keyup", "click", "change", "drop", "touchend", "input"]
79
- },
80
-
81
- bind: {
82
- events: ["keyup", "click", "change", "drop", "touchend", "input"]
83
- },
84
-
85
- reportValidity: {
86
- selector: "input,select,textarea",
87
- },
88
- });
89
-
90
- obj["features"]["mutationObserver"] = false;
91
- obj["features"]["writeBack"] = true;
92
- obj["features"]["bind"] = true;
93
-
94
- return obj;
95
- }
96
-
97
- /**
98
- *
99
- * @return {string}
100
- */
101
- static getTag() {
102
- return "monster-form";
103
- }
104
-
105
- /**
106
- * @return {CSSStyleSheet[]}
107
- */
108
- static getCSSStyleSheet() {
109
- return [FormStyleSheet];
110
- }
111
-
112
- /**
113
- *
114
- */
115
- [assembleMethodSymbol]() {
116
- super[assembleMethodSymbol]();
117
-
118
- initControlReferences.call(this);
119
- initEventHandler.call(this);
120
- initDataSourceHandler.call(this);
121
- }
122
-
123
- /**
124
- * This method is called when the component is created.
125
- * @since 3.70.0
126
- * @returns {DataSet}
127
- */
128
- refresh() {
129
- this.write();
130
- super.refresh();
131
- return this;
132
- }
133
-
134
- /**
135
- * Run reportValidation on all child html form controls.
136
- *
137
- * @since 2.10.0
138
- * @returns {boolean}
139
- */
140
- reportValidity() {
141
- let valid = true;
142
-
143
- const selector = this.getOption("reportValidity.selector");
144
- const nodes = getSlottedElements.call(this, selector);
145
-
146
- nodes.forEach((node) => {
147
- if (typeof node.reportValidity === "function") {
148
- if (node.reportValidity() === false) {
149
- valid = false;
150
- }
151
- }
152
- });
153
-
154
- return valid;
155
- }
41
+ /**
42
+ *
43
+ * @property {Object} templates Template definitions
44
+ * @property {string} templates.main Main template
45
+ * @property {Object} classes Class definitions
46
+ * @property {string} classes.form Form class
47
+ * @property {Object} writeBack Write back definitions
48
+ * @property {string[]} writeBack.events Write back events
49
+ * @property {Object} bind Bind definitions
50
+ * @property {Object} reportValidity Report validity definitions
51
+ * @property {string} reportValidity.selector Report validity selector
52
+ * @property {boolean} features.mutationObserver Mutation observer feature
53
+ * @property {boolean} features.writeBack Write back feature
54
+ * @property {boolean} features.bind Bind feature
55
+ */
56
+ get defaults() {
57
+ const obj = Object.assign({}, super.defaults, {
58
+ templates: {
59
+ main: getTemplate(),
60
+ },
61
+
62
+ classes: {
63
+ form: "",
64
+ },
65
+
66
+ writeBack: {
67
+ events: ["keyup", "click", "change", "drop", "touchend", "input"],
68
+ },
69
+
70
+ reportValidity: {
71
+ selector: "input,select,textarea",
72
+ },
73
+
74
+ eventProcessing: true,
75
+ });
76
+
77
+ obj["features"]["mutationObserver"] = false;
78
+ obj["features"]["writeBack"] = true;
79
+
80
+ return obj;
81
+ }
82
+
83
+ /**
84
+ *
85
+ * @return {string}
86
+ */
87
+ static getTag() {
88
+ return "monster-form";
89
+ }
90
+
91
+ /**
92
+ * @return {CSSStyleSheet[]}
93
+ */
94
+ static getCSSStyleSheet() {
95
+ return [FormStyleSheet];
96
+ }
97
+
98
+ /**
99
+ *
100
+ */
101
+ [assembleMethodSymbol]() {
102
+ const selector = this.getOption("datasource.selector");
103
+
104
+ if (!selector) {
105
+ this[datasourceLinkedElementSymbol] = new Datasource(this);
106
+ }
107
+
108
+ super[assembleMethodSymbol]();
109
+
110
+ initControlReferences.call(this);
111
+ initEventHandler.call(this);
112
+ initDataSourceHandler.call(this);
113
+ }
114
+
115
+ /**
116
+ * This method is called when the component is created.
117
+ * @since 3.70.0
118
+ * @returns {DataSet}
119
+ */
120
+ refresh() {
121
+ this.write();
122
+ super.refresh();
123
+ return this;
124
+ }
125
+
126
+ /**
127
+ * Run reportValidation on all child html form controls.
128
+ *
129
+ * @since 2.10.0
130
+ * @returns {boolean}
131
+ */
132
+ reportValidity() {
133
+ let valid = true;
134
+
135
+ const selector = this.getOption("reportValidity.selector");
136
+ const nodes = getSlottedElements.call(this, selector);
137
+
138
+ nodes.forEach((node) => {
139
+ if (typeof node.reportValidity === "function") {
140
+ if (node.reportValidity() === false) {
141
+ valid = false;
142
+ }
143
+ }
144
+ });
145
+
146
+ return valid;
147
+ }
156
148
  }
157
149
 
158
- function initDataSourceHandler() {
159
- }
150
+ function initDataSourceHandler() {}
160
151
 
161
152
  /**
162
153
  * @private
163
154
  * @returns {initEventHandler}
164
155
  */
165
156
  function initEventHandler() {
166
- this[debounceBindSymbol] = {};
167
-
168
- if (this.getOption("features.bind") === true) {
169
- const events = this.getOption("bind.events");
170
-
171
- for (const event of events) {
172
- this.addEventListener(event, (e) => {
173
- const element = findTargetElementFromEvent(e, ATTRIBUTE_FORM_BIND);
174
-
175
- if (!(element instanceof HTMLElement)) {
176
- return;
177
- }
178
-
179
- let elementID
180
- if (!element.hasAttribute("data-monster-debounce-id")) {
181
- elementID = new ID('debounce').toString();
182
- element.setAttribute("data-monster-debounce-id", elementID);
183
- } else {
184
- elementID = element.getAttribute("data-monster-debounce-id");
185
- }
186
-
187
- if (this[debounceBindSymbol][elementID] instanceof DeadMansSwitch) {
188
- try {
189
- this[debounceBindSymbol][elementID].touch();
190
- return;
191
- } catch (e) {
192
- if (e.message !== "has already run") {
193
- throw e;
194
- }
195
-
196
- delete this[debounceBindSymbol][elementID];
197
- }
198
- }
199
-
200
- this[debounceBindSymbol][elementID] = new DeadMansSwitch(200, () => {
201
- delete this[debounceBindSymbol][elementID];
202
- retrieveAndSetValue.call(this, element);
203
- });
204
- });
205
- }
206
- }
207
-
208
- if (this.getOption("features.writeBack") === true) {
209
- const events = this.getOption("writeBack.events");
210
- for (const event of events) {
211
- this.addEventListener(event, (e) => {
212
- if (!this.reportValidity()) {
213
- this.classList.add("invalid");
214
- setTimeout(() => {
215
- this.classList.remove("invalid");
216
- }, 1000);
217
-
218
- return;
219
- }
220
-
221
- if (this[debounceWriteBackSymbol] instanceof DeadMansSwitch) {
222
- try {
223
- this[debounceWriteBackSymbol].touch();
224
- return;
225
- } catch (e) {
226
- if (e.message !== "has already run") {
227
- throw e;
228
- }
229
- delete this[debounceWriteBackSymbol];
230
- }
231
- }
232
-
233
- this[debounceWriteBackSymbol] = new DeadMansSwitch(200, () => {
234
- setTimeout(() => {
235
- this.write();
236
- }, 0);
237
- });
238
- });
239
- }
240
- }
241
-
242
- return this;
157
+ this[debounceBindSymbol] = {};
158
+
159
+ if (this.getOption("features.writeBack") === true) {
160
+ const events = this.getOption("writeBack.events");
161
+ for (const event of events) {
162
+ this.addEventListener(event, (e) => {
163
+ if (!this.reportValidity()) {
164
+ this.classList.add("invalid");
165
+ setTimeout(() => {
166
+ this.classList.remove("invalid");
167
+ }, 1000);
168
+
169
+ return;
170
+ }
171
+
172
+ if (this[debounceWriteBackSymbol] instanceof DeadMansSwitch) {
173
+ try {
174
+ this[debounceWriteBackSymbol].touch();
175
+ return;
176
+ } catch (e) {
177
+ if (e.message !== "has already run") {
178
+ throw e;
179
+ }
180
+ delete this[debounceWriteBackSymbol];
181
+ }
182
+ }
183
+
184
+ this[debounceWriteBackSymbol] = new DeadMansSwitch(200, () => {
185
+ setTimeout(() => {
186
+ this.write();
187
+ }, 0);
188
+ });
189
+ });
190
+ }
191
+ }
192
+
193
+ return this;
243
194
  }
244
195
 
245
196
  /**
@@ -247,115 +198,10 @@ function initEventHandler() {
247
198
  * @return {FilterButton}
248
199
  */
249
200
  function initControlReferences() {
250
- if (!this.shadowRoot) {
251
- throw new Error("no shadow-root is defined");
252
- }
253
- return this;
254
- }
255
-
256
- /**
257
- * @throws {Error} the bind argument must start as a value with a path
258
- * @param {HTMLElement} element
259
- * @return void
260
- * @memberOf Monster.DOM
261
- * @private
262
- */
263
- function retrieveAndSetValue(element) {
264
- let path = element.getAttribute(ATTRIBUTE_FORM_BIND);
265
- if (path === null)
266
- throw new Error("the bind argument must start as a value with a path");
267
-
268
- if (path.indexOf("path:") !== 0) {
269
- throw new Error("the bind argument must start as a value with a path");
270
- }
271
-
272
- path = path.substring(5); // remove path: from the string
273
-
274
- let value;
275
-
276
- if (element instanceof HTMLInputElement) {
277
- switch (element.type) {
278
- case "checkbox":
279
- value = element.checked ? element.value : undefined;
280
- break;
281
- default:
282
- value = element.value;
283
- break;
284
- }
285
- } else if (element instanceof HTMLTextAreaElement) {
286
- value = element.value;
287
- } else if (element instanceof HTMLSelectElement) {
288
- switch (element.type) {
289
- case "select-one":
290
- value = element.value;
291
- break;
292
- case "select-multiple":
293
- value = element.value;
294
-
295
- let options = element?.selectedOptions;
296
- if (options === undefined)
297
- options = element.querySelectorAll(":scope option:checked");
298
- value = Array.from(options).map(({value}) => value);
299
-
300
- break;
301
- }
302
-
303
- // values from custom elements
304
- } else if (
305
- (element?.constructor?.prototype &&
306
- !!Object.getOwnPropertyDescriptor(
307
- element.constructor.prototype,
308
- "value",
309
- )?.["get"]) ||
310
- element.hasOwnProperty("value")
311
- ) {
312
- value = element?.["value"];
313
- } else {
314
- throw new Error("unsupported object");
315
- }
316
-
317
- if (isString(value)) {
318
- const type = element.getAttribute(ATTRIBUTE_FORM_BIND_TYPE);
319
- switch (type) {
320
- case "number":
321
- case "int":
322
- case "float":
323
- case "integer":
324
- value = Number(value);
325
- if (isNaN(value)) {
326
- value = 0;
327
- }
328
- break;
329
- case "boolean":
330
- case "bool":
331
- case "checkbox":
332
- value = value === "true" || value === "1" || value === "on";
333
- break;
334
- case "array":
335
- case "list":
336
- value = value.split(",");
337
- break;
338
- case "object":
339
- case "json":
340
- value = JSON.parse(value);
341
- break;
342
- default:
343
- break;
344
- }
345
- }
346
-
347
- const copy = clone(this[internalSymbol].getRealSubject()?.options);
348
-
349
- const pf = new Pathfinder(copy);
350
- pf.setVia(path, value);
351
-
352
- const diffResult = diff(copy, this[internalSymbol].getRealSubject()?.options);
353
-
354
- if (diffResult.length > 0) {
355
- setTimeout(() => {
356
- this.setOption(path, value);
357
- }, 50);
358
- }
201
+ if (!this.shadowRoot) {
202
+ throw new Error("no shadow-root is defined");
203
+ }
204
+ return this;
359
205
  }
360
206
 
361
207
  /**
@@ -363,8 +209,8 @@ function retrieveAndSetValue(element) {
363
209
  * @return {string}
364
210
  */
365
211
  function getTemplate() {
366
- // language=HTML
367
- return `
212
+ // language=HTML
213
+ return `
368
214
  <div data-monster-role="control" part="control">
369
215
  <form data-monster-attributes="disabled path:disabled | if:true, class path:classes.form"
370
216
  data-monster-role="form"
@@ -327,6 +327,7 @@ class CustomElement extends HTMLElement {
327
327
  * @property {Object} templates Specifies the templates used by the control.
328
328
  * @property {string} templates.main=undefined Specifies the main template used by the control.
329
329
  * @property {Object} templateMapping Specifies the mapping of templates.
330
+ * @property {Boolean} eventProcessing=false Specifies whether the control processes events.
330
331
  * @since 1.8.0
331
332
  */
332
333
  get defaults() {
@@ -338,6 +339,8 @@ class CustomElement extends HTMLElement {
338
339
  main: undefined,
339
340
  },
340
341
  templateMapping: {},
342
+
343
+ eventProcessing: false,
341
344
  };
342
345
  }
343
346
 
@@ -644,11 +647,17 @@ class CustomElement extends HTMLElement {
644
647
  this[internalSymbol].getRealSubject()["options"],
645
648
  );
646
649
 
650
+ const cfg = {};
651
+ if (this.getOption("eventProcessing") === true) {
652
+ cfg.eventProcessing = true;
653
+ }
654
+
647
655
  addObjectWithUpdaterToElement.call(
648
656
  this,
649
657
  nodeList,
650
658
  customElementUpdaterLinkSymbol,
651
659
  this[updateCloneDataSymbol],
660
+ cfg,
652
661
  );
653
662
 
654
663
  // Attach a mutation observer to observe changes to the attributes of the element