@schukai/monster 4.136.25 → 4.136.27

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.25"}
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.27"}
@@ -359,6 +359,25 @@ function isElementTrulyVisible(element) {
359
359
  );
360
360
  }
361
361
 
362
+ /**
363
+ * @private
364
+ * @param {HTMLElement} element
365
+ * @return {boolean}
366
+ */
367
+ function isElementSelfHidden(element) {
368
+ if (!(element instanceof HTMLElement)) {
369
+ return true;
370
+ }
371
+
372
+ const computedStyle = getComputedStyle(element);
373
+ return (
374
+ element.hidden === true ||
375
+ element.hasAttribute("hidden") ||
376
+ computedStyle.display === "none" ||
377
+ computedStyle.visibility === "hidden"
378
+ );
379
+ }
380
+
362
381
  /**
363
382
  * @private
364
383
  */
@@ -372,8 +391,10 @@ function initEventHandler() {
372
391
  const target = mutation.target;
373
392
  if (target instanceof HTMLElement) {
374
393
  const ref = target.getAttribute("data-monster-reference");
375
- if (ref && !isElementTrulyVisible(target)) {
376
- self[dimensionsSymbol].setVia(`data.button.${ref}`, 0);
394
+ if (ref) {
395
+ if (!isElementTrulyVisible(target)) {
396
+ self[dimensionsSymbol].setVia(`data.button.${ref}`, 0);
397
+ }
377
398
  needsRecalc = true;
378
399
  }
379
400
  }
@@ -596,12 +617,10 @@ function rearrangeButtons() {
596
617
  // In this case, we assume the width is 0.
597
618
  // This can happen for buttons that have never been visible.
598
619
  }
599
- const style = getComputedStyle(element);
600
-
601
620
  buttonEntries.push({
602
621
  element,
603
622
  width: buttonWidth,
604
- hidden: style.display === "none",
623
+ hidden: isElementSelfHidden(element),
605
624
  });
606
625
  }
607
626
 
@@ -617,11 +636,7 @@ function rearrangeButtons() {
617
636
 
618
637
  for (const entry of buttonEntries) {
619
638
  if (entry.hidden) {
620
- if (entry.width > 0) {
621
- buttonsToMoveToPopper.push(entry.element);
622
- } else {
623
- visibleButtonsInMainSlot.push(entry.element);
624
- }
639
+ visibleButtonsInMainSlot.push(entry.element);
625
640
  continue;
626
641
  }
627
642
 
@@ -826,7 +841,7 @@ function updateResizeObserverObservation() {
826
841
  if (this[mutationObserverSymbol]) {
827
842
  this[mutationObserverSymbol].observe(node, {
828
843
  attributes: true,
829
- attributeFilter: ["style", "class"],
844
+ attributeFilter: ["style", "class", "hidden"],
830
845
  });
831
846
  }
832
847
  });
@@ -456,7 +456,10 @@ function applyOverlayAwareContentHeight(
456
456
  preferredHeight > contentMaxHeight;
457
457
 
458
458
  contentElement.style.removeProperty("overflow");
459
- contentElement.style.overflowX = "visible";
459
+ contentElement.style.overflowX =
460
+ contentElement.dataset.monsterOverflowMode === "horizontal"
461
+ ? "auto"
462
+ : "visible";
460
463
  contentElement.style.overflowY = isConstrained ? "auto" : "visible";
461
464
 
462
465
  if (Number.isFinite(constrainedHeight) && constrainedHeight > 0) {
@@ -203,6 +203,9 @@ class Tabs extends CustomElement {
203
203
  standard: [],
204
204
  popper: [],
205
205
  },
206
+ updater: {
207
+ batchUpdates: true,
208
+ },
206
209
  fetch: {
207
210
  redirect: "error",
208
211
  method: "GET",
@@ -43,7 +43,8 @@ div[data-monster-role="popper"] > [part="content"] {
43
43
  div[data-monster-role="popper"]
44
44
  > [part="content"][data-monster-overflow-mode="horizontal"] {
45
45
  clip-path: none;
46
- overflow: visible;
46
+ overflow-x: auto;
47
+ overflow-y: visible;
47
48
  }
48
49
  div[data-monster-role="popper"]
49
50
  > [part="content"][data-monster-overflow-mode="visible"] {
@@ -47,7 +47,8 @@ div[data-monster-role=popper] {
47
47
  }
48
48
 
49
49
  & > [part=content][data-monster-overflow-mode=horizontal] {
50
- overflow: visible;
50
+ overflow-x: auto;
51
+ overflow-y: visible;
51
52
  clip-path: none;
52
53
  }
53
54
 
@@ -25,7 +25,7 @@ try {
25
25
  FloatingUiStyleSheet.insertRule(
26
26
  `
27
27
  @layer floatingui {
28
- div[data-monster-role=popper]{align-content:center;background:var(--monster-bg-color-primary-1);border-color:var(--monster-bg-color-primary-4);border-radius:var(--monster-border-radius);border-style:var(--monster-border-style);border-width:var(--monster-border-width);box-shadow:var(--monster-box-shadow-1);box-sizing:border-box;color:var(--monster-color-primary-1);display:none;justify-content:space-between;left:0;max-height:var(--monster-popper-max-height,calc(100vh - 2rem));max-width:var(--monster-popper-max-width,calc(100vw - 2rem));opacity:1;padding:1.1em;position:absolute;top:0;transition:opacity var(--monster-popper-fade-duration,.14s) var(--monster-popper-fade-timing,cubic-bezier(.2,0,0,1));width:-moz-max-content;width:max-content;z-index:var(--monster-z-index-modal)}div[data-monster-role=popper][data-monster-appearance=opening]{opacity:0}div[data-monster-role=popper][data-monster-appearance=open]{opacity:1}@media (prefers-reduced-motion:reduce){div[data-monster-role=popper]{transition:none}}div[data-monster-role=popper]>[part=content]{max-height:var(--monster-popper-content-max-height,calc(100vh - 4.2rem));max-width:100%;overflow:auto}div[data-monster-role=popper]>[part=content][data-monster-overflow-mode=horizontal]{clip-path:none;overflow:visible}div[data-monster-role=popper]>[part=content][data-monster-overflow-mode=visible]{clip-path:none;max-height:none;max-width:none;overflow:visible}div[data-monster-role=popper] div[data-monster-role=arrow]{background:var(--monster-bg-color-primary-1);height:calc(max(var(--monster-popper-witharrrow-distance), -1 * var(--monster-popper-witharrrow-distance))*2);pointer-events:none;position:absolute;width:calc(max(var(--monster-popper-witharrrow-distance), -1 * var(--monster-popper-witharrrow-distance))*2);z-index:-1}
28
+ div[data-monster-role=popper]{align-content:center;background:var(--monster-bg-color-primary-1);border-color:var(--monster-bg-color-primary-4);border-radius:var(--monster-border-radius);border-style:var(--monster-border-style);border-width:var(--monster-border-width);box-shadow:var(--monster-box-shadow-1);box-sizing:border-box;color:var(--monster-color-primary-1);display:none;justify-content:space-between;left:0;max-height:var(--monster-popper-max-height,calc(100vh - 2rem));max-width:var(--monster-popper-max-width,calc(100vw - 2rem));opacity:1;padding:1.1em;position:absolute;top:0;transition:opacity var(--monster-popper-fade-duration,.14s) var(--monster-popper-fade-timing,cubic-bezier(.2,0,0,1));width:-moz-max-content;width:max-content;z-index:var(--monster-z-index-modal)}div[data-monster-role=popper][data-monster-appearance=opening]{opacity:0}div[data-monster-role=popper][data-monster-appearance=open]{opacity:1}@media (prefers-reduced-motion:reduce){div[data-monster-role=popper]{transition:none}}div[data-monster-role=popper]>[part=content]{max-height:var(--monster-popper-content-max-height,calc(100vh - 4.2rem));max-width:100%;overflow:auto}div[data-monster-role=popper]>[part=content][data-monster-overflow-mode=horizontal]{clip-path:none;overflow-x:auto;overflow-y:visible}div[data-monster-role=popper]>[part=content][data-monster-overflow-mode=visible]{clip-path:none;max-height:none;max-width:none;overflow:visible}div[data-monster-role=popper] div[data-monster-role=arrow]{background:var(--monster-bg-color-primary-1);height:calc(max(var(--monster-popper-witharrrow-distance), -1 * var(--monster-popper-witharrrow-distance))*2);pointer-events:none;position:absolute;width:calc(max(var(--monster-popper-witharrrow-distance), -1 * var(--monster-popper-witharrrow-distance))*2);z-index:-1}
29
29
  }`,
30
30
  0,
31
31
  );
@@ -310,4 +310,29 @@ describe("form floating-ui boundary resolution", function () {
310
310
  expect(popper.style.width).to.equal("240px");
311
311
  expect(popper.style.minWidth).to.equal("240px");
312
312
  });
313
+
314
+ it("should constrain horizontal overlay-aware content on the inline axis", function () {
315
+ const mocks = document.getElementById("mocks");
316
+ const popper = document.createElement("div");
317
+ const content = document.createElement("div");
318
+
319
+ content.setAttribute("part", "content");
320
+ content.setAttribute("data-monster-overflow-mode", "horizontal");
321
+
322
+ Object.defineProperty(content, "scrollHeight", {
323
+ configurable: true,
324
+ value: 120,
325
+ });
326
+
327
+ popper.appendChild(content);
328
+ mocks.appendChild(popper);
329
+
330
+ applyAdaptiveFloatingElementSize(popper, {
331
+ availableWidth: 240,
332
+ availableHeight: 200,
333
+ });
334
+
335
+ expect(content.style.overflowX).to.equal("auto");
336
+ expect(content.style.overflowY).to.equal("visible");
337
+ });
313
338
  });
@@ -2,6 +2,7 @@ import {getGlobal} from "../../../../source/types/global.mjs";
2
2
  import * as chai from 'chai';
3
3
  import {chaiDom} from "../../../util/chai-dom.mjs";
4
4
  import {initJSDOM} from "../../../util/jsdom.mjs";
5
+ import {ResizeObserverMock} from "../../../util/resize-observer.mjs";
5
6
 
6
7
  let expect = chai.expect;
7
8
  chai.use(chaiDom);
@@ -61,6 +62,35 @@ let htmlOverflow = `
61
62
  `;
62
63
 
63
64
  let Tabs;
65
+ let restoreBoundingClientRect = null;
66
+ let restoreResizeObserver = null;
67
+
68
+ function waitForCondition(check, {timeout = 4000, interval = 10} = {}) {
69
+ const startedAt = Date.now();
70
+
71
+ return new Promise((resolve, reject) => {
72
+ const tick = () => {
73
+ try {
74
+ if (check() === true) {
75
+ resolve();
76
+ return;
77
+ }
78
+ } catch (e) {
79
+ reject(e);
80
+ return;
81
+ }
82
+
83
+ if (Date.now() - startedAt > timeout) {
84
+ reject(new Error('condition timed out'));
85
+ return;
86
+ }
87
+
88
+ setTimeout(tick, interval);
89
+ };
90
+
91
+ tick();
92
+ });
93
+ }
64
94
 
65
95
  describe('Tabs', function () {
66
96
 
@@ -92,6 +122,14 @@ describe('Tabs', function () {
92
122
  describe('document.createElement()', function () {
93
123
 
94
124
  afterEach(() => {
125
+ if (restoreBoundingClientRect instanceof Function) {
126
+ restoreBoundingClientRect();
127
+ restoreBoundingClientRect = null;
128
+ }
129
+ if (restoreResizeObserver instanceof Function) {
130
+ restoreResizeObserver();
131
+ restoreResizeObserver = null;
132
+ }
95
133
  let mocks = document.getElementById('mocks');
96
134
  mocks.innerHTML = "";
97
135
  })
@@ -219,10 +257,35 @@ describe('Tabs', function () {
219
257
  });
220
258
 
221
259
  it('should move overflowing tabs into the popper without losing them', function (done) {
260
+ this.timeout(5000);
222
261
 
223
262
  let mocks = document.getElementById('mocks');
263
+ const OriginalResizeObserver = window.ResizeObserver;
264
+ const originalGlobalResizeObserver = global.ResizeObserver;
265
+ class TrackingResizeObserver extends ResizeObserverMock {
266
+ static instances = [];
267
+ static callbackCount = 0;
268
+
269
+ constructor(callback) {
270
+ super((entries, observer) => {
271
+ TrackingResizeObserver.callbackCount++;
272
+ callback(entries, observer);
273
+ });
274
+ TrackingResizeObserver.instances.push(this);
275
+ }
276
+ }
277
+ window.ResizeObserver = TrackingResizeObserver;
278
+ global.ResizeObserver = TrackingResizeObserver;
279
+ restoreResizeObserver = () => {
280
+ window.ResizeObserver = OriginalResizeObserver;
281
+ global.ResizeObserver = originalGlobalResizeObserver;
282
+ };
224
283
  const originalGetBoundingClientRect =
225
284
  global.HTMLElement.prototype.getBoundingClientRect;
285
+ restoreBoundingClientRect = () => {
286
+ global.HTMLElement.prototype.getBoundingClientRect =
287
+ originalGetBoundingClientRect;
288
+ };
226
289
  global.HTMLElement.prototype.getBoundingClientRect = function () {
227
290
  const role = this.getAttribute?.('data-monster-role');
228
291
  const label = this.textContent?.trim() || '';
@@ -258,63 +321,90 @@ describe('Tabs', function () {
258
321
 
259
322
  mocks.innerHTML = htmlOverflow;
260
323
 
261
- setTimeout(() => {
324
+ waitForCondition(() => {
325
+ const tabs = document.getElementById('overflow-tabs');
326
+ const switchButton = tabs?.shadowRoot?.querySelector(
327
+ '[data-monster-role="switch"]',
328
+ );
329
+ const standardButtons = tabs?.getOption?.('buttons.standard') || [];
330
+ const popperButtons = tabs?.getOption?.('buttons.popper') || [];
331
+
332
+ return (
333
+ tabs instanceof Tabs &&
334
+ switchButton !== null &&
335
+ standardButtons.length > 0 &&
336
+ popperButtons.length > 0 &&
337
+ standardButtons.length + popperButtons.length === 11 &&
338
+ switchButton.classList.contains('hidden') === false
339
+ );
340
+ }).then(() => {
262
341
  try {
263
342
  const tabs = document.getElementById('overflow-tabs');
264
343
  expect(tabs).is.instanceof(Tabs);
265
-
266
- setTimeout(() => {
267
- try {
268
- const switchButton = tabs.shadowRoot.querySelector(
269
- '[data-monster-role="switch"]',
344
+ const switchButton = tabs.shadowRoot.querySelector(
345
+ '[data-monster-role="switch"]',
346
+ );
347
+ let standardButtons = tabs.getOption('buttons.standard');
348
+ let popperButtons = tabs.getOption('buttons.popper');
349
+
350
+ expect(switchButton).to.not.equal(null);
351
+ expect(standardButtons.length).to.be.greaterThan(0);
352
+ expect(popperButtons.length).to.be.greaterThan(0);
353
+ expect(standardButtons.length + popperButtons.length).to.equal(11);
354
+ expect(switchButton.classList.contains('hidden')).to.equal(false);
355
+
356
+ const resizeObserver = TrackingResizeObserver.instances.find(
357
+ (observer) => observer.elements.includes(tabs.shadowRoot.querySelector('[data-monster-role="nav"]')),
358
+ );
359
+ expect(resizeObserver).to.not.equal(undefined);
360
+ resizeObserver.triggerResize([]);
361
+
362
+ return waitForCondition(() => {
363
+ return (
364
+ TrackingResizeObserver.callbackCount > 0 &&
365
+ tabs.getOption('buttons.standard').length === 11 &&
366
+ tabs.getOption('buttons.popper').length === 0
367
+ );
368
+ }).then(() => {
369
+ return waitForCondition(() => {
370
+ standardButtons = tabs.getOption('buttons.standard');
371
+ popperButtons = tabs.getOption('buttons.popper');
372
+ return (
373
+ standardButtons.length > 0 &&
374
+ popperButtons.length > 0 &&
375
+ standardButtons.length + popperButtons.length === 11 &&
376
+ new Set(
377
+ standardButtons
378
+ .concat(popperButtons)
379
+ .map((button) => button.reference),
380
+ ).size === 11
270
381
  );
271
- let standardButtons = tabs.getOption('buttons.standard');
272
- let popperButtons = tabs.getOption('buttons.popper');
273
-
274
- expect(switchButton).to.not.equal(null);
275
- expect(standardButtons.length).to.be.greaterThan(0);
276
- expect(popperButtons.length).to.be.greaterThan(0);
277
- expect(
278
- standardButtons.length + popperButtons.length,
279
- ).to.equal(11);
280
- expect(switchButton.classList.contains('hidden')).to.equal(false);
281
-
282
- window.dispatchEvent(new Event('resize'));
283
- setTimeout(() => {
284
- try {
285
- standardButtons = tabs.getOption('buttons.standard');
286
- popperButtons = tabs.getOption('buttons.popper');
287
- expect(standardButtons.length).to.be.greaterThan(0);
288
- expect(popperButtons.length).to.be.greaterThan(0);
289
- expect(
290
- standardButtons.length + popperButtons.length,
291
- ).to.equal(11);
292
- expect(
293
- new Set(
294
- standardButtons.concat(popperButtons).map((button) => button.reference),
295
- ).size,
296
- ).to.equal(11);
297
- global.HTMLElement.prototype.getBoundingClientRect =
298
- originalGetBoundingClientRect;
299
- done();
300
- } catch (e) {
301
- global.HTMLElement.prototype.getBoundingClientRect =
302
- originalGetBoundingClientRect;
303
- done(e);
304
- }
305
- }, 250);
306
- } catch (e) {
307
- global.HTMLElement.prototype.getBoundingClientRect =
308
- originalGetBoundingClientRect;
309
- done(e);
310
- }
311
- }, 250);
382
+ });
383
+ }).then(() => {
384
+ restoreBoundingClientRect();
385
+ restoreBoundingClientRect = null;
386
+ restoreResizeObserver();
387
+ restoreResizeObserver = null;
388
+ done();
389
+ });
312
390
  } catch (e) {
313
- global.HTMLElement.prototype.getBoundingClientRect =
314
- originalGetBoundingClientRect;
391
+ restoreBoundingClientRect();
392
+ restoreBoundingClientRect = null;
393
+ restoreResizeObserver();
394
+ restoreResizeObserver = null;
315
395
  return done(e);
316
396
  }
317
- }, 0);
397
+ }).catch((e) => {
398
+ if (restoreBoundingClientRect instanceof Function) {
399
+ restoreBoundingClientRect();
400
+ restoreBoundingClientRect = null;
401
+ }
402
+ if (restoreResizeObserver instanceof Function) {
403
+ restoreResizeObserver();
404
+ restoreResizeObserver = null;
405
+ }
406
+ done(e);
407
+ });
318
408
  });
319
409
 
320
410
  });