@schukai/monster 4.136.4 → 4.136.6
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 +707 -204
- package/source/components/form/style/select.pcss +18 -0
- package/source/components/form/stylesheet/context-error.mjs +13 -6
- package/source/components/form/stylesheet/context-help.mjs +13 -6
- package/source/components/form/stylesheet/select.mjs +7 -14
- package/source/components/form/util/floating-ui.mjs +152 -23
- package/source/components/layout/popper.mjs +23 -4
- package/test/cases/components/form/select.mjs +478 -7
|
@@ -41,6 +41,7 @@ import { getLocaleOfDocument } from "../../dom/locale.mjs";
|
|
|
41
41
|
import {
|
|
42
42
|
findElementWithSelectorUpwards,
|
|
43
43
|
getDocument,
|
|
44
|
+
getWindow,
|
|
44
45
|
} from "../../dom/util.mjs";
|
|
45
46
|
import {
|
|
46
47
|
getDocumentTranslations,
|
|
@@ -69,6 +70,8 @@ import {
|
|
|
69
70
|
isPositionedPopperOpen,
|
|
70
71
|
openPositionedPopper,
|
|
71
72
|
positionPopper,
|
|
73
|
+
resolveClippingBoundaryElement,
|
|
74
|
+
resolveParentPopperContentBoundary,
|
|
72
75
|
} from "./util/floating-ui.mjs";
|
|
73
76
|
import { Pathfinder } from "../../data/pathfinder.mjs";
|
|
74
77
|
import { TokenList } from "../../types/tokenlist.mjs";
|
|
@@ -76,9 +79,14 @@ import { TokenList } from "../../types/tokenlist.mjs";
|
|
|
76
79
|
import "../datatable/pagination.mjs";
|
|
77
80
|
|
|
78
81
|
export {
|
|
82
|
+
getDefaultSelectPopperPositionProfile,
|
|
79
83
|
getSelectionTemplate,
|
|
80
84
|
getSummaryTemplate,
|
|
81
85
|
popperElementSymbol,
|
|
86
|
+
resolveSelectPopperWidthConstraints,
|
|
87
|
+
resolveSelectListDimension,
|
|
88
|
+
resolveSelectVisibleRect,
|
|
89
|
+
resolveSelectViewportMetrics,
|
|
82
90
|
Select,
|
|
83
91
|
};
|
|
84
92
|
|
|
@@ -129,6 +137,14 @@ const clearOptionEventHandler = Symbol("clearOptionEventHandler");
|
|
|
129
137
|
* @type {Symbol}
|
|
130
138
|
*/
|
|
131
139
|
const resizeObserverSymbol = Symbol("resizeObserver");
|
|
140
|
+
const resizeObserverFrameSymbol = Symbol("resizeObserverFrame");
|
|
141
|
+
const visualViewportResizeHandlerSymbol = Symbol("visualViewportResizeHandler");
|
|
142
|
+
const visualViewportScrollHandlerSymbol = Symbol("visualViewportScrollHandler");
|
|
143
|
+
const visibilityChangeHandlerSymbol = Symbol("visibilityChangeHandler");
|
|
144
|
+
const windowResizeHandlerSymbol = Symbol("windowResizeHandler");
|
|
145
|
+
const windowOrientationChangeHandlerSymbol = Symbol(
|
|
146
|
+
"windowOrientationChangeHandler",
|
|
147
|
+
);
|
|
132
148
|
|
|
133
149
|
/**
|
|
134
150
|
* local symbol
|
|
@@ -246,21 +262,17 @@ const remoteInfoElementSymbol = Symbol("remoteInfoElement");
|
|
|
246
262
|
const areOptionsAvailableAndInitSymbol = Symbol("@@areOptionsAvailableAndInit");
|
|
247
263
|
|
|
248
264
|
/**
|
|
265
|
+
* Internal sentinel used to suppress a remote request after URL formatting.
|
|
266
|
+
*
|
|
267
|
+
* This is currently inserted only when `filter.defaultValue` resolves to
|
|
268
|
+
* `undefined` or `null`. An empty string is treated as a real filter value and
|
|
269
|
+
* therefore still produces a request.
|
|
270
|
+
*
|
|
249
271
|
* @private
|
|
250
272
|
* @type {symbol}
|
|
251
273
|
*/
|
|
252
274
|
const disabledRequestMarker = Symbol("@@disabledRequestMarker");
|
|
253
275
|
|
|
254
|
-
/**
|
|
255
|
-
* @private
|
|
256
|
-
* @type {symbol}
|
|
257
|
-
*/
|
|
258
|
-
const runLookupOnceSymbol = Symbol("runLookupOnce");
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* @private
|
|
262
|
-
* @type {symbol}
|
|
263
|
-
*/
|
|
264
276
|
const cleanupOptionsListSymbol = Symbol("cleanupOptionsList");
|
|
265
277
|
const optionsVersionSymbol = Symbol("optionsVersion");
|
|
266
278
|
const pendingSelectionSymbol = Symbol("pendingSelection");
|
|
@@ -285,16 +297,6 @@ const debounceOptionsMutationObserverSymbol = Symbol(
|
|
|
285
297
|
*/
|
|
286
298
|
const currentPageSymbol = Symbol("currentPage");
|
|
287
299
|
|
|
288
|
-
/**
|
|
289
|
-
* @private
|
|
290
|
-
* @type {symbol}
|
|
291
|
-
*/
|
|
292
|
-
const remoteFilterFirstOpendSymbol = Symbol("remoteFilterFirstOpend");
|
|
293
|
-
|
|
294
|
-
/**
|
|
295
|
-
* @private
|
|
296
|
-
* @type {symbol}
|
|
297
|
-
*/
|
|
298
300
|
const lookupCacheSymbol = Symbol("lookupCache");
|
|
299
301
|
|
|
300
302
|
/**
|
|
@@ -302,6 +304,7 @@ const lookupCacheSymbol = Symbol("lookupCache");
|
|
|
302
304
|
* @type {symbol}
|
|
303
305
|
*/
|
|
304
306
|
const lookupInProgressSymbol = Symbol("lookupInProgress");
|
|
307
|
+
const unresolvedSelectionValuesSymbol = Symbol("unresolvedSelectionValues");
|
|
305
308
|
const fetchRequestVersionSymbol = Symbol("fetchRequestVersion");
|
|
306
309
|
|
|
307
310
|
/**
|
|
@@ -343,6 +346,9 @@ const FILTER_POSITION_POPPER = "popper";
|
|
|
343
346
|
* @type {string}
|
|
344
347
|
*/
|
|
345
348
|
const FILTER_POSITION_INLINE = "inline";
|
|
349
|
+
const SELECT_MIN_POPPER_WIDTH = 240;
|
|
350
|
+
const SELECT_MAX_POPPER_HEIGHT = 500;
|
|
351
|
+
const SELECT_VIEWPORT_PADDING = 12;
|
|
346
352
|
|
|
347
353
|
/**
|
|
348
354
|
* A select control that can be used to select o
|
|
@@ -380,6 +386,7 @@ class Select extends CustomControl {
|
|
|
380
386
|
this[currentPageSymbol] = 1;
|
|
381
387
|
this[lookupCacheSymbol] = new Map();
|
|
382
388
|
this[lookupInProgressSymbol] = new Map();
|
|
389
|
+
this[unresolvedSelectionValuesSymbol] = new Set();
|
|
383
390
|
this[optionsMapSymbol] = new Map();
|
|
384
391
|
this[closeOnSelectAutoSymbol] = true;
|
|
385
392
|
initOptionObserver.call(this);
|
|
@@ -462,8 +469,8 @@ class Select extends CustomControl {
|
|
|
462
469
|
* @property {string} name - Name of the hidden form field for form submission.
|
|
463
470
|
* @property {string|null} url - URL to dynamically fetch options via HTTP when opening or filtering.
|
|
464
471
|
* @property {number|null} total - Total number of available options, useful for pagination with remote data.
|
|
465
|
-
* @property {Object} lookup - Configuration for
|
|
466
|
-
* @property {string|null} lookup.url - URL template with a `${filter}` placeholder to look up selected entries
|
|
472
|
+
* @property {Object} lookup - Configuration for hydrating already selected values.
|
|
473
|
+
* @property {string|null} lookup.url - URL template with a `${filter}` placeholder to look up selected entries. Prefer a stable value-based lookup endpoint. This is used for initial hydration and for resolving labels of externally assigned selections that are not yet present in the local option map.
|
|
467
474
|
* @property {boolean} lookup.grouping - If `true`, all selected values are fetched in a single request; otherwise, a separate request is sent for each value.
|
|
468
475
|
* @property {Object} fetch - Configuration for HTTP requests via `fetch`.
|
|
469
476
|
* @property {string} fetch.redirect - Fetch redirect mode (e.g., "error", "follow").
|
|
@@ -472,7 +479,7 @@ class Select extends CustomControl {
|
|
|
472
479
|
* @property {string} fetch.credentials - Credentials policy for fetch (e.g., "include", "same-origin").
|
|
473
480
|
* @property {Object.<string, string>} fetch.headers - HTTP headers to be sent with every request.
|
|
474
481
|
* @property {Object} labels - Text labels for various states and UI elements.
|
|
475
|
-
* @property {string} labels.cannot-be-loaded - Message displayed when options cannot be loaded.
|
|
482
|
+
* @property {string} labels.cannot-be-loaded - Message displayed when options cannot be loaded. Unresolved selected values keep their raw key instead of replacing it with this label.
|
|
476
483
|
* @property {string} labels.no-options-available - Message displayed when no static options are provided.
|
|
477
484
|
* @property {string} labels.click-to-load-options - Prompt to load options when `features.lazyLoad` is enabled.
|
|
478
485
|
* @property {string} labels.select-an-option - Placeholder text when no selection has been made.
|
|
@@ -495,10 +502,10 @@ class Select extends CustomControl {
|
|
|
495
502
|
* @property {Object} placeholder - Placeholder texts for input fields.
|
|
496
503
|
* @property {string} placeholder.filter - Placeholder text for the filter input field.
|
|
497
504
|
* @property {Object} filter - Configuration for the filtering functionality.
|
|
498
|
-
* @property {string|null} filter.defaultValue - Default filter value for remote requests. An empty string
|
|
505
|
+
* @property {string|null} filter.defaultValue - Default filter value for remote requests. In the current implementation only `undefined` or `null` suppress the request by inserting an internal disabled marker. An empty string `""` is still formatted into the URL and therefore does not prevent the request.
|
|
499
506
|
* @property {"options"|"remote"|"disabled"} filter.mode - Filter mode: `"options"` (client-side), `"remote"` (server-side, `lazyLoad` is ignored), or `"disabled"`.
|
|
500
507
|
* @property {"inline"|"popper"} filter.position - Position of the filter input: `"inline"` (inside the control) or `"popper"` (inside the dropdown).
|
|
501
|
-
* @property {string|null} filter.defaultOptionsUrl - URL to load
|
|
508
|
+
* @property {string|null} filter.defaultOptionsUrl - URL to load options when `filter.mode` is `"remote"` and no filter value has been entered. This is used as the empty-filter source both on open and after the filter is cleared again.
|
|
502
509
|
* @property {Object} filter.marker - Markers for embedding the filter value into the `url` for server-side filtering.
|
|
503
510
|
* @property {string} filter.marker.open - Opening marker (e.g., `{`).
|
|
504
511
|
* @property {string} filter.marker.close - Closing marker (e.g., `}`).
|
|
@@ -526,6 +533,7 @@ class Select extends CustomControl {
|
|
|
526
533
|
* @property {Function} formatter.selection - Callback `(value, option) => string` to format the display text of selected values.
|
|
527
534
|
* @property {Object} classes - CSS classes for various elements.
|
|
528
535
|
* @property {string} classes.badge - CSS class for selection badges.
|
|
536
|
+
* @property {string} classes.badgeUnresolved - CSS class used for selection badges whose value could not be hydrated. By default this swaps the badge into a warning style while keeping the raw key visible.
|
|
529
537
|
* @property {string} classes.statusOrRemoveBadge - CSS class for the status or remove badge.
|
|
530
538
|
* @property {string} classes.remoteInfo - CSS class for the remote info badge.
|
|
531
539
|
* @property {string} classes.noOptions - CSS class for the "no options" message.
|
|
@@ -607,6 +615,7 @@ class Select extends CustomControl {
|
|
|
607
615
|
|
|
608
616
|
classes: {
|
|
609
617
|
badge: "monster-badge-primary",
|
|
618
|
+
badgeUnresolved: "monster-badge-warning",
|
|
610
619
|
statusOrRemoveBadge: "empty",
|
|
611
620
|
remoteInfo: "monster-margin-start-4 monster-margin-top-4",
|
|
612
621
|
noOptions: "monster-margin-top-4 monster-margin-start-4",
|
|
@@ -663,6 +672,7 @@ class Select extends CustomControl {
|
|
|
663
672
|
// Clear the lookup cache
|
|
664
673
|
this[lookupCacheSymbol].clear();
|
|
665
674
|
this[lookupInProgressSymbol].clear();
|
|
675
|
+
this[unresolvedSelectionValuesSymbol].clear();
|
|
666
676
|
|
|
667
677
|
setSelection
|
|
668
678
|
.call(this, null)
|
|
@@ -684,7 +694,6 @@ class Select extends CustomControl {
|
|
|
684
694
|
resetErrorAttribute(this);
|
|
685
695
|
|
|
686
696
|
this[lazyLoadDoneSymbol] = false;
|
|
687
|
-
this[runLookupOnceSymbol] = false;
|
|
688
697
|
|
|
689
698
|
checkOptionState.call(this);
|
|
690
699
|
calcAndSetOptionsDimension.call(this);
|
|
@@ -735,7 +744,9 @@ class Select extends CustomControl {
|
|
|
735
744
|
|
|
736
745
|
if (self.getOption("url") !== null) {
|
|
737
746
|
if (lazyLoadFlag || remoteFilterFlag) {
|
|
738
|
-
|
|
747
|
+
if (self.getOption("lookup.url")) {
|
|
748
|
+
lookupSelection.call(self);
|
|
749
|
+
}
|
|
739
750
|
} else {
|
|
740
751
|
self
|
|
741
752
|
.fetch()
|
|
@@ -1758,9 +1769,12 @@ function getTranslations() {
|
|
|
1758
1769
|
}
|
|
1759
1770
|
|
|
1760
1771
|
/**
|
|
1761
|
-
*
|
|
1762
|
-
|
|
1763
|
-
|
|
1772
|
+
* Hydrates the current selection once the component becomes visible.
|
|
1773
|
+
*
|
|
1774
|
+
* This path is used for initial hydration when remote or lazy-loaded options
|
|
1775
|
+
* are not yet present locally. It is intentionally limited to `lookup.url`
|
|
1776
|
+
* and does not fall back to the main remote option source.
|
|
1777
|
+
*
|
|
1764
1778
|
* @private
|
|
1765
1779
|
*/
|
|
1766
1780
|
function lookupSelection() {
|
|
@@ -1799,7 +1813,10 @@ function runSelectionLookupWhenVisible(self) {
|
|
|
1799
1813
|
return;
|
|
1800
1814
|
}
|
|
1801
1815
|
|
|
1802
|
-
|
|
1816
|
+
const url = self.getOption("lookup.url");
|
|
1817
|
+
if (!url) {
|
|
1818
|
+
return;
|
|
1819
|
+
}
|
|
1803
1820
|
self[cleanupOptionsListSymbol] = false;
|
|
1804
1821
|
|
|
1805
1822
|
if (self.getOption("lookup.grouping") === true) {
|
|
@@ -1975,21 +1992,8 @@ function initOptionsFromArguments() {
|
|
|
1975
1992
|
* @private
|
|
1976
1993
|
*/
|
|
1977
1994
|
function attachResizeObserver() {
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
if (this[timerCallbackSymbol] instanceof DeadMansSwitch) {
|
|
1981
|
-
try {
|
|
1982
|
-
this[timerCallbackSymbol].touch();
|
|
1983
|
-
return;
|
|
1984
|
-
} catch (e) {
|
|
1985
|
-
delete this[timerCallbackSymbol];
|
|
1986
|
-
}
|
|
1987
|
-
}
|
|
1988
|
-
|
|
1989
|
-
this[timerCallbackSymbol] = new DeadMansSwitch(200, () => {
|
|
1990
|
-
updatePopper.call(this);
|
|
1991
|
-
delete this[timerCallbackSymbol];
|
|
1992
|
-
});
|
|
1995
|
+
this[resizeObserverSymbol] = new ResizeObserver(() => {
|
|
1996
|
+
scheduleResizeObserverPopperUpdate.call(this);
|
|
1993
1997
|
});
|
|
1994
1998
|
|
|
1995
1999
|
let parent = this.parentNode;
|
|
@@ -2000,6 +2004,70 @@ function attachResizeObserver() {
|
|
|
2000
2004
|
if (parent instanceof HTMLElement) {
|
|
2001
2005
|
this[resizeObserverSymbol].observe(parent);
|
|
2002
2006
|
}
|
|
2007
|
+
|
|
2008
|
+
for (const element of [
|
|
2009
|
+
this,
|
|
2010
|
+
this[controlElementSymbol],
|
|
2011
|
+
this[popperElementSymbol],
|
|
2012
|
+
this[popperFilterContainerElementSymbol],
|
|
2013
|
+
this[optionsElementSymbol],
|
|
2014
|
+
this[paginationElementSymbol],
|
|
2015
|
+
]) {
|
|
2016
|
+
if (element instanceof HTMLElement) {
|
|
2017
|
+
this[resizeObserverSymbol].observe(element);
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
|
|
2021
|
+
const boundaryElement = resolveClippingBoundaryElement(
|
|
2022
|
+
this[controlElementSymbol],
|
|
2023
|
+
this[popperElementSymbol],
|
|
2024
|
+
);
|
|
2025
|
+
if (boundaryElement instanceof HTMLElement) {
|
|
2026
|
+
this[resizeObserverSymbol].observe(boundaryElement);
|
|
2027
|
+
}
|
|
2028
|
+
|
|
2029
|
+
const viewport = getGlobal().visualViewport;
|
|
2030
|
+
const windowObject = getWindow();
|
|
2031
|
+
this[visualViewportResizeHandlerSymbol] = () => {
|
|
2032
|
+
updatePopper.call(this);
|
|
2033
|
+
};
|
|
2034
|
+
this[visualViewportScrollHandlerSymbol] = () => {
|
|
2035
|
+
updatePopper.call(this);
|
|
2036
|
+
};
|
|
2037
|
+
this[visibilityChangeHandlerSymbol] = () => {
|
|
2038
|
+
if (document.visibilityState === "visible") {
|
|
2039
|
+
updatePopper.call(this);
|
|
2040
|
+
}
|
|
2041
|
+
};
|
|
2042
|
+
this[windowResizeHandlerSymbol] = () => {
|
|
2043
|
+
updatePopper.call(this);
|
|
2044
|
+
};
|
|
2045
|
+
this[windowOrientationChangeHandlerSymbol] = () => {
|
|
2046
|
+
updatePopper.call(this);
|
|
2047
|
+
};
|
|
2048
|
+
|
|
2049
|
+
if (viewport) {
|
|
2050
|
+
viewport.addEventListener(
|
|
2051
|
+
"resize",
|
|
2052
|
+
this[visualViewportResizeHandlerSymbol],
|
|
2053
|
+
);
|
|
2054
|
+
viewport.addEventListener(
|
|
2055
|
+
"scroll",
|
|
2056
|
+
this[visualViewportScrollHandlerSymbol],
|
|
2057
|
+
);
|
|
2058
|
+
}
|
|
2059
|
+
|
|
2060
|
+
document.addEventListener(
|
|
2061
|
+
"visibilitychange",
|
|
2062
|
+
this[visibilityChangeHandlerSymbol],
|
|
2063
|
+
);
|
|
2064
|
+
if (windowObject?.addEventListener instanceof Function) {
|
|
2065
|
+
windowObject.addEventListener("resize", this[windowResizeHandlerSymbol]);
|
|
2066
|
+
windowObject.addEventListener(
|
|
2067
|
+
"orientationchange",
|
|
2068
|
+
this[windowOrientationChangeHandlerSymbol],
|
|
2069
|
+
);
|
|
2070
|
+
}
|
|
2003
2071
|
}
|
|
2004
2072
|
|
|
2005
2073
|
/**
|
|
@@ -2009,6 +2077,83 @@ function disconnectResizeObserver() {
|
|
|
2009
2077
|
if (this[resizeObserverSymbol] instanceof ResizeObserver) {
|
|
2010
2078
|
this[resizeObserverSymbol].disconnect();
|
|
2011
2079
|
}
|
|
2080
|
+
cancelScheduledResizeObserverPopperUpdate.call(this);
|
|
2081
|
+
|
|
2082
|
+
const viewport = getGlobal().visualViewport;
|
|
2083
|
+
if (
|
|
2084
|
+
viewport &&
|
|
2085
|
+
this[visualViewportResizeHandlerSymbol] instanceof Function
|
|
2086
|
+
) {
|
|
2087
|
+
viewport.removeEventListener(
|
|
2088
|
+
"resize",
|
|
2089
|
+
this[visualViewportResizeHandlerSymbol],
|
|
2090
|
+
);
|
|
2091
|
+
}
|
|
2092
|
+
if (
|
|
2093
|
+
viewport &&
|
|
2094
|
+
this[visualViewportScrollHandlerSymbol] instanceof Function
|
|
2095
|
+
) {
|
|
2096
|
+
viewport.removeEventListener(
|
|
2097
|
+
"scroll",
|
|
2098
|
+
this[visualViewportScrollHandlerSymbol],
|
|
2099
|
+
);
|
|
2100
|
+
}
|
|
2101
|
+
if (this[visibilityChangeHandlerSymbol] instanceof Function) {
|
|
2102
|
+
document.removeEventListener(
|
|
2103
|
+
"visibilitychange",
|
|
2104
|
+
this[visibilityChangeHandlerSymbol],
|
|
2105
|
+
);
|
|
2106
|
+
}
|
|
2107
|
+
const windowObject = getWindow();
|
|
2108
|
+
if (
|
|
2109
|
+
windowObject?.removeEventListener instanceof Function &&
|
|
2110
|
+
this[windowResizeHandlerSymbol] instanceof Function
|
|
2111
|
+
) {
|
|
2112
|
+
windowObject.removeEventListener("resize", this[windowResizeHandlerSymbol]);
|
|
2113
|
+
}
|
|
2114
|
+
if (
|
|
2115
|
+
windowObject?.removeEventListener instanceof Function &&
|
|
2116
|
+
this[windowOrientationChangeHandlerSymbol] instanceof Function
|
|
2117
|
+
) {
|
|
2118
|
+
windowObject.removeEventListener(
|
|
2119
|
+
"orientationchange",
|
|
2120
|
+
this[windowOrientationChangeHandlerSymbol],
|
|
2121
|
+
);
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
|
|
2125
|
+
function scheduleResizeObserverPopperUpdate() {
|
|
2126
|
+
const globalObject = getGlobal();
|
|
2127
|
+
if (typeof this[resizeObserverFrameSymbol] === "number") {
|
|
2128
|
+
return;
|
|
2129
|
+
}
|
|
2130
|
+
|
|
2131
|
+
const schedule =
|
|
2132
|
+
globalObject?.requestAnimationFrame instanceof Function
|
|
2133
|
+
? globalObject.requestAnimationFrame.bind(globalObject)
|
|
2134
|
+
: (callback) => {
|
|
2135
|
+
return globalObject.setTimeout(callback, 16);
|
|
2136
|
+
};
|
|
2137
|
+
|
|
2138
|
+
this[resizeObserverFrameSymbol] = schedule(() => {
|
|
2139
|
+
delete this[resizeObserverFrameSymbol];
|
|
2140
|
+
updatePopper.call(this);
|
|
2141
|
+
});
|
|
2142
|
+
}
|
|
2143
|
+
|
|
2144
|
+
function cancelScheduledResizeObserverPopperUpdate() {
|
|
2145
|
+
const globalObject = getGlobal();
|
|
2146
|
+
const frameId = this[resizeObserverFrameSymbol];
|
|
2147
|
+
if (typeof frameId !== "number") {
|
|
2148
|
+
return;
|
|
2149
|
+
}
|
|
2150
|
+
|
|
2151
|
+
if (globalObject?.cancelAnimationFrame instanceof Function) {
|
|
2152
|
+
globalObject.cancelAnimationFrame(frameId);
|
|
2153
|
+
} else {
|
|
2154
|
+
globalObject.clearTimeout(frameId);
|
|
2155
|
+
}
|
|
2156
|
+
delete this[resizeObserverFrameSymbol];
|
|
2012
2157
|
}
|
|
2013
2158
|
|
|
2014
2159
|
/**
|
|
@@ -2091,22 +2236,120 @@ function parseSlotsToOptions() {
|
|
|
2091
2236
|
* @return {*}
|
|
2092
2237
|
*/
|
|
2093
2238
|
function buildSelectionLabel(value) {
|
|
2094
|
-
// First, check the lookup cache.
|
|
2095
|
-
if (this[lookupCacheSymbol].has(value)) {
|
|
2096
|
-
return this[lookupCacheSymbol].get(value);
|
|
2097
|
-
}
|
|
2098
|
-
|
|
2099
2239
|
const strict = this.getOption("features.useStrictValueComparison") === true;
|
|
2100
2240
|
const map = this[optionsMapSymbol];
|
|
2101
2241
|
const key = strict ? value : String(value);
|
|
2102
2242
|
|
|
2103
2243
|
if (map && map.has(key)) {
|
|
2244
|
+
if (clearUnresolvedSelectionValue.call(this, value)) {
|
|
2245
|
+
this[lookupCacheSymbol].delete(value);
|
|
2246
|
+
}
|
|
2104
2247
|
return map.get(key);
|
|
2105
2248
|
}
|
|
2106
2249
|
|
|
2250
|
+
if (this[lookupCacheSymbol].has(value)) {
|
|
2251
|
+
return this[lookupCacheSymbol].get(value);
|
|
2252
|
+
}
|
|
2253
|
+
|
|
2107
2254
|
return undefined;
|
|
2108
2255
|
}
|
|
2109
2256
|
|
|
2257
|
+
/**
|
|
2258
|
+
* @private
|
|
2259
|
+
* @param {*} value
|
|
2260
|
+
* @returns {*}
|
|
2261
|
+
*/
|
|
2262
|
+
function getSelectionStateKey(value) {
|
|
2263
|
+
return this.getOption("features.useStrictValueComparison") === true
|
|
2264
|
+
? value
|
|
2265
|
+
: String(value);
|
|
2266
|
+
}
|
|
2267
|
+
|
|
2268
|
+
/**
|
|
2269
|
+
* @private
|
|
2270
|
+
* @param {*} value
|
|
2271
|
+
* @returns {boolean}
|
|
2272
|
+
*/
|
|
2273
|
+
function markSelectionAsUnresolved(value) {
|
|
2274
|
+
const key = getSelectionStateKey.call(this, value);
|
|
2275
|
+
if (this[unresolvedSelectionValuesSymbol].has(key)) {
|
|
2276
|
+
return false;
|
|
2277
|
+
}
|
|
2278
|
+
|
|
2279
|
+
this[unresolvedSelectionValuesSymbol].add(key);
|
|
2280
|
+
return true;
|
|
2281
|
+
}
|
|
2282
|
+
|
|
2283
|
+
/**
|
|
2284
|
+
* @private
|
|
2285
|
+
* @param {*} value
|
|
2286
|
+
* @returns {boolean}
|
|
2287
|
+
*/
|
|
2288
|
+
function clearUnresolvedSelectionValue(value) {
|
|
2289
|
+
const key = getSelectionStateKey.call(this, value);
|
|
2290
|
+
return this[unresolvedSelectionValuesSymbol].delete(key);
|
|
2291
|
+
}
|
|
2292
|
+
|
|
2293
|
+
/**
|
|
2294
|
+
* @private
|
|
2295
|
+
* @param {*} value
|
|
2296
|
+
* @returns {boolean}
|
|
2297
|
+
*/
|
|
2298
|
+
function isSelectionValueUnresolved(value) {
|
|
2299
|
+
const key = getSelectionStateKey.call(this, value);
|
|
2300
|
+
return this[unresolvedSelectionValuesSymbol].has(key);
|
|
2301
|
+
}
|
|
2302
|
+
|
|
2303
|
+
/**
|
|
2304
|
+
* @private
|
|
2305
|
+
* @param {*} value
|
|
2306
|
+
* @returns {string}
|
|
2307
|
+
*/
|
|
2308
|
+
function getSelectionBadgeClass(value) {
|
|
2309
|
+
const classes = new TokenList(this.getOption("classes.badge"));
|
|
2310
|
+
if (!isSelectionValueUnresolved.call(this, value)) {
|
|
2311
|
+
return classes.toString();
|
|
2312
|
+
}
|
|
2313
|
+
|
|
2314
|
+
const unresolvedClasses = new TokenList(
|
|
2315
|
+
this.getOption("classes.badgeUnresolved"),
|
|
2316
|
+
);
|
|
2317
|
+
for (const token of classes.entries()) {
|
|
2318
|
+
if (token.startsWith("monster-badge-")) {
|
|
2319
|
+
classes.remove(token);
|
|
2320
|
+
}
|
|
2321
|
+
}
|
|
2322
|
+
|
|
2323
|
+
classes.add(unresolvedClasses.entries());
|
|
2324
|
+
return classes.toString();
|
|
2325
|
+
}
|
|
2326
|
+
|
|
2327
|
+
/**
|
|
2328
|
+
* @private
|
|
2329
|
+
* @param {*} value
|
|
2330
|
+
* @param {string} [preferredLabel]
|
|
2331
|
+
* @returns {{class: string, label: string, unresolved: boolean, value: *}}
|
|
2332
|
+
*/
|
|
2333
|
+
function buildSelectionItem(value, preferredLabel) {
|
|
2334
|
+
let label = getSelectionLabel.call(this, value);
|
|
2335
|
+
const unresolved = isSelectionValueUnresolved.call(this, value);
|
|
2336
|
+
if (
|
|
2337
|
+
!unresolved &&
|
|
2338
|
+
`${label}` === `${value}` &&
|
|
2339
|
+
isString(preferredLabel) &&
|
|
2340
|
+
preferredLabel.length > 0
|
|
2341
|
+
) {
|
|
2342
|
+
label = preferredLabel;
|
|
2343
|
+
}
|
|
2344
|
+
|
|
2345
|
+
return {
|
|
2346
|
+
label,
|
|
2347
|
+
value,
|
|
2348
|
+
class: getSelectionBadgeClass.call(this, value),
|
|
2349
|
+
unresolved,
|
|
2350
|
+
};
|
|
2351
|
+
}
|
|
2352
|
+
|
|
2110
2353
|
/**
|
|
2111
2354
|
* @private
|
|
2112
2355
|
* @param {string} value The value to look up.
|
|
@@ -2132,6 +2375,7 @@ async function lookupValueAndCache(value) {
|
|
|
2132
2375
|
|
|
2133
2376
|
let hasError = false;
|
|
2134
2377
|
let found = false;
|
|
2378
|
+
let refreshSelection = false;
|
|
2135
2379
|
try {
|
|
2136
2380
|
this[lookupInProgressSymbol].set(value, true);
|
|
2137
2381
|
|
|
@@ -2163,28 +2407,47 @@ async function lookupValueAndCache(value) {
|
|
|
2163
2407
|
}
|
|
2164
2408
|
|
|
2165
2409
|
// The lookup might return more than the requested value, so we cache all of them.
|
|
2166
|
-
if (
|
|
2410
|
+
if (this[lookupCacheSymbol].get(itemValue) !== itemLabel) {
|
|
2167
2411
|
this[lookupCacheSymbol].set(itemValue, itemLabel);
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2412
|
+
refreshSelection = true;
|
|
2413
|
+
}
|
|
2414
|
+
|
|
2415
|
+
if (clearUnresolvedSelectionValue.call(this, itemValue)) {
|
|
2416
|
+
refreshSelection = true;
|
|
2417
|
+
}
|
|
2418
|
+
|
|
2419
|
+
if (`${itemValue}` === `${value}`) {
|
|
2420
|
+
found = true;
|
|
2171
2421
|
}
|
|
2172
2422
|
}
|
|
2173
2423
|
|
|
2174
2424
|
if (!found && !this[lookupCacheSymbol].has(value)) {
|
|
2175
|
-
|
|
2176
|
-
|
|
2425
|
+
this[lookupCacheSymbol].set(value, `${value}`);
|
|
2426
|
+
refreshSelection = true;
|
|
2177
2427
|
}
|
|
2178
2428
|
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
await setSelection.call(this, this.getOption("selection"));
|
|
2429
|
+
if (!found && markSelectionAsUnresolved.call(this, value)) {
|
|
2430
|
+
refreshSelection = true;
|
|
2182
2431
|
}
|
|
2183
2432
|
} catch (e) {
|
|
2184
2433
|
hasError = true;
|
|
2185
2434
|
addErrorAttribute(this, e);
|
|
2435
|
+
|
|
2436
|
+
if (!this[lookupCacheSymbol].has(value)) {
|
|
2437
|
+
this[lookupCacheSymbol].set(value, `${value}`);
|
|
2438
|
+
refreshSelection = true;
|
|
2439
|
+
}
|
|
2440
|
+
|
|
2441
|
+
if (markSelectionAsUnresolved.call(this, value)) {
|
|
2442
|
+
refreshSelection = true;
|
|
2443
|
+
}
|
|
2186
2444
|
} finally {
|
|
2187
2445
|
this[lookupInProgressSymbol].delete(value);
|
|
2446
|
+
|
|
2447
|
+
if (refreshSelection) {
|
|
2448
|
+
await setSelection.call(this, this.getOption("selection"));
|
|
2449
|
+
}
|
|
2450
|
+
|
|
2188
2451
|
if (hasError) {
|
|
2189
2452
|
setStatusOrRemoveBadges.call(this, "error");
|
|
2190
2453
|
} else {
|
|
@@ -2468,10 +2731,9 @@ function calcAndSetOptionsDimension() {
|
|
|
2468
2731
|
}
|
|
2469
2732
|
|
|
2470
2733
|
let visible = 0;
|
|
2471
|
-
|
|
2734
|
+
const visibleOptionHeights = [];
|
|
2472
2735
|
const max = this.getOption("showMaxOptions", 10);
|
|
2473
2736
|
|
|
2474
|
-
let scrollFlag = false;
|
|
2475
2737
|
for (const [, option] of Object.entries(options)) {
|
|
2476
2738
|
const computedStyle = getGlobal().getComputedStyle(option);
|
|
2477
2739
|
if (computedStyle.display === "none") continue;
|
|
@@ -2479,18 +2741,8 @@ function calcAndSetOptionsDimension() {
|
|
|
2479
2741
|
let h = option.getBoundingClientRect().height;
|
|
2480
2742
|
h += parseInt(computedStyle.getPropertyValue("margin-top"), 10);
|
|
2481
2743
|
h += parseInt(computedStyle.getPropertyValue("margin-bottom"), 10);
|
|
2482
|
-
|
|
2483
|
-
|
|
2744
|
+
visibleOptionHeights.push(h);
|
|
2484
2745
|
visible++;
|
|
2485
|
-
|
|
2486
|
-
if (visible > max) {
|
|
2487
|
-
break;
|
|
2488
|
-
}
|
|
2489
|
-
}
|
|
2490
|
-
|
|
2491
|
-
if (visible > max) {
|
|
2492
|
-
visible = max;
|
|
2493
|
-
scrollFlag = true;
|
|
2494
2746
|
}
|
|
2495
2747
|
|
|
2496
2748
|
if (visible === 0) {
|
|
@@ -2535,61 +2787,310 @@ function calcAndSetOptionsDimension() {
|
|
|
2535
2787
|
let margin = parseInt(styles.getPropertyValue("margin-top"), 10);
|
|
2536
2788
|
margin += parseInt(styles.getPropertyValue("margin-bottom"), 10);
|
|
2537
2789
|
|
|
2538
|
-
|
|
2539
|
-
const
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2790
|
+
const geometry = getSelectPopperGeometry.call(this);
|
|
2791
|
+
const listDimension = resolveSelectListDimension({
|
|
2792
|
+
visibleOptionHeights,
|
|
2793
|
+
maxVisibleOptions: max,
|
|
2794
|
+
availableHeight: geometry.availableHeight,
|
|
2795
|
+
padding,
|
|
2796
|
+
margin,
|
|
2797
|
+
});
|
|
2798
|
+
const widthConstraints = resolveSelectPopperWidthConstraints({
|
|
2799
|
+
controlWidth: this[controlElementSymbol].getBoundingClientRect().width,
|
|
2800
|
+
availableWidth: geometry.availableWidth,
|
|
2801
|
+
});
|
|
2543
2802
|
|
|
2544
|
-
container.style.height =
|
|
2803
|
+
container.style.height =
|
|
2804
|
+
listDimension.desiredHeight > 0 ? `${listDimension.desiredHeight}px` : "0px";
|
|
2805
|
+
container.style.maxHeight =
|
|
2806
|
+
listDimension.maxHeight > 0 ? `${listDimension.maxHeight}px` : "0px";
|
|
2807
|
+
container.style.overflowY = listDimension.overflowY;
|
|
2545
2808
|
|
|
2546
|
-
|
|
2547
|
-
|
|
2809
|
+
this[popperElementSymbol].dataset.monsterPreferredWidth = `${Math.ceil(
|
|
2810
|
+
widthConstraints.preferredWidth,
|
|
2811
|
+
)}`;
|
|
2812
|
+
if (
|
|
2813
|
+
Number.isFinite(widthConstraints.maxWidth) &&
|
|
2814
|
+
widthConstraints.maxWidth > 0
|
|
2815
|
+
) {
|
|
2816
|
+
this[popperElementSymbol].dataset.monsterPreferredMaxWidth = `${Math.ceil(
|
|
2817
|
+
widthConstraints.maxWidth,
|
|
2818
|
+
)}`;
|
|
2819
|
+
this[popperElementSymbol].style.maxWidth = `${Math.ceil(
|
|
2820
|
+
widthConstraints.maxWidth,
|
|
2821
|
+
)}px`;
|
|
2548
2822
|
} else {
|
|
2549
|
-
|
|
2823
|
+
delete this[popperElementSymbol].dataset.monsterPreferredMaxWidth;
|
|
2824
|
+
this[popperElementSymbol].style.removeProperty("maxWidth");
|
|
2550
2825
|
}
|
|
2826
|
+
this[popperElementSymbol].style.width = "";
|
|
2827
|
+
this[popperElementSymbol].style.removeProperty("minWidth");
|
|
2828
|
+
this[popperElementSymbol].style.maxHeight = `${Math.ceil(
|
|
2829
|
+
Math.min(geometry.availableHeight, SELECT_MAX_POPPER_HEIGHT),
|
|
2830
|
+
)}px`;
|
|
2831
|
+
container.style.overflowX = "hidden";
|
|
2551
2832
|
|
|
2552
|
-
|
|
2833
|
+
if (content instanceof HTMLElement) {
|
|
2834
|
+
content.style.overflow = "hidden";
|
|
2835
|
+
content.style.maxHeight =
|
|
2836
|
+
listDimension.maxHeight > 0 ? `${listDimension.maxHeight}px` : "";
|
|
2837
|
+
}
|
|
2838
|
+
}
|
|
2553
2839
|
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
this[popperElementSymbol].style.width = `${domRect.width}px`;
|
|
2558
|
-
this[popperElementSymbol].style.minWidth = `${domRect.width}px`;
|
|
2559
|
-
container.style.overflowX = "auto";
|
|
2840
|
+
function resolveSelectPopperPreferredWidth(controlWidth) {
|
|
2841
|
+
return Math.max(Math.ceil(controlWidth), SELECT_MIN_POPPER_WIDTH);
|
|
2842
|
+
}
|
|
2560
2843
|
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2844
|
+
function resolveSelectPopperWidthConstraints({
|
|
2845
|
+
controlWidth = 0,
|
|
2846
|
+
availableWidth = 0,
|
|
2847
|
+
}) {
|
|
2848
|
+
const preferredWidth = resolveSelectPopperPreferredWidth(controlWidth);
|
|
2849
|
+
const maxWidth = Math.max(0, availableWidth);
|
|
2850
|
+
|
|
2851
|
+
return {
|
|
2852
|
+
preferredWidth,
|
|
2853
|
+
maxWidth: maxWidth > 0 ? maxWidth : Infinity,
|
|
2854
|
+
};
|
|
2855
|
+
}
|
|
2856
|
+
|
|
2857
|
+
function resolveSelectListDimension({
|
|
2858
|
+
visibleOptionHeights = [],
|
|
2859
|
+
maxVisibleOptions = 10,
|
|
2860
|
+
availableHeight,
|
|
2861
|
+
padding = 0,
|
|
2862
|
+
margin = 0,
|
|
2863
|
+
}) {
|
|
2864
|
+
const visibleHeights = visibleOptionHeights.filter((height) => {
|
|
2865
|
+
return Number.isFinite(height) && height > 0;
|
|
2866
|
+
});
|
|
2867
|
+
const naturalListHeight =
|
|
2868
|
+
visibleHeights
|
|
2869
|
+
.slice(0, Math.max(0, maxVisibleOptions))
|
|
2870
|
+
.reduce((sum, height) => {
|
|
2871
|
+
return sum + height;
|
|
2872
|
+
}, 0) +
|
|
2873
|
+
padding +
|
|
2874
|
+
margin;
|
|
2875
|
+
const fullListHeight =
|
|
2876
|
+
visibleHeights.reduce((sum, height) => {
|
|
2877
|
+
return sum + height;
|
|
2878
|
+
}, 0) +
|
|
2879
|
+
padding +
|
|
2880
|
+
margin;
|
|
2881
|
+
const maxHeight = resolveSelectListMaxHeight({
|
|
2882
|
+
availableHeight,
|
|
2883
|
+
});
|
|
2884
|
+
const desiredHeight = Math.min(
|
|
2885
|
+
maxHeight,
|
|
2886
|
+
fullListHeight > 0 ? naturalListHeight : 0,
|
|
2887
|
+
);
|
|
2888
|
+
|
|
2889
|
+
return {
|
|
2890
|
+
desiredHeight,
|
|
2891
|
+
fullHeight: fullListHeight,
|
|
2892
|
+
maxHeight,
|
|
2893
|
+
overflowY: fullListHeight > desiredHeight ? "auto" : "hidden",
|
|
2894
|
+
};
|
|
2895
|
+
}
|
|
2896
|
+
|
|
2897
|
+
function resolveSelectListMaxHeight(geometry) {
|
|
2898
|
+
if (!geometry || !Number.isFinite(geometry.availableHeight)) {
|
|
2899
|
+
return SELECT_MAX_POPPER_HEIGHT;
|
|
2564
2900
|
}
|
|
2901
|
+
|
|
2902
|
+
return Math.max(
|
|
2903
|
+
0,
|
|
2904
|
+
Math.min(geometry.availableHeight, SELECT_MAX_POPPER_HEIGHT),
|
|
2905
|
+
);
|
|
2565
2906
|
}
|
|
2566
2907
|
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2908
|
+
function getViewportMetrics() {
|
|
2909
|
+
const globalObject = getGlobal();
|
|
2910
|
+
const documentElement = globalObject.document?.documentElement;
|
|
2911
|
+
const viewport = globalObject.visualViewport;
|
|
2912
|
+
const metrics = resolveSelectViewportMetrics({
|
|
2913
|
+
layoutWidth: Math.max(
|
|
2914
|
+
documentElement?.clientWidth || 0,
|
|
2915
|
+
globalObject.innerWidth || 0,
|
|
2916
|
+
),
|
|
2917
|
+
layoutHeight: Math.max(
|
|
2918
|
+
documentElement?.clientHeight || 0,
|
|
2919
|
+
globalObject.innerHeight || 0,
|
|
2920
|
+
),
|
|
2921
|
+
visualWidth: viewport?.width || 0,
|
|
2922
|
+
visualHeight: viewport?.height || 0,
|
|
2923
|
+
offsetLeft: viewport?.offsetLeft || 0,
|
|
2924
|
+
offsetTop: viewport?.offsetTop || 0,
|
|
2925
|
+
padding: SELECT_VIEWPORT_PADDING,
|
|
2926
|
+
});
|
|
2573
2927
|
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2928
|
+
return {
|
|
2929
|
+
width: metrics.width,
|
|
2930
|
+
height: metrics.height,
|
|
2931
|
+
left: metrics.left,
|
|
2932
|
+
top: metrics.top,
|
|
2933
|
+
padding: metrics.padding,
|
|
2934
|
+
};
|
|
2935
|
+
}
|
|
2577
2936
|
|
|
2578
|
-
|
|
2937
|
+
function resolveSelectViewportMetrics({
|
|
2938
|
+
layoutWidth = 0,
|
|
2939
|
+
layoutHeight = 0,
|
|
2940
|
+
visualWidth = 0,
|
|
2941
|
+
visualHeight = 0,
|
|
2942
|
+
offsetLeft = 0,
|
|
2943
|
+
offsetTop = 0,
|
|
2944
|
+
padding = SELECT_VIEWPORT_PADDING,
|
|
2945
|
+
}) {
|
|
2946
|
+
return {
|
|
2947
|
+
width: Math.max(layoutWidth, visualWidth),
|
|
2948
|
+
height: Math.max(layoutHeight, visualHeight),
|
|
2949
|
+
left: offsetLeft,
|
|
2950
|
+
top: offsetTop,
|
|
2951
|
+
padding,
|
|
2952
|
+
};
|
|
2953
|
+
}
|
|
2579
2954
|
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2955
|
+
function resolveSelectVisibleRect({
|
|
2956
|
+
viewportMetrics,
|
|
2957
|
+
boundaryRect = null,
|
|
2958
|
+
}) {
|
|
2959
|
+
const viewportLeft = (viewportMetrics?.left || 0) + (viewportMetrics?.padding || 0);
|
|
2960
|
+
const viewportTop = (viewportMetrics?.top || 0) + (viewportMetrics?.padding || 0);
|
|
2961
|
+
const viewportRight =
|
|
2962
|
+
(viewportMetrics?.left || 0) +
|
|
2963
|
+
(viewportMetrics?.width || 0) -
|
|
2964
|
+
(viewportMetrics?.padding || 0);
|
|
2965
|
+
const viewportBottom =
|
|
2966
|
+
(viewportMetrics?.top || 0) +
|
|
2967
|
+
(viewportMetrics?.height || 0) -
|
|
2968
|
+
(viewportMetrics?.padding || 0);
|
|
2969
|
+
|
|
2970
|
+
const left = boundaryRect
|
|
2971
|
+
? Math.max(viewportLeft, boundaryRect.left)
|
|
2972
|
+
: viewportLeft;
|
|
2973
|
+
const top = boundaryRect
|
|
2974
|
+
? Math.max(viewportTop, boundaryRect.top)
|
|
2975
|
+
: viewportTop;
|
|
2976
|
+
const right = boundaryRect
|
|
2977
|
+
? Math.min(viewportRight, boundaryRect.right)
|
|
2978
|
+
: viewportRight;
|
|
2979
|
+
const bottom = boundaryRect
|
|
2980
|
+
? Math.min(viewportBottom, boundaryRect.bottom)
|
|
2981
|
+
: viewportBottom;
|
|
2583
2982
|
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2983
|
+
return {
|
|
2984
|
+
left,
|
|
2985
|
+
top,
|
|
2986
|
+
right,
|
|
2987
|
+
bottom,
|
|
2988
|
+
width: Math.max(0, right - left),
|
|
2989
|
+
height: Math.max(0, bottom - top),
|
|
2990
|
+
};
|
|
2991
|
+
}
|
|
2992
|
+
|
|
2993
|
+
function getSelectPopperGeometry() {
|
|
2994
|
+
const viewport = getViewportMetrics();
|
|
2995
|
+
const boundaryElement = resolveClippingBoundaryElement(
|
|
2996
|
+
this[controlElementSymbol],
|
|
2997
|
+
this[popperElementSymbol],
|
|
2998
|
+
);
|
|
2999
|
+
const controlRect = this[controlElementSymbol].getBoundingClientRect();
|
|
3000
|
+
const boundaryRect =
|
|
3001
|
+
boundaryElement instanceof HTMLElement
|
|
3002
|
+
? boundaryElement.getBoundingClientRect()
|
|
3003
|
+
: null;
|
|
3004
|
+
const visibleRect = resolveSelectVisibleRect({
|
|
3005
|
+
viewportMetrics: viewport,
|
|
3006
|
+
boundaryRect,
|
|
3007
|
+
});
|
|
3008
|
+
const spaceAbove = Math.max(
|
|
3009
|
+
0,
|
|
3010
|
+
controlRect.top - visibleRect.top,
|
|
3011
|
+
);
|
|
3012
|
+
const spaceBelow = Math.max(
|
|
3013
|
+
0,
|
|
3014
|
+
visibleRect.bottom - controlRect.bottom,
|
|
3015
|
+
);
|
|
3016
|
+
const availableHeight = Math.max(spaceAbove, spaceBelow, 0);
|
|
3017
|
+
|
|
3018
|
+
return {
|
|
3019
|
+
spaceAbove,
|
|
3020
|
+
spaceBelow,
|
|
3021
|
+
availableHeight,
|
|
3022
|
+
availableWidth: visibleRect.width,
|
|
3023
|
+
boundaryElement,
|
|
3024
|
+
};
|
|
3025
|
+
}
|
|
3026
|
+
|
|
3027
|
+
function getSelectPopperPositionOptions() {
|
|
3028
|
+
const popperOptions = Object.assign({}, this.getOption("popper", {}));
|
|
3029
|
+
const middleware = popperOptions.middleware;
|
|
3030
|
+
const usesLegacyDefaultMiddleware =
|
|
3031
|
+
isArray(middleware) &&
|
|
3032
|
+
middleware.length === 2 &&
|
|
3033
|
+
middleware[0] === "flip" &&
|
|
3034
|
+
middleware[1] === "offset:1";
|
|
3035
|
+
|
|
3036
|
+
if (
|
|
3037
|
+
!isArray(middleware) ||
|
|
3038
|
+
middleware.length === 0 ||
|
|
3039
|
+
usesLegacyDefaultMiddleware
|
|
3040
|
+
) {
|
|
3041
|
+
popperOptions.middleware = getDefaultSelectPopperPositionProfile().middleware;
|
|
3042
|
+
}
|
|
3043
|
+
|
|
3044
|
+
if (
|
|
3045
|
+
!isString(popperOptions.placement) ||
|
|
3046
|
+
popperOptions.placement === "bottom"
|
|
3047
|
+
) {
|
|
3048
|
+
popperOptions.placement =
|
|
3049
|
+
getDefaultSelectPopperPositionProfile().placement;
|
|
3050
|
+
}
|
|
3051
|
+
|
|
3052
|
+
if (
|
|
3053
|
+
resolveParentPopperContentBoundary(
|
|
3054
|
+
this[controlElementSymbol],
|
|
3055
|
+
this[popperElementSymbol],
|
|
3056
|
+
)
|
|
3057
|
+
) {
|
|
3058
|
+
// Nested selects inside another popper must position against the viewport
|
|
3059
|
+
// so the parent content wrapper does not become their clipping boundary.
|
|
3060
|
+
popperOptions.strategy = "fixed";
|
|
3061
|
+
}
|
|
3062
|
+
|
|
3063
|
+
return popperOptions;
|
|
3064
|
+
}
|
|
3065
|
+
|
|
3066
|
+
function getDefaultSelectPopperPositionProfile() {
|
|
3067
|
+
return {
|
|
3068
|
+
placement: "bottom-start",
|
|
3069
|
+
middleware: ["flip", "offset:4", "shift:crossAxis", "size"],
|
|
3070
|
+
};
|
|
3071
|
+
}
|
|
3072
|
+
|
|
3073
|
+
function resetSelectPopperDimensionStyles() {
|
|
3074
|
+
if (!(this[popperElementSymbol] instanceof HTMLElement)) {
|
|
3075
|
+
return;
|
|
3076
|
+
}
|
|
3077
|
+
|
|
3078
|
+
delete this[popperElementSymbol].dataset.monsterPreferredWidth;
|
|
3079
|
+
delete this[popperElementSymbol].dataset.monsterPreferredMaxWidth;
|
|
3080
|
+
this[popperElementSymbol].style.removeProperty("width");
|
|
3081
|
+
this[popperElementSymbol].style.removeProperty("minWidth");
|
|
3082
|
+
this[popperElementSymbol].style.removeProperty("maxWidth");
|
|
3083
|
+
this[popperElementSymbol].style.removeProperty("maxHeight");
|
|
3084
|
+
|
|
3085
|
+
const content = this[popperElementSymbol].querySelector('[part="content"]');
|
|
3086
|
+
if (content instanceof HTMLElement) {
|
|
3087
|
+
content.style.removeProperty("maxHeight");
|
|
2590
3088
|
}
|
|
2591
3089
|
|
|
2592
|
-
|
|
3090
|
+
if (this[optionsElementSymbol] instanceof HTMLElement) {
|
|
3091
|
+
this[optionsElementSymbol].style.removeProperty("height");
|
|
3092
|
+
this[optionsElementSymbol].style.removeProperty("maxHeight");
|
|
3093
|
+
}
|
|
2593
3094
|
}
|
|
2594
3095
|
|
|
2595
3096
|
/**
|
|
@@ -2805,12 +3306,6 @@ function filterFromRemote() {
|
|
|
2805
3306
|
return Promise.reject(new Error("Missing Filter Element."));
|
|
2806
3307
|
}
|
|
2807
3308
|
|
|
2808
|
-
const url = this.getOption("url");
|
|
2809
|
-
if (!url) {
|
|
2810
|
-
addErrorAttribute(this, "Missing URL for Remote Filter.");
|
|
2811
|
-
return Promise.reject(new Error("Missing URL for Remote Filter."));
|
|
2812
|
-
}
|
|
2813
|
-
|
|
2814
3309
|
let filterValue;
|
|
2815
3310
|
let showFlag = false;
|
|
2816
3311
|
|
|
@@ -2834,6 +3329,16 @@ function filterFromRemote() {
|
|
|
2834
3329
|
page: this[currentPageSymbol] || 1,
|
|
2835
3330
|
};
|
|
2836
3331
|
|
|
3332
|
+
if (shouldUseDefaultOptionsUrl.call(this, filterValue)) {
|
|
3333
|
+
return loadDefaultOptionsFromUrl.call(this, showFlag);
|
|
3334
|
+
}
|
|
3335
|
+
|
|
3336
|
+
const url = this.getOption("url");
|
|
3337
|
+
if (!url) {
|
|
3338
|
+
addErrorAttribute(this, "Missing URL for Remote Filter.");
|
|
3339
|
+
return Promise.reject(new Error("Missing URL for Remote Filter."));
|
|
3340
|
+
}
|
|
3341
|
+
|
|
2837
3342
|
return filterFromRemoteByValue.call(this, url, params, showFlag);
|
|
2838
3343
|
}
|
|
2839
3344
|
|
|
@@ -2843,7 +3348,8 @@ function filterFromRemote() {
|
|
|
2843
3348
|
* @param {object} params
|
|
2844
3349
|
* @returns {string}
|
|
2845
3350
|
*/
|
|
2846
|
-
function formatURL(url, params = {}) {
|
|
3351
|
+
function formatURL(url, params = {}, formatOptions = {}) {
|
|
3352
|
+
const preserveEmptyFilter = formatOptions?.preserveEmptyFilter === true;
|
|
2847
3353
|
const paramsDefaults = this.getOption("filter.paramsDefaults");
|
|
2848
3354
|
const externalParams = this.getOption("filter.params");
|
|
2849
3355
|
if (isObject(paramsDefaults) || isObject(externalParams)) {
|
|
@@ -2871,7 +3377,7 @@ function formatURL(url, params = {}) {
|
|
|
2871
3377
|
if (
|
|
2872
3378
|
params.filter === undefined ||
|
|
2873
3379
|
params.filter === null ||
|
|
2874
|
-
params.filter === ""
|
|
3380
|
+
(params.filter === "" && preserveEmptyFilter !== true)
|
|
2875
3381
|
) {
|
|
2876
3382
|
const defaultValue = this.getOption("filter.defaultValue");
|
|
2877
3383
|
if (defaultValue === undefined || defaultValue === null) {
|
|
@@ -2911,9 +3417,14 @@ function formatURL(url, params = {}) {
|
|
|
2911
3417
|
* @param {boolean} [openPopper] Flag indicating whether to open the popper.
|
|
2912
3418
|
* @return {string} The formatted URL with the applied filters and markers.
|
|
2913
3419
|
*/
|
|
2914
|
-
function filterFromRemoteByValue(
|
|
3420
|
+
function filterFromRemoteByValue(
|
|
3421
|
+
optionUrl,
|
|
3422
|
+
params,
|
|
3423
|
+
openPopper,
|
|
3424
|
+
formatOptions = {},
|
|
3425
|
+
) {
|
|
2915
3426
|
return new Processing(() => {
|
|
2916
|
-
let url = formatURL.call(this, optionUrl, params);
|
|
3427
|
+
let url = formatURL.call(this, optionUrl, params, formatOptions);
|
|
2917
3428
|
|
|
2918
3429
|
if (url.indexOf(disabledRequestMarker.toString()) !== -1) {
|
|
2919
3430
|
this.setOption("total", null);
|
|
@@ -3027,6 +3538,51 @@ function getCurrentFilterValue() {
|
|
|
3027
3538
|
return "";
|
|
3028
3539
|
}
|
|
3029
3540
|
|
|
3541
|
+
function shouldUseDefaultOptionsUrl(filterValue) {
|
|
3542
|
+
if (!isString(this.getOption("filter.defaultOptionsUrl"))) {
|
|
3543
|
+
return false;
|
|
3544
|
+
}
|
|
3545
|
+
|
|
3546
|
+
if (filterValue === undefined || filterValue === null) {
|
|
3547
|
+
return true;
|
|
3548
|
+
}
|
|
3549
|
+
|
|
3550
|
+
if (isString(filterValue)) {
|
|
3551
|
+
return filterValue.trim() === "";
|
|
3552
|
+
}
|
|
3553
|
+
|
|
3554
|
+
return false;
|
|
3555
|
+
}
|
|
3556
|
+
|
|
3557
|
+
function loadDefaultOptionsFromUrl(openPopper = false) {
|
|
3558
|
+
const url = this.getOption("filter.defaultOptionsUrl");
|
|
3559
|
+
if (!isString(url)) {
|
|
3560
|
+
return Promise.resolve(false);
|
|
3561
|
+
}
|
|
3562
|
+
|
|
3563
|
+
this[cleanupOptionsListSymbol] = true;
|
|
3564
|
+
|
|
3565
|
+
return filterFromRemoteByValue
|
|
3566
|
+
.call(
|
|
3567
|
+
this,
|
|
3568
|
+
url,
|
|
3569
|
+
{
|
|
3570
|
+
filter: "",
|
|
3571
|
+
page: this[currentPageSymbol] || 1,
|
|
3572
|
+
},
|
|
3573
|
+
openPopper,
|
|
3574
|
+
{
|
|
3575
|
+
preserveEmptyFilter: true,
|
|
3576
|
+
},
|
|
3577
|
+
)
|
|
3578
|
+
.then(() => {
|
|
3579
|
+
if (isPositionedPopperOpen(this[popperElementSymbol])) {
|
|
3580
|
+
setStatusOrRemoveBadges.call(this, "open");
|
|
3581
|
+
}
|
|
3582
|
+
return true;
|
|
3583
|
+
});
|
|
3584
|
+
}
|
|
3585
|
+
|
|
3030
3586
|
/**
|
|
3031
3587
|
* @private
|
|
3032
3588
|
*/
|
|
@@ -3146,10 +3702,7 @@ function gatherState() {
|
|
|
3146
3702
|
);
|
|
3147
3703
|
|
|
3148
3704
|
for (const e of elements) {
|
|
3149
|
-
selection.push(
|
|
3150
|
-
label: getSelectionLabel.call(this, e.value),
|
|
3151
|
-
value: e.value,
|
|
3152
|
-
});
|
|
3705
|
+
selection.push(buildSelectionItem.call(this, e.value));
|
|
3153
3706
|
}
|
|
3154
3707
|
|
|
3155
3708
|
filteredSelection = selection;
|
|
@@ -3166,10 +3719,7 @@ function gatherState() {
|
|
|
3166
3719
|
(sel) => !currentInputValues.has(sel.value),
|
|
3167
3720
|
);
|
|
3168
3721
|
for (const input of checkedElements) {
|
|
3169
|
-
filteredSelection.push(
|
|
3170
|
-
label: getSelectionLabel.call(this, input.value),
|
|
3171
|
-
value: input.value,
|
|
3172
|
-
});
|
|
3722
|
+
filteredSelection.push(buildSelectionItem.call(this, input.value));
|
|
3173
3723
|
}
|
|
3174
3724
|
}
|
|
3175
3725
|
|
|
@@ -3448,16 +3998,10 @@ function convertValueToSelection(value) {
|
|
|
3448
3998
|
}
|
|
3449
3999
|
|
|
3450
4000
|
if (isString(value) || isInteger(value)) {
|
|
3451
|
-
selection.push(
|
|
3452
|
-
label: getSelectionLabel.call(this, value),
|
|
3453
|
-
value: value,
|
|
3454
|
-
});
|
|
4001
|
+
selection.push(buildSelectionItem.call(this, value));
|
|
3455
4002
|
} else if (isArray(value)) {
|
|
3456
4003
|
for (const v of value) {
|
|
3457
|
-
selection.push(
|
|
3458
|
-
label: getSelectionLabel.call(this, v),
|
|
3459
|
-
value: v,
|
|
3460
|
-
});
|
|
4004
|
+
selection.push(buildSelectionItem.call(this, v));
|
|
3461
4005
|
}
|
|
3462
4006
|
|
|
3463
4007
|
value = value.join(",");
|
|
@@ -3582,8 +4126,6 @@ function areSelectionValuesEqual(current, next) {
|
|
|
3582
4126
|
* @returns {Promise<unknown | void>}
|
|
3583
4127
|
*/
|
|
3584
4128
|
function setSelection(selection) {
|
|
3585
|
-
const self = this;
|
|
3586
|
-
|
|
3587
4129
|
if (isString(selection) || isInteger(selection)) {
|
|
3588
4130
|
const result = convertValueToSelection.call(this, selection);
|
|
3589
4131
|
selection = result?.selection;
|
|
@@ -3600,15 +4142,9 @@ function setSelection(selection) {
|
|
|
3600
4142
|
continue;
|
|
3601
4143
|
}
|
|
3602
4144
|
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
}
|
|
3607
|
-
|
|
3608
|
-
resultSelection.push({
|
|
3609
|
-
label: l,
|
|
3610
|
-
value: selection[i].value,
|
|
3611
|
-
});
|
|
4145
|
+
resultSelection.push(
|
|
4146
|
+
buildSelectionItem.call(this, selection[i].value, selection[i].label),
|
|
4147
|
+
);
|
|
3612
4148
|
}
|
|
3613
4149
|
|
|
3614
4150
|
selection = resultSelection;
|
|
@@ -3643,17 +4179,6 @@ function setSelection(selection) {
|
|
|
3643
4179
|
fireEvent(this, "change"); // https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/291
|
|
3644
4180
|
}
|
|
3645
4181
|
|
|
3646
|
-
if (this[runLookupOnceSymbol] !== true && selection.length > 0) {
|
|
3647
|
-
this[runLookupOnceSymbol] = true;
|
|
3648
|
-
|
|
3649
|
-
const lazyLoadFlag =
|
|
3650
|
-
this.getOption("features.lazyLoad") && this[lazyLoadDoneSymbol] !== true;
|
|
3651
|
-
const remoteFilterFlag = getFilterMode.call(this) === FILTER_MODE_REMOTE;
|
|
3652
|
-
if (lazyLoadFlag || remoteFilterFlag) {
|
|
3653
|
-
lookupSelection.call(self);
|
|
3654
|
-
}
|
|
3655
|
-
}
|
|
3656
|
-
|
|
3657
4182
|
return new Processing(() => {
|
|
3658
4183
|
const CLASSNAME = "selected";
|
|
3659
4184
|
|
|
@@ -3751,6 +4276,7 @@ function fetchData(url) {
|
|
|
3751
4276
|
* @private
|
|
3752
4277
|
*/
|
|
3753
4278
|
function hide() {
|
|
4279
|
+
resetSelectPopperDimensionStyles.call(this);
|
|
3754
4280
|
closePositionedPopper(this[popperElementSymbol]);
|
|
3755
4281
|
setStatusOrRemoveBadges.call(this, "closed");
|
|
3756
4282
|
removeAttributeToken(this[controlElementSymbol], "class", "open");
|
|
@@ -3804,12 +4330,6 @@ function show() {
|
|
|
3804
4330
|
return;
|
|
3805
4331
|
}
|
|
3806
4332
|
|
|
3807
|
-
const hasDefaultOptionsUrl = isString(
|
|
3808
|
-
this.getOption("filter.defaultOptionsUrl"),
|
|
3809
|
-
);
|
|
3810
|
-
|
|
3811
|
-
initDefaultOptionsFromUrl.call(this);
|
|
3812
|
-
|
|
3813
4333
|
const hasPopperFilterFlag =
|
|
3814
4334
|
this.getOption("filter.position") === FILTER_POSITION_POPPER &&
|
|
3815
4335
|
getFilterMode.call(this) !== FILTER_MODE_DISABLED;
|
|
@@ -3819,11 +4339,12 @@ function show() {
|
|
|
3819
4339
|
return;
|
|
3820
4340
|
}
|
|
3821
4341
|
|
|
4342
|
+
resetSelectPopperDimensionStyles.call(this);
|
|
3822
4343
|
this[popperElementSymbol].style.visibility = "hidden";
|
|
3823
4344
|
openPositionedPopper(
|
|
3824
4345
|
this[controlElementSymbol],
|
|
3825
4346
|
this[popperElementSymbol],
|
|
3826
|
-
|
|
4347
|
+
getSelectPopperPositionOptions.call(this),
|
|
3827
4348
|
);
|
|
3828
4349
|
setStatusOrRemoveBadges.call(this, "open");
|
|
3829
4350
|
|
|
@@ -3831,17 +4352,23 @@ function show() {
|
|
|
3831
4352
|
registerWithHost.call(this);
|
|
3832
4353
|
|
|
3833
4354
|
new Processing(() => {
|
|
3834
|
-
|
|
3835
|
-
self
|
|
4355
|
+
const shouldLoadRemoteOptions =
|
|
4356
|
+
getFilterMode.call(self) === FILTER_MODE_REMOTE &&
|
|
4357
|
+
getOptionElements.call(self).length === 0;
|
|
3836
4358
|
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
4359
|
+
if (shouldUseDefaultOptionsUrl.call(self, getCurrentFilterValue.call(self))) {
|
|
4360
|
+
setTimeout(() => {
|
|
4361
|
+
loadDefaultOptionsFromUrl.call(self).catch((e) => {
|
|
4362
|
+
addErrorAttribute(self, e);
|
|
4363
|
+
});
|
|
4364
|
+
}, 0);
|
|
4365
|
+
} else if (shouldLoadRemoteOptions) {
|
|
4366
|
+
setTimeout(() => {
|
|
4367
|
+
self[cleanupOptionsListSymbol] = true;
|
|
4368
|
+
filterFromRemote.call(self).catch((e) => {
|
|
4369
|
+
addErrorAttribute(self, e);
|
|
4370
|
+
});
|
|
4371
|
+
}, 0);
|
|
3845
4372
|
}
|
|
3846
4373
|
calcAndSetOptionsDimension.call(this);
|
|
3847
4374
|
focusFilter.call(this);
|
|
@@ -3905,31 +4432,6 @@ function unregisterFromHost() {
|
|
|
3905
4432
|
this[hostElementSymbol].unregisterDismissable?.(this);
|
|
3906
4433
|
}
|
|
3907
4434
|
|
|
3908
|
-
function initDefaultOptionsFromUrl() {
|
|
3909
|
-
const url = this.getOption("filter.defaultOptionsUrl");
|
|
3910
|
-
if (!url) {
|
|
3911
|
-
return;
|
|
3912
|
-
}
|
|
3913
|
-
|
|
3914
|
-
this.setOption("filter.defaultOptionsUrl", null);
|
|
3915
|
-
|
|
3916
|
-
fetchData
|
|
3917
|
-
.call(this, url)
|
|
3918
|
-
.then((data) => {
|
|
3919
|
-
this[cleanupOptionsListSymbol] = false;
|
|
3920
|
-
importOptionsIntern.call(this, data);
|
|
3921
|
-
setStatusOrRemoveBadges.call(this, "open");
|
|
3922
|
-
initTotal.call(this, data);
|
|
3923
|
-
})
|
|
3924
|
-
.catch((e) => {
|
|
3925
|
-
addErrorAttribute(this, e);
|
|
3926
|
-
setStatusOrRemoveBadges.call(this, "error");
|
|
3927
|
-
});
|
|
3928
|
-
}
|
|
3929
|
-
|
|
3930
|
-
/**
|
|
3931
|
-
* @private
|
|
3932
|
-
*/
|
|
3933
4435
|
/**
|
|
3934
4436
|
* @private
|
|
3935
4437
|
*/
|
|
@@ -4493,7 +4995,7 @@ function updatePopper() {
|
|
|
4493
4995
|
this,
|
|
4494
4996
|
this[controlElementSymbol],
|
|
4495
4997
|
this[popperElementSymbol],
|
|
4496
|
-
|
|
4998
|
+
getSelectPopperPositionOptions.call(this),
|
|
4497
4999
|
);
|
|
4498
5000
|
requestAnimationFrame(() => {
|
|
4499
5001
|
refreshSelectPaginationLayout.call(this);
|
|
@@ -4540,7 +5042,8 @@ function getTemplate() {
|
|
|
4540
5042
|
part="badge"
|
|
4541
5043
|
data-monster-attributes="
|
|
4542
5044
|
data-monster-value path:selection | index:value,
|
|
4543
|
-
|
|
5045
|
+
data-monster-unresolved path:selection | index:unresolved,
|
|
5046
|
+
class path:selection | index:class,
|
|
4544
5047
|
part path:type | suffix:-option | prefix: form-" tabindex="-1">
|
|
4545
5048
|
<div data-monster-replace="path:selection | index:label" part="badge-label"
|
|
4546
5049
|
data-monster-role="badge-label"></div>
|