@schukai/monster 3.73.3 → 3.73.5

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.
@@ -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
@@ -156,7 +156,6 @@ class Updater extends Base {
156
156
 
157
157
  for (const type of this[internalSymbol].eventTypes) {
158
158
  // @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
159
-
160
159
  this[internalSymbol].element.addEventListener(
161
160
  type,
162
161
  getControlEventHandler.call(this),
@@ -745,9 +744,7 @@ function runUpdateContent(container, parts, subject) {
745
744
 
746
745
  /**
747
746
  * @private
748
- * @license AGPLv3
749
747
  * @since 1.8.0
750
- * @param {string} path
751
748
  * @param {object} change
752
749
  * @return {void}
753
750
  */
@@ -900,6 +897,10 @@ function handleInputControlAttributeUpdate(element, name, value) {
900
897
  * @param {NodeList|HTMLElement|Set<HTMLElement>} elements
901
898
  * @param {Symbol} symbol
902
899
  * @param {object} object
900
+ * @param {object} config
901
+ *
902
+ * Config: enableEventProcessing {boolean} - default: false - enables the event processing
903
+ *
903
904
  * @return {Promise[]}
904
905
  * @license AGPLv3
905
906
  * @since 1.23.0
@@ -908,7 +909,7 @@ function handleInputControlAttributeUpdate(element, name, value) {
908
909
  * @throws {TypeError} the context of the function is not an instance of HTMLElement
909
910
  * @throws {TypeError} symbol must be an instance of Symbol
910
911
  */
911
- function addObjectWithUpdaterToElement(elements, symbol, object) {
912
+ function addObjectWithUpdaterToElement(elements, symbol, object, config = {}) {
912
913
  if (!(this instanceof HTMLElement)) {
913
914
  throw new TypeError(
914
915
  "the context of this function must be an instance of HTMLElement",
@@ -974,7 +975,11 @@ function addObjectWithUpdaterToElement(elements, symbol, object) {
974
975
 
975
976
  result.push(
976
977
  u.run().then(() => {
977
- return u.enableEventProcessing();
978
+ if (config.eventProcessing === true) {
979
+ u.enableEventProcessing();
980
+ }
981
+
982
+ return u;
978
983
  }),
979
984
  );
980
985
  });
@@ -159,7 +159,7 @@ function getMonsterVersion() {
159
159
  }
160
160
 
161
161
  /** don't touch, replaced by make with package.json version */
162
- monsterVersion = new Version("3.65.0");
162
+ monsterVersion = new Version("3.73.2");
163
163
 
164
164
  return monsterVersion;
165
165
  }
@@ -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("3.65.0")
10
+ monsterVersion = new Version("3.73.2")
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 3.65.0</h1>
13
- <div id="lastupdate" style='font-size:0.7em'>last update Di 18. Jun 22:58:11 CEST 2024</div>
12
+ <h1 style='margin-bottom: 0.1em;'>Monster 3.73.2</h1>
13
+ <div id="lastupdate" style='font-size:0.7em'>last update Di 2. Jul 20:16:21 CEST 2024</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>