@schukai/monster 4.129.7 → 4.129.8
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.129.
|
|
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.129.8"}
|
|
@@ -28,6 +28,7 @@ import { Processing } from "../../../util/processing.mjs";
|
|
|
28
28
|
|
|
29
29
|
export {
|
|
30
30
|
closePositionedPopper,
|
|
31
|
+
resolveClippingBoundaryElement,
|
|
31
32
|
isPositionedPopperOpen,
|
|
32
33
|
openPositionedPopper,
|
|
33
34
|
positionPopper,
|
|
@@ -43,7 +44,7 @@ const autoUpdateCleanupMap = new WeakMap();
|
|
|
43
44
|
* @return {Promise|*}
|
|
44
45
|
*/
|
|
45
46
|
function positionPopper(controlElement, popperElement, options) {
|
|
46
|
-
const config = normalizePopperConfig(options);
|
|
47
|
+
const config = normalizePopperConfig(options, controlElement, popperElement);
|
|
47
48
|
|
|
48
49
|
return new Processing(() => {
|
|
49
50
|
enableFloatingPositioning(controlElement, popperElement, config);
|
|
@@ -51,7 +52,7 @@ function positionPopper(controlElement, popperElement, options) {
|
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
function openPositionedPopper(controlElement, popperElement, options) {
|
|
54
|
-
const config = normalizePopperConfig(options);
|
|
55
|
+
const config = normalizePopperConfig(options, controlElement, popperElement);
|
|
55
56
|
|
|
56
57
|
stopAutoUpdate(popperElement);
|
|
57
58
|
popperElement.style.display = "block";
|
|
@@ -115,7 +116,7 @@ function isPositionedPopperOpen(popperElement) {
|
|
|
115
116
|
return popperElement.style.display === "block";
|
|
116
117
|
}
|
|
117
118
|
|
|
118
|
-
function normalizePopperConfig(options) {
|
|
119
|
+
function normalizePopperConfig(options, controlElement, popperElement) {
|
|
119
120
|
const config = Object.assign(
|
|
120
121
|
{},
|
|
121
122
|
{
|
|
@@ -126,13 +127,28 @@ function normalizePopperConfig(options) {
|
|
|
126
127
|
options,
|
|
127
128
|
);
|
|
128
129
|
|
|
130
|
+
config.boundaryElement = resolveClippingBoundaryElement(
|
|
131
|
+
controlElement,
|
|
132
|
+
popperElement,
|
|
133
|
+
);
|
|
134
|
+
config.detectOverflowOptions = buildDetectOverflowOptions(
|
|
135
|
+
config.boundaryElement,
|
|
136
|
+
);
|
|
129
137
|
config.middleware = normalizeMiddleware(config);
|
|
130
138
|
config.middlewareTokens = config.middleware.filter((line) => isString(line));
|
|
131
139
|
config.floatingMiddleware = buildFloatingMiddleware(
|
|
132
140
|
config.middleware,
|
|
133
141
|
config.placement,
|
|
142
|
+
config.detectOverflowOptions,
|
|
143
|
+
popperElement,
|
|
134
144
|
);
|
|
135
145
|
|
|
146
|
+
if (!config.middlewareTokens.includes("size")) {
|
|
147
|
+
config.floatingMiddleware.push(
|
|
148
|
+
createAdaptiveSizeMiddleware(config.detectOverflowOptions, popperElement),
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
136
152
|
return config;
|
|
137
153
|
}
|
|
138
154
|
|
|
@@ -149,7 +165,12 @@ function normalizeMiddleware(config) {
|
|
|
149
165
|
return [];
|
|
150
166
|
}
|
|
151
167
|
|
|
152
|
-
function buildFloatingMiddleware(
|
|
168
|
+
function buildFloatingMiddleware(
|
|
169
|
+
middleware,
|
|
170
|
+
placement,
|
|
171
|
+
detectOverflowOptions,
|
|
172
|
+
popperElement,
|
|
173
|
+
) {
|
|
153
174
|
const result = [...middleware];
|
|
154
175
|
|
|
155
176
|
for (const key in result) {
|
|
@@ -169,10 +190,10 @@ function buildFloatingMiddleware(middleware, placement) {
|
|
|
169
190
|
|
|
170
191
|
switch (fn) {
|
|
171
192
|
case "flip":
|
|
172
|
-
result[key] = flip();
|
|
193
|
+
result[key] = flip(detectOverflowOptions);
|
|
173
194
|
break;
|
|
174
195
|
case "shift":
|
|
175
|
-
result[key] = shift();
|
|
196
|
+
result[key] = shift(detectOverflowOptions);
|
|
176
197
|
break;
|
|
177
198
|
case "autoPlacement":
|
|
178
199
|
let defaultAllowedPlacements = ["top", "bottom", "left", "right"];
|
|
@@ -192,31 +213,28 @@ function buildFloatingMiddleware(middleware, placement) {
|
|
|
192
213
|
}
|
|
193
214
|
defaultAllowedPlacements.unshift(placement);
|
|
194
215
|
|
|
195
|
-
result[key] = autoPlacement(
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
216
|
+
result[key] = autoPlacement(
|
|
217
|
+
Object.assign({}, detectOverflowOptions, {
|
|
218
|
+
crossAxis: true,
|
|
219
|
+
autoAlignment: true,
|
|
220
|
+
allowedPlacements: defaultAllowedPlacements,
|
|
221
|
+
}),
|
|
222
|
+
);
|
|
200
223
|
break;
|
|
201
224
|
case "arrow":
|
|
202
225
|
result[key] = null;
|
|
203
226
|
break;
|
|
204
227
|
case "size":
|
|
205
|
-
result[key] =
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
maxWidth: `${availableWidth}px`,
|
|
210
|
-
maxHeight: `${availableHeight}px`,
|
|
211
|
-
});
|
|
212
|
-
},
|
|
213
|
-
});
|
|
228
|
+
result[key] = createAdaptiveSizeMiddleware(
|
|
229
|
+
detectOverflowOptions,
|
|
230
|
+
popperElement,
|
|
231
|
+
);
|
|
214
232
|
break;
|
|
215
233
|
case "offset":
|
|
216
234
|
result[key] = offset(parseInt(kv?.shift()) || 10);
|
|
217
235
|
break;
|
|
218
236
|
case "hide":
|
|
219
|
-
result[key] = hide();
|
|
237
|
+
result[key] = hide(detectOverflowOptions);
|
|
220
238
|
break;
|
|
221
239
|
default:
|
|
222
240
|
throw new Error(`Unknown function: ${fn}`);
|
|
@@ -226,6 +244,135 @@ function buildFloatingMiddleware(middleware, placement) {
|
|
|
226
244
|
return result.filter(Boolean);
|
|
227
245
|
}
|
|
228
246
|
|
|
247
|
+
function createAdaptiveSizeMiddleware(detectOverflowOptions, popperElement) {
|
|
248
|
+
return size(
|
|
249
|
+
Object.assign({}, detectOverflowOptions, {
|
|
250
|
+
apply({ availableWidth, availableHeight, elements }) {
|
|
251
|
+
const floatingElement = elements?.floating || popperElement;
|
|
252
|
+
if (!(floatingElement instanceof HTMLElement)) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const maxWidth = clampAvailableDimension(
|
|
257
|
+
availableWidth,
|
|
258
|
+
readMaxDimension(floatingElement, "maxWidth"),
|
|
259
|
+
);
|
|
260
|
+
const maxHeight = clampAvailableDimension(
|
|
261
|
+
availableHeight,
|
|
262
|
+
readMaxDimension(floatingElement, "maxHeight"),
|
|
263
|
+
);
|
|
264
|
+
const nextStyle = {
|
|
265
|
+
boxSizing: "border-box",
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
if (Number.isFinite(maxWidth) && maxWidth > 0) {
|
|
269
|
+
nextStyle.maxWidth = `${maxWidth}px`;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (Number.isFinite(maxHeight) && maxHeight > 0) {
|
|
273
|
+
nextStyle.maxHeight = `${maxHeight}px`;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
Object.assign(floatingElement.style, nextStyle);
|
|
277
|
+
},
|
|
278
|
+
}),
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function buildDetectOverflowOptions(boundaryElement) {
|
|
283
|
+
const result = {
|
|
284
|
+
rootBoundary: "viewport",
|
|
285
|
+
padding: 0,
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
if (boundaryElement instanceof HTMLElement) {
|
|
289
|
+
result.boundary = boundaryElement;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return result;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function resolveClippingBoundaryElement(...elements) {
|
|
296
|
+
for (const element of elements) {
|
|
297
|
+
const clippingBoundary = findNearestClippingContainer(element);
|
|
298
|
+
if (clippingBoundary instanceof HTMLElement) {
|
|
299
|
+
return clippingBoundary;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function findNearestClippingContainer(element) {
|
|
307
|
+
let current = getComposedParent(element);
|
|
308
|
+
|
|
309
|
+
while (current) {
|
|
310
|
+
if (
|
|
311
|
+
current instanceof HTMLElement &&
|
|
312
|
+
isClippingContainer(getComputedStyle(current))
|
|
313
|
+
) {
|
|
314
|
+
return current;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
current = getComposedParent(current);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return null;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function getComposedParent(node) {
|
|
324
|
+
if (!node) {
|
|
325
|
+
return null;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (node instanceof ShadowRoot) {
|
|
329
|
+
return node.host || null;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (node.parentElement instanceof HTMLElement) {
|
|
333
|
+
return node.parentElement;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const rootNode = node.getRootNode?.();
|
|
337
|
+
if (rootNode instanceof ShadowRoot) {
|
|
338
|
+
return rootNode.host || null;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return node.parentNode || null;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function isClippingContainer(style) {
|
|
345
|
+
if (!style) {
|
|
346
|
+
return false;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return [style.overflow, style.overflowX, style.overflowY].some((value) => {
|
|
350
|
+
return ["hidden", "auto", "scroll", "clip"].includes(value);
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function readMaxDimension(element, property) {
|
|
355
|
+
const rawValue = getComputedStyle(element)?.[property];
|
|
356
|
+
if (!rawValue || rawValue === "none") {
|
|
357
|
+
return Infinity;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const value = Number.parseFloat(rawValue);
|
|
361
|
+
return Number.isFinite(value) ? value : Infinity;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function clampAvailableDimension(available, configuredMax) {
|
|
365
|
+
if (!Number.isFinite(available) || available <= 0) {
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if (!Number.isFinite(configuredMax)) {
|
|
370
|
+
return available;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
return Math.min(available, configuredMax);
|
|
374
|
+
}
|
|
375
|
+
|
|
229
376
|
function startAutoUpdate(controlElement, popperElement, callback) {
|
|
230
377
|
stopAutoUpdate(popperElement);
|
|
231
378
|
autoUpdateCleanupMap.set(
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { getGlobal } from "../../../../source/types/global.mjs";
|
|
2
|
+
import * as chai from "chai";
|
|
3
|
+
import { chaiDom } from "../../../util/chai-dom.mjs";
|
|
4
|
+
import { initJSDOM } from "../../../util/jsdom.mjs";
|
|
5
|
+
import { ResizeObserverMock } from "../../../util/resize-observer.mjs";
|
|
6
|
+
|
|
7
|
+
let expect = chai.expect;
|
|
8
|
+
chai.use(chaiDom);
|
|
9
|
+
|
|
10
|
+
const global = getGlobal();
|
|
11
|
+
|
|
12
|
+
let ContextHelp;
|
|
13
|
+
let resolveClippingBoundaryElement;
|
|
14
|
+
|
|
15
|
+
describe("ContextHelp", function () {
|
|
16
|
+
before(function (done) {
|
|
17
|
+
initJSDOM().then(() => {
|
|
18
|
+
import("element-internals-polyfill").catch((e) => done(e));
|
|
19
|
+
|
|
20
|
+
if (!global.ResizeObserver) {
|
|
21
|
+
global.ResizeObserver = ResizeObserverMock;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
Promise.all([
|
|
25
|
+
import("../../../../source/components/form/context-help.mjs"),
|
|
26
|
+
import("../../../../source/components/form/util/floating-ui.mjs"),
|
|
27
|
+
])
|
|
28
|
+
.then(([contextHelpModule, floatingUiModule]) => {
|
|
29
|
+
ContextHelp = contextHelpModule.ContextHelp;
|
|
30
|
+
resolveClippingBoundaryElement =
|
|
31
|
+
floatingUiModule.resolveClippingBoundaryElement;
|
|
32
|
+
done();
|
|
33
|
+
})
|
|
34
|
+
.catch((e) => done(e));
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
afterEach(() => {
|
|
39
|
+
let mocks = document.getElementById("mocks");
|
|
40
|
+
mocks.innerHTML = "";
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("should resolve the nearest clipping parent across the shadow boundary", function (done) {
|
|
44
|
+
let mocks = document.getElementById("mocks");
|
|
45
|
+
const outer = document.createElement("div");
|
|
46
|
+
const wrapper = document.createElement("div");
|
|
47
|
+
const help = document.createElement("monster-context-help");
|
|
48
|
+
|
|
49
|
+
outer.style.overflow = "visible";
|
|
50
|
+
wrapper.style.overflow = "hidden";
|
|
51
|
+
wrapper.appendChild(help);
|
|
52
|
+
outer.appendChild(wrapper);
|
|
53
|
+
mocks.appendChild(outer);
|
|
54
|
+
help.innerHTML = "<p>Inline help</p>";
|
|
55
|
+
|
|
56
|
+
setTimeout(() => {
|
|
57
|
+
try {
|
|
58
|
+
expect(help).to.be.instanceof(ContextHelp);
|
|
59
|
+
|
|
60
|
+
const popper = help.shadowRoot.querySelector(
|
|
61
|
+
'[data-monster-role="popper"]',
|
|
62
|
+
);
|
|
63
|
+
expect(popper).to.exist;
|
|
64
|
+
expect(resolveClippingBoundaryElement(help, popper)).to.equal(wrapper);
|
|
65
|
+
done();
|
|
66
|
+
} catch (e) {
|
|
67
|
+
done(e);
|
|
68
|
+
}
|
|
69
|
+
}, 0);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
@@ -254,87 +254,41 @@ describe('Select', function () {
|
|
|
254
254
|
|
|
255
255
|
const pagination = () => select.shadowRoot.querySelector('[data-monster-role=pagination]');
|
|
256
256
|
|
|
257
|
-
const
|
|
258
|
-
|
|
259
|
-
filterInput.value = value;
|
|
260
|
-
filterInput.dispatchEvent(new InputEvent('input', {
|
|
261
|
-
bubbles: true,
|
|
262
|
-
composed: true,
|
|
263
|
-
data: value
|
|
264
|
-
}));
|
|
265
|
-
filterInput.dispatchEvent(new KeyboardEvent('keydown', {
|
|
266
|
-
code: 'KeyA',
|
|
267
|
-
key: 'a',
|
|
268
|
-
bubbles: true,
|
|
269
|
-
composed: true
|
|
270
|
-
}));
|
|
271
|
-
};
|
|
272
|
-
|
|
273
|
-
const startedAt = Date.now();
|
|
274
|
-
const pollLoadedState = () => {
|
|
275
|
-
try {
|
|
276
|
-
const options = select.getOption('options');
|
|
277
|
-
const pager = pagination();
|
|
278
|
-
|
|
279
|
-
if (
|
|
280
|
-
options?.length !== 1 ||
|
|
281
|
-
!pager ||
|
|
282
|
-
pager.getOption('currentPage') !== 1 ||
|
|
283
|
-
pager.getOption('pages') !== 2 ||
|
|
284
|
-
pager.getOption('objectsPerPage') !== 1
|
|
285
|
-
) {
|
|
286
|
-
if (Date.now() - startedAt < 3000) {
|
|
287
|
-
return setTimeout(pollLoadedState, 50);
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
expect(options.length).to.equal(1);
|
|
292
|
-
expect(pager.getOption('currentPage')).to.equal(1);
|
|
293
|
-
expect(pager.getOption('pages')).to.equal(2);
|
|
294
|
-
expect(pager.getOption('objectsPerPage')).to.equal(1);
|
|
295
|
-
|
|
296
|
-
triggerFilter('b');
|
|
297
|
-
setTimeout(pollErrorState, 50);
|
|
298
|
-
} catch (e) {
|
|
299
|
-
done(e);
|
|
300
|
-
}
|
|
301
|
-
};
|
|
302
|
-
|
|
303
|
-
const pollErrorState = () => {
|
|
304
|
-
try {
|
|
305
|
-
const optionsAfterError = select.getOption('options');
|
|
306
|
-
const pager = pagination();
|
|
307
|
-
|
|
308
|
-
if (
|
|
309
|
-
optionsAfterError?.length !== 0 ||
|
|
310
|
-
!pager ||
|
|
311
|
-
pager.getOption('currentPage') !== null ||
|
|
312
|
-
pager.getOption('pages') !== null ||
|
|
313
|
-
pager.getOption('objectsPerPage') !== null ||
|
|
314
|
-
select.getOption('total') !== null ||
|
|
315
|
-
select.getOption('messages.total') !== ""
|
|
316
|
-
) {
|
|
317
|
-
if (Date.now() - startedAt < 4500) {
|
|
318
|
-
return setTimeout(pollErrorState, 50);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
expect(optionsAfterError.length).to.equal(0);
|
|
323
|
-
expect(pager.getOption('currentPage')).to.equal(null);
|
|
324
|
-
expect(pager.getOption('pages')).to.equal(null);
|
|
325
|
-
expect(pager.getOption('objectsPerPage')).to.equal(null);
|
|
326
|
-
expect(select.getOption('total')).to.equal(null);
|
|
327
|
-
expect(select.getOption('messages.total')).to.equal("");
|
|
328
|
-
} catch (e) {
|
|
329
|
-
return done(e);
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
done();
|
|
257
|
+
const fetchRemotePage = (value) => {
|
|
258
|
+
return select.fetch(`https://example.com/items?filter=${encodeURIComponent(value)}&page=1`);
|
|
333
259
|
};
|
|
334
260
|
|
|
335
261
|
setTimeout(() => {
|
|
336
|
-
|
|
337
|
-
|
|
262
|
+
fetchRemotePage('a')
|
|
263
|
+
.then(() => {
|
|
264
|
+
const options = select.getOption('options');
|
|
265
|
+
const pager = pagination();
|
|
266
|
+
|
|
267
|
+
expect(options.length).to.equal(1);
|
|
268
|
+
expect(pager.getOption('currentPage')).to.equal(1);
|
|
269
|
+
expect(pager.getOption('pages')).to.equal(2);
|
|
270
|
+
expect(pager.getOption('objectsPerPage')).to.equal(1);
|
|
271
|
+
|
|
272
|
+
return fetchRemotePage('b')
|
|
273
|
+
.then(() => Promise.reject(new Error('Expected remote fetch to fail')))
|
|
274
|
+
.catch((e) => {
|
|
275
|
+
if (e.message === 'Expected remote fetch to fail') {
|
|
276
|
+
throw e;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const optionsAfterError = select.getOption('options');
|
|
280
|
+
const pager = pagination();
|
|
281
|
+
|
|
282
|
+
expect(optionsAfterError.length).to.equal(0);
|
|
283
|
+
expect(pager.getOption('currentPage')).to.equal(null);
|
|
284
|
+
expect(pager.getOption('pages')).to.equal(null);
|
|
285
|
+
expect(pager.getOption('objectsPerPage')).to.equal(null);
|
|
286
|
+
expect(select.getOption('total')).to.equal(null);
|
|
287
|
+
expect(select.getOption('messages.total')).to.equal("");
|
|
288
|
+
});
|
|
289
|
+
})
|
|
290
|
+
.then(() => done())
|
|
291
|
+
.catch((e) => done(e));
|
|
338
292
|
}, 50);
|
|
339
293
|
});
|
|
340
294
|
});
|