@schukai/monster 4.128.3 → 4.129.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/package.json CHANGED
@@ -1 +1 @@
1
- {"author":"Volker Schukai","dependencies":{"@floating-ui/dom":"^1.7.6","@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.128.3"}
1
+ {"author":"Volker Schukai","dependencies":{"@floating-ui/dom":"^1.7.6","@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.129.0"}
@@ -1692,7 +1692,10 @@ function updateGrid() {
1692
1692
  * @return {void}
1693
1693
  */
1694
1694
  function scheduleGridUpdate() {
1695
- if (this[resizeFrameRequestSymbol] !== null && this[resizeFrameRequestSymbol] !== undefined) {
1695
+ if (
1696
+ this[resizeFrameRequestSymbol] !== null &&
1697
+ this[resizeFrameRequestSymbol] !== undefined
1698
+ ) {
1696
1699
  return;
1697
1700
  }
1698
1701
 
@@ -238,6 +238,8 @@ class Pagination extends CustomElement {
238
238
  list.setAttribute("data-monster-adaptive-ready", "true");
239
239
  }
240
240
  }
241
+
242
+ syncPaginationStateToDom.call(this, pagination);
241
243
  }
242
244
  /**
243
245
  *
@@ -803,6 +805,42 @@ function buildPagination(current, max) {
803
805
  };
804
806
  }
805
807
 
808
+ /**
809
+ * @private
810
+ * @param {object} pagination
811
+ * @returns {void}
812
+ */
813
+ function syncPaginationStateToDom(pagination) {
814
+ if (!this.shadowRoot || !pagination) {
815
+ return;
816
+ }
817
+
818
+ const prevLink = this.shadowRoot.querySelector(
819
+ "a[data-monster-role=pagination-prev]",
820
+ );
821
+ if (prevLink instanceof HTMLElement) {
822
+ prevLink.setAttribute("href", pagination.prevHref || "#");
823
+ prevLink.setAttribute("data-page-no", `${pagination.prevNo ?? ""}`);
824
+ prevLink.className = `previous${pagination.prevClass || ""}`.trim();
825
+ }
826
+
827
+ const nextLink = this.shadowRoot.querySelector(
828
+ "a[data-monster-role=pagination-next]",
829
+ );
830
+ if (nextLink instanceof HTMLElement) {
831
+ nextLink.setAttribute("href", pagination.nextHref || "#");
832
+ nextLink.setAttribute("data-page-no", `${pagination.nextNo ?? ""}`);
833
+ nextLink.className = `next${pagination.nextClass || ""}`.trim();
834
+ }
835
+
836
+ const summary = this.shadowRoot.querySelector(
837
+ "[data-monster-role=pagination-summary] span",
838
+ );
839
+ if (summary instanceof HTMLElement) {
840
+ summary.textContent = pagination.summary || "";
841
+ }
842
+ }
843
+
806
844
  /**
807
845
  * @private
808
846
  */
@@ -1226,7 +1264,7 @@ function getTemplate() {
1226
1264
  data-monster-replace="path:items.label"></a></li>
1227
1265
  </template>
1228
1266
 
1229
- <div data-monster-role="control" part="control">
1267
+ <div data-monster-role="control" part="control" data-monster-select-this="true">
1230
1268
  <nav data-monster-role="pagination" role="navigation" aria-label="pagination" part="nav">
1231
1269
  <ul class="pagination-list" data-monster-insert="items path:pagination.items"
1232
1270
  data-monster-adaptive-ready="false"
@@ -89,6 +89,30 @@ const resetLoginProcessLinksSymbol = Symbol("resetLoginProcessLink");
89
89
  */
90
90
  const loggedInCollapseSymbol = Symbol("loggedInCollapse");
91
91
 
92
+ /**
93
+ * @private
94
+ * @type {symbol}
95
+ */
96
+ const customCollapseSymbol = Symbol("customCollapse");
97
+
98
+ /**
99
+ * @private
100
+ * @type {symbol}
101
+ */
102
+ const customCollapseHeaderSymbol = Symbol("customCollapseHeader");
103
+
104
+ /**
105
+ * @private
106
+ * @type {symbol}
107
+ */
108
+ const customCollapseContentSymbol = Symbol("customCollapseContent");
109
+
110
+ /**
111
+ * @private
112
+ * @type {symbol}
113
+ */
114
+ const customCollapseFooterSymbol = Symbol("customCollapseFooter");
115
+
92
116
  /**
93
117
  * @private
94
118
  * @type {symbol}
@@ -473,6 +497,98 @@ class Login extends CustomElement {
473
497
  return this;
474
498
  }
475
499
 
500
+ /**
501
+ * Opens the custom collapse and focuses the first focusable element if available.
502
+ *
503
+ * @returns {Login}
504
+ */
505
+ openCustomCollapse() {
506
+ this[customCollapseSymbol].open();
507
+ focusFirstCustomCollapseElement.call(this);
508
+ return this;
509
+ }
510
+
511
+ /**
512
+ * Replaces the header content of the custom collapse.
513
+ *
514
+ * @param {string|number|boolean|Node|Array<string|number|boolean|Node>|null|undefined} content
515
+ * @returns {Login}
516
+ */
517
+ setCustomCollapseHeader(content) {
518
+ setCustomCollapseSectionContent.call(
519
+ this,
520
+ this[customCollapseHeaderSymbol],
521
+ content,
522
+ );
523
+ return this;
524
+ }
525
+
526
+ /**
527
+ * Replaces the main content of the custom collapse.
528
+ *
529
+ * @param {string|number|boolean|Node|Array<string|number|boolean|Node>|null|undefined} content
530
+ * @returns {Login}
531
+ */
532
+ setCustomCollapseContent(content) {
533
+ setCustomCollapseSectionContent.call(
534
+ this,
535
+ this[customCollapseContentSymbol],
536
+ content,
537
+ );
538
+ return this;
539
+ }
540
+
541
+ /**
542
+ * Replaces the footer content of the custom collapse.
543
+ *
544
+ * @param {string|number|boolean|Node|Array<string|number|boolean|Node>|null|undefined} content
545
+ * @returns {Login}
546
+ */
547
+ setCustomCollapseFooter(content) {
548
+ setCustomCollapseSectionContent.call(
549
+ this,
550
+ this[customCollapseFooterSymbol],
551
+ content,
552
+ );
553
+ return this;
554
+ }
555
+
556
+ /**
557
+ * Clears all custom collapse sections.
558
+ *
559
+ * @returns {Login}
560
+ */
561
+ clearCustomCollapse() {
562
+ this[customCollapseHeaderSymbol].replaceChildren();
563
+ this[customCollapseContentSymbol].replaceChildren();
564
+ this[customCollapseFooterSymbol].replaceChildren();
565
+ return this;
566
+ }
567
+
568
+ /**
569
+ * Replaces the custom collapse content and opens it.
570
+ *
571
+ * @param {string|number|boolean|Node|Array<string|number|boolean|Node>|Object|null|undefined} content
572
+ * @returns {Login}
573
+ */
574
+ showCustomCollapse(content) {
575
+ if (isObject(content) && !(content instanceof Node) && !isArray(content)) {
576
+ if ("header" in content) {
577
+ this.setCustomCollapseHeader(content.header);
578
+ }
579
+ if ("content" in content) {
580
+ this.setCustomCollapseContent(content.content);
581
+ }
582
+ if ("footer" in content) {
583
+ this.setCustomCollapseFooter(content.footer);
584
+ }
585
+ } else if (content !== undefined) {
586
+ this.setCustomCollapseContent(content);
587
+ }
588
+
589
+ return this.openCustomCollapse();
590
+ }
591
+
476
592
  /**
477
593
  * @return {string}
478
594
  */
@@ -1745,6 +1861,20 @@ function initControlReferences() {
1745
1861
  `[data-monster-role="logged-in-collapse"]`,
1746
1862
  );
1747
1863
 
1864
+ this[customCollapseSymbol] = this.shadowRoot.querySelector(
1865
+ `[data-monster-role="custom-collapse"]`,
1866
+ );
1867
+
1868
+ this[customCollapseHeaderSymbol] = this.shadowRoot.getElementById(
1869
+ "customCollapseHeader",
1870
+ );
1871
+ this[customCollapseContentSymbol] = this.shadowRoot.getElementById(
1872
+ "customCollapseContent",
1873
+ );
1874
+ this[customCollapseFooterSymbol] = this.shadowRoot.getElementById(
1875
+ "customCollapseFooter",
1876
+ );
1877
+
1748
1878
  this[digitsCollapseSymbol] = this.shadowRoot.querySelector(
1749
1879
  `[data-monster-role="digits-collapse"]`,
1750
1880
  );
@@ -1957,8 +2087,75 @@ function getTemplate() {
1957
2087
  <slot name="logged-in-footer"></slot>
1958
2088
  </div>
1959
2089
  </monster-collapse>
2090
+ <monster-collapse data-monster-role="custom-collapse"
2091
+ exportparts="
2092
+ control:collapse-custom-control,
2093
+ container:collapse-custom-container,
2094
+ deco:collapse-custom-deco"
2095
+ part="custom-collapse">
2096
+ <div part="custom-collapse-wrapper">
2097
+ <div id="customCollapseHeader" part="custom-collapse-header"></div>
2098
+ <div id="customCollapseContent" part="custom-collapse-content"></div>
2099
+ <div id="customCollapseFooter" part="custom-collapse-footer"></div>
2100
+ </div>
2101
+ </monster-collapse>
1960
2102
 
1961
2103
  </div>`;
1962
2104
  }
1963
2105
 
2106
+ /**
2107
+ * @private
2108
+ * @param {HTMLElement} target
2109
+ * @param {string|number|boolean|Node|Array<string|number|boolean|Node>|null|undefined} content
2110
+ * @returns {void}
2111
+ */
2112
+ function setCustomCollapseSectionContent(target, content) {
2113
+ if (!(target instanceof HTMLElement)) {
2114
+ return;
2115
+ }
2116
+
2117
+ target.replaceChildren(...normalizeCustomCollapseContent.call(this, content));
2118
+
2119
+ if (this[customCollapseSymbol]?.isOpen()) {
2120
+ this[customCollapseSymbol].adjustHeight();
2121
+ }
2122
+ }
2123
+
2124
+ /**
2125
+ * @private
2126
+ * @param {string|number|boolean|Node|Array<string|number|boolean|Node>|null|undefined} content
2127
+ * @returns {Node[]}
2128
+ */
2129
+ function normalizeCustomCollapseContent(content) {
2130
+ if (content === null || content === undefined) {
2131
+ return [];
2132
+ }
2133
+
2134
+ if (content instanceof Node) {
2135
+ return [content];
2136
+ }
2137
+
2138
+ if (isArray(content)) {
2139
+ return content.flatMap((entry) =>
2140
+ normalizeCustomCollapseContent.call(this, entry),
2141
+ );
2142
+ }
2143
+
2144
+ return [this.ownerDocument.createTextNode(String(content))];
2145
+ }
2146
+
2147
+ /**
2148
+ * @private
2149
+ * @returns {void}
2150
+ */
2151
+ function focusFirstCustomCollapseElement() {
2152
+ setTimeout(() => {
2153
+ const focusable = this[customCollapseSymbol]?.querySelector(
2154
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])',
2155
+ );
2156
+
2157
+ focusable?.focus?.();
2158
+ }, 0);
2159
+ }
2160
+
1964
2161
  registerCustomElement(Login);
@@ -2004,7 +2004,7 @@ function getSelectionTemplate() {
2004
2004
  data-monster-role="filter"
2005
2005
  autocomplete="off"
2006
2006
  tabindex="0"
2007
- ><div data-monster-replace="path:messages.control"
2007
+ ><div data-monster-replace="path:messages.control" part="selection-messages"
2008
2008
  data-monster-attributes="class path:messages.control | length | gt:0 | ?::hidden"
2009
2009
  data-monster-role="selection-messages"></div>
2010
2010
  </div>`;
@@ -2023,7 +2023,7 @@ function getSummaryTemplate() {
2023
2023
  autocomplete="off"
2024
2024
  tabindex="0"
2025
2025
  >
2026
- <div data-monster-replace="path:messages.selected" data-monster-role="summary-messages"></div>
2026
+ <div data-monster-replace="path:messages.selected" part="summary-messages" data-monster-role="summary-messages"></div>
2027
2027
  </div>`;
2028
2028
  }
2029
2029
 
@@ -4469,7 +4469,7 @@ function getTemplate() {
4469
4469
  <slot class="hidden"></slot>
4470
4470
 
4471
4471
  <div data-monster-role="control" part="control" tabindex="0">
4472
- <div data-monster-role="container">
4472
+ <div data-monster-role="container" part="container">
4473
4473
  \${selected}
4474
4474
  </div>
4475
4475
 
@@ -4487,7 +4487,7 @@ function getTemplate() {
4487
4487
  <div part="options" data-monster-role="options" data-monster-insert="options path:options"
4488
4488
  tabindex="-1"></div>
4489
4489
  </div>
4490
- <monster-pagination data-monster-role="pagination"></monster-pagination>
4490
+ <monster-pagination data-monster-role="pagination" part="pagination"></monster-pagination>
4491
4491
  <div part="remote-info"
4492
4492
  data-monster-role="remote-info"
4493
4493
  data-monster-attributes="class path:classes.remoteInfo"
@@ -547,12 +547,12 @@ function getTemplate() {
547
547
  <slot class="hidden"></slot>
548
548
 
549
549
  <div data-monster-role="control" part="control" tabindex="0">
550
- <div data-monster-role="container">
550
+ <div data-monster-role="container" part="container">
551
551
  \${selected}
552
552
  </div>
553
553
 
554
554
  <div data-monster-role="popper" part="popper" tabindex="-1" class="monster-color-primary-1">
555
- <div class="option-filter-control" role="search">
555
+ <div class="option-filter-control" role="search" part="popper-filter-control">
556
556
  <input type="text" role="searchbox"
557
557
  part="popper-filter" name="popper-filter"
558
558
  data-monster-role="filter"
@@ -118,6 +118,8 @@ const updateCloneDataSymbol = Symbol("@schukai/monster/dom/@@updateCloneData");
118
118
  */
119
119
  const scriptHostElementSymbol = Symbol("scriptHostElement");
120
120
  const managedShadowRootSymbol = Symbol("managedShadowRoot");
121
+ const visibilityStateSymbol = Symbol("visibilityState");
122
+ let hostVisibilityStyleSheet = null;
121
123
 
122
124
  /**
123
125
  * The `CustomElement` class provides a way to define a new HTML element using the power of Custom Elements.
@@ -278,6 +280,24 @@ class CustomElement extends HTMLElement {
278
280
  return this;
279
281
  }
280
282
 
283
+ /**
284
+ * Returns whether the host element is currently visible.
285
+ *
286
+ * @returns {boolean}
287
+ */
288
+ get visible() {
289
+ return !this.hidden;
290
+ }
291
+
292
+ /**
293
+ * Alias for the current host visibility state.
294
+ *
295
+ * @returns {boolean}
296
+ */
297
+ get isVisible() {
298
+ return this.visible;
299
+ }
300
+
281
301
  /**
282
302
  * The `customization` property allows overwriting the defaults.
283
303
  * Unlike the defaults that expect an object, the customization is a Map.
@@ -541,6 +561,54 @@ class CustomElement extends HTMLElement {
541
561
  return this;
542
562
  }
543
563
 
564
+ /**
565
+ * Shows the host element.
566
+ *
567
+ * @returns {CustomElement}
568
+ */
569
+ show() {
570
+ return this.setVisible(true);
571
+ }
572
+
573
+ /**
574
+ * Hides the host element.
575
+ *
576
+ * @returns {CustomElement}
577
+ */
578
+ hide() {
579
+ return this.setVisible(false);
580
+ }
581
+
582
+ /**
583
+ * Sets the host visibility.
584
+ *
585
+ * @param {boolean} visible
586
+ * @returns {CustomElement}
587
+ */
588
+ setVisible(visible) {
589
+ if (visible) {
590
+ this.removeAttribute("hidden");
591
+ } else {
592
+ this.setAttribute("hidden", "");
593
+ }
594
+
595
+ return this;
596
+ }
597
+
598
+ /**
599
+ * Toggles the host visibility.
600
+ *
601
+ * @param {boolean} [force]
602
+ * @returns {CustomElement}
603
+ */
604
+ toggleVisibility(force) {
605
+ if (typeof force === "boolean") {
606
+ return this.setVisible(force);
607
+ }
608
+
609
+ return this.setVisible(!this.visible);
610
+ }
611
+
544
612
  /**
545
613
  * Is called once via the constructor
546
614
  *
@@ -674,6 +742,8 @@ class CustomElement extends HTMLElement {
674
742
  this[internalSymbol].syncDisabledState();
675
743
  }
676
744
 
745
+ syncVisibilityState.call(this);
746
+
677
747
  return this;
678
748
  }
679
749
 
@@ -747,6 +817,10 @@ class CustomElement extends HTMLElement {
747
817
  );
748
818
  }
749
819
 
820
+ if (attrName === "hidden") {
821
+ syncVisibilityState.call(this);
822
+ }
823
+
750
824
  const callback = this[attributeObserverSymbol]?.[attrName];
751
825
  if (isFunction(callback)) {
752
826
  try {
@@ -1296,6 +1370,73 @@ function initHtmlContent() {
1296
1370
  return this;
1297
1371
  }
1298
1372
 
1373
+ /**
1374
+ * @private
1375
+ * @returns {void}
1376
+ */
1377
+ function syncVisibilityState() {
1378
+ const visible = !this.hidden;
1379
+ if (typeof this[visibilityStateSymbol] === "undefined") {
1380
+ this[visibilityStateSymbol] = visible;
1381
+ if (!visible) {
1382
+ blurFocusedElement.call(this);
1383
+ }
1384
+ return;
1385
+ }
1386
+
1387
+ if (this[visibilityStateSymbol] === visible) {
1388
+ return;
1389
+ }
1390
+
1391
+ if (!visible) {
1392
+ blurFocusedElement.call(this);
1393
+ }
1394
+
1395
+ this[visibilityStateSymbol] = visible;
1396
+ dispatchVisibilityChangedEvent.call(this, visible);
1397
+ }
1398
+
1399
+ /**
1400
+ * @private
1401
+ * @returns {void}
1402
+ */
1403
+ function blurFocusedElement() {
1404
+ const shadowRoot = getManagedShadowRoot.call(this);
1405
+ const activeShadowElement = shadowRoot?.activeElement;
1406
+
1407
+ if (activeShadowElement instanceof HTMLElement) {
1408
+ activeShadowElement.blur();
1409
+ }
1410
+
1411
+ if (document.activeElement === this && typeof this.blur === "function") {
1412
+ this.blur();
1413
+ }
1414
+
1415
+ if (shadowRoot?.activeElement instanceof HTMLElement) {
1416
+ const body = getDocument()?.body;
1417
+ if (body instanceof HTMLElement) {
1418
+ body.setAttribute("tabindex", "-1");
1419
+ body.focus();
1420
+ body.removeAttribute("tabindex");
1421
+ }
1422
+ }
1423
+ }
1424
+
1425
+ /**
1426
+ * @private
1427
+ * @param {boolean} visible
1428
+ * @returns {void}
1429
+ */
1430
+ function dispatchVisibilityChangedEvent(visible) {
1431
+ this.dispatchEvent(
1432
+ new CustomEvent("monster-visibility-changed", {
1433
+ bubbles: true,
1434
+ composed: true,
1435
+ detail: { visible },
1436
+ }),
1437
+ );
1438
+ }
1439
+
1299
1440
  /**
1300
1441
  * @private
1301
1442
  * @return {CustomElement}
@@ -1309,15 +1450,18 @@ function initCSSStylesheet() {
1309
1450
  if (!(shadowRoot instanceof ShadowRoot)) {
1310
1451
  return this;
1311
1452
  }
1453
+ const visibilityStyleSheet = getHostVisibilityStyleSheet();
1312
1454
 
1313
1455
  const styleSheet = this.constructor.getCSSStyleSheet();
1314
1456
 
1315
1457
  if (styleSheet instanceof CSSStyleSheet) {
1316
1458
  if (styleSheet.cssRules.length > 0) {
1317
- shadowRoot.adoptedStyleSheets = [styleSheet];
1459
+ shadowRoot.adoptedStyleSheets = [visibilityStyleSheet, styleSheet];
1460
+ } else {
1461
+ shadowRoot.adoptedStyleSheets = [visibilityStyleSheet];
1318
1462
  }
1319
1463
  } else if (isArray(styleSheet)) {
1320
- const assign = [];
1464
+ const assign = [visibilityStyleSheet];
1321
1465
  for (const s of styleSheet) {
1322
1466
  if (isString(s)) {
1323
1467
  const trimedStyleSheet = s.trim();
@@ -1340,17 +1484,53 @@ function initCSSStylesheet() {
1340
1484
  shadowRoot.adoptedStyleSheets = assign;
1341
1485
  }
1342
1486
  } else if (isString(styleSheet)) {
1487
+ shadowRoot.adoptedStyleSheets = [visibilityStyleSheet];
1343
1488
  const trimedStyleSheet = styleSheet.trim();
1344
1489
  if (trimedStyleSheet !== "") {
1345
1490
  const style = document.createElement("style");
1346
1491
  style.innerHTML = styleSheet;
1347
1492
  shadowRoot.prepend(style);
1348
1493
  }
1494
+ } else {
1495
+ shadowRoot.adoptedStyleSheets = [visibilityStyleSheet];
1349
1496
  }
1350
1497
 
1351
1498
  return this;
1352
1499
  }
1353
1500
 
1501
+ /**
1502
+ * @private
1503
+ * @returns {CSSStyleSheet}
1504
+ */
1505
+ function getHostVisibilityStyleSheet() {
1506
+ if (hostVisibilityStyleSheet instanceof CSSStyleSheet) {
1507
+ return hostVisibilityStyleSheet;
1508
+ }
1509
+
1510
+ hostVisibilityStyleSheet = new CSSStyleSheet();
1511
+ hostVisibilityStyleSheet.replaceSync(
1512
+ ":host([hidden]){display:none !important}",
1513
+ );
1514
+
1515
+ return hostVisibilityStyleSheet;
1516
+ }
1517
+
1518
+ /**
1519
+ * @private
1520
+ * @param {ShadowRoot} shadowRoot
1521
+ * @returns {void}
1522
+ */
1523
+ function appendVisibilityHostStyle(shadowRoot) {
1524
+ if (!(shadowRoot instanceof ShadowRoot)) {
1525
+ return;
1526
+ }
1527
+
1528
+ const style = document.createElement("style");
1529
+ style.setAttribute("data-monster-host-visibility", "true");
1530
+ style.textContent = ":host([hidden]){display:none !important}";
1531
+ shadowRoot.prepend(style);
1532
+ }
1533
+
1354
1534
  /**
1355
1535
  * @private
1356
1536
  * @return {CustomElement}
@@ -156,7 +156,7 @@ function getMonsterVersion() {
156
156
  }
157
157
 
158
158
  /** don't touch, replaced by make with package.json version */
159
- monsterVersion = new Version("4.128.2");
159
+ monsterVersion = new Version("4.128.3");
160
160
 
161
161
  return monsterVersion;
162
162
  }