@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 +2 -0
- package/package.json +1 -1
- package/source/components/form/select.mjs +135 -29
- package/source/components/form/util/floating-ui.mjs +19 -11
- package/source/components/form/util/popper.mjs +4 -0
- package/source/components/navigation/site-navigation.mjs +79 -58
- package/source/types/version.mjs +1 -1
- package/test/cases/components/form/message-state-button.mjs +36 -60
- package/test/cases/components/form/select.mjs +195 -0
- package/test/cases/monster.mjs +1 -1
- package/test/web/import.js +4 -0
- package/test/web/test.html +2 -2
- package/test/web/tests.js +6841 -2575
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.
|
|
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 :
|
|
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
|
-
|
|
2251
|
-
|
|
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
|
-
:
|
|
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(
|
|
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
|
-
|
|
2411
|
-
|
|
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 (
|
|
2505
|
+
if (areLookupValuesEqual.call(this, itemValue, value)) {
|
|
2420
2506
|
found = true;
|
|
2421
2507
|
}
|
|
2422
2508
|
}
|
|
2423
2509
|
|
|
2424
|
-
if (!found && !this[lookupCacheSymbol].has(
|
|
2425
|
-
this[lookupCacheSymbol].set(
|
|
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(
|
|
2437
|
-
this[lookupCacheSymbol].set(
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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.
|
|
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 (
|
|
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
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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 (
|
|
957
|
+
if (
|
|
958
|
+
typeof ResizeObserver === "function" &&
|
|
959
|
+
observer instanceof ResizeObserver
|
|
960
|
+
) {
|
|
953
961
|
observer.disconnect();
|
|
954
962
|
}
|
|
955
963
|
floatingResizeObserverMap.delete(popperElement);
|
|
@@ -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(
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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:
|
|
242
|
-
left:
|
|
243
|
-
top:
|
|
259
|
+
position: "",
|
|
260
|
+
left: "",
|
|
261
|
+
top: "",
|
|
244
262
|
});
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
|
|
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(
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
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
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
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
|
|
package/source/types/version.mjs
CHANGED