@schukai/monster 4.103.1 → 4.104.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,14 @@
2
2
 
3
3
 
4
4
 
5
+ ## [4.104.0] - 2026-01-19
6
+
7
+ ### Add Features
8
+
9
+ - Add batching feature for updates in the DataTable and CustomElement
10
+
11
+
12
+
5
13
  ## [4.103.1] - 2026-01-19
6
14
 
7
15
  ### Bug Fixes
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.103.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.104.0"}
@@ -202,6 +202,8 @@ class DataTable extends CustomElement {
202
202
  * @property {string} copy.quoteOpen Quote open character
203
203
  * @property {string} copy.quoteClose Quote close character
204
204
  * @property {string} copy.rowBreak Row break character
205
+ * @property {Object} updater Updater configuration
206
+ * @property {boolean} updater.batchUpdates Enables batched updater content/attribute updates
205
207
  * @property {Object} templateMapping Template mapping
206
208
  * @property {string} templateMapping.row-key Row key
207
209
  * @property {string} templateMapping.filter-id Filter id
@@ -257,6 +259,10 @@ class DataTable extends CustomElement {
257
259
  rowBreak: "\n",
258
260
  },
259
261
 
262
+ updater: {
263
+ batchUpdates: true,
264
+ },
265
+
260
266
  templateMapping: {
261
267
  "row-key": null,
262
268
  "filter-id": null,
@@ -314,6 +314,8 @@ class CustomElement extends HTMLElement {
314
314
  * @property {Function} templateFormatter.marker.close=null Specifies the closing marker for the templates.
315
315
  * @property {Boolean} templateFormatter.i18n=false Specifies whether the templates should be formatted with i18n.
316
316
  * @property {Boolean} eventProcessing=false Specifies whether the control processes events.
317
+ * @property {Object} updater Specifies updater options.
318
+ * @property {Boolean} updater.batchUpdates=false Batches updater content/attribute updates per diff.
317
319
  * @since 1.8.0
318
320
  */
319
321
  get defaults() {
@@ -334,6 +336,9 @@ class CustomElement extends HTMLElement {
334
336
  },
335
337
 
336
338
  eventProcessing: false,
339
+ updater: {
340
+ batchUpdates: false,
341
+ },
337
342
  };
338
343
  }
339
344
 
@@ -651,6 +656,9 @@ class CustomElement extends HTMLElement {
651
656
  if (this.getOption("eventProcessing") === true) {
652
657
  cfg.eventProcessing = true;
653
658
  }
659
+ if (this.getOption("updater.batchUpdates") === true) {
660
+ cfg.batchUpdates = true;
661
+ }
654
662
  addObjectWithUpdaterToElement.call(
655
663
  this,
656
664
  nodeList,
@@ -71,6 +71,8 @@ const pendingDiffsSymbol = Symbol("pendingDiffs");
71
71
  * @type {symbol}
72
72
  */
73
73
  const processingSymbol = Symbol("processing");
74
+ const processQueueSymbol = Symbol("processQueue");
75
+ const applyChangeSymbol = Symbol("applyChange");
74
76
  const updaterRootSymbol = Symbol.for("@schukai/monster/dom/@@updater-root");
75
77
 
76
78
  /**
@@ -125,6 +127,9 @@ class Updater extends Base {
125
127
  callbacks: new Map(),
126
128
  eventTypes: ["keyup", "click", "change", "drop", "touchend", "input"],
127
129
  subject: subject,
130
+ features: {
131
+ batchUpdates: false,
132
+ },
128
133
  };
129
134
 
130
135
  this[internalSymbol].callbacks.set(
@@ -144,7 +149,7 @@ class Updater extends Base {
144
149
  return Promise.resolve();
145
150
  }
146
151
  this[pendingDiffsSymbol].push(diffResult);
147
- return this.#processQueue();
152
+ return this[processQueueSymbol]();
148
153
  }),
149
154
  );
150
155
  }
@@ -153,7 +158,7 @@ class Updater extends Base {
153
158
  * @private
154
159
  * @return {Promise}
155
160
  */
156
- async #processQueue() {
161
+ async [processQueueSymbol]() {
157
162
  if (this[processingSymbol]) {
158
163
  return Promise.resolve();
159
164
  }
@@ -162,8 +167,34 @@ class Updater extends Base {
162
167
  try {
163
168
  while (this[pendingDiffsSymbol].length) {
164
169
  const diffResult = this[pendingDiffsSymbol].shift();
165
- for (const change of Object.values(diffResult)) {
166
- await this.#applyChange(change);
170
+ if (this[internalSymbol].features.batchUpdates === true) {
171
+ const updatePaths = new Map();
172
+ for (const change of Object.values(diffResult)) {
173
+ removeElement.call(this, change);
174
+ insertElement.call(this, change);
175
+
176
+ const path = isArray(change?.["path"]) ? change["path"] : null;
177
+ if (!path) {
178
+ continue;
179
+ }
180
+ if (path.length === 0) {
181
+ updatePaths.set("", path);
182
+ continue;
183
+ }
184
+ const root = path[0];
185
+ if (!updatePaths.has(root)) {
186
+ updatePaths.set(root, [root]);
187
+ }
188
+ }
189
+
190
+ for (const path of updatePaths.values()) {
191
+ updateContent.call(this, { path });
192
+ updateAttributes.call(this, { path });
193
+ }
194
+ } else {
195
+ for (const change of Object.values(diffResult)) {
196
+ await this[applyChangeSymbol](change);
197
+ }
167
198
  }
168
199
  }
169
200
  } catch (err) {
@@ -174,7 +205,7 @@ class Updater extends Base {
174
205
  }
175
206
 
176
207
  /** @private **/
177
- async #applyChange(change) {
208
+ async [applyChangeSymbol](change) {
178
209
  removeElement.call(this, change);
179
210
  insertElement.call(this, change);
180
211
  updateContent.call(this, change);
@@ -195,6 +226,18 @@ class Updater extends Base {
195
226
  return this;
196
227
  }
197
228
 
229
+ /**
230
+ * Enable or disable batched update processing.
231
+ *
232
+ * @since 4.104.0
233
+ * @param {boolean} enabled
234
+ * @return {Updater}
235
+ */
236
+ setBatchUpdates(enabled) {
237
+ this[internalSymbol].features.batchUpdates = enabled === true;
238
+ return this;
239
+ }
240
+
198
241
  /**
199
242
  * With this method, the eventlisteners are hooked in and the magic begins.
200
243
  *
@@ -1097,6 +1140,7 @@ function handleInputControlAttributeUpdate(element, name, value) {
1097
1140
  * @param {object} config
1098
1141
  *
1099
1142
  * Config: enableEventProcessing {boolean} - default: false - enables the event processing
1143
+ * Config: batchUpdates {boolean} - default: false - batches updateContent/updateAttributes per diff
1100
1144
  *
1101
1145
  * @return {Promise[]}
1102
1146
  * @license AGPLv3
@@ -1169,6 +1213,9 @@ function addObjectWithUpdaterToElement(elements, symbol, object, config = {}) {
1169
1213
 
1170
1214
  result.push(
1171
1215
  u.run().then(() => {
1216
+ if (config.batchUpdates === true) {
1217
+ u.setBatchUpdates(true);
1218
+ }
1172
1219
  if (config.eventProcessing === true) {
1173
1220
  u.enableEventProcessing();
1174
1221
  }