@schukai/monster 4.136.23 → 4.136.24
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.
|
|
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.24"}
|
|
@@ -41,6 +41,7 @@ import { ButtonBarStyleSheet } from "./stylesheet/button-bar.mjs";
|
|
|
41
41
|
import { positionPopper } from "./util/floating-ui.mjs";
|
|
42
42
|
import { convertToPixels } from "../../dom/dimension.mjs";
|
|
43
43
|
import { addErrorAttribute } from "../../dom/error.mjs";
|
|
44
|
+
import { Processing } from "../../util/processing.mjs";
|
|
44
45
|
export { ButtonBar };
|
|
45
46
|
|
|
46
47
|
/**
|
|
@@ -126,6 +127,8 @@ const switchElementSymbol = Symbol("switchElement");
|
|
|
126
127
|
* @type {symbol}
|
|
127
128
|
*/
|
|
128
129
|
const layoutStateSymbol = Symbol("layoutState");
|
|
130
|
+
const layoutFrameSymbol = Symbol("layoutFrame");
|
|
131
|
+
const layoutTokenSymbol = Symbol("layoutToken");
|
|
129
132
|
|
|
130
133
|
/**
|
|
131
134
|
* @private
|
|
@@ -201,6 +204,7 @@ class ButtonBar extends CustomElement {
|
|
|
201
204
|
this[dimensionsSymbol] = new Pathfinder({ data: {} });
|
|
202
205
|
this[layoutStateSymbol] = {
|
|
203
206
|
scheduled: false,
|
|
207
|
+
running: false,
|
|
204
208
|
needsMeasure: true,
|
|
205
209
|
needsLayout: true,
|
|
206
210
|
needsObserve: true,
|
|
@@ -283,6 +287,12 @@ class ButtonBar extends CustomElement {
|
|
|
283
287
|
document.removeEventListener(type, this[closeEventHandler]);
|
|
284
288
|
}
|
|
285
289
|
|
|
290
|
+
if (typeof this[layoutFrameSymbol] === "number") {
|
|
291
|
+
cancelAnimationFrame(this[layoutFrameSymbol]);
|
|
292
|
+
}
|
|
293
|
+
delete this[layoutFrameSymbol];
|
|
294
|
+
this[layoutTokenSymbol] = (this[layoutTokenSymbol] || 0) + 1;
|
|
295
|
+
|
|
286
296
|
disconnectResizeObserver.call(this);
|
|
287
297
|
if (this[mutationObserverSymbol]) {
|
|
288
298
|
this[mutationObserverSymbol].disconnect();
|
|
@@ -442,12 +452,27 @@ function scheduleLayout(options = {}) {
|
|
|
442
452
|
state.needsLayout = state.needsLayout || options.layout === true;
|
|
443
453
|
state.needsObserve = state.needsObserve || options.observe === true;
|
|
444
454
|
|
|
445
|
-
if (state.scheduled) {
|
|
455
|
+
if (state.scheduled || state.running) {
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
scheduleLayoutFrame.call(this);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function scheduleLayoutFrame() {
|
|
463
|
+
const state = this[layoutStateSymbol];
|
|
464
|
+
if (!state || state.scheduled || state.running) {
|
|
446
465
|
return;
|
|
447
466
|
}
|
|
448
467
|
|
|
449
468
|
state.scheduled = true;
|
|
450
|
-
|
|
469
|
+
const token = (this[layoutTokenSymbol] || 0) + 1;
|
|
470
|
+
this[layoutTokenSymbol] = token;
|
|
471
|
+
this[layoutFrameSymbol] = requestAnimationFrame(() => {
|
|
472
|
+
if (this[layoutTokenSymbol] !== token) {
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
delete this[layoutFrameSymbol];
|
|
451
476
|
runLayout.call(this);
|
|
452
477
|
});
|
|
453
478
|
}
|
|
@@ -463,36 +488,61 @@ function runLayout() {
|
|
|
463
488
|
return;
|
|
464
489
|
}
|
|
465
490
|
|
|
466
|
-
if (state.
|
|
467
|
-
|
|
468
|
-
state.needsObserve = false;
|
|
491
|
+
if (state.running) {
|
|
492
|
+
return;
|
|
469
493
|
}
|
|
470
494
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
495
|
+
const needsObserve = state.needsObserve;
|
|
496
|
+
const needsMeasure = state.needsMeasure;
|
|
497
|
+
const needsLayout = state.needsLayout;
|
|
498
|
+
|
|
499
|
+
state.needsObserve = false;
|
|
500
|
+
state.needsMeasure = false;
|
|
501
|
+
state.needsLayout = false;
|
|
502
|
+
state.running = true;
|
|
503
|
+
|
|
504
|
+
new Processing(() => {
|
|
505
|
+
if (needsObserve) {
|
|
506
|
+
updateResizeObserverObservation.call(this);
|
|
479
507
|
}
|
|
480
|
-
state.needsMeasure = false;
|
|
481
|
-
}
|
|
482
508
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
509
|
+
if (needsMeasure) {
|
|
510
|
+
try {
|
|
511
|
+
calculateButtonBarDimensions.call(this);
|
|
512
|
+
} catch (error) {
|
|
513
|
+
addErrorAttribute(
|
|
514
|
+
this,
|
|
515
|
+
error?.message || "An error occurred while calculating dimensions",
|
|
516
|
+
);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
if (needsLayout) {
|
|
521
|
+
try {
|
|
522
|
+
rearrangeButtons.call(this);
|
|
523
|
+
} catch (error) {
|
|
524
|
+
addErrorAttribute(
|
|
525
|
+
this,
|
|
526
|
+
error?.message || "An error occurred while rearranging the buttons",
|
|
527
|
+
);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
return updatePopper.call(this);
|
|
532
|
+
})
|
|
533
|
+
.run()
|
|
534
|
+
.catch((error) => {
|
|
487
535
|
addErrorAttribute(
|
|
488
536
|
this,
|
|
489
|
-
error?.message || "An error occurred while
|
|
537
|
+
error?.message || "An error occurred while running the button bar layout",
|
|
490
538
|
);
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
539
|
+
})
|
|
540
|
+
.finally(() => {
|
|
541
|
+
state.running = false;
|
|
542
|
+
if (state.needsObserve || state.needsMeasure || state.needsLayout) {
|
|
543
|
+
scheduleLayoutFrame.call(this);
|
|
544
|
+
}
|
|
545
|
+
});
|
|
496
546
|
}
|
|
497
547
|
|
|
498
548
|
/**
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
CustomElement,
|
|
26
26
|
registerCustomElement,
|
|
27
27
|
} from "../../dom/customelement.mjs";
|
|
28
|
+
import { addErrorAttribute } from "../../dom/error.mjs";
|
|
28
29
|
import { fireCustomEvent } from "../../dom/events.mjs";
|
|
29
30
|
import {
|
|
30
31
|
findElementWithSelectorUpwards,
|
|
@@ -115,6 +116,8 @@ const dismissRecordSymbol = Symbol("dismissRecord");
|
|
|
115
116
|
* @type {symbol}
|
|
116
117
|
*/
|
|
117
118
|
const usesHostDismissSymbol = Symbol("usesHostDismiss");
|
|
119
|
+
const actionQueueSymbol = Symbol("actionQueue");
|
|
120
|
+
const pendingActionSymbol = Symbol("pendingAction");
|
|
118
121
|
|
|
119
122
|
/**
|
|
120
123
|
* Popper component for displaying floating UI elements
|
|
@@ -274,7 +277,7 @@ class Popper extends CustomElement {
|
|
|
274
277
|
* @return {Popper} The popper instance
|
|
275
278
|
*/
|
|
276
279
|
showDialog() {
|
|
277
|
-
|
|
280
|
+
queuePopperAction.call(this, "show");
|
|
278
281
|
return this;
|
|
279
282
|
}
|
|
280
283
|
|
|
@@ -284,8 +287,7 @@ class Popper extends CustomElement {
|
|
|
284
287
|
* @return {Popper}
|
|
285
288
|
*/
|
|
286
289
|
recalcPopper() {
|
|
287
|
-
|
|
288
|
-
updatePopper.call(this);
|
|
290
|
+
queuePopperAction.call(this, "update");
|
|
289
291
|
return this;
|
|
290
292
|
}
|
|
291
293
|
|
|
@@ -326,7 +328,7 @@ class Popper extends CustomElement {
|
|
|
326
328
|
* @return {Popper} The popper instance
|
|
327
329
|
*/
|
|
328
330
|
hideDialog() {
|
|
329
|
-
|
|
331
|
+
queuePopperAction.call(this, "hide");
|
|
330
332
|
return this;
|
|
331
333
|
}
|
|
332
334
|
|
|
@@ -335,11 +337,7 @@ class Popper extends CustomElement {
|
|
|
335
337
|
* @return {Popper} The popper instance
|
|
336
338
|
*/
|
|
337
339
|
toggleDialog() {
|
|
338
|
-
|
|
339
|
-
this.hideDialog();
|
|
340
|
-
} else {
|
|
341
|
-
this.showDialog();
|
|
342
|
-
}
|
|
340
|
+
queuePopperAction.call(this, "toggle");
|
|
343
341
|
return this;
|
|
344
342
|
}
|
|
345
343
|
}
|
|
@@ -396,6 +394,59 @@ function initEventHandler() {
|
|
|
396
394
|
return this;
|
|
397
395
|
}
|
|
398
396
|
|
|
397
|
+
/**
|
|
398
|
+
* Serializes popper state changes so open/close/update events do not interleave.
|
|
399
|
+
* The latest requested action wins while a queue is already running.
|
|
400
|
+
*
|
|
401
|
+
* @private
|
|
402
|
+
* @param {"show"|"hide"|"toggle"|"update"} action
|
|
403
|
+
* @return {Promise<void>}
|
|
404
|
+
*/
|
|
405
|
+
function queuePopperAction(action) {
|
|
406
|
+
this[pendingActionSymbol] = action;
|
|
407
|
+
|
|
408
|
+
if (this[actionQueueSymbol] instanceof Promise) {
|
|
409
|
+
return this[actionQueueSymbol];
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
this[actionQueueSymbol] = (async () => {
|
|
413
|
+
while (this[pendingActionSymbol]) {
|
|
414
|
+
const nextAction = this[pendingActionSymbol];
|
|
415
|
+
delete this[pendingActionSymbol];
|
|
416
|
+
await Promise.resolve(runPopperAction.call(this, nextAction));
|
|
417
|
+
}
|
|
418
|
+
})()
|
|
419
|
+
.catch((e) => {
|
|
420
|
+
addErrorAttribute(this, e);
|
|
421
|
+
})
|
|
422
|
+
.finally(() => {
|
|
423
|
+
delete this[actionQueueSymbol];
|
|
424
|
+
if (this[pendingActionSymbol]) {
|
|
425
|
+
void queuePopperAction.call(this, this[pendingActionSymbol]);
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
return this[actionQueueSymbol];
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function runPopperAction(action) {
|
|
433
|
+
switch (action) {
|
|
434
|
+
case "toggle":
|
|
435
|
+
if (isPositionedPopperOpen(this[popperElementSymbol])) {
|
|
436
|
+
return performHide.call(this);
|
|
437
|
+
}
|
|
438
|
+
return performShow.call(this);
|
|
439
|
+
case "hide":
|
|
440
|
+
return performHide.call(this);
|
|
441
|
+
case "show":
|
|
442
|
+
return performShow.call(this);
|
|
443
|
+
case "update":
|
|
444
|
+
return performUpdate.call(this);
|
|
445
|
+
default:
|
|
446
|
+
return undefined;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
399
450
|
function isEventInsidePopperOwner(
|
|
400
451
|
owner,
|
|
401
452
|
event,
|
|
@@ -545,6 +596,14 @@ function disconnectResizeObserver() {
|
|
|
545
596
|
* @private
|
|
546
597
|
*/
|
|
547
598
|
function hide() {
|
|
599
|
+
void queuePopperAction.call(this, "hide");
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* Hides the popper element
|
|
604
|
+
* @private
|
|
605
|
+
*/
|
|
606
|
+
function performHide() {
|
|
548
607
|
const self = this;
|
|
549
608
|
const popperElement = self[popperElementSymbol];
|
|
550
609
|
const controlElement = self[controlElementSymbol];
|
|
@@ -580,6 +639,14 @@ function hide() {
|
|
|
580
639
|
* @private
|
|
581
640
|
*/
|
|
582
641
|
function show() {
|
|
642
|
+
void queuePopperAction.call(this, "show");
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/**
|
|
646
|
+
* Shows the popper element
|
|
647
|
+
* @private
|
|
648
|
+
*/
|
|
649
|
+
function performShow() {
|
|
583
650
|
const self = this;
|
|
584
651
|
const popperElement = self[popperElementSymbol];
|
|
585
652
|
const controlElement = self[controlElementSymbol];
|
|
@@ -615,13 +682,13 @@ function show() {
|
|
|
615
682
|
|
|
616
683
|
addAttributeToken(controlElement, "class", "open");
|
|
617
684
|
registerWithHost.call(self);
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
});
|
|
624
|
-
}
|
|
685
|
+
return performUpdate.call(self).then(() => {
|
|
686
|
+
setTimeout(() => {
|
|
687
|
+
fireCustomEvent(self, "monster-popper-opened", {
|
|
688
|
+
self,
|
|
689
|
+
});
|
|
690
|
+
}, 0);
|
|
691
|
+
});
|
|
625
692
|
}
|
|
626
693
|
|
|
627
694
|
/**
|
|
@@ -629,6 +696,14 @@ function show() {
|
|
|
629
696
|
* @private
|
|
630
697
|
*/
|
|
631
698
|
function updatePopper() {
|
|
699
|
+
void queuePopperAction.call(this, "update");
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
/**
|
|
703
|
+
* Updates popper positioning
|
|
704
|
+
* @private
|
|
705
|
+
*/
|
|
706
|
+
function performUpdate() {
|
|
632
707
|
if (
|
|
633
708
|
!this.isConnected ||
|
|
634
709
|
!(this[controlElementSymbol] instanceof HTMLElement) ||
|
|
@@ -647,7 +722,7 @@ function updatePopper() {
|
|
|
647
722
|
|
|
648
723
|
applyContentOverflowMode.call(this);
|
|
649
724
|
|
|
650
|
-
positionPopper.call(
|
|
725
|
+
return positionPopper.call(
|
|
651
726
|
this,
|
|
652
727
|
this[controlElementSymbol],
|
|
653
728
|
this[popperElementSymbol],
|
|
@@ -195,84 +195,97 @@ describe("PopperButton", function () {
|
|
|
195
195
|
}, 0);
|
|
196
196
|
});
|
|
197
197
|
|
|
198
|
-
it("should move the popper through the opening appearance states", function (
|
|
198
|
+
it("should move the popper through the opening appearance states", async function () {
|
|
199
199
|
let mocks = document.getElementById("mocks");
|
|
200
200
|
const button = document.createElement("monster-popper-button");
|
|
201
201
|
mocks.appendChild(button);
|
|
202
202
|
|
|
203
|
-
|
|
204
|
-
try {
|
|
205
|
-
button.showDialog();
|
|
203
|
+
await waitForTimeout();
|
|
206
204
|
|
|
207
|
-
|
|
208
|
-
'[data-monster-role="popper"]',
|
|
209
|
-
);
|
|
210
|
-
expect(popper).to.exist;
|
|
211
|
-
expect(popper.dataset.monsterAppearance).to.equal("opening");
|
|
205
|
+
button.showDialog();
|
|
212
206
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
done(e);
|
|
231
|
-
}
|
|
232
|
-
}, 30);
|
|
233
|
-
} catch (e) {
|
|
234
|
-
done(e);
|
|
235
|
-
}
|
|
236
|
-
}, 0);
|
|
207
|
+
const popper = button.shadowRoot.querySelector(
|
|
208
|
+
'[data-monster-role="popper"]',
|
|
209
|
+
);
|
|
210
|
+
expect(popper).to.exist;
|
|
211
|
+
expect(popper.dataset.monsterAppearance).to.equal("opening");
|
|
212
|
+
|
|
213
|
+
await waitForCondition(() => {
|
|
214
|
+
return popper.dataset.monsterAppearance === "open";
|
|
215
|
+
});
|
|
216
|
+
expect(popper.dataset.monsterAppearance).to.equal("open");
|
|
217
|
+
|
|
218
|
+
button.hideDialog();
|
|
219
|
+
|
|
220
|
+
await waitForCondition(() => {
|
|
221
|
+
return !popper.hasAttribute("data-monster-appearance");
|
|
222
|
+
});
|
|
223
|
+
expect(popper.hasAttribute("data-monster-appearance")).to.equal(false);
|
|
237
224
|
});
|
|
238
225
|
|
|
239
|
-
it("should finish the opening appearance state when requestAnimationFrame stalls", function (
|
|
226
|
+
it("should finish the opening appearance state when requestAnimationFrame stalls", async function () {
|
|
240
227
|
let mocks = document.getElementById("mocks");
|
|
241
228
|
const button = document.createElement("monster-popper-button");
|
|
242
229
|
const originalRequestAnimationFrame = globalThis.requestAnimationFrame;
|
|
243
230
|
const originalCancelAnimationFrame = globalThis.cancelAnimationFrame;
|
|
244
231
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
232
|
+
try {
|
|
233
|
+
globalThis.requestAnimationFrame = () => 1;
|
|
234
|
+
globalThis.cancelAnimationFrame = () => {};
|
|
235
|
+
mocks.appendChild(button);
|
|
236
|
+
|
|
237
|
+
await waitForTimeout();
|
|
238
|
+
|
|
239
|
+
button.showDialog();
|
|
240
|
+
|
|
241
|
+
const popper = button.shadowRoot.querySelector(
|
|
242
|
+
'[data-monster-role="popper"]',
|
|
243
|
+
);
|
|
244
|
+
expect(popper).to.exist;
|
|
245
|
+
expect(popper.dataset.monsterAppearance).to.equal("opening");
|
|
246
|
+
|
|
247
|
+
await waitForCondition(() => {
|
|
248
|
+
return popper.dataset.monsterAppearance === "open";
|
|
249
|
+
});
|
|
250
|
+
expect(popper.dataset.monsterAppearance).to.equal("open");
|
|
251
|
+
button.hideDialog();
|
|
252
|
+
} finally {
|
|
253
|
+
globalThis.requestAnimationFrame = originalRequestAnimationFrame;
|
|
254
|
+
globalThis.cancelAnimationFrame = originalCancelAnimationFrame;
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
});
|
|
248
258
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
259
|
+
function waitForTimeout(timeout = 0) {
|
|
260
|
+
return new Promise((resolve) => setTimeout(resolve, timeout));
|
|
261
|
+
}
|
|
252
262
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
);
|
|
256
|
-
expect(popper).to.exist;
|
|
257
|
-
expect(popper.dataset.monsterAppearance).to.equal("opening");
|
|
263
|
+
function waitForCondition(check, { timeout = 1000, interval = 10 } = {}) {
|
|
264
|
+
const deadline = Date.now() + timeout;
|
|
258
265
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
} catch (e) {
|
|
265
|
-
done(e);
|
|
266
|
-
} finally {
|
|
267
|
-
globalThis.requestAnimationFrame = originalRequestAnimationFrame;
|
|
268
|
-
globalThis.cancelAnimationFrame = originalCancelAnimationFrame;
|
|
269
|
-
}
|
|
270
|
-
}, 30);
|
|
266
|
+
return new Promise((resolve, reject) => {
|
|
267
|
+
const tick = () => {
|
|
268
|
+
let done = false;
|
|
269
|
+
try {
|
|
270
|
+
done = check();
|
|
271
271
|
} catch (e) {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
done(e);
|
|
272
|
+
reject(e);
|
|
273
|
+
return;
|
|
275
274
|
}
|
|
276
|
-
|
|
275
|
+
|
|
276
|
+
if (done) {
|
|
277
|
+
resolve();
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (Date.now() >= deadline) {
|
|
282
|
+
reject(new Error("Timed out waiting for condition."));
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
setTimeout(tick, interval);
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
tick();
|
|
277
290
|
});
|
|
278
|
-
}
|
|
291
|
+
}
|