@schukai/monster 4.56.0 → 4.58.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.
Files changed (31) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/package.json +1 -1
  3. package/source/components/data/stylesheet/metric-graph.mjs +1 -1
  4. package/source/components/data/stylesheet/metric.mjs +1 -1
  5. package/source/components/datatable/dataset.mjs +10 -0
  6. package/source/components/datatable/datasource/rest.mjs +141 -14
  7. package/source/components/datatable/datasource.mjs +8 -1
  8. package/source/components/datatable/datatable.mjs +3 -7
  9. package/source/components/datatable/save-button.mjs +348 -334
  10. package/source/components/datatable/status.mjs +7 -0
  11. package/source/components/datatable/util.mjs +7 -0
  12. package/source/components/form/button-bar.mjs +193 -95
  13. package/source/components/form/field-set.mjs +283 -283
  14. package/source/components/form/form.mjs +407 -162
  15. package/source/components/form/login.mjs +1571 -1571
  16. package/source/components/form/quantity.mjs +233 -233
  17. package/source/components/form/select.mjs +3106 -3101
  18. package/source/components/form/style/field-set.pcss +6 -2
  19. package/source/components/form/style/form.pcss +8 -0
  20. package/source/components/form/stylesheet/field-set.mjs +1 -1
  21. package/source/components/form/stylesheet/form.mjs +1 -1
  22. package/source/components/form/stylesheet/select.mjs +13 -6
  23. package/source/components/style/typography.css +2 -2
  24. package/source/components/tree-menu/stylesheet/tree-menu.mjs +1 -1
  25. package/source/constraints/abstract.mjs +17 -17
  26. package/source/dom/customelement.mjs +962 -963
  27. package/source/dom/updater.mjs +874 -863
  28. package/source/dom/util/init-options-from-attributes.mjs +56 -56
  29. package/source/monster.mjs +0 -1
  30. package/source/net/webconnect.mjs +325 -325
  31. package/source/types/is.mjs +66 -66
@@ -187,6 +187,9 @@ function initEventHandler() {
187
187
 
188
188
  this[datasourceLinkedElementSymbol] = element;
189
189
  element.addEventListener("monster-datasource-fetched", function () {
190
+ if (typeof self[errorElementSymbol]?.resetErrorMessage === "function") {
191
+ self[errorElementSymbol].resetErrorMessage();
192
+ }
190
193
  fadeOutTimer = setTimeout(() => {
191
194
  self.setOption("state.spinner", "hide");
192
195
  }, 800);
@@ -198,6 +201,10 @@ function initEventHandler() {
198
201
  fadeOutTimer = null;
199
202
  }
200
203
 
204
+ if (typeof self[errorElementSymbol]?.resetErrorMessage === "function") {
205
+ self[errorElementSymbol].resetErrorMessage();
206
+ }
207
+
201
208
  self.setOption("state.spinner", "show");
202
209
  });
203
210
 
@@ -24,6 +24,7 @@ export { handleDataSourceChanges, datasourceLinkedElementSymbol };
24
24
  * @type {symbol}
25
25
  */
26
26
  const datasourceLinkedElementSymbol = Symbol("datasourceLinkedElement");
27
+ const dataChangeVersionSymbol = Symbol("dataChangeVersion");
27
28
 
28
29
  /**
29
30
  * @private
@@ -33,6 +34,9 @@ function handleDataSourceChanges() {
33
34
  return;
34
35
  }
35
36
 
37
+ const version = (this[dataChangeVersionSymbol] || 0) + 1;
38
+ this[dataChangeVersionSymbol] = version;
39
+
36
40
  let data = this[datasourceLinkedElementSymbol]?.data;
37
41
 
38
42
  if (isPrimitive(data)) {
@@ -71,6 +75,9 @@ function handleDataSourceChanges() {
71
75
  }
72
76
 
73
77
  queueMicrotask(() => {
78
+ if (this[dataChangeVersionSymbol] !== version) {
79
+ return;
80
+ }
74
81
  this.setOption("data", data);
75
82
  });
76
83
  }
@@ -35,8 +35,6 @@ import { findTargetElementFromEvent } from "../../dom/events.mjs";
35
35
  import { getDocument } from "../../dom/util.mjs";
36
36
  import { getGlobal } from "../../types/global.mjs";
37
37
  import { ID } from "../../types/id.mjs";
38
- import { DeadMansSwitch } from "../../util/deadmansswitch.mjs";
39
- import { Processing } from "../../util/processing.mjs";
40
38
  import { STYLE_DISPLAY_MODE_BLOCK } from "./constants.mjs";
41
39
  import { ButtonBarStyleSheet } from "./stylesheet/button-bar.mjs";
42
40
  import { positionPopper } from "./util/floating-ui.mjs";
@@ -48,8 +46,6 @@ export { ButtonBar };
48
46
  * @private
49
47
  * @type {symbol}
50
48
  */
51
- const timerCallbackSymbol = Symbol("timerCallback");
52
-
53
49
  /**
54
50
  * local symbol
55
51
  * @private
@@ -124,6 +120,12 @@ const mutationObserverSymbol = Symbol("mutationObserver");
124
120
  */
125
121
  const switchElementSymbol = Symbol("switchElement");
126
122
 
123
+ /**
124
+ * @private
125
+ * @type {symbol}
126
+ */
127
+ const layoutStateSymbol = Symbol("layoutState");
128
+
127
129
  /**
128
130
  * @private
129
131
  * @type {string}
@@ -187,14 +189,20 @@ class ButtonBar extends CustomElement {
187
189
  super[assembleMethodSymbol]();
188
190
 
189
191
  this[dimensionsSymbol] = new Pathfinder({ data: {} });
192
+ this[layoutStateSymbol] = {
193
+ scheduled: false,
194
+ needsMeasure: true,
195
+ needsLayout: true,
196
+ needsObserve: true,
197
+ suppressSlotChange: false,
198
+ };
190
199
 
191
200
  initControlReferences.call(this);
192
201
  initEventHandler.call(this);
193
202
 
194
203
  // setup structure
195
- initButtonBar.call(this).then(() => {
196
- initPopperSwitch.call(this);
197
- });
204
+ initButtonBar.call(this);
205
+ initPopperSwitch.call(this);
198
206
  }
199
207
 
200
208
  /**
@@ -230,10 +238,7 @@ class ButtonBar extends CustomElement {
230
238
  document.addEventListener(type, this[closeEventHandler]);
231
239
  }
232
240
 
233
- setTimeout(() => {
234
- updatePopper.call(this);
235
- updateResizeObserverObservation.call(this);
236
- }, 0);
241
+ scheduleLayout.call(this, { measure: true, layout: true, observe: true });
237
242
  }
238
243
 
239
244
  /**
@@ -315,11 +320,17 @@ function initDefaultsFromAttributes(obj) {
315
320
  * @return {boolean}
316
321
  */
317
322
  function isElementTrulyVisible(element) {
318
- if (!(element instanceof HTMLElement)) {
319
- return false;
320
- }
321
- const computedStyle = getComputedStyle(element);
322
- return computedStyle.display !== 'none' && computedStyle.visibility !== 'hidden' && computedStyle.opacity !== '0' && element.offsetWidth > 0 && element.offsetHeight > 0;
323
+ if (!(element instanceof HTMLElement)) {
324
+ return false;
325
+ }
326
+ const computedStyle = getComputedStyle(element);
327
+ return (
328
+ computedStyle.display !== "none" &&
329
+ computedStyle.visibility !== "hidden" &&
330
+ computedStyle.opacity !== "0" &&
331
+ element.offsetWidth > 0 &&
332
+ element.offsetHeight > 0
333
+ );
323
334
  }
324
335
 
325
336
  /**
@@ -328,33 +339,6 @@ function isElementTrulyVisible(element) {
328
339
  function initEventHandler() {
329
340
  const self = this;
330
341
 
331
- const triggerRecalculation = () => {
332
- if (self[timerCallbackSymbol] instanceof DeadMansSwitch) {
333
- try {
334
- self[timerCallbackSymbol].touch();
335
- return;
336
- } catch (e) {
337
- if (e.message !== "has already run") throw e;
338
- delete self[timerCallbackSymbol];
339
- }
340
- }
341
-
342
- self[timerCallbackSymbol] = new DeadMansSwitch(50, () => {
343
- requestAnimationFrame(() => {
344
- updatePopper.call(self);
345
- self[dimensionsSymbol].setVia("data.calculated", false);
346
- try {
347
- checkAndRearrangeButtons.call(self);
348
- } catch (error) {
349
- addErrorAttribute(
350
- self,
351
- error?.message || "An error occurred while rearranging the buttons",
352
- );
353
- }
354
- });
355
- });
356
- };
357
-
358
342
  const mutationCallback = (mutationList) => {
359
343
  let needsRecalc = false;
360
344
  for (const mutation of mutationList) {
@@ -370,7 +354,7 @@ function initEventHandler() {
370
354
  }
371
355
  }
372
356
  if (needsRecalc) {
373
- triggerRecalculation();
357
+ scheduleLayout.call(self, { measure: true, layout: true });
374
358
  }
375
359
  };
376
360
 
@@ -387,13 +371,27 @@ function initEventHandler() {
387
371
 
388
372
  if (self[buttonBarSlotElementSymbol]) {
389
373
  self[buttonBarSlotElementSymbol].addEventListener("slotchange", () => {
390
- checkAndRearrangeButtons.call(self);
374
+ if (self[layoutStateSymbol]?.suppressSlotChange) {
375
+ return;
376
+ }
377
+ scheduleLayout.call(self, {
378
+ measure: true,
379
+ layout: true,
380
+ observe: true,
381
+ });
391
382
  });
392
383
  }
393
384
 
394
385
  if (self[popperElementSymbol]) {
395
386
  self[popperElementSymbol].addEventListener("slotchange", () => {
396
- checkAndRearrangeButtons.call(self);
387
+ if (self[layoutStateSymbol]?.suppressSlotChange) {
388
+ return;
389
+ }
390
+ scheduleLayout.call(self, {
391
+ measure: true,
392
+ layout: true,
393
+ observe: true,
394
+ });
397
395
  });
398
396
  }
399
397
 
@@ -401,7 +399,9 @@ function initEventHandler() {
401
399
  self.setOption("classes.button", value);
402
400
  };
403
401
 
404
- self[resizeObserverSymbol] = new ResizeObserver(triggerRecalculation);
402
+ self[resizeObserverSymbol] = new ResizeObserver(() => {
403
+ scheduleLayout.call(self, { measure: true, layout: true });
404
+ });
405
405
  self[mutationObserverSymbol] = new MutationObserver(mutationCallback);
406
406
 
407
407
  initSlotChangedHandler.call(self);
@@ -409,16 +409,74 @@ function initEventHandler() {
409
409
 
410
410
  function initSlotChangedHandler() {
411
411
  this[buttonBarElementSymbol].addEventListener("slotchange", () => {
412
- updateResizeObserverObservation.call(this);
412
+ if (this[layoutStateSymbol]?.suppressSlotChange) {
413
+ return;
414
+ }
415
+ scheduleLayout.call(this, { observe: true });
413
416
  });
414
417
  }
415
418
 
416
- function checkAndRearrangeButtons() {
417
- if (this[dimensionsSymbol].getVia("data.calculated", false) !== true) {
418
- calculateButtonBarDimensions.call(this);
419
+ function scheduleLayout(options = {}) {
420
+ if (!this[layoutStateSymbol]) {
421
+ return;
422
+ }
423
+
424
+ const state = this[layoutStateSymbol];
425
+ state.needsMeasure = state.needsMeasure || options.measure === true;
426
+ state.needsLayout = state.needsLayout || options.layout === true;
427
+ state.needsObserve = state.needsObserve || options.observe === true;
428
+
429
+ if (state.scheduled) {
430
+ return;
431
+ }
432
+
433
+ state.scheduled = true;
434
+ requestAnimationFrame(() => {
435
+ runLayout.call(this);
436
+ });
437
+ }
438
+
439
+ function runLayout() {
440
+ const state = this[layoutStateSymbol];
441
+ if (!state) {
442
+ return;
443
+ }
444
+
445
+ state.scheduled = false;
446
+ if (!this.isConnected) {
447
+ return;
448
+ }
449
+
450
+ if (state.needsObserve) {
451
+ updateResizeObserverObservation.call(this);
452
+ state.needsObserve = false;
453
+ }
454
+
455
+ if (state.needsMeasure) {
456
+ try {
457
+ calculateButtonBarDimensions.call(this);
458
+ } catch (error) {
459
+ addErrorAttribute(
460
+ this,
461
+ error?.message || "An error occurred while calculating dimensions",
462
+ );
463
+ }
464
+ state.needsMeasure = false;
465
+ }
466
+
467
+ if (state.needsLayout) {
468
+ try {
469
+ rearrangeButtons.call(this);
470
+ } catch (error) {
471
+ addErrorAttribute(
472
+ this,
473
+ error?.message || "An error occurred while rearranging the buttons",
474
+ );
475
+ }
476
+ state.needsLayout = false;
419
477
  }
420
478
 
421
- rearrangeButtons.call(this);
479
+ updatePopper.call(this);
422
480
  }
423
481
 
424
482
  /**
@@ -426,19 +484,19 @@ function checkAndRearrangeButtons() {
426
484
  * @return {Object}
427
485
  */
428
486
  function rearrangeButtons() {
429
- let sum = 0;
430
- // Only add the popper switch's width if it's currently visible.
431
- if (isElementTrulyVisible(this[switchElementSymbol])) {
432
- sum += this[switchElementSymbol].offsetWidth;
433
- }
434
- const space = this[dimensionsSymbol].getVia("data.space");
487
+ const state = this[layoutStateSymbol];
488
+ let space = 0;
489
+ try {
490
+ space = this[dimensionsSymbol].getVia("data.space");
491
+ } catch {}
435
492
 
436
493
  const buttonReferences = this[dimensionsSymbol].getVia(
437
494
  "data.buttonReferences",
495
+ [],
438
496
  );
497
+ const hasButtons = buttonReferences.length > 0;
439
498
 
440
- const visibleButtonsInMainSlot = [];
441
- const buttonsToMoveToPopper = [];
499
+ const buttonEntries = [];
442
500
 
443
501
  for (const ref of buttonReferences) {
444
502
  let elements = getSlottedElements.call(
@@ -474,51 +532,75 @@ function rearrangeButtons() {
474
532
  }
475
533
  const style = getComputedStyle(element);
476
534
 
477
- // A user-hidden button should not participate in layout calculations.
478
- // We will assign it to the popper to keep it out of the flow.
479
- if (style.display === "none") {
480
- // This button is not visible. It could be user-hidden, or it could be
481
- // in the popper which is currently hidden.
482
- // We should only count it towards the popper if it has a non-zero width,
483
- // which implies it's a genuinely overflowing button, not one that is
484
- // user-hidden from the start.
485
- if (buttonWidth > 0) {
486
- buttonsToMoveToPopper.push(element);
535
+ buttonEntries.push({
536
+ element,
537
+ width: buttonWidth,
538
+ hidden: style.display === "none",
539
+ });
540
+ }
541
+
542
+ const switchWidth = this[dimensionsSymbol].getVia("data.switchWidth") || 2;
543
+
544
+ const layoutButtons = (availableSpace) => {
545
+ if (availableSpace < 0) {
546
+ availableSpace = 0;
547
+ }
548
+ let sum = 0;
549
+ const visibleButtonsInMainSlot = [];
550
+ const buttonsToMoveToPopper = [];
551
+
552
+ for (const entry of buttonEntries) {
553
+ if (entry.hidden) {
554
+ if (entry.width > 0) {
555
+ buttonsToMoveToPopper.push(entry.element);
556
+ } else {
557
+ visibleButtonsInMainSlot.push(entry.element);
558
+ }
559
+ continue;
560
+ }
561
+
562
+ if (sum + entry.width > availableSpace) {
563
+ buttonsToMoveToPopper.push(entry.element);
487
564
  } else {
488
- // It has 0 width. Treat it as a truly hidden element.
489
- // Put it in the main slot, where it will be invisible and take no space.
490
- visibleButtonsInMainSlot.push(element);
565
+ sum += entry.width;
566
+ visibleButtonsInMainSlot.push(entry.element);
491
567
  }
492
- continue;
493
568
  }
494
569
 
495
- const switchWidth = this[dimensionsSymbol].getVia("data.switchWidth") || 2;
496
- if (sum + buttonWidth > space - switchWidth) { buttonsToMoveToPopper.push(element);
497
- } else {
498
- sum += buttonWidth;
499
- visibleButtonsInMainSlot.push(element);
500
- }
570
+ return { visibleButtonsInMainSlot, buttonsToMoveToPopper };
571
+ };
572
+
573
+ let layout = layoutButtons(space);
574
+ if (layout.buttonsToMoveToPopper.length > 0) {
575
+ layout = layoutButtons(space - switchWidth);
501
576
  }
502
577
 
503
- for (const button of buttonsToMoveToPopper) {
578
+ const shouldShowSwitch =
579
+ layout.buttonsToMoveToPopper.length > 0 && hasButtons;
580
+
581
+ if (state) {
582
+ state.suppressSlotChange = true;
583
+ }
584
+
585
+ for (const button of layout.buttonsToMoveToPopper) {
504
586
  button.setAttribute("slot", "popper");
505
587
  }
506
588
 
507
- for (const button of visibleButtonsInMainSlot) {
589
+ for (const button of layout.visibleButtonsInMainSlot) {
508
590
  button.removeAttribute("slot");
509
591
  }
510
592
 
511
- const buttonsInPopper = getSlottedElements.call(this, ":scope", "popper");
593
+ if (state) {
594
+ state.suppressSlotChange = false;
595
+ }
512
596
 
513
- if (buttonsInPopper.size > 0) {
597
+ if (shouldShowSwitch) {
514
598
  this[switchElementSymbol].removeAttribute("hidden");
515
599
  this[switchElementSymbol].classList.remove("hidden");
516
600
  } else {
517
601
  this[switchElementSymbol].setAttribute("hidden", "");
518
602
  this[switchElementSymbol].classList.add("hidden");
519
- setTimeout(() => {
520
- hide.call(this);
521
- }, 1);
603
+ hide.call(this);
522
604
  }
523
605
  }
524
606
 
@@ -551,6 +633,14 @@ function calcBoxWidth(node) {
551
633
  * @return {Object}
552
634
  */
553
635
  function calculateButtonBarDimensions() {
636
+ if (!(this.parentElement instanceof HTMLElement)) {
637
+ this[dimensionsSymbol].setVia("data.space", 0);
638
+ this[dimensionsSymbol].setVia("data.visible", false);
639
+ this[dimensionsSymbol].setVia("data.calculated", true);
640
+ this[dimensionsSymbol].setVia("data.buttonReferences", []);
641
+ return;
642
+ }
643
+
554
644
  const computedStyle = getComputedStyle(this.parentElement);
555
645
 
556
646
  if (computedStyle === null) {
@@ -603,12 +693,22 @@ function calculateButtonBarDimensions() {
603
693
 
604
694
  // Get all buttons, regardless of their current slot
605
695
  const allButtons = Array.from(getSlottedElements.call(this, ":scope", null));
606
- const popperButtons = Array.from(getSlottedElements.call(this, ":scope", "popper"));
696
+ const popperButtons = Array.from(
697
+ getSlottedElements.call(this, ":scope", "popper"),
698
+ );
607
699
 
608
- const combinedButtons = [...allButtons, ...popperButtons].filter((button, index, self) => {
609
- // Filter out duplicates based on data-monster-reference if present, or element itself
610
- return self.findIndex(b => b.dataset.monsterReference === button.dataset.monsterReference || b === button) === index;
611
- });
700
+ const combinedButtons = [...allButtons, ...popperButtons].filter(
701
+ (button, index, self) => {
702
+ // Filter out duplicates based on data-monster-reference if present, or element itself
703
+ return (
704
+ self.findIndex(
705
+ (b) =>
706
+ b.dataset.monsterReference === button.dataset.monsterReference ||
707
+ b === button,
708
+ ) === index
709
+ );
710
+ },
711
+ );
612
712
 
613
713
  for (const button of combinedButtons) {
614
714
  if (!(button instanceof HTMLElement)) {
@@ -793,9 +893,7 @@ function initButtonBar() {
793
893
  throw new Error("no shadow-root is defined");
794
894
  }
795
895
 
796
- return new Processing(() => {
797
- checkAndRearrangeButtons.call(this);
798
- }).run();
896
+ scheduleLayout.call(this, { measure: true, layout: true, observe: true });
799
897
  }
800
898
 
801
899
  /**