@schukai/monster 4.136.18 → 4.136.20

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"},"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.136.18"}
1
+ {"author":"Volker Schukai","dependencies":{"@floating-ui/dom":"^1.7.6"},"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.136.20"}
@@ -205,10 +205,33 @@ class Pagination extends CustomElement {
205
205
  return;
206
206
  }
207
207
 
208
+ const previousCurrentPage = Number.parseInt(
209
+ this.getOption("currentPage"),
210
+ 10,
211
+ );
212
+ const previousTotalPages = Number.parseInt(this.getOption("pages"), 10);
213
+ const stateUnchanged =
214
+ Number.isFinite(previousCurrentPage) &&
215
+ Number.isFinite(previousTotalPages) &&
216
+ previousCurrentPage === currentPage &&
217
+ previousTotalPages === totalPages;
218
+
208
219
  // 1. Update the component's internal options with the new values.
209
220
  this.setOption("currentPage", currentPage);
210
221
  this.setOption("pages", totalPages);
211
222
 
223
+ if (stateUnchanged) {
224
+ if (isAdaptivePagination.call(this)) {
225
+ schedulePaginationLayoutUpdate.call(this);
226
+ } else {
227
+ const list = this.shadowRoot?.querySelector(".pagination-list");
228
+ if (list) {
229
+ list.setAttribute("data-monster-adaptive-ready", "true");
230
+ }
231
+ }
232
+ return;
233
+ }
234
+
212
235
  // 2. Call the existing buildPagination function to recalculate the links.
213
236
  const pagination = buildPagination.call(
214
237
  this,
@@ -2959,6 +2959,7 @@ function calcAndSetOptionsDimension() {
2959
2959
  this[popperElementSymbol].dataset.monsterPreferredWidth = `${Math.ceil(
2960
2960
  widthConstraints.preferredWidth,
2961
2961
  )}`;
2962
+ this[popperElementSymbol].dataset.monsterWidthBehavior = "preferred";
2962
2963
  if (
2963
2964
  Number.isFinite(widthConstraints.maxWidth) &&
2964
2965
  widthConstraints.maxWidth > 0
@@ -2973,8 +2974,12 @@ function calcAndSetOptionsDimension() {
2973
2974
  delete this[popperElementSymbol].dataset.monsterPreferredMaxWidth;
2974
2975
  this[popperElementSymbol].style.removeProperty("maxWidth");
2975
2976
  }
2976
- this[popperElementSymbol].style.width = "";
2977
- this[popperElementSymbol].style.removeProperty("minWidth");
2977
+ this[popperElementSymbol].style.width = `${Math.ceil(
2978
+ Math.min(widthConstraints.preferredWidth, widthConstraints.maxWidth),
2979
+ )}px`;
2980
+ this[popperElementSymbol].style.minWidth = `${Math.ceil(
2981
+ Math.min(widthConstraints.preferredWidth, widthConstraints.maxWidth),
2982
+ )}px`;
2978
2983
  this[popperElementSymbol].style.maxHeight = `${Math.ceil(
2979
2984
  Math.min(geometry.availableHeight, SELECT_MAX_POPPER_HEIGHT),
2980
2985
  )}px`;
@@ -3227,6 +3232,7 @@ function resetSelectPopperDimensionStyles() {
3227
3232
 
3228
3233
  delete this[popperElementSymbol].dataset.monsterPreferredWidth;
3229
3234
  delete this[popperElementSymbol].dataset.monsterPreferredMaxWidth;
3235
+ delete this[popperElementSymbol].dataset.monsterWidthBehavior;
3230
3236
  this[popperElementSymbol].style.removeProperty("width");
3231
3237
  this[popperElementSymbol].style.removeProperty("minWidth");
3232
3238
  this[popperElementSymbol].style.removeProperty("maxWidth");
@@ -639,6 +639,18 @@ function syncPreferredFloatingWidth(floatingElement, maxWidth) {
639
639
  if (!Number.isFinite(preferredWidth) || preferredWidth <= 0) {
640
640
  return;
641
641
  }
642
+ const widthBehavior = floatingElement.dataset.monsterWidthBehavior;
643
+ if (widthBehavior === "preferred") {
644
+ const nextFloatingWidth = clampAvailableDimension(
645
+ preferredWidth,
646
+ Number.isFinite(maxWidth) ? maxWidth : Infinity,
647
+ );
648
+ if (Number.isFinite(nextFloatingWidth) && nextFloatingWidth > 0) {
649
+ floatingElement.style.width = `${nextFloatingWidth}px`;
650
+ floatingElement.style.minWidth = `${nextFloatingWidth}px`;
651
+ }
652
+ return;
653
+ }
642
654
 
643
655
  const contentElement = getFloatingContentElement(floatingElement);
644
656
  if (!(contentElement instanceof HTMLElement)) {
@@ -286,12 +286,8 @@ function calcAndSetNavigationTopScrollableParentContext() {
286
286
 
287
287
  const parentRect = this[scrollableParentSymbol].getBoundingClientRect();
288
288
  const rect = this.getBoundingClientRect();
289
- const thisTop = rect.top - parentRect.top;
290
289
  const thisBottom = rect.bottom - parentRect.top;
291
- let top = 0;
292
- if (thisTop < 0) {
293
- top = -1 * thisTop;
294
- }
290
+ let top = this[scrollableParentSymbol].scrollTop || 0;
295
291
 
296
292
  const offset = this.getOption("offset");
297
293
  if (offset > 0) {
@@ -536,6 +532,20 @@ function initEventHandler() {
536
532
  * @return {HTMLElement|Window} - The found scrollable parent or the Window object.
537
533
  */
538
534
  function findScrollableParent(element) {
535
+ function isScrollable(elementToCheck) {
536
+ const styles = window.getComputedStyle(elementToCheck);
537
+ const overflowY = styles.overflowY;
538
+ const canScrollY =
539
+ (overflowY === "scroll" || overflowY === "auto") &&
540
+ elementToCheck.scrollHeight > elementToCheck.clientHeight;
541
+ const overflowX = styles.overflowX;
542
+ const canScrollX =
543
+ (overflowX === "scroll" || overflowX === "auto") &&
544
+ elementToCheck.scrollWidth > elementToCheck.clientWidth;
545
+
546
+ return canScrollY || canScrollX;
547
+ }
548
+
539
549
  // Helper function to find the next parent element, checking for Shadow DOM
540
550
  function getNextParent(elem) {
541
551
  // If the element is in a Shadow DOM, attempt to climb up to the host
@@ -551,9 +561,7 @@ function findScrollableParent(element) {
551
561
 
552
562
  let parent = getNextParent(element);
553
563
  while (parent) {
554
- // Check the scroll properties of the element
555
- const overflowY = window.getComputedStyle(parent).overflowY;
556
- if (overflowY === "scroll" || overflowY === "auto") {
564
+ if (isScrollable(parent)) {
557
565
  return parent;
558
566
  }
559
567
  parent = getNextParent(parent); // Move to the next parent element
@@ -7,6 +7,7 @@ let expect = chai.expect;
7
7
  chai.use(chaiDom);
8
8
 
9
9
  let Pagination;
10
+ let EmbeddedPagination;
10
11
 
11
12
  describe('Pagination', function () {
12
13
  this.timeout(10000);
@@ -15,6 +16,9 @@ describe('Pagination', function () {
15
16
  initJSDOM().then(() => {
16
17
  import("../../../../source/components/datatable/pagination.mjs").then((m) => {
17
18
  Pagination = m['Pagination'];
19
+ return import("../../../../source/components/datatable/embedded-pagination.mjs");
20
+ }).then((m) => {
21
+ EmbeddedPagination = m['EmbeddedPagination'];
18
22
  done();
19
23
  }).catch(e => done(e));
20
24
  });
@@ -90,10 +94,65 @@ describe('Pagination', function () {
90
94
  }, 0);
91
95
  });
92
96
 
97
+ it('keeps adaptive pagination visible when the same state is applied again', function (done) {
98
+ const mocks = document.getElementById('mocks');
99
+ const wrapper = document.createElement('div');
100
+ const control = document.createElement('monster-embedded-pagination');
101
+
102
+ wrapper.getBoundingClientRect = () => ({
103
+ width: 320,
104
+ height: 40,
105
+ top: 0,
106
+ left: 0,
107
+ right: 320,
108
+ bottom: 40,
109
+ x: 0,
110
+ y: 0
111
+ });
112
+
113
+ mocks.appendChild(wrapper);
114
+ wrapper.appendChild(control);
115
+
116
+ setTimeout(() => {
117
+ try {
118
+ control.setPaginationState({currentPage: 2, totalPages: 5});
119
+
120
+ setTimeout(() => {
121
+ try {
122
+ const list = control.shadowRoot.querySelector('.pagination-list');
123
+ expect(list).to.exist;
124
+
125
+ list.setAttribute('data-monster-adaptive-ready', 'true');
126
+ control.setPaginationState({currentPage: 2, totalPages: 5});
127
+
128
+ expect(list.getAttribute('data-monster-adaptive-ready')).to.equal('true');
129
+
130
+ setTimeout(() => {
131
+ try {
132
+ expect(list.getAttribute('data-monster-adaptive-ready')).to.equal('true');
133
+ done();
134
+ } catch (e) {
135
+ done(e);
136
+ }
137
+ }, 50);
138
+ } catch (e) {
139
+ done(e);
140
+ }
141
+ }, 0);
142
+ } catch (e) {
143
+ done(e);
144
+ }
145
+ }, 0);
146
+ });
147
+
93
148
  describe('document.createElement', function () {
94
149
  it('should instance of Pagination', function () {
95
150
  expect(document.createElement('monster-pagination')).is.instanceof(Pagination);
96
151
  });
152
+
153
+ it('should instance of EmbeddedPagination', function () {
154
+ expect(document.createElement('monster-embedded-pagination')).is.instanceof(EmbeddedPagination);
155
+ });
97
156
  });
98
157
 
99
158
  });
@@ -237,4 +237,77 @@ describe("form floating-ui boundary resolution", function () {
237
237
  expect(options.style.height).to.equal("72px");
238
238
  expect(options.style.maxHeight).to.equal("72px");
239
239
  });
240
+
241
+ it("should keep a preferred-width popper stable instead of growing with content", function () {
242
+ const mocks = document.getElementById("mocks");
243
+ const popper = document.createElement("div");
244
+ const header = document.createElement("div");
245
+ const content = document.createElement("div");
246
+ const options = document.createElement("div");
247
+
248
+ popper.dataset.monsterPreferredWidth = "240";
249
+ popper.dataset.monsterWidthBehavior = "preferred";
250
+ popper.style.maxWidth = "600px";
251
+ content.setAttribute("part", "content");
252
+ options.style.overflowY = "auto";
253
+
254
+ Object.defineProperty(content, "scrollWidth", {
255
+ configurable: true,
256
+ value: 620,
257
+ });
258
+ Object.defineProperty(options, "scrollWidth", {
259
+ configurable: true,
260
+ value: 620,
261
+ });
262
+
263
+ header.getBoundingClientRect = () => {
264
+ return {
265
+ width: 240,
266
+ height: 24,
267
+ top: 0,
268
+ left: 0,
269
+ right: 240,
270
+ bottom: 24,
271
+ x: 0,
272
+ y: 0,
273
+ };
274
+ };
275
+ content.getBoundingClientRect = () => {
276
+ return {
277
+ width: 220,
278
+ height: 120,
279
+ top: 0,
280
+ left: 0,
281
+ right: 220,
282
+ bottom: 120,
283
+ x: 0,
284
+ y: 0,
285
+ };
286
+ };
287
+ popper.getBoundingClientRect = () => {
288
+ return {
289
+ width: 240,
290
+ height: 160,
291
+ top: 0,
292
+ left: 0,
293
+ right: 240,
294
+ bottom: 160,
295
+ x: 0,
296
+ y: 0,
297
+ };
298
+ };
299
+
300
+ content.appendChild(options);
301
+ popper.appendChild(header);
302
+ popper.appendChild(content);
303
+ mocks.appendChild(popper);
304
+
305
+ applyAdaptiveFloatingElementSize(popper, {
306
+ availableWidth: 600,
307
+ availableHeight: 200,
308
+ });
309
+
310
+ expect(popper.style.width).to.equal("240px");
311
+ expect(popper.style.minWidth).to.equal("240px");
312
+ });
240
313
  });
@@ -284,7 +284,8 @@ describe('Select', function () {
284
284
  shadowRoot.querySelector('[data-monster-role=container]').click();
285
285
  setTimeout(() => {
286
286
  try {
287
- expect(popper.style.minWidth).to.equal('');
287
+ expect(popper.style.minWidth).to.equal('240px');
288
+ expect(popper.dataset.monsterWidthBehavior).to.equal('preferred');
288
289
  expect(popper.dataset.monsterPreferredWidth).to.equal('240');
289
290
  done();
290
291
  } catch (e) {
@@ -0,0 +1,115 @@
1
+ 'use strict';
2
+
3
+ import * as chai from 'chai';
4
+ import { getDocument } from "../../../../source/dom/util.mjs";
5
+ import { chaiDom } from "../../../util/chai-dom.mjs";
6
+ import { initJSDOM } from "../../../util/jsdom.mjs";
7
+
8
+ let expect = chai.expect;
9
+ chai.use(chaiDom);
10
+
11
+ describe('TableOfContent', function () {
12
+ this.timeout(10000);
13
+
14
+ let document;
15
+
16
+ before(async function () {
17
+ await initJSDOM({});
18
+ globalThis.IntersectionObserver = class {
19
+ observe() {}
20
+ disconnect() {}
21
+ unobserve() {}
22
+ };
23
+ globalThis.HTMLHeadingElement ??= globalThis.HTMLElement;
24
+ await import("../../../../source/components/navigation/table-of-content.mjs");
25
+ document = getDocument();
26
+ });
27
+
28
+ beforeEach(() => {
29
+ let mocks = document.getElementById('mocks');
30
+ mocks.innerHTML = '<div id="target"></div>';
31
+ });
32
+
33
+ afterEach(() => {
34
+ let mocks = document.getElementById('mocks');
35
+ mocks.innerHTML = '';
36
+ });
37
+
38
+ it('should bind to the first actually scrollable parent', function (done) {
39
+ const target = document.getElementById('target');
40
+ const outerScroller = document.createElement('div');
41
+ const innerWrapper = document.createElement('div');
42
+ const tableOfContent = document.createElement('monster-table-of-content');
43
+ const heading = document.createElement('h2');
44
+ const paragraph = document.createElement('p');
45
+
46
+ heading.textContent = 'Introduction';
47
+ paragraph.textContent = 'Content';
48
+ tableOfContent.appendChild(heading);
49
+ tableOfContent.appendChild(paragraph);
50
+
51
+ Object.defineProperties(outerScroller, {
52
+ clientHeight: { configurable: true, value: 200 },
53
+ scrollHeight: { configurable: true, value: 600 },
54
+ clientWidth: { configurable: true, value: 300 },
55
+ scrollWidth: { configurable: true, value: 300 },
56
+ });
57
+
58
+ Object.defineProperties(innerWrapper, {
59
+ clientHeight: { configurable: true, value: 400 },
60
+ scrollHeight: { configurable: true, value: 400 },
61
+ clientWidth: { configurable: true, value: 300 },
62
+ scrollWidth: { configurable: true, value: 300 },
63
+ });
64
+
65
+ outerScroller.style.overflowY = 'auto';
66
+ innerWrapper.style.overflowY = 'auto';
67
+
68
+ outerScroller.getBoundingClientRect = () => ({
69
+ top: 0,
70
+ bottom: 200,
71
+ left: 0,
72
+ right: 300,
73
+ width: 300,
74
+ height: 200,
75
+ x: 0,
76
+ y: 0,
77
+ });
78
+
79
+ tableOfContent.getBoundingClientRect = () => ({
80
+ top: outerScroller.scrollTop ? -20 : 100,
81
+ bottom: outerScroller.scrollTop ? 80 : 200,
82
+ left: 0,
83
+ right: 300,
84
+ width: 300,
85
+ height: 100,
86
+ x: 0,
87
+ y: outerScroller.scrollTop ? -20 : 100,
88
+ });
89
+
90
+ innerWrapper.appendChild(tableOfContent);
91
+ outerScroller.appendChild(innerWrapper);
92
+ target.appendChild(outerScroller);
93
+
94
+ setTimeout(() => {
95
+ try {
96
+ const navigation = tableOfContent.shadowRoot.querySelector('.navigation');
97
+ expect(navigation.style.top).to.equal('50px');
98
+
99
+ outerScroller.scrollTop = 120;
100
+ outerScroller.dispatchEvent(new window.Event('scroll'));
101
+
102
+ setTimeout(() => {
103
+ try {
104
+ expect(navigation.style.top).to.equal('170px');
105
+ done();
106
+ } catch (error) {
107
+ done(error);
108
+ }
109
+ }, 20);
110
+ } catch (error) {
111
+ done(error);
112
+ }
113
+ }, 30);
114
+ });
115
+ });