@schukai/monster 3.56.1 → 3.58.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/example/components/form/toggle-switch.mjs +7 -0
  3. package/package.json +1 -1
  4. package/source/components/datatable/change-button.mjs +8 -4
  5. package/source/components/datatable/dataset.mjs +1 -1
  6. package/source/components/datatable/filter.mjs +0 -1
  7. package/source/components/form/button.mjs +3 -3
  8. package/source/components/form/select.mjs +55 -15
  9. package/source/components/form/style/button-bar.pcss +2 -0
  10. package/source/components/form/style/button.pcss +2 -0
  11. package/source/components/form/style/select.pcss +1 -1
  12. package/source/components/form/style/toggle-switch.pcss +74 -0
  13. package/source/components/form/stylesheet/button-bar.mjs +1 -1
  14. package/source/components/form/stylesheet/button.mjs +1 -1
  15. package/source/components/form/stylesheet/select.mjs +1 -1
  16. package/source/components/form/stylesheet/toggle-switch.mjs +27 -0
  17. package/source/components/form/tabs.mjs +0 -1
  18. package/source/components/form/toggle-switch.mjs +430 -0
  19. package/source/data/transformer.mjs +30 -0
  20. package/source/dom/attributes.mjs +1 -1
  21. package/source/dom/customcontrol.mjs +6 -2
  22. package/source/dom/customelement.mjs +43 -5
  23. package/source/dom/events.mjs +3 -3
  24. package/source/dom/updater.mjs +43 -16
  25. package/source/i18n/translations.mjs +1 -1
  26. package/source/monster.mjs +7 -0
  27. package/source/types/version.mjs +1 -1
  28. package/test/cases/components/form/select.mjs +1 -1
  29. package/test/cases/components/form/toggle-switch.mjs +310 -0
  30. package/test/cases/components/form/tree-select.mjs +1 -8
  31. package/test/cases/data/transformer.mjs +16 -0
  32. package/test/cases/dom/customcontrol.mjs +53 -8
  33. package/test/cases/dom/customelement-initfromscripthost.mjs +0 -4
  34. package/test/cases/dom/customelement.mjs +30 -26
  35. package/test/cases/dom/updater.mjs +14 -3
  36. package/test/cases/monster.mjs +1 -1
  37. package/test/util/jsdom.mjs +9 -10
  38. package/test/web/import.js +1 -0
  39. package/test/web/test.html +2 -2
  40. package/test/web/tests.js +3966 -1415
@@ -0,0 +1,430 @@
1
+ /**
2
+ * Copyright schukai GmbH and contributors 2024. All Rights Reserved.
3
+ * Node module: @schukai/monster
4
+ * This file is licensed under the AGPLv3 License.
5
+ * License text available at https://www.gnu.org/licenses/agpl-3.0.en.html
6
+ */
7
+ import { instanceSymbol } from "../../constants.mjs";
8
+ import { internalSymbol } from "../../constants.mjs";
9
+ import { CustomControl } from "../../dom/customcontrol.mjs";
10
+ import { Observer } from "../../types/observer.mjs";
11
+ import { ProxyObserver } from "../../types/proxyobserver.mjs";
12
+
13
+ import { addAttributeToken } from "../../dom/attributes.mjs";
14
+ import {
15
+ assembleMethodSymbol,
16
+ registerCustomElement,
17
+ updaterTransformerMethodsSymbol,
18
+ } from "../../dom/customelement.mjs";
19
+ import { isObject } from "../../types/is.mjs";
20
+ import { ToggleSwitchStyleSheet } from "./stylesheet/toggle-switch.mjs";
21
+ import {
22
+ ATTRIBUTE_ERRORMESSAGE,
23
+ ATTRIBUTE_ROLE,
24
+ } from "../../dom/constants.mjs";
25
+ export { ToggleSwitch };
26
+
27
+ /**
28
+ * @private
29
+ * @type {symbol}
30
+ */
31
+ const switchElementSymbol = Symbol("switchElement");
32
+
33
+ /**
34
+ * @private
35
+ * @type {symbol}
36
+ */
37
+ const switchElementSymbolOn = Symbol("switchElementOn");
38
+
39
+ /**
40
+ * @private
41
+ * @type {symbol}
42
+ */
43
+ const switchElementSymbolOff = Symbol("switchElementOff");
44
+
45
+ /**
46
+ * @type {string}
47
+ */
48
+ export const STATE_ON = "on";
49
+
50
+ /**
51
+ * @type {string}
52
+ */
53
+ export const STATE_OFF = "off";
54
+
55
+ /**
56
+ * This CustomControl creates a ToggleSwitch element
57
+ *
58
+ * <img src="./images/switch.png">
59
+ *
60
+ *
61
+ * @startuml toggleswitch.png
62
+ * skinparam monochrome true
63
+ * skinparam shadowing false
64
+ * HTMLElement <|-- CustomElement
65
+ * CustomElement <|-- CustomControl
66
+ * CustomControl <|-- ToggleSwitch
67
+ * @enduml
68
+ *
69
+ * @since 3.57.0
70
+ * @copyright schukai GmbH
71
+ * @memberOf Monster.Components.Form
72
+ * @summary A simple Switch
73
+ */
74
+ class ToggleSwitch extends CustomControl {
75
+ /**
76
+ * This method is called by the `instanceof` operator.
77
+ * @returns {symbol}
78
+ * @since 2.1.0
79
+ */
80
+ static get [instanceSymbol]() {
81
+ return Symbol.for(
82
+ "@schukai/monster/components/form/toggle-switch@@instance",
83
+ );
84
+ }
85
+
86
+ static getTag() {
87
+ return "monster-toggle-switch";
88
+ }
89
+
90
+ /**
91
+ * To set the options via the html tag the attribute `data-monster-options` must be used.
92
+ * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
93
+ *
94
+ * The individual configuration values can be found in the table.
95
+ *
96
+ * @property {string} value=current value of the element
97
+ * @property {Boolean} disabled=disabled=false Disabled state
98
+ * @property {Object} classes
99
+ * @property {string} classes.on=specifies the class for the on state.
100
+ * @property {string} classes.off=specifies the class for the off state.
101
+ * @property {Object} values
102
+ * @property {string} values.off=specifies the value of the element if it is not selected
103
+ * @property {Object} labels
104
+ * @property {string} labels.on=specifies the label for the on state.
105
+ * @property {string} labels.off=specifies the label for the off state.
106
+ * @property {Object} templates
107
+ * @property {string} templates.main=specifies the main template used by the control.
108
+ *
109
+ * @since 3.57.0
110
+ */
111
+ get defaults() {
112
+ return Object.assign({}, super.defaults, {
113
+ value: null,
114
+ disabled: false,
115
+ classes: {
116
+ on: "monster-theme-primary-3",
117
+ off: "monster-theme-primary-2",
118
+ },
119
+ values: {
120
+ on: "on",
121
+ off: "off",
122
+ },
123
+ labels: {
124
+ "toggle-switch-on": "ON",
125
+ "toggle-switch-off": "OFF",
126
+ },
127
+ templates: {
128
+ main: getTemplate(),
129
+ },
130
+ });
131
+ }
132
+
133
+ /**
134
+ *
135
+ * @return {Monster.Components.Form.ToggleSwitch}
136
+ */
137
+ [assembleMethodSymbol]() {
138
+ const self = this;
139
+ super[assembleMethodSymbol]();
140
+ initControlReferences.call(this);
141
+ initEventHandler.call(this);
142
+
143
+ /**
144
+ * init value to off
145
+ * if the value was not defined before inserting it into the HTML
146
+ */
147
+ if (self.getOption("value") === null) {
148
+ self.setOption("value", self.getOption("values.off"));
149
+ }
150
+
151
+ /**
152
+ * value from attribute
153
+ */
154
+ if (self.hasAttribute("value")) {
155
+ self.setOption("value", self.getAttribute("value"));
156
+ }
157
+
158
+ /**
159
+ * validate value
160
+ */
161
+ validateAndSetValue.call(self);
162
+
163
+ if (this.state === STATE_ON) {
164
+ toggleClassOn.call(self);
165
+ } else {
166
+ toggleClassOff.call(self);
167
+ }
168
+
169
+ /**
170
+ * is called when options changed
171
+ */
172
+ self[internalSymbol].attachObserver(
173
+ new Observer(function () {
174
+ if (isObject(this) && this instanceof ProxyObserver) {
175
+ validateAndSetValue.call(self);
176
+ toggleClass.call(self);
177
+ }
178
+ }),
179
+ );
180
+
181
+ return this;
182
+ }
183
+
184
+ /**
185
+ * updater transformer methods for pipe
186
+ *
187
+ * @return {function}
188
+ */
189
+ [updaterTransformerMethodsSymbol]() {;
190
+ return {
191
+ "state-callback": (Wert) => {
192
+ return this.state;
193
+ },
194
+ };
195
+ }
196
+
197
+ /**
198
+ * @return [ToggleSwitchStyleSheet]
199
+ */
200
+ static getCSSStyleSheet() {
201
+ return [ToggleSwitchStyleSheet];
202
+ }
203
+
204
+ /**
205
+ * toggle switch
206
+ *
207
+ * ```
208
+ * e = document.querySelector('monster-toggle-switch');
209
+ * e.click()
210
+ * ```
211
+ */
212
+ click() {
213
+ toggleValues.call(this);
214
+ }
215
+
216
+ /**
217
+ * toggle switch on/off
218
+ *
219
+ * ```
220
+ * e = document.querySelector('monster-toggle-switch');
221
+ * e.toggle()
222
+ * ```
223
+ *
224
+ * @return {ToggleSwitch}
225
+ */
226
+ toggle() {
227
+ this.click();
228
+ return this;
229
+ }
230
+
231
+ /**
232
+ * toggle switch on
233
+ *
234
+ * ```
235
+ * e = document.querySelector('monster-toggle-switch');
236
+ * e.toggleOn()
237
+ * ```
238
+ *
239
+ * @return {ToggleSwitch}
240
+ */
241
+ toggleOn() {
242
+ this.setOption("value", this.getOption("values.on"));
243
+ return this;
244
+ }
245
+
246
+ /**
247
+ * toggle switch off
248
+ *
249
+ * ```
250
+ * e = document.querySelector('monster-toggle-switch');
251
+ * e.toggleOff()
252
+ * ```
253
+ *
254
+ * @return {ToggleSwitch}
255
+ */
256
+ toggleOff() {
257
+ this.setOption("value", this.getOption("values.off"));
258
+ return this;
259
+ }
260
+
261
+ /**
262
+ * returns the status of the element
263
+ *
264
+ * ```
265
+ * e = document.querySelector('monster-toggle-switch');
266
+ * console.log(e.state)
267
+ * // ↦ off
268
+ * ```
269
+ *
270
+ * @return {string}
271
+ */
272
+ get state() {
273
+ return this.getOption("value") === this.getOption("values.on")
274
+ ? STATE_ON
275
+ : STATE_OFF;
276
+ }
277
+
278
+ /**
279
+ * The current value of the Switch
280
+ *
281
+ * ```
282
+ * e = document.querySelector('monster-toggle-switch');
283
+ * console.log(e.value)
284
+ * // ↦ on
285
+ * ```
286
+ *
287
+ * @return {string}
288
+ */
289
+ get value() {
290
+ return this.state === STATE_ON
291
+ ? this.getOption("values.on")
292
+ : this.getOption("values.off");
293
+ }
294
+
295
+ /**
296
+ * Set value
297
+ *
298
+ * ```
299
+ * e = document.querySelector('monster-toggle-switch');
300
+ * e.value="on"
301
+ * ```
302
+ *
303
+ * @property {string} value
304
+ */
305
+ set value(value) {
306
+ this.setOption("value", value);
307
+ }
308
+ }
309
+
310
+ /**
311
+ * @private
312
+ */
313
+ function initControlReferences() {
314
+ this[switchElementSymbol] = this.shadowRoot.querySelector(
315
+ `[${ATTRIBUTE_ROLE}=switch]`,
316
+ );
317
+ }
318
+
319
+ /**
320
+ * @private
321
+ */
322
+ function toggleClassOn() {
323
+ this[switchElementSymbol].classList.remove(this.getOption("classes.off")); // change color
324
+ this[switchElementSymbol].classList.add(this.getOption("classes.on")); // change color
325
+ }
326
+
327
+ /**
328
+ * @private
329
+ */
330
+ function toggleClassOff() {
331
+ this[switchElementSymbol].classList.remove(this.getOption("classes.on")); // change color
332
+ this[switchElementSymbol].classList.add(this.getOption("classes.off")); // change color
333
+ }
334
+
335
+ /**
336
+ * @private
337
+ */
338
+ function toggleClass() {;
339
+ if (this.getOption("value") === this.getOption("values.on")) {
340
+ toggleClassOn.call(this);
341
+ } else {
342
+ toggleClassOff.call(this);
343
+ }
344
+ }
345
+
346
+ /**
347
+ * @private
348
+ */
349
+ function toggleValues() {;
350
+
351
+ if (this.getOption("disabled") === true) {
352
+ return;
353
+ }
354
+
355
+ if (this.getOption("value") === this.getOption("values.on")) {
356
+ this.setOption("value", this.getOption("values.off"));
357
+ this?.setFormValue(this.getOption("value")); // set form value
358
+ } else {
359
+ this.setOption("value", this.getOption("values.on"));
360
+ this?.setFormValue(this.getOption("values.off")); // set form value
361
+ }
362
+
363
+ this.setOption("state", this.state);
364
+ }
365
+
366
+ /**
367
+ * @private
368
+ */
369
+ function validateAndSetValue() {;
370
+ const value = this.getOption("value");
371
+
372
+ const validatedValues = [];
373
+ validatedValues.push(this.getOption("values.on"));
374
+ validatedValues.push(this.getOption("values.off"));
375
+
376
+ if (validatedValues.includes(value) === false) {
377
+ addAttributeToken(
378
+ this,
379
+ ATTRIBUTE_ERRORMESSAGE,
380
+ 'The value "' +
381
+ value +
382
+ '" must be "' +
383
+ this.getOption("values.on") +
384
+ '" or "' +
385
+ this.getOption("values.off"),
386
+ );
387
+ this.setOption("disabled", true);
388
+ this.formDisabledCallback(true);
389
+ } else {
390
+ this.setOption("disabled", false);
391
+ this.formDisabledCallback(false);
392
+ }
393
+ }
394
+
395
+ /**
396
+ * @private
397
+ * @return {initEventHandler}
398
+ */
399
+ function initEventHandler() {
400
+ const self = this;
401
+ self.addEventListener("keyup", function (event) {
402
+ if (event.code === "Space") {
403
+ self[switchElementSymbol].click();
404
+ }
405
+ });
406
+ self.addEventListener("click", function (event) {
407
+ toggleValues.call(self);
408
+ });
409
+ return this;
410
+ }
411
+
412
+ /**
413
+ * @private
414
+ * @return {string}
415
+ */
416
+ function getTemplate() {
417
+ // language=HTML
418
+ return `
419
+ <div data-monster-role="control" part="control" tabindex="0">
420
+
421
+ <div class="switch" data-monster-role="switch" data-monster-attributes="data-monster-state path:value | call:state-callback " >
422
+ <div class="label on" data-monster-replace="path:labels.toggle-switch-on"></div>
423
+ <div class="label off" data-monster-replace="path:labels.toggle-switch-off"></div>
424
+ <div class="switch-slider"></div>
425
+ </div>
426
+
427
+ </div>`;
428
+ }
429
+
430
+ registerCustomElement(ToggleSwitch);
@@ -776,6 +776,36 @@ function transform(value) {
776
776
 
777
777
  return translations.getText(key, defaultValue);
778
778
 
779
+ case "set-toggle":
780
+ case "set-set":
781
+ case "set-remove":
782
+ const modifier = args.shift();
783
+ let delimiter = args.shift();
784
+ if (delimiter === undefined) {
785
+ delimiter = " ";
786
+ }
787
+
788
+ const set = new Set(value.split(delimiter));
789
+ const toggle = new Set(modifier.split(delimiter));
790
+ if (this.command === "set-toggle") {
791
+ for (const t of toggle) {
792
+ if (set.has(t)) {
793
+ set.delete(t);
794
+ } else {
795
+ set.add(t);
796
+ }
797
+ }
798
+ } else if (this.command === "set-set") {
799
+ for (const t of toggle) {
800
+ set.add(t);
801
+ }
802
+ } else if (this.command === "set-remove") {
803
+ for (const t of toggle) {
804
+ set.delete(t);
805
+ }
806
+ }
807
+ return Array.from(set).join(delimiter);
808
+
779
809
  default:
780
810
  throw new Error(`unknown command ${this.command}`);
781
811
  }
@@ -377,7 +377,7 @@ function findClosestByAttribute(element, key, value) {
377
377
  }
378
378
 
379
379
  /**
380
- * This function searches, starting from an `HTMLElemement`, for the next element that has a certain attribute.
380
+ * This function searches, starting from an `HTMLElement`, for the next element that has a certain attribute.
381
381
  *
382
382
  * ```html
383
383
  * <div class="myclass" id="2">
@@ -320,9 +320,13 @@ class CustomControl extends CustomElement {
320
320
  */
321
321
  formDisabledCallback(disabled) {
322
322
  if (disabled) {
323
- this.setAttribute("disabled", "");
323
+ if (!this.hasAttribute("disabled")) {
324
+ this.setAttribute("disabled", "");
325
+ }
324
326
  } else {
325
- this.removeAttribute("disabled");
327
+ if (this.hasAttribute("disabled")) {
328
+ this.removeAttribute("disabled");
329
+ }
326
330
  }
327
331
  }
328
332
 
@@ -26,7 +26,6 @@ import {
26
26
  validateFunction,
27
27
  validateInstance,
28
28
  validateObject,
29
- validateString,
30
29
  } from "../types/validate.mjs";
31
30
  import { clone } from "../util/clone.mjs";
32
31
  import {
@@ -47,10 +46,7 @@ import {
47
46
  import { findDocumentTemplate, Template } from "./template.mjs";
48
47
  import { addObjectWithUpdaterToElement } from "./updater.mjs";
49
48
  import { instanceSymbol } from "../constants.mjs";
50
- import {
51
- getDocumentTranslations,
52
- Translations,
53
- } from "../i18n/translations.mjs";
49
+ import { getDocumentTranslations } from "../i18n/translations.mjs";
54
50
  import { getSlottedElements } from "./slotted.mjs";
55
51
  import { initOptionsFromAttributes } from "./util/init-options-from-attributes.mjs";
56
52
  import { setOptionFromAttribute } from "./util/set-option-from-attribute.mjs";
@@ -62,6 +58,7 @@ export {
62
58
  attributeObserverSymbol,
63
59
  registerCustomElement,
64
60
  getSlottedElements,
61
+ updaterTransformerMethodsSymbol,
65
62
  };
66
63
 
67
64
  /**
@@ -78,6 +75,14 @@ const assembleMethodSymbol = Symbol.for(
78
75
  "@schukai/monster/dom/@@assembleMethodSymbol",
79
76
  );
80
77
 
78
+ /**
79
+ * @memberOf Monster.DOM
80
+ * @type {symbol}
81
+ */
82
+ const updaterTransformerMethodsSymbol = Symbol.for(
83
+ "@schukai/monster/dom/@@updaterTransformerMethodsSymbol",
84
+ );
85
+
81
86
  /**
82
87
  * this symbol holds the attribute observer callbacks. The key is the attribute name.
83
88
  * @memberOf Monster.DOM
@@ -527,6 +532,39 @@ class CustomElement extends HTMLElement {
527
532
  return this;
528
533
  }
529
534
 
535
+ /**
536
+ * This method is called once when the object is equipped with update for the dynamic change of the dom.
537
+ * The functions returned here can be used as pipe functions in the template.
538
+ *
539
+ * In the example, the function `my-transformer` is defined. In the template you can use it as follows:
540
+ *
541
+ * ```html
542
+ * <my-element data-monster-option-transformer="path:my-value | call:my-transformer"></my-element>
543
+ * ```
544
+ *
545
+ * @example
546
+ * [updaterTransformerMethodsSymbol]() {
547
+ * return {
548
+ * "my-transformer": (value) => {
549
+ * switch (typeof Wert) {
550
+ * case "string":
551
+ * return value + "!";
552
+ * case "Zahl":
553
+ * return value + 1;
554
+ * default:
555
+ * return value;
556
+ * }
557
+ * }
558
+ * };
559
+ * };
560
+ *
561
+ * @return {object}
562
+ * @since 2.43.0
563
+ */
564
+ [updaterTransformerMethodsSymbol]() {
565
+ return {};
566
+ }
567
+
530
568
  /**
531
569
  * This method is called once when the object is included in the DOM for the first time. It performs the following actions:
532
570
  * 1. Extracts the options from the attributes and the script tag of the element and sets them.
@@ -14,7 +14,7 @@ export { fireEvent, fireCustomEvent, findTargetElementFromEvent };
14
14
  /**
15
15
  * The function sends an event
16
16
  *
17
- * @param {HTMLElement|HTMLCollection|NodeList} element
17
+ * @param {Element | Node | HTMLCollection | NodeList} element
18
18
  * @param {string} type
19
19
  * @return {void}
20
20
  * @license AGPLv3
@@ -55,8 +55,9 @@ function fireEvent(element, type) {
55
55
  /**
56
56
  * You can call the function via the monster namespace `new Monster.DOM.fireCustomEvent()`.
57
57
  *
58
- * @param {HTMLElement|HTMLCollection|NodeList} element
58
+ * @param {Element | Node | HTMLCollection | NodeList} element
59
59
  * @param {string} type
60
+ * @param {object} detail
60
61
  * @return {void}
61
62
  * @license AGPLv3
62
63
  * @since 1.29.0
@@ -66,7 +67,6 @@ function fireEvent(element, type) {
66
67
  * @summary Construct and send and event
67
68
  */
68
69
  function fireCustomEvent(element, type, detail) {
69
- const document = getDocument();
70
70
 
71
71
  if (element instanceof HTMLElement) {
72
72
  if (!isObject(detail)) {
@@ -29,20 +29,23 @@ import { validateArray, validateInstance } from "../types/validate.mjs";
29
29
  import { Sleep } from "../util/sleep.mjs";
30
30
  import { clone } from "../util/clone.mjs";
31
31
  import { trimSpaces } from "../util/trimspaces.mjs";
32
- import { addToObjectLink } from "./attributes.mjs";
32
+ import { addAttributeToken, addToObjectLink } from "./attributes.mjs";
33
+ import { updaterTransformerMethodsSymbol } from "./customelement.mjs";
33
34
  import { findTargetElementFromEvent } from "./events.mjs";
34
35
  import { findDocumentTemplate } from "./template.mjs";
35
36
 
36
37
  export { Updater, addObjectWithUpdaterToElement };
37
38
 
38
39
  /**
39
- * The updater class connects an object with the dom. In this way, structures and contents in the DOM can be programmatically adapted via attributes.
40
+ * The updater class connects an object with the dom. In this way, structures and contents in the DOM can be
41
+ * programmatically adapted via attributes.
40
42
  *
41
43
  * For example, to include a string from an object, the attribute `data-monster-replace` can be used.
42
44
  * a further explanation can be found under [monsterjs.org](https://monsterjs.org/)
43
45
  *
44
- * Changes to attributes are made only when the direct values are changed. If you want to assign changes to other values
45
- * as well, you have to insert the attribute `data-monster-select-this`. This should be done with care, as it can reduce performance.
46
+ * Changes to attributes are made only when the direct values are changed. If you want to assign changes
47
+ * to other values as well, you have to insert the attribute `data-monster-select-this`. This should be
48
+ * done with care, as it can reduce performance.
46
49
  *
47
50
  * @externalExample ../../example/dom/updater.mjs
48
51
  * @license AGPLv3
@@ -287,8 +290,9 @@ function getControlEventHandler() {
287
290
  if (element === undefined) {
288
291
  return;
289
292
  }
290
-
291
- retrieveAndSetValue.call(this, element);
293
+ setTimeout(() => {
294
+ retrieveAndSetValue.call(this, element);
295
+ }, 0);
292
296
  };
293
297
 
294
298
  return this[symbol];
@@ -857,22 +861,14 @@ function handleInputControlAttributeUpdate(element, name, value) {
857
861
  switch (element.type) {
858
862
  case "radio":
859
863
  if (name === "checked") {
860
- if (value !== undefined) {
861
- element.checked = true;
862
- } else {
863
- element.checked = false;
864
- }
864
+ element.checked = value !== undefined;
865
865
  }
866
866
 
867
867
  break;
868
868
 
869
869
  case "checkbox":
870
870
  if (name === "checked") {
871
- if (value !== undefined) {
872
- element.checked = true;
873
- } else {
874
- element.checked = false;
875
- }
871
+ element.checked = value !== undefined;
876
872
  }
877
873
 
878
874
  break;
@@ -929,6 +925,31 @@ function addObjectWithUpdaterToElement(elements, symbol, object) {
929
925
 
930
926
  const result = [];
931
927
 
928
+ const updaterCallbacks = [];
929
+ const cb = this?.[updaterTransformerMethodsSymbol];
930
+ if (this instanceof HTMLElement && typeof cb === "function") {
931
+ const callbacks = cb.call(this);
932
+ if (typeof callbacks === "object") {
933
+ for (const [name, callback] of Object.entries(callbacks)) {
934
+ if (typeof callback === "function") {
935
+ updaterCallbacks.push([name, callback]);
936
+ } else {
937
+ addAttributeToken(
938
+ this,
939
+ ATTRIBUTE_ERRORMESSAGE,
940
+ `onUpdaterPipeCallbacks: ${name} is not a function`,
941
+ );
942
+ }
943
+ }
944
+ } else {
945
+ addAttributeToken(
946
+ this,
947
+ ATTRIBUTE_ERRORMESSAGE,
948
+ `onUpdaterPipeCallbacks do not return an object with functions`,
949
+ );
950
+ }
951
+ }
952
+
932
953
  elements.forEach((element) => {
933
954
  if (!(element instanceof HTMLElement)) return;
934
955
  if (element instanceof HTMLTemplateElement) return;
@@ -936,6 +957,12 @@ function addObjectWithUpdaterToElement(elements, symbol, object) {
936
957
  const u = new Updater(element, object);
937
958
  updaters.add(u);
938
959
 
960
+ if (updaterCallbacks.length > 0) {
961
+ for (const [name, callback] of updaterCallbacks) {
962
+ u.setCallback(name, callback);
963
+ }
964
+ }
965
+
939
966
  result.push(
940
967
  u.run().then(() => {
941
968
  return u.enableEventProcessing();