@schukai/monster 4.124.1 → 4.124.2

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.124.2] - 2026-03-02
6
+
7
+ ### Bug Fixes
8
+
9
+ - **digits:** stabilize rendering and input behavior ([#384](https://gitlab.schukai.com/oss/libraries/javascript/monster/issues/384))
10
+
11
+
12
+
5
13
  ## [4.124.1] - 2026-02-23
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.124.1"}
1
+ {"author":"Volker Schukai","dependencies":{"@floating-ui/dom":"^1.7.5","@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.124.2"}
@@ -100,10 +100,11 @@ class Digits extends CustomControl {
100
100
  * @property {string} value
101
101
  */
102
102
  set value(value) {
103
- const chars = String(value).split("");
103
+ const normalizedValue = value == null ? "" : String(value);
104
+ const chars = normalizedValue.split("");
104
105
 
105
- this.setOption("value", value);
106
- this.setFormValue(value);
106
+ this.setOption("value", normalizedValue);
107
+ this.setFormValue(normalizedValue);
107
108
 
108
109
  if (chars.every(checkCharacter.bind(this))) {
109
110
  this.setValidity({ badInput: false }, "");
@@ -209,7 +210,9 @@ function updateDigitControls() {
209
210
 
210
211
  const controls = [];
211
212
 
212
- const values = this.getOption("value") || "";
213
+ const values = this.getOption("value") == null
214
+ ? ""
215
+ : String(this.getOption("value"));
213
216
 
214
217
  if (this[digitsElementSymbol]) {
215
218
  this[digitsElementSymbol].style.gridTemplateColumns =
@@ -218,11 +221,17 @@ function updateDigitControls() {
218
221
 
219
222
  for (let i = 0; i < digits; i++) {
220
223
  controls.push({
221
- value: values[i] ?? " ",
224
+ value: values[i] ?? "",
222
225
  });
223
226
  }
224
227
 
225
228
  this.setOption("digitsControls", controls);
229
+
230
+ // Keep real input.value in sync with the option model. Attribute updates alone
231
+ // can drift after user interaction because inputs keep an internal dirty value.
232
+ setTimeout(() => {
233
+ syncInputValues.call(this, controls);
234
+ }, 0);
226
235
  }
227
236
 
228
237
  /**
@@ -287,7 +296,7 @@ function initEventHandler() {
287
296
  }
288
297
 
289
298
  if (pressedKey === "Backspace") {
290
- if (inputControl.value !== "" && inputControl.value !== " ") {
299
+ if (inputControl.value !== "") {
291
300
  event.preventDefault();
292
301
  inputControl.value = "";
293
302
  collectValues.call(self);
@@ -313,16 +322,14 @@ function initEventHandler() {
313
322
  return;
314
323
  }
315
324
 
316
- if (inputControl.value.length === 1) {
317
- event.preventDefault();
318
- inputControl.value = pressedKey;
319
- const nextControl = inputControl.nextElementSibling;
320
- if (nextControl && nextControl.tagName === "INPUT") {
321
- nextControl.focus();
322
- }
323
- collectValues.call(self);
324
- return;
325
+ event.preventDefault();
326
+ inputControl.value = pressedKey;
327
+ const nextControl = inputControl.nextElementSibling;
328
+ if (nextControl && nextControl.tagName === "INPUT") {
329
+ nextControl.focus();
325
330
  }
331
+ collectValues.call(self);
332
+ return;
326
333
  }
327
334
  });
328
335
 
@@ -344,10 +351,28 @@ function initEventHandler() {
344
351
  function collectValues() {
345
352
  const controlsValues = Array.from(
346
353
  this[digitsElementSymbol].querySelectorAll("input"),
347
- ).map((input) => input.value || " ");
354
+ ).map((input) => input.value || "");
348
355
  this.value = controlsValues.join("");
349
356
  }
350
357
 
358
+ /**
359
+ * @private
360
+ * @param {Array<{value:string}>} controls
361
+ * @returns {void}
362
+ */
363
+ function syncInputValues(controls) {
364
+ if (!this[digitsElementSymbol]) return;
365
+ const inputs = this[digitsElementSymbol].querySelectorAll("input");
366
+ if (!inputs.length) return;
367
+
368
+ for (let i = 0; i < inputs.length; i++) {
369
+ const nextValue = controls?.[i]?.value ?? "";
370
+ if (inputs[i].value !== nextValue) {
371
+ inputs[i].value = nextValue;
372
+ }
373
+ }
374
+ }
375
+
351
376
  /**
352
377
  * @private
353
378
  * @return {void}