@schukai/monster 3.96.2 → 3.96.3

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,16 @@
2
2
 
3
3
 
4
4
 
5
+ ## [3.96.3] - 2025-01-03
6
+
7
+ ### Bug Fixes
8
+
9
+ - optimize the filter rest api pipeline [#274](https://gitlab.schukai.com/oss/libraries/javascript/monster/issues/274) [#241](https://gitlab.schukai.com/oss/libraries/javascript/monster/issues/241)
10
+ - update undefined handling [#275](https://gitlab.schukai.com/oss/libraries/javascript/monster/issues/275)
11
+ - repair some small issues [#274](https://gitlab.schukai.com/oss/libraries/javascript/monster/issues/274)
12
+
13
+
14
+
5
15
  ## [3.96.2] - 2024-12-31
6
16
 
7
17
  ### Bug Fixes
package/package.json CHANGED
@@ -1 +1 @@
1
- {"author":"schukai GmbH","dependencies":{"@floating-ui/dom":"^1.6.12","@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.96.2"}
1
+ {"author":"schukai GmbH","dependencies":{"@floating-ui/dom":"^1.6.12","@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.96.3"}
@@ -104,7 +104,7 @@ class Rest extends Datasource {
104
104
  * @property {string} templates.main Main template
105
105
  * @property {Object} features Feature definitions
106
106
  * @property {boolean} features.autoInit If true, the component is initialized automatically
107
- * @property {boolean} features.filter If true, the component is initialized automatically
107
+ * @property {boolean} features.filter If true, the filter.id is used to attach a filter control
108
108
  * @property {Object} autoInit Auto init definitions
109
109
  * @property {boolean} autoInit.intersectionObserver If true, the intersection observer is initialized automatically
110
110
  * @property {boolean} autoInit.oneTime If true, the intersection observer is initialized only once
@@ -131,7 +131,7 @@ class Rest extends Datasource {
131
131
 
132
132
  restOptions.read.parameters = {
133
133
  filter: null,
134
- oderBy: null,
134
+ orderBy: null,
135
135
  page: "1",
136
136
  };
137
137
 
@@ -199,7 +199,6 @@ class Rest extends Datasource {
199
199
  [assembleMethodSymbol]() {
200
200
  super[assembleMethodSymbol]();
201
201
  initEventHandler.call(this);
202
- initAutoInit.call(this);
203
202
  }
204
203
 
205
204
  /**
@@ -260,6 +259,12 @@ class Rest extends Datasource {
260
259
  queueMicrotask(() => {
261
260
  if (this.getOption("features.filter", false) === true) {
262
261
  initFilter.call(this);
262
+
263
+ if (!this[filterObserverSymbol]) {
264
+ initAutoInit.call(this);
265
+ }
266
+ } else {
267
+ initAutoInit.call(this);
263
268
  }
264
269
  });
265
270
  }
@@ -286,12 +291,26 @@ class Rest extends Datasource {
286
291
  this[dataSourceSymbol].setOption("read", opt);
287
292
 
288
293
  let url = this.getOption("read.url");
289
- const formatter = new Formatter(this.getOption("read.parameters"));
290
-
291
294
  if (!url) {
292
295
  return Promise.reject(new Error("No url defined"));
293
296
  }
294
297
 
298
+ const param = this.getOption("read.parameters", {});
299
+
300
+ if (param.query === null || param.query === undefined) {
301
+ param.query = "";
302
+ }
303
+
304
+ if (param.page === null || param.page === undefined) {
305
+ param.page = "1";
306
+ }
307
+
308
+ if (param.orderBy === null || param.orderBy === undefined) {
309
+ param.orderBy = "";
310
+ }
311
+
312
+ const formatter = new Formatter(param);
313
+
295
314
  url = formatter.format(url);
296
315
 
297
316
  this[dataSourceSymbol].setOption("read.url", url);
@@ -394,23 +413,28 @@ function initFilter() {
394
413
  throw new Error("filter feature is enabled but no filter id is defined");
395
414
 
396
415
  const filterControl = findElementWithIdUpwards(this, filterID);
397
- if (!filterControl)
398
- throw new Error(
416
+ if (!filterControl) {
417
+ addAttributeToken(
418
+ this,
419
+ ATTRIBUTE_ERRORMESSAGE,
399
420
  "filter feature is enabled but no filter control with id " +
400
421
  filterID +
401
422
  " is found",
402
423
  );
424
+ return;
425
+ }
403
426
 
404
427
  this[filterObserverSymbol] = new Observer(() => {
405
428
  const query = filterControl.getOption("query");
406
429
  if (query === undefined) {
407
430
  return;
408
431
  }
432
+
409
433
  this.setParameters({ query: query });
410
434
  this.fetch()
411
435
  .then((response) => {
412
436
  if (!(response instanceof Response)) {
413
- throw new Error("Response is not an instance of Response");
437
+ return Promise.reject(new Error("response is not a Response object"));
414
438
  }
415
439
 
416
440
  if (response?.ok === true) {
@@ -565,8 +589,7 @@ function initIntersectionObserver() {
565
589
  */
566
590
  function getTemplate() {
567
591
  // language=HTML
568
- return `
569
- <slot></slot>`;
592
+ return `<slot></slot>`;
570
593
  }
571
594
 
572
595
  registerCustomElement(Rest);
@@ -13,7 +13,10 @@
13
13
  */
14
14
 
15
15
  import { instanceSymbol } from "../../constants.mjs";
16
- import { findTargetElementFromEvent } from "../../dom/events.mjs";
16
+ import {
17
+ findTargetElementFromEvent,
18
+ fireCustomEvent,
19
+ } from "../../dom/events.mjs";
17
20
  import {
18
21
  findElementWithIdUpwards,
19
22
  findElementWithSelectorUpwards,
@@ -25,7 +28,6 @@ import {
25
28
  registerCustomElement,
26
29
  } from "../../dom/customelement.mjs";
27
30
  import { ID } from "../../types/id.mjs";
28
- import { DeadMansSwitch } from "../../util/deadmansswitch.mjs";
29
31
  import { Settings } from "./filter/settings.mjs";
30
32
  import { FilterStyleSheet } from "./stylesheet/filter.mjs";
31
33
  import { getDocument, getWindow } from "../../dom/util.mjs";
@@ -54,6 +56,14 @@ import {
54
56
 
55
57
  import "./filter/select.mjs";
56
58
 
59
+ import "../form/button.mjs";
60
+ import "../form/select.mjs";
61
+ import "../form/popper-button.mjs";
62
+ import "../form/toggle-switch.mjs";
63
+ import "../form/context-help.mjs";
64
+ import "../form/context-error.mjs";
65
+ import "../form/message-state-button.mjs";
66
+
57
67
  export { Filter };
58
68
 
59
69
  /**
@@ -130,9 +140,23 @@ const sizeDataSymbol = Symbol("sizeData");
130
140
  */
131
141
  const debounceSizeSymbol = Symbol("debounceSize");
132
142
 
143
+ /**
144
+ * @private
145
+ * @type {symbol}
146
+ */
147
+ const hashChangeSymbol = Symbol("hashChange");
148
+
133
149
  /**
134
150
  * The Filter component is used to show and handle the filter values.
135
151
  *
152
+ * @fragments /fragments/components/datatable/filter
153
+ *
154
+ * @example /examples/components/datatable/filter-simple First filter
155
+ * @example /examples/components/datatable/filter-advanced Advanced filter
156
+ * @example /examples/components/datatable/filter-store Store filter
157
+ *
158
+ * @issue https://localhost.alvine.dev:8443/development/issues/closed/272.html
159
+ *
136
160
  * @copyright schukai GmbH
137
161
  * @summary The Filter component is used to show and handle the filter values.
138
162
  */
@@ -143,6 +167,9 @@ class Filter extends CustomElement {
143
167
  constructor() {
144
168
  super();
145
169
  this[settingsSymbol] = new Settings();
170
+
171
+ // debounce the hash change event if doSearch is called by click the search button
172
+ this[hashChangeSymbol] = 0;
146
173
  }
147
174
 
148
175
  /**
@@ -171,7 +198,7 @@ class Filter extends CustomElement {
171
198
 
172
199
  /**
173
200
  *
174
- * @return {{Filter}}
201
+ * @return {Filter}
175
202
  */
176
203
  resetFailureMessage() {
177
204
  this[searchButtonElementSymbol].hideMessage();
@@ -181,7 +208,7 @@ class Filter extends CustomElement {
181
208
 
182
209
  /**
183
210
  *
184
- * @return {{Filter}}
211
+ * @return {Filter}
185
212
  */
186
213
  showSuccess() {
187
214
  this[searchButtonElementSymbol].setState(
@@ -210,8 +237,10 @@ class Filter extends CustomElement {
210
237
  * @property {Object} formatter.marker Marker definitions
211
238
  * @property {Object} formatter.marker.open Marker open
212
239
  * @property {Object} formatter.marker.close Marker close
240
+ * @property {Object} features Feature definitions
241
+ * @property {boolean} features.storedConfig Stored configuration, this replaces the setting `storedConfig.enabled` @since 3.97.0
213
242
  * @property {Object} storedConfig Stored configuration
214
- * @property {boolean} storedConfig.enabled Enabled
243
+ * @property {boolean} storedConfig.enabled The store has been enabled, this option will no longer have any effect. @deprecated 20250101
215
244
  * @property {string} storedConfig.selector Selector
216
245
  * @property {Object} timeouts Timeout definitions
217
246
  * @property {number} timeouts.message Message timeout
@@ -227,19 +256,23 @@ class Filter extends CustomElement {
227
256
  templates: {
228
257
  main: getTemplate(),
229
258
  },
259
+
230
260
  formatter: {
231
261
  marker: {
232
262
  open: null,
233
263
  close: null,
234
264
  },
235
265
  },
266
+
236
267
  labels: {
237
268
  search: "Search",
238
269
  reset: "Reset",
239
270
  save: "Save",
240
271
  "filter-name": "Filter name",
241
- "empty-query-and-no-default": "Please select a filter",
242
- "query-not-changed": "The query has not changed",
272
+ "empty-query-and-no-default":
273
+ "The query is empty and there is no default query.",
274
+ "query-not-changed":
275
+ "The search request has not changed, so no search is required.",
243
276
  },
244
277
 
245
278
  templateMapping: {
@@ -247,6 +280,10 @@ class Filter extends CustomElement {
247
280
  "filter-name-label": name,
248
281
  },
249
282
 
283
+ features: {
284
+ storedConfig: false,
285
+ },
286
+
250
287
  storedConfig: {
251
288
  enabled: true,
252
289
  selector: "",
@@ -268,8 +305,8 @@ class Filter extends CustomElement {
268
305
  },
269
306
  },
270
307
 
271
- query: undefined,
272
- defaultQuery: "",
308
+ query: null,
309
+ defaultQuery: null,
273
310
  eventProcessing: true,
274
311
  });
275
312
  }
@@ -284,12 +321,14 @@ class Filter extends CustomElement {
284
321
 
285
322
  /**
286
323
  * @return {FilterButton}
324
+ * @fires monster-filter-initialized
287
325
  */
288
326
  [assembleMethodSymbol]() {
289
327
  this.setOption(
290
328
  "templateMapping.filter-save-label",
291
329
  this.getOption("labels.save"),
292
330
  );
331
+
293
332
  this.setOption(
294
333
  "templateMapping.filter-name-label",
295
334
  this.getOption("labels.filter-name"),
@@ -302,12 +341,19 @@ class Filter extends CustomElement {
302
341
 
303
342
  initFromConfig
304
343
  .call(this)
305
- .then(() => {
306
- initFilter.call(this);
307
- updateFilterTabs.call(this);
308
- })
344
+ .then(() => {})
309
345
  .catch((error) => {
310
346
  addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, error?.message);
347
+ })
348
+ .finally(() => {
349
+ initFilter.call(this);
350
+ updateFilterTabs.call(this);
351
+ doSearch
352
+ .call(this, { showEffect: false })
353
+ .then(() => {
354
+ fireCustomEvent(this, "monster-filter-initialized");
355
+ })
356
+ .catch(() => {});
311
357
  });
312
358
  }
313
359
 
@@ -316,7 +362,6 @@ class Filter extends CustomElement {
316
362
  */
317
363
  connectedCallback() {
318
364
  super.connectedCallback();
319
-
320
365
  getWindow().addEventListener(
321
366
  "hashchange",
322
367
  this[locationChangeHandlerSymbol],
@@ -385,10 +430,13 @@ function updateFilterSelections() {
385
430
  queueMicrotask(() => {
386
431
  const options = this[settingsSymbol].getOptions();
387
432
  this[filterSelectElementSymbol].setOption("options", options);
388
- queueMicrotask(() => {
389
- this[filterSelectElementSymbol].value =
390
- this[settingsSymbol].getSelected();
391
- });
433
+
434
+ setTimeout(() => {
435
+ window.requestAnimationFrame(() => {
436
+ this[filterSelectElementSymbol].value =
437
+ this[settingsSymbol].getSelected();
438
+ });
439
+ }, 0);
392
440
  });
393
441
  }
394
442
 
@@ -411,7 +459,12 @@ function initFilter() {
411
459
  .forEach((element) => {
412
460
  const label = element.getAttribute("data-monster-label");
413
461
  if (!label) {
414
- throw new Error("no filter label is defined");
462
+ addAttributeToken(
463
+ this,
464
+ ATTRIBUTE_ERRORMESSAGE,
465
+ "no filter label is defined",
466
+ );
467
+ return;
415
468
  }
416
469
 
417
470
  let value = element.id;
@@ -448,6 +501,7 @@ function initFilter() {
448
501
 
449
502
  this[settingsSymbol].set({ value, label, visible });
450
503
  });
504
+
451
505
  updateFilterSelections.call(this);
452
506
  }
453
507
 
@@ -499,6 +553,33 @@ function setSlotAttribute(element, visible) {
499
553
  */
500
554
  function initEventHandler() {
501
555
  const self = this;
556
+
557
+ let lastHash = getGlobal().location.hash;
558
+ self[locationChangeHandlerSymbol] = () => {
559
+ if (lastHash === getGlobal().location.hash) {
560
+ return;
561
+ }
562
+
563
+ /**
564
+ * debounce the hash change event if doSearch
565
+ * is called by click the search button
566
+ */
567
+ if (self[hashChangeSymbol] > 0) {
568
+ self[hashChangeSymbol]--;
569
+ return;
570
+ }
571
+
572
+ initFilter.call(this);
573
+
574
+ doSearch
575
+ .call(self)
576
+ .then(() => {})
577
+ .catch((error) => {})
578
+ .finally(() => {
579
+ lastHash = getGlobal().location.hash;
580
+ });
581
+ };
582
+
502
583
  /**
503
584
  * Monster.Components.Form.event:monster-selection-cleared
504
585
  */
@@ -542,6 +623,7 @@ function initEventHandler() {
542
623
  );
543
624
  }
544
625
 
626
+ /** save the current filter */
545
627
  if (self[filterSaveActionButtonElementSymbol]) {
546
628
  self[filterSaveActionButtonElementSymbol].setOption(
547
629
  "actions.click",
@@ -641,6 +723,8 @@ function initEventHandler() {
641
723
  }
642
724
 
643
725
  self[searchButtonElementSymbol].setOption("actions.click", () => {
726
+ self[hashChangeSymbol] = 1;
727
+
644
728
  doSearch
645
729
  .call(self)
646
730
  .then(() => {})
@@ -684,7 +768,10 @@ function initEventHandler() {
684
768
  }
685
769
 
686
770
  if (event.keyCode === 13) {
687
- doSearch.call(self, { showEffect: false });
771
+ doSearch
772
+ .call(self, { showEffect: false })
773
+ .then(() => {})
774
+ .catch((error) => {});
688
775
  }
689
776
  });
690
777
 
@@ -696,6 +783,7 @@ function initEventHandler() {
696
783
  }
697
784
 
698
785
  function initTabEvents() {
786
+ const self = this;
699
787
  this[filterTabElementSymbol].addEventListener(
700
788
  "monster-tab-changed",
701
789
  (event) => {
@@ -836,7 +924,18 @@ function doSearch({ showEffect } = { showEffect: true }) {
836
924
  .call(this)
837
925
  .then((query) => {
838
926
  const buildQuery = buildSearchQuery.call(this, query);
839
- if (buildQuery === "" && !this.getOption("defaultQuery")) {
927
+ if (buildQuery === null) {
928
+ const msg = this.getOption("labels.empty-query-and-no-default");
929
+ if (showEffect) {
930
+ this[searchButtonElementSymbol].removeState();
931
+ this[searchButtonElementSymbol]
932
+ .setMessage(msg)
933
+ .showMessage(this.getOption("timeouts.message", 4000));
934
+ }
935
+ return Promise.reject(new Error(msg));
936
+ }
937
+
938
+ if (buildQuery === "" && this.getOption("defaultQuery") === null) {
840
939
  const msg = this.getOption("labels.empty-query-and-no-default");
841
940
 
842
941
  if (showEffect) {
@@ -846,7 +945,7 @@ function doSearch({ showEffect } = { showEffect: true }) {
846
945
  .showMessage(this.getOption("timeouts.message", 4000));
847
946
  }
848
947
 
849
- throw new Error(msg);
948
+ return Promise.reject(new Error(msg));
850
949
  }
851
950
 
852
951
  if (buildQuery === this.getOption("query")) {
@@ -859,7 +958,7 @@ function doSearch({ showEffect } = { showEffect: true }) {
859
958
  .showMessage(this.getOption("timeouts.message", 4000));
860
959
  }
861
960
 
862
- throw new Error(msg);
961
+ return Promise.reject(new Error(msg));
863
962
  }
864
963
 
865
964
  if (showEffect) {
@@ -870,6 +969,8 @@ function doSearch({ showEffect } = { showEffect: true }) {
870
969
  }
871
970
 
872
971
  this.setOption("query", buildSearchQuery.call(this, query));
972
+
973
+ return Promise.resolve();
873
974
  })
874
975
  .catch((error) => {
875
976
  if (error instanceof Error) {
@@ -945,7 +1046,6 @@ function collectSearchQueries() {
945
1046
  return;
946
1047
  }
947
1048
 
948
- //const visible = window.getComputedStyle(element).display !== "none";
949
1049
  const visible = getVisibilityFromSlotAttribute(element);
950
1050
  if (!visible) {
951
1051
  return;
@@ -1136,49 +1236,50 @@ function updateConfig() {
1136
1236
  */
1137
1237
  function getTemplate() {
1138
1238
  // language=HTML
1139
- return `<div data-monster-role="control" part="control">
1140
- <div data-monster-role="container">
1141
- <div data-monster-role="layout">
1142
- <div data-monster-role="filter">
1143
- <slot></slot>
1144
- <slot name="hidden"></slot>
1145
- </div>
1146
- <div data-monster-role="select-and-search">
1147
- <monster-message-state-button data-monster-role="search-button" class="stretched-control"
1148
- data-monster-replace="path:labels.search">
1149
- </monster-message-state-button>
1150
- <monster-select class="stretched-control"
1151
- data-monster-selected-template="summary"
1152
- data-monster-option-type="checkbox"
1153
- data-monster-option-filter-mode="options"
1154
- data-monster-option-filter-position="popper"
1155
- data-monster-role="filter-select"></monster-select>
1156
- <monster-popper-button data-monster-role="save-button" class="stretched-control"
1157
- data-monster-attributes="data-monster-visible path:storedConfig.enabled">
1158
- <div slot="button">\${filter-save-label}</div>
1159
- <div class="monster-form" data-monster-role="form">
1160
-
1161
- <label for="filter-name">\${filter-name-label}
1162
- <input name="filter-name"
1163
- type="search"
1164
- class="monster-margin-bottom-5"></label>
1165
- <monster-message-state-button
1166
- data-monster-role="save-action-button"
1167
- data-monster-option-labels-button="\${filter-save-label}">
1239
+ return `
1240
+ <div data-monster-role="control" part="control">
1241
+ <div data-monster-role="container">
1242
+ <div data-monster-role="layout">
1243
+ <div data-monster-role="filter">
1244
+ <slot></slot>
1245
+ <slot name="hidden"></slot>
1246
+ </div>
1247
+ <div data-monster-role="select-and-search">
1248
+ <monster-message-state-button data-monster-role="search-button" class="stretched-control"
1249
+ data-monster-replace="path:labels.search">
1168
1250
  </monster-message-state-button>
1169
-
1251
+ <monster-select class="stretched-control"
1252
+ data-monster-selected-template="summary"
1253
+ data-monster-option-type="checkbox"
1254
+ data-monster-option-filter-mode="options"
1255
+ data-monster-option-filter-position="popper"
1256
+ data-monster-role="filter-select"></monster-select>
1257
+ <monster-popper-button data-monster-role="save-button" class="stretched-control"
1258
+ data-monster-attributes="data-monster-visible path:features.storedConfig">
1259
+ <div slot="button">\${filter-save-label}</div>
1260
+ <div class="monster-form" data-monster-role="form">
1261
+
1262
+ <label for="filter-name">\${filter-name-label}
1263
+ <input name="filter-name"
1264
+ type="search"
1265
+ class="monster-margin-bottom-5"></label>
1266
+ <monster-message-state-button
1267
+ data-monster-role="save-action-button"
1268
+ data-monster-option-labels-button="\${filter-save-label}">
1269
+ </monster-message-state-button>
1270
+
1271
+ </div>
1272
+ </monster-popper-button>
1273
+ <monster-button data-monster-role="reset-button" class="stretched-control"
1274
+ data-monster-replace="path:labels.reset">
1275
+ </monster-button>
1170
1276
  </div>
1171
- </monster-popper-button>
1172
- <monster-button data-monster-role="reset-button" class="stretched-control"
1173
- data-monster-replace="path:labels.reset">
1174
- </monster-button>
1175
- </div>
1176
1277
 
1278
+ </div>
1279
+ <input class="hidden" name="query" data-monster-role="query"
1280
+ data-monster-attributes="value path:query">
1281
+ </div>
1177
1282
  </div>
1178
- <input class="hidden" name="query" data-monster-role="query"
1179
- data-monster-attributes="value path:query">
1180
- </div>
1181
- </div>
1182
1283
  `;
1183
1284
  }
1184
1285
 
@@ -22,6 +22,7 @@ import {
22
22
  } from "../../dom/customelement.mjs";
23
23
  import { datasourceLinkedElementSymbol } from "../datatable/util.mjs";
24
24
  import { FormStyleSheet } from "./stylesheet/form.mjs";
25
+ import { addAttributeToken } from "../../dom/attributes.mjs";
25
26
 
26
27
  export { Form };
27
28
 
@@ -54,7 +55,6 @@ const debounceBindSymbol = Symbol("debounceBind");
54
55
  */
55
56
  class Form extends DataSet {
56
57
  /**
57
- *
58
58
  * @property {Object} templates Template definitions
59
59
  * @property {string} templates.main Main template
60
60
  * @property {Object} classes Class definitions
@@ -199,7 +199,9 @@ function initEventHandler() {
199
199
 
200
200
  this[debounceWriteBackSymbol] = new DeadMansSwitch(200, () => {
201
201
  setTimeout(() => {
202
- this.write();
202
+ this.write().catch((e) => {
203
+ addAttributeToken(this, "error", e.message || `${e}`);
204
+ });
203
205
  }, 0);
204
206
  });
205
207
  });
@@ -12,7 +12,12 @@
12
12
  * SPDX-License-Identifier: AGPL-3.0
13
13
  */
14
14
 
15
- export { internalSymbol, internalStateSymbol, instanceSymbol };
15
+ export {
16
+ internalSymbol,
17
+ internalStateSymbol,
18
+ instanceSymbol,
19
+ proxyInstanceMarker,
20
+ };
16
21
 
17
22
  /**
18
23
  * @private
@@ -35,3 +40,11 @@ const internalStateSymbol = Symbol.for("@schukai/monster/state");
35
40
  * @type {symbol}
36
41
  */
37
42
  const instanceSymbol = Symbol.for("@schukai/monster/instance");
43
+
44
+ /**
45
+ * @private
46
+ * @type {symbol}
47
+ */
48
+ const proxyInstanceMarker = Symbol.for(
49
+ "@schukai/monster/proxy-instance-marker",
50
+ );
@@ -53,7 +53,8 @@ function extend(...args) {
53
53
  for (const k in a) {
54
54
  const v = a?.[k];
55
55
 
56
- if (v === o?.[k]) {
56
+ if (k in o && v === o?.[k]) {
57
+ // k in o is for the case of undefined
57
58
  continue;
58
59
  }
59
60
 
@@ -265,9 +265,11 @@ function transform(value) {
265
265
  validateString(value);
266
266
  return value.toUpperCase();
267
267
 
268
+ case "to-string":
268
269
  case "tostring":
269
270
  return `${value}`;
270
271
 
272
+ case "to-integer":
271
273
  case "tointeger":
272
274
  const n = parseInt(value);
273
275
  validateInteger(n);
@@ -610,9 +610,13 @@ class CustomElement extends HTMLElement {
610
610
  nodeList = elements;
611
611
  }
612
612
 
613
- this[updateCloneDataSymbol] = clone(
614
- this[internalSymbol].getRealSubject()["options"],
615
- );
613
+ try {
614
+ this[updateCloneDataSymbol] = clone(
615
+ this[internalSymbol].getRealSubject()["options"],
616
+ );
617
+ } catch (e) {
618
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e?.messages || `${e}`);
619
+ }
616
620
 
617
621
  const cfg = {};
618
622
  if (this.getOption("eventProcessing") === true) {
@@ -307,7 +307,11 @@ function getControlEventHandler() {
307
307
  }
308
308
 
309
309
  queueMicrotask(() => {
310
- retrieveAndSetValue.call(this, element);
310
+ try {
311
+ retrieveAndSetValue.call(this, element);
312
+ } catch (e) {
313
+ addAttributeToken(element, ATTRIBUTE_ERRORMESSAGE, e.message || `${e}`);
314
+ }
311
315
  });
312
316
  };
313
317