@schukai/monster 4.136.7 → 4.136.10

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/CHANGELOG.md CHANGED
@@ -18,6 +18,8 @@
18
18
  - **message-state-button:** distinguish overlay, prose, and wide message layouts for smart popper sizing and overflow ([#401](https://gitlab.schukai.com/oss/libraries/javascript/monster/issues/401))
19
19
  - **popper:** support kebab-case camelCase option attributes such as `data-monster-option-popper-content-overflow` ([#401](https://gitlab.schukai.com/oss/libraries/javascript/monster/issues/401))
20
20
  - **popper/select:** let nested `monster-select` poppers escape parent popper content wrappers without shrinking normal parent content sizing ([#416](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/work_items/416))
21
+ - **select:** skip hidden remote info requests when `showRemoteInfo` is disabled ([#418](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/work_items/418))
22
+ - **select:** defer remote info loading until the dropdown opens and reuse pagination totals from remote option fetches when available ([#418](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/work_items/418))
21
23
 
22
24
  ### Changes
23
25
 
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.7"}
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.10"}
@@ -306,6 +306,7 @@ const lookupCacheSymbol = Symbol("lookupCache");
306
306
  const lookupInProgressSymbol = Symbol("lookupInProgress");
307
307
  const unresolvedSelectionValuesSymbol = Symbol("unresolvedSelectionValues");
308
308
  const fetchRequestVersionSymbol = Symbol("fetchRequestVersion");
309
+ const remoteInfoRequestSymbol = Symbol("remoteInfoRequest");
309
310
 
310
311
  /**
311
312
  * @private
@@ -458,6 +459,28 @@ class Select extends CustomControl {
458
459
  /**
459
460
  * Defines the default configuration options for the monster-control.
460
461
  * These options can be overridden via the HTML attribute `data-monster-options`.
462
+ * Use JSON in `data-monster-options` when option values must keep their JSON
463
+ * types, for example:
464
+ *
465
+ * ```html
466
+ * <monster-select
467
+ * data-monster-options='{"empty":{"equivalents":[null,"",0]}}'>
468
+ * </monster-select>
469
+ * ```
470
+ *
471
+ * When the value comes from an updater object, bind it through
472
+ * `data-monster-properties`. The bound object value keeps its JavaScript type,
473
+ * so an array such as `[null, "", 0]` keeps the numeric `0` as a number:
474
+ *
475
+ * ```html
476
+ * <monster-select
477
+ * data-monster-properties="option:empty.equivalents path:emptyEquivalents">
478
+ * </monster-select>
479
+ * ```
480
+ *
481
+ * Do not use `data-monster-option-empty-equivalents` for typed primitive
482
+ * values; array option attributes are split as strings and cannot express a
483
+ * numeric `0`.
461
484
  * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
462
485
  *
463
486
  * @property {string[]} toggleEventType - Array of DOM event names (e.g., ["click", "touch"]) that toggle the dropdown.
@@ -528,7 +551,7 @@ class Select extends CustomControl {
528
551
  * @property {Object} empty - Handling of empty or undefined values.
529
552
  * @property {string} empty.defaultValueRadio - Default value for `type="radio"` when no selection exists.
530
553
  * @property {Array} empty.defaultValueCheckbox - Default value (empty array) for `type="checkbox"`.
531
- * @property {Array} empty.equivalents - Values that are considered "empty" (e.g., `undefined`, `null`, `""`) and are normalized to the default value.
554
+ * @property {Array} empty.equivalents - Values that are considered "empty" (e.g., `undefined`, `null`, `""`, `NaN`) and are normalized to the default value. Matching is type-safe: a configured string `"0"` only matches the string `"0"`; configure numeric `0` explicitly, via `data-monster-options` JSON, `data-monster-properties`, or `setOption()`, when the number `0` should be treated as empty.
532
555
  * @property {Object} formatter - Functions for formatting display values.
533
556
  * @property {Function} formatter.selection - Callback `(value, option) => string` to format the display text of selected values.
534
557
  * @property {Object} classes - CSS classes for various elements.
@@ -698,7 +721,6 @@ class Select extends CustomControl {
698
721
  checkOptionState.call(this);
699
722
  calcAndSetOptionsDimension.call(this);
700
723
  updatePopper.call(this);
701
- initTotal.call(this);
702
724
  })
703
725
  .catch((e) => {
704
726
  addErrorAttribute(this, e);
@@ -721,8 +743,6 @@ class Select extends CustomControl {
721
743
  let lazyLoadFlag = self.getOption("features.lazyLoad", false);
722
744
  const remoteFilterFlag = getFilterMode.call(this) === FILTER_MODE_REMOTE;
723
745
 
724
- initTotal.call(self);
725
-
726
746
  if (getFilterMode.call(this) === FILTER_MODE_REMOTE) {
727
747
  self.setOption("features.lazyLoad", false);
728
748
  lazyLoadFlag = false;
@@ -2238,22 +2258,56 @@ function parseSlotsToOptions() {
2238
2258
  function buildSelectionLabel(value) {
2239
2259
  const strict = this.getOption("features.useStrictValueComparison") === true;
2240
2260
  const map = this[optionsMapSymbol];
2241
- const key = strict ? value : String(value);
2261
+ const key = strict ? value : getSelectionStateKey.call(this, value);
2242
2262
 
2243
2263
  if (map && map.has(key)) {
2244
2264
  if (clearUnresolvedSelectionValue.call(this, value)) {
2245
- this[lookupCacheSymbol].delete(value);
2265
+ this[lookupCacheSymbol].delete(getSelectionCacheKey.call(this, value));
2246
2266
  }
2247
2267
  return map.get(key);
2248
2268
  }
2249
2269
 
2250
- if (this[lookupCacheSymbol].has(value)) {
2251
- return this[lookupCacheSymbol].get(value);
2270
+ const cacheKey = getSelectionCacheKey.call(this, value);
2271
+ if (this[lookupCacheSymbol].has(cacheKey)) {
2272
+ return this[lookupCacheSymbol].get(cacheKey);
2252
2273
  }
2253
2274
 
2254
2275
  return undefined;
2255
2276
  }
2256
2277
 
2278
+ /**
2279
+ * @private
2280
+ * @param {*} value
2281
+ * @returns {string}
2282
+ */
2283
+ function getSelectionValueLabel(value) {
2284
+ if (isValueIsEmpty.call(this, value)) {
2285
+ return "";
2286
+ }
2287
+
2288
+ if (isPrimitive(value)) {
2289
+ return String(value);
2290
+ }
2291
+
2292
+ if (isObject(value)) {
2293
+ for (const key of ["value", "id", "key"]) {
2294
+ const candidate = value?.[key];
2295
+ if (!isValueIsEmpty.call(this, candidate) && isPrimitive(candidate)) {
2296
+ return String(candidate);
2297
+ }
2298
+ }
2299
+
2300
+ try {
2301
+ const json = JSON.stringify(value);
2302
+ if (isString(json)) {
2303
+ return json;
2304
+ }
2305
+ } catch (e) {}
2306
+ }
2307
+
2308
+ return String(value);
2309
+ }
2310
+
2257
2311
  /**
2258
2312
  * @private
2259
2313
  * @param {*} value
@@ -2262,7 +2316,33 @@ function buildSelectionLabel(value) {
2262
2316
  function getSelectionStateKey(value) {
2263
2317
  return this.getOption("features.useStrictValueComparison") === true
2264
2318
  ? value
2265
- : String(value);
2319
+ : getSelectionValueLabel.call(this, value);
2320
+ }
2321
+
2322
+ /**
2323
+ * @private
2324
+ * @param {*} value
2325
+ * @returns {*}
2326
+ */
2327
+ function getSelectionCacheKey(value) {
2328
+ return getSelectionStateKey.call(this, value);
2329
+ }
2330
+
2331
+ /**
2332
+ * @private
2333
+ * @param {*} current
2334
+ * @param {*} expected
2335
+ * @returns {boolean}
2336
+ */
2337
+ function areLookupValuesEqual(current, expected) {
2338
+ if (this.getOption("features.useStrictValueComparison") === true) {
2339
+ return current === expected;
2340
+ }
2341
+
2342
+ return (
2343
+ getSelectionValueLabel.call(this, current) ===
2344
+ getSelectionValueLabel.call(this, expected)
2345
+ );
2266
2346
  }
2267
2347
 
2268
2348
  /**
@@ -2357,6 +2437,8 @@ function buildSelectionItem(value, preferredLabel) {
2357
2437
  */
2358
2438
  async function lookupValueAndCache(value) {
2359
2439
  const lookupUrl = this.getOption("lookup.url");
2440
+ const lookupValue = getSelectionValueLabel.call(this, value);
2441
+ const cacheKey = getSelectionCacheKey.call(this, value);
2360
2442
  // A more robust check for invalid values. 0 and "" are valid.
2361
2443
  if (
2362
2444
  !lookupUrl ||
@@ -2381,7 +2463,10 @@ async function lookupValueAndCache(value) {
2381
2463
 
2382
2464
  const markerOpen = this.getOption("filter.marker.open", "{");
2383
2465
  const markerClose = this.getOption("filter.marker.close", "}");
2384
- const url = lookupUrl.replace(`${markerOpen}filter${markerClose}`, value);
2466
+ const url = lookupUrl.replace(
2467
+ `${markerOpen}filter${markerClose}`,
2468
+ lookupValue,
2469
+ );
2385
2470
 
2386
2471
  const data = await fetchData.call(this, url);
2387
2472
 
@@ -2407,8 +2492,9 @@ async function lookupValueAndCache(value) {
2407
2492
  }
2408
2493
 
2409
2494
  // The lookup might return more than the requested value, so we cache all of them.
2410
- if (this[lookupCacheSymbol].get(itemValue) !== itemLabel) {
2411
- this[lookupCacheSymbol].set(itemValue, itemLabel);
2495
+ const itemCacheKey = getSelectionCacheKey.call(this, itemValue);
2496
+ if (this[lookupCacheSymbol].get(itemCacheKey) !== itemLabel) {
2497
+ this[lookupCacheSymbol].set(itemCacheKey, itemLabel);
2412
2498
  refreshSelection = true;
2413
2499
  }
2414
2500
 
@@ -2416,13 +2502,13 @@ async function lookupValueAndCache(value) {
2416
2502
  refreshSelection = true;
2417
2503
  }
2418
2504
 
2419
- if (`${itemValue}` === `${value}`) {
2505
+ if (areLookupValuesEqual.call(this, itemValue, value)) {
2420
2506
  found = true;
2421
2507
  }
2422
2508
  }
2423
2509
 
2424
- if (!found && !this[lookupCacheSymbol].has(value)) {
2425
- this[lookupCacheSymbol].set(value, `${value}`);
2510
+ if (!found && !this[lookupCacheSymbol].has(cacheKey)) {
2511
+ this[lookupCacheSymbol].set(cacheKey, lookupValue);
2426
2512
  refreshSelection = true;
2427
2513
  }
2428
2514
 
@@ -2433,8 +2519,8 @@ async function lookupValueAndCache(value) {
2433
2519
  hasError = true;
2434
2520
  addErrorAttribute(this, e);
2435
2521
 
2436
- if (!this[lookupCacheSymbol].has(value)) {
2437
- this[lookupCacheSymbol].set(value, `${value}`);
2522
+ if (!this[lookupCacheSymbol].has(cacheKey)) {
2523
+ this[lookupCacheSymbol].set(cacheKey, lookupValue);
2438
2524
  refreshSelection = true;
2439
2525
  }
2440
2526
 
@@ -2482,11 +2568,7 @@ function getSelectionLabel(value) {
2482
2568
  });
2483
2569
  }
2484
2570
 
2485
- if (isString(value) || isInteger(value)) {
2486
- return `${value}`;
2487
- }
2488
-
2489
- return this.getOption("labels.cannot-be-loaded", value);
2571
+ return getSelectionValueLabel.call(this, value);
2490
2572
  }
2491
2573
 
2492
2574
  /**
@@ -2556,17 +2638,15 @@ function initOptionObserver() {
2556
2638
  if (isArray(options)) {
2557
2639
  for (const o of options) {
2558
2640
  if (isPrimitive(o)) {
2559
- const key = strict ? o : String(o);
2641
+ const key = getSelectionStateKey.call(self, o);
2560
2642
  if (!map.has(key)) {
2561
2643
  map.set(key, o);
2562
2644
  }
2563
2645
  } else if (isObject(o)) {
2564
2646
  const v = o?.value;
2565
2647
  const l = o?.label;
2566
- // If strict is true, use value as is (could be number, symbol, etc.)
2567
- // If strict is false, use String(value) to allow loose matching
2568
2648
  if (v !== undefined) {
2569
- const key = strict ? v : String(v);
2649
+ const key = getSelectionStateKey.call(self, v);
2570
2650
  if (!map.has(key)) {
2571
2651
  map.set(key, l);
2572
2652
  }
@@ -4053,6 +4133,9 @@ function convertSelectionToValue(selection) {
4053
4133
  * @private
4054
4134
  * @param value
4055
4135
  * @returns {boolean}
4136
+ *
4137
+ * Empty-equivalent matching is intentionally type-safe. Consumers that want
4138
+ * numeric `0` to count as empty must configure `0`, not `"0"`.
4056
4139
  */
4057
4140
  function isValueIsEmpty(value) {
4058
4141
  let equivalents = this.getOption("empty.equivalents");
@@ -4063,7 +4146,7 @@ function isValueIsEmpty(value) {
4063
4146
  equivalents = [equivalents];
4064
4147
  }
4065
4148
 
4066
- return equivalents.indexOf(value) !== -1;
4149
+ return equivalents.includes(value);
4067
4150
  }
4068
4151
 
4069
4152
  /**
@@ -4355,8 +4438,10 @@ function show() {
4355
4438
  const shouldLoadRemoteOptions =
4356
4439
  getFilterMode.call(self) === FILTER_MODE_REMOTE &&
4357
4440
  getOptionElements.call(self).length === 0;
4441
+ const shouldLoadDefaultOptions =
4442
+ shouldUseDefaultOptionsUrl.call(self, getCurrentFilterValue.call(self));
4358
4443
 
4359
- if (shouldUseDefaultOptionsUrl.call(self, getCurrentFilterValue.call(self))) {
4444
+ if (shouldLoadDefaultOptions) {
4360
4445
  setTimeout(() => {
4361
4446
  loadDefaultOptionsFromUrl.call(self).catch((e) => {
4362
4447
  addErrorAttribute(self, e);
@@ -4369,6 +4454,8 @@ function show() {
4369
4454
  addErrorAttribute(self, e);
4370
4455
  });
4371
4456
  }, 0);
4457
+ } else {
4458
+ initTotal.call(self);
4372
4459
  }
4373
4460
  calcAndSetOptionsDimension.call(this);
4374
4461
  focusFilter.call(this);
@@ -4440,6 +4527,18 @@ function initTotal() {
4440
4527
  return;
4441
4528
  }
4442
4529
 
4530
+ if (this.getOption("features.showRemoteInfo") !== true) {
4531
+ return;
4532
+ }
4533
+
4534
+ if (isInteger(this.getOption("total"))) {
4535
+ return;
4536
+ }
4537
+
4538
+ if (this[remoteInfoRequestSymbol]) {
4539
+ return;
4540
+ }
4541
+
4443
4542
  const url = this.getOption("remoteInfo.url");
4444
4543
  const mappingTotal = this.getOption("mapping.total");
4445
4544
 
@@ -4449,7 +4548,7 @@ function initTotal() {
4449
4548
 
4450
4549
  const fetchOptions = this.getOption("fetch", {});
4451
4550
 
4452
- getGlobal()
4551
+ const remoteInfoRequest = getGlobal()
4453
4552
  .fetch(url, fetchOptions)
4454
4553
  .then((response) => {
4455
4554
  if (!response.ok) {
@@ -4473,7 +4572,14 @@ function initTotal() {
4473
4572
  })
4474
4573
  .catch((e) => {
4475
4574
  addErrorAttribute(this, e);
4575
+ })
4576
+ .finally(() => {
4577
+ if (this[remoteInfoRequestSymbol] === remoteInfoRequest) {
4578
+ this[remoteInfoRequestSymbol] = null;
4579
+ }
4476
4580
  });
4581
+
4582
+ this[remoteInfoRequestSymbol] = remoteInfoRequest;
4477
4583
  }
4478
4584
 
4479
4585
  function updatePagination(total, currentPage, objectsPerPage) {
@@ -219,15 +219,13 @@ function buildFloatingMiddleware(
219
219
  const kv = line.split(":");
220
220
  const fn = kv.shift();
221
221
 
222
- switch (fn) {
223
- case "flip":
224
- result[key] = flip(detectOverflowOptions);
225
- break;
226
- case "shift":
227
- result[key] = shift(
228
- normalizeShiftOptions(kv, detectOverflowOptions),
229
- );
230
- break;
222
+ switch (fn) {
223
+ case "flip":
224
+ result[key] = flip(detectOverflowOptions);
225
+ break;
226
+ case "shift":
227
+ result[key] = shift(normalizeShiftOptions(kv, detectOverflowOptions));
228
+ break;
231
229
  case "autoPlacement":
232
230
  let defaultAllowedPlacements = ["top", "bottom", "left", "right"];
233
231
 
@@ -922,7 +920,10 @@ function startAutoUpdate(controlElement, popperElement, callback) {
922
920
  stopAutoUpdate(popperElement);
923
921
  autoUpdateCleanupMap.set(
924
922
  popperElement,
925
- autoUpdate(controlElement, popperElement, callback),
923
+ autoUpdate(controlElement, popperElement, callback, {
924
+ elementResize: typeof ResizeObserver === "function",
925
+ layoutShift: false,
926
+ }),
926
927
  );
927
928
  }
928
929
 
@@ -939,6 +940,10 @@ function stopAutoUpdate(popperElement) {
939
940
  function startFloatingResizeObserver(controlElement, popperElement, config) {
940
941
  stopFloatingResizeObserver(popperElement);
941
942
 
943
+ if (typeof ResizeObserver !== "function") {
944
+ return;
945
+ }
946
+
942
947
  const observer = new ResizeObserver(() => {
943
948
  scheduleSettlingPass(controlElement, popperElement, config);
944
949
  });
@@ -949,7 +954,10 @@ function startFloatingResizeObserver(controlElement, popperElement, config) {
949
954
 
950
955
  function stopFloatingResizeObserver(popperElement) {
951
956
  const observer = floatingResizeObserverMap.get(popperElement);
952
- if (observer instanceof ResizeObserver) {
957
+ if (
958
+ typeof ResizeObserver === "function" &&
959
+ observer instanceof ResizeObserver
960
+ ) {
953
961
  observer.disconnect();
954
962
  }
955
963
  floatingResizeObserverMap.delete(popperElement);
@@ -180,6 +180,10 @@ function syncAutoUpdate(instance) {
180
180
  () => {
181
181
  void instance.update();
182
182
  },
183
+ {
184
+ elementResize: typeof ResizeObserver === "function",
185
+ layoutShift: false,
186
+ },
183
187
  );
184
188
  }
185
189
 
@@ -44,6 +44,13 @@ const activeSubmenuHiderSymbol = Symbol("activeSubmenuHider");
44
44
  const hideHamburgerMenuSymbol = Symbol("hideHamburgerMenu");
45
45
  const hamburgerCloseButtonSymbol = Symbol("hamburgerCloseButton");
46
46
 
47
+ function getAutoUpdateOptions() {
48
+ return {
49
+ elementResize: typeof ResizeObserver === "function",
50
+ layoutShift: false,
51
+ };
52
+ }
53
+
47
54
  /**
48
55
  * A responsive site navigation that automatically moves menu items into a hamburger menu
49
56
  * when there isn't enough available space.
@@ -215,39 +222,48 @@ function initEventHandler() {
215
222
 
216
223
  hamburgerNav.scrollIntoView({ block: "start", behavior: "smooth" });
217
224
 
218
- cleanup = autoUpdate(hamburgerButton, hamburgerNav, () => {
219
- if (window.innerWidth > 768) {
220
- const strategy = getBestPositionStrategy(this);
221
-
222
- computePosition(hamburgerButton, hamburgerNav, {
223
- placement: "bottom-end",
224
- strategy: strategy,
225
- middleware: [
226
- offset(8),
227
- flip(),
228
- shift({ padding: 8 }),
229
- size({
230
- apply: ({ availableHeight, elements }) => {
231
- Object.assign(elements.floating.style, {
232
- maxHeight: `${availableHeight}px`,
233
- overflowY: "auto",
234
- });
235
- },
236
- padding: 8,
237
- }),
238
- ],
239
- }).then(({ x, y, strategy }) => {
225
+ cleanup = autoUpdate(
226
+ hamburgerButton,
227
+ hamburgerNav,
228
+ () => {
229
+ if (window.innerWidth > 768) {
230
+ const strategy = getBestPositionStrategy(this);
231
+
232
+ computePosition(hamburgerButton, hamburgerNav, {
233
+ placement: "bottom-end",
234
+ strategy: strategy,
235
+ middleware: [
236
+ offset(8),
237
+ flip(),
238
+ shift({ padding: 8 }),
239
+ size({
240
+ apply: ({ availableHeight, elements }) => {
241
+ Object.assign(elements.floating.style, {
242
+ maxHeight: `${availableHeight}px`,
243
+ overflowY: "auto",
244
+ });
245
+ },
246
+ padding: 8,
247
+ }),
248
+ ],
249
+ }).then(({ x, y, strategy }) => {
250
+ Object.assign(hamburgerNav.style, {
251
+ position: strategy,
252
+ left: `${x}px`,
253
+ top: `${y}px`,
254
+ });
255
+ });
256
+ } else {
257
+ // Mobile view (fullscreen overlay), position is handled by CSS
240
258
  Object.assign(hamburgerNav.style, {
241
- position: strategy,
242
- left: `${x}px`,
243
- top: `${y}px`,
259
+ position: "",
260
+ left: "",
261
+ top: "",
244
262
  });
245
- });
246
- } else {
247
- // Mobile view (fullscreen overlay), position is handled by CSS
248
- Object.assign(hamburgerNav.style, { position: "", left: "", top: "" });
249
- }
250
- });
263
+ }
264
+ },
265
+ getAutoUpdateOptions(),
266
+ );
251
267
  setTimeout(() => document.addEventListener("click", handleOutsideClick), 0);
252
268
  };
253
269
 
@@ -395,35 +411,40 @@ function setupSubmenu(parentLi, context = "visible", level = 1) {
395
411
  level,
396
412
  });
397
413
  if (!cleanup) {
398
- cleanup = autoUpdate(parentLi, submenu, () => {
399
- const middleware = [offset(8), flip(), shift({ padding: 8 })];
400
- const containsSubmenus = submenu.querySelector(
401
- "ul, div[part='mega-menu']",
402
- );
403
- if (!containsSubmenus) {
404
- middleware.push(
405
- size({
406
- apply: ({ availableHeight, elements }) => {
407
- Object.assign(elements.floating.style, {
408
- maxHeight: `${availableHeight}px`,
409
- overflowY: "auto",
410
- });
411
- },
412
- padding: 8,
413
- }),
414
+ cleanup = autoUpdate(
415
+ parentLi,
416
+ submenu,
417
+ () => {
418
+ const middleware = [offset(8), flip(), shift({ padding: 8 })];
419
+ const containsSubmenus = submenu.querySelector(
420
+ "ul, div[part='mega-menu']",
414
421
  );
415
- }
416
- computePosition(parentLi, submenu, {
417
- placement: level === 1 ? "bottom-start" : "right-start",
418
- middleware: middleware,
419
- }).then(({ x, y, strategy }) => {
420
- Object.assign(submenu.style, {
421
- position: strategy,
422
- left: `${x}px`,
423
- top: `${y}px`,
422
+ if (!containsSubmenus) {
423
+ middleware.push(
424
+ size({
425
+ apply: ({ availableHeight, elements }) => {
426
+ Object.assign(elements.floating.style, {
427
+ maxHeight: `${availableHeight}px`,
428
+ overflowY: "auto",
429
+ });
430
+ },
431
+ padding: 8,
432
+ }),
433
+ );
434
+ }
435
+ computePosition(parentLi, submenu, {
436
+ placement: level === 1 ? "bottom-start" : "right-start",
437
+ middleware: middleware,
438
+ }).then(({ x, y, strategy }) => {
439
+ Object.assign(submenu.style, {
440
+ position: strategy,
441
+ left: `${x}px`,
442
+ top: `${y}px`,
443
+ });
424
444
  });
425
- });
426
- });
445
+ },
446
+ getAutoUpdateOptions(),
447
+ );
427
448
  }
428
449
  };
429
450
 
@@ -156,7 +156,7 @@ function getMonsterVersion() {
156
156
  }
157
157
 
158
158
  /** don't touch, replaced by make with package.json version */
159
- monsterVersion = new Version("4.128.3");
159
+ monsterVersion = new Version("4.136.7");
160
160
 
161
161
  return monsterVersion;
162
162
  }