@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 +1 -1
- package/source/components/datatable/pagination.mjs +23 -0
- package/source/components/form/select.mjs +8 -2
- package/source/components/form/util/floating-ui.mjs +12 -0
- package/source/components/navigation/table-of-content.mjs +16 -8
- package/test/cases/components/datatable/pagination.mjs +59 -0
- package/test/cases/components/form/floating-ui.mjs +73 -0
- package/test/cases/components/form/select.mjs +2 -1
- package/test/cases/components/navigation/table-of-content.mjs +115 -0
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
});
|