@schukai/monster 4.148.0 → 4.148.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/source/components/form/select.mjs +151 -34
- package/source/components/form/util/floating-layout-queue.mjs +38 -18
- package/source/components/form/util/floating-ui.mjs +68 -12
- package/test/cases/components/form/floating-ui.mjs +48 -1
- package/test/cases/components/form/select.mjs +276 -23
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.148.
|
|
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.148.1"}
|
|
@@ -323,6 +323,8 @@ const lookupCacheSymbol = Symbol("lookupCache");
|
|
|
323
323
|
const lookupInProgressSymbol = Symbol("lookupInProgress");
|
|
324
324
|
const unresolvedSelectionValuesSymbol = Symbol("unresolvedSelectionValues");
|
|
325
325
|
const fetchRequestVersionSymbol = Symbol("fetchRequestVersion");
|
|
326
|
+
const remoteLifecycleVersionSymbol = Symbol("remoteLifecycleVersion");
|
|
327
|
+
const activeFetchRequestsSymbol = Symbol("activeFetchRequests");
|
|
326
328
|
const remoteInfoRequestSymbol = Symbol("remoteInfoRequest");
|
|
327
329
|
const remoteInfoStableMessageSymbol = Symbol("remoteInfoStableMessage");
|
|
328
330
|
|
|
@@ -419,6 +421,8 @@ class Select extends CustomControl {
|
|
|
419
421
|
this[lookupInProgressSymbol] = new Map();
|
|
420
422
|
this[unresolvedSelectionValuesSymbol] = new Set();
|
|
421
423
|
this[optionsMapSymbol] = new Map();
|
|
424
|
+
this[remoteLifecycleVersionSymbol] = 0;
|
|
425
|
+
this[activeFetchRequestsSymbol] = new Set();
|
|
422
426
|
this[closeOnSelectAutoSymbol] = true;
|
|
423
427
|
initOptionObserver.call(this);
|
|
424
428
|
}
|
|
@@ -720,6 +724,7 @@ class Select extends CustomControl {
|
|
|
720
724
|
*/
|
|
721
725
|
reset() {
|
|
722
726
|
try {
|
|
727
|
+
invalidatePendingRemoteWork.call(this);
|
|
723
728
|
hide.call(this);
|
|
724
729
|
|
|
725
730
|
// Clear the lookup cache
|
|
@@ -963,6 +968,8 @@ class Select extends CustomControl {
|
|
|
963
968
|
*/
|
|
964
969
|
disconnectedCallback() {
|
|
965
970
|
super.disconnectedCallback();
|
|
971
|
+
invalidatePendingRemoteWork.call(this);
|
|
972
|
+
hide.call(this, false);
|
|
966
973
|
clearPendingOpenIntent.call(this);
|
|
967
974
|
if (!this[usesHostDismissSymbol]) {
|
|
968
975
|
const document = getDocument();
|
|
@@ -2021,6 +2028,65 @@ function runSelectionLookupWhenVisible(self) {
|
|
|
2021
2028
|
function isEmptyLookupValue(value) {
|
|
2022
2029
|
return isValueIsEmpty.call(this, value);
|
|
2023
2030
|
}
|
|
2031
|
+
|
|
2032
|
+
function getRemoteLifecycleVersion() {
|
|
2033
|
+
return Number.isInteger(this[remoteLifecycleVersionSymbol])
|
|
2034
|
+
? this[remoteLifecycleVersionSymbol]
|
|
2035
|
+
: 0;
|
|
2036
|
+
}
|
|
2037
|
+
|
|
2038
|
+
function isRemoteLifecycleCurrent(version) {
|
|
2039
|
+
return getRemoteLifecycleVersion.call(this) === version;
|
|
2040
|
+
}
|
|
2041
|
+
|
|
2042
|
+
function invalidatePendingRemoteWork() {
|
|
2043
|
+
this[remoteLifecycleVersionSymbol] = getRemoteLifecycleVersion.call(this) + 1;
|
|
2044
|
+
this[fetchRequestVersionSymbol] =
|
|
2045
|
+
(Number.isInteger(this[fetchRequestVersionSymbol])
|
|
2046
|
+
? this[fetchRequestVersionSymbol]
|
|
2047
|
+
: 0) + 1;
|
|
2048
|
+
|
|
2049
|
+
if (this[activeFetchRequestsSymbol] instanceof Set) {
|
|
2050
|
+
this[activeFetchRequestsSymbol].clear();
|
|
2051
|
+
}
|
|
2052
|
+
this[isLoadingSymbol] = false;
|
|
2053
|
+
this[remoteInfoRequestSymbol] = null;
|
|
2054
|
+
|
|
2055
|
+
if (this[keyFilterEventSymbol] instanceof DeadMansSwitch) {
|
|
2056
|
+
try {
|
|
2057
|
+
this[keyFilterEventSymbol].defuse();
|
|
2058
|
+
} catch (e) {}
|
|
2059
|
+
delete this[keyFilterEventSymbol];
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
|
|
2063
|
+
function beginTrackedFetch() {
|
|
2064
|
+
if (!(this[activeFetchRequestsSymbol] instanceof Set)) {
|
|
2065
|
+
this[activeFetchRequestsSymbol] = new Set();
|
|
2066
|
+
}
|
|
2067
|
+
|
|
2068
|
+
const token = {};
|
|
2069
|
+
this[activeFetchRequestsSymbol].add(token);
|
|
2070
|
+
this[isLoadingSymbol] = true;
|
|
2071
|
+
return token;
|
|
2072
|
+
}
|
|
2073
|
+
|
|
2074
|
+
function isTrackedFetchActive(token) {
|
|
2075
|
+
return (
|
|
2076
|
+
this[activeFetchRequestsSymbol] instanceof Set &&
|
|
2077
|
+
this[activeFetchRequestsSymbol].has(token)
|
|
2078
|
+
);
|
|
2079
|
+
}
|
|
2080
|
+
|
|
2081
|
+
function finishTrackedFetch(token) {
|
|
2082
|
+
if (this[activeFetchRequestsSymbol] instanceof Set) {
|
|
2083
|
+
this[activeFetchRequestsSymbol].delete(token);
|
|
2084
|
+
this[isLoadingSymbol] = this[activeFetchRequestsSymbol].size > 0;
|
|
2085
|
+
return;
|
|
2086
|
+
}
|
|
2087
|
+
|
|
2088
|
+
this[isLoadingSymbol] = false;
|
|
2089
|
+
}
|
|
2024
2090
|
/**
|
|
2025
2091
|
*
|
|
2026
2092
|
* @param url
|
|
@@ -2029,6 +2095,7 @@ function isEmptyLookupValue(value) {
|
|
|
2029
2095
|
*/
|
|
2030
2096
|
function fetchIt(url, controlOptions) {
|
|
2031
2097
|
const self = this;
|
|
2098
|
+
const lifecycleVersion = getRemoteLifecycleVersion.call(this);
|
|
2032
2099
|
|
|
2033
2100
|
if (url instanceof URL) {
|
|
2034
2101
|
url = url.toString();
|
|
@@ -2066,7 +2133,10 @@ function fetchIt(url, controlOptions) {
|
|
|
2066
2133
|
fetchData
|
|
2067
2134
|
.call(this, url)
|
|
2068
2135
|
.then((map) => {
|
|
2069
|
-
if (
|
|
2136
|
+
if (
|
|
2137
|
+
requestVersion !== this[fetchRequestVersionSymbol] ||
|
|
2138
|
+
!isRemoteLifecycleCurrent.call(this, lifecycleVersion)
|
|
2139
|
+
) {
|
|
2070
2140
|
resolve();
|
|
2071
2141
|
return;
|
|
2072
2142
|
}
|
|
@@ -2088,6 +2158,13 @@ function fetchIt(url, controlOptions) {
|
|
|
2088
2158
|
this[lastFetchedDataSymbol] = map;
|
|
2089
2159
|
|
|
2090
2160
|
queueMicrotask(() => {
|
|
2161
|
+
if (
|
|
2162
|
+
requestVersion !== this[fetchRequestVersionSymbol] ||
|
|
2163
|
+
!isRemoteLifecycleCurrent.call(this, lifecycleVersion)
|
|
2164
|
+
) {
|
|
2165
|
+
resolve();
|
|
2166
|
+
return;
|
|
2167
|
+
}
|
|
2091
2168
|
checkOptionState.call(this);
|
|
2092
2169
|
if (
|
|
2093
2170
|
getFilterMode.call(this) === FILTER_MODE_REMOTE &&
|
|
@@ -2112,7 +2189,10 @@ function fetchIt(url, controlOptions) {
|
|
|
2112
2189
|
reject(new Error("invalid response"));
|
|
2113
2190
|
})
|
|
2114
2191
|
.catch((e) => {
|
|
2115
|
-
if (
|
|
2192
|
+
if (
|
|
2193
|
+
requestVersion !== this[fetchRequestVersionSymbol] ||
|
|
2194
|
+
!isRemoteLifecycleCurrent.call(this, lifecycleVersion)
|
|
2195
|
+
) {
|
|
2116
2196
|
resolve();
|
|
2117
2197
|
return;
|
|
2118
2198
|
}
|
|
@@ -2125,6 +2205,10 @@ function fetchIt(url, controlOptions) {
|
|
|
2125
2205
|
})
|
|
2126
2206
|
.run()
|
|
2127
2207
|
.catch((e) => {
|
|
2208
|
+
if (!isRemoteLifecycleCurrent.call(this, lifecycleVersion)) {
|
|
2209
|
+
resolve();
|
|
2210
|
+
return;
|
|
2211
|
+
}
|
|
2128
2212
|
setStatusOrRemoveBadges.call(this, "error");
|
|
2129
2213
|
addErrorAttribute(this, e);
|
|
2130
2214
|
clearOptionsOnError.call(this);
|
|
@@ -2852,6 +2936,8 @@ function buildSelectionItem(value, preferredLabel) {
|
|
|
2852
2936
|
* @returns {Promise<void>}
|
|
2853
2937
|
*/
|
|
2854
2938
|
async function lookupValueAndCache(value) {
|
|
2939
|
+
const lifecycleVersion = getRemoteLifecycleVersion.call(this);
|
|
2940
|
+
const lookupToken = {};
|
|
2855
2941
|
const lookupUrl = this.getOption("lookup.url");
|
|
2856
2942
|
const lookupValue = getSelectionValueLabel.call(this, value);
|
|
2857
2943
|
const cacheKey = getSelectionCacheKey.call(this, value);
|
|
@@ -2875,7 +2961,7 @@ async function lookupValueAndCache(value) {
|
|
|
2875
2961
|
let found = false;
|
|
2876
2962
|
let refreshSelection = false;
|
|
2877
2963
|
try {
|
|
2878
|
-
this[lookupInProgressSymbol].set(value,
|
|
2964
|
+
this[lookupInProgressSymbol].set(value, lookupToken);
|
|
2879
2965
|
|
|
2880
2966
|
const markerOpen = this.getOption("filter.marker.open", "{");
|
|
2881
2967
|
const markerClose = this.getOption("filter.marker.close", "}");
|
|
@@ -2885,6 +2971,9 @@ async function lookupValueAndCache(value) {
|
|
|
2885
2971
|
);
|
|
2886
2972
|
|
|
2887
2973
|
const data = await fetchData.call(this, url);
|
|
2974
|
+
if (!isRemoteLifecycleCurrent.call(this, lifecycleVersion)) {
|
|
2975
|
+
return;
|
|
2976
|
+
}
|
|
2888
2977
|
|
|
2889
2978
|
const mappingOptions = this.getOption("mapping", {});
|
|
2890
2979
|
const map = buildMap(
|
|
@@ -2932,6 +3021,9 @@ async function lookupValueAndCache(value) {
|
|
|
2932
3021
|
refreshSelection = true;
|
|
2933
3022
|
}
|
|
2934
3023
|
} catch (e) {
|
|
3024
|
+
if (!isRemoteLifecycleCurrent.call(this, lifecycleVersion)) {
|
|
3025
|
+
return;
|
|
3026
|
+
}
|
|
2935
3027
|
hasError = true;
|
|
2936
3028
|
addErrorAttribute(this, e);
|
|
2937
3029
|
|
|
@@ -2944,16 +3036,19 @@ async function lookupValueAndCache(value) {
|
|
|
2944
3036
|
refreshSelection = true;
|
|
2945
3037
|
}
|
|
2946
3038
|
} finally {
|
|
2947
|
-
this[lookupInProgressSymbol].
|
|
2948
|
-
|
|
2949
|
-
if (refreshSelection) {
|
|
2950
|
-
await setSelection.call(this, this.getOption("selection"));
|
|
3039
|
+
if (this[lookupInProgressSymbol].get(value) === lookupToken) {
|
|
3040
|
+
this[lookupInProgressSymbol].delete(value);
|
|
2951
3041
|
}
|
|
3042
|
+
if (isRemoteLifecycleCurrent.call(this, lifecycleVersion)) {
|
|
3043
|
+
if (refreshSelection) {
|
|
3044
|
+
await setSelection.call(this, this.getOption("selection"));
|
|
3045
|
+
}
|
|
2952
3046
|
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
3047
|
+
if (hasError) {
|
|
3048
|
+
setStatusOrRemoveBadges.call(this, "error");
|
|
3049
|
+
} else {
|
|
3050
|
+
setStatusOrRemoveBadges.call(this);
|
|
3051
|
+
}
|
|
2957
3052
|
}
|
|
2958
3053
|
}
|
|
2959
3054
|
}
|
|
@@ -3573,11 +3668,14 @@ function resolveSelectViewportMetrics({
|
|
|
3573
3668
|
offsetTop = 0,
|
|
3574
3669
|
padding = SELECT_VIEWPORT_PADDING,
|
|
3575
3670
|
}) {
|
|
3671
|
+
const hasVisualWidth = Number.isFinite(visualWidth) && visualWidth > 0;
|
|
3672
|
+
const hasVisualHeight = Number.isFinite(visualHeight) && visualHeight > 0;
|
|
3673
|
+
|
|
3576
3674
|
return {
|
|
3577
|
-
width: Math.max(
|
|
3578
|
-
height: Math.max(
|
|
3579
|
-
left: offsetLeft,
|
|
3580
|
-
top: offsetTop,
|
|
3675
|
+
width: hasVisualWidth ? visualWidth : Math.max(0, layoutWidth),
|
|
3676
|
+
height: hasVisualHeight ? visualHeight : Math.max(0, layoutHeight),
|
|
3677
|
+
left: hasVisualWidth ? offsetLeft : 0,
|
|
3678
|
+
top: hasVisualHeight ? offsetTop : 0,
|
|
3581
3679
|
padding,
|
|
3582
3680
|
};
|
|
3583
3681
|
}
|
|
@@ -3676,6 +3774,7 @@ function getSelectPopperPositionOptions() {
|
|
|
3676
3774
|
}
|
|
3677
3775
|
|
|
3678
3776
|
popperOptions.adaptiveSize = false;
|
|
3777
|
+
popperOptions.hideWhenReferenceHidden = true;
|
|
3679
3778
|
|
|
3680
3779
|
if (
|
|
3681
3780
|
resolveParentPopperContentBoundary(
|
|
@@ -4090,7 +4189,12 @@ function filterFromRemoteByValue(
|
|
|
4090
4189
|
openPopper,
|
|
4091
4190
|
formatOptions = {},
|
|
4092
4191
|
) {
|
|
4192
|
+
const lifecycleVersion = getRemoteLifecycleVersion.call(this);
|
|
4193
|
+
|
|
4093
4194
|
return new Processing(() => {
|
|
4195
|
+
if (!isRemoteLifecycleCurrent.call(this, lifecycleVersion)) {
|
|
4196
|
+
return;
|
|
4197
|
+
}
|
|
4094
4198
|
let url = formatURL.call(this, optionUrl, params, formatOptions);
|
|
4095
4199
|
|
|
4096
4200
|
if (url.indexOf(disabledRequestMarker.toString()) !== -1) {
|
|
@@ -4104,12 +4208,18 @@ function filterFromRemoteByValue(
|
|
|
4104
4208
|
disableHiding: true,
|
|
4105
4209
|
})
|
|
4106
4210
|
.then(() => {
|
|
4211
|
+
if (!isRemoteLifecycleCurrent.call(this, lifecycleVersion)) {
|
|
4212
|
+
return;
|
|
4213
|
+
}
|
|
4107
4214
|
checkOptionState.call(this);
|
|
4108
4215
|
if (openPopper === true) {
|
|
4109
4216
|
show.call(this);
|
|
4110
4217
|
}
|
|
4111
4218
|
})
|
|
4112
4219
|
.catch((e) => {
|
|
4220
|
+
if (!isRemoteLifecycleCurrent.call(this, lifecycleVersion)) {
|
|
4221
|
+
return;
|
|
4222
|
+
}
|
|
4113
4223
|
if (getFilterMode.call(this) === FILTER_MODE_REMOTE) {
|
|
4114
4224
|
this.setOption("total", null);
|
|
4115
4225
|
resetPaginationState.call(this);
|
|
@@ -4897,31 +5007,24 @@ function setSelection(selection) {
|
|
|
4897
5007
|
* @throws {TypeError} unsupported response
|
|
4898
5008
|
*/
|
|
4899
5009
|
function fetchData(url) {
|
|
4900
|
-
const self = this;
|
|
4901
5010
|
if (!url) url = this.getOption("url");
|
|
4902
5011
|
if (!url) return Promise.resolve();
|
|
4903
5012
|
|
|
4904
5013
|
const fetchOptions = this.getOption("fetch", {});
|
|
4905
|
-
|
|
4906
|
-
let delayWatch = false;
|
|
5014
|
+
const fetchToken = beginTrackedFetch.call(this);
|
|
4907
5015
|
|
|
4908
5016
|
// if fetch short time, do not show loading badge, because of flickering
|
|
4909
5017
|
requestAnimationFrame(() => {
|
|
4910
|
-
if (
|
|
5018
|
+
if (!isTrackedFetchActive.call(this, fetchToken)) return;
|
|
4911
5019
|
setStatusOrRemoveBadges.call(this, "loading");
|
|
4912
|
-
delayWatch = true;
|
|
4913
5020
|
});
|
|
4914
5021
|
|
|
4915
5022
|
url = formatURL.call(this, url);
|
|
4916
5023
|
|
|
4917
|
-
self[isLoadingSymbol] = true;
|
|
4918
5024
|
const global = getGlobal();
|
|
4919
|
-
return
|
|
4920
|
-
.fetch(url, fetchOptions)
|
|
5025
|
+
return Promise.resolve()
|
|
5026
|
+
.then(() => global.fetch(url, fetchOptions))
|
|
4921
5027
|
.then((response) => {
|
|
4922
|
-
self[isLoadingSymbol] = false;
|
|
4923
|
-
delayWatch = true;
|
|
4924
|
-
|
|
4925
5028
|
if (!(response.status >= 200 && response.status < 300)) {
|
|
4926
5029
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
4927
5030
|
}
|
|
@@ -4940,23 +5043,24 @@ function fetchData(url) {
|
|
|
4940
5043
|
throw new TypeError("the result cannot be parsed, check the URL");
|
|
4941
5044
|
}
|
|
4942
5045
|
})
|
|
4943
|
-
.
|
|
4944
|
-
|
|
4945
|
-
delayWatch = true;
|
|
4946
|
-
throw e;
|
|
5046
|
+
.finally(() => {
|
|
5047
|
+
finishTrackedFetch.call(this, fetchToken);
|
|
4947
5048
|
});
|
|
4948
5049
|
}
|
|
4949
5050
|
|
|
4950
5051
|
/**
|
|
4951
5052
|
* @private
|
|
5053
|
+
* @param {boolean} updateStatus
|
|
4952
5054
|
*/
|
|
4953
|
-
function hide() {
|
|
5055
|
+
function hide(updateStatus = true) {
|
|
4954
5056
|
clearPendingOpenIntent.call(this);
|
|
4955
5057
|
cancelFloatingLayout(this[popperElementSymbol]);
|
|
4956
5058
|
resetSelectPopperDimensionStyles.call(this);
|
|
4957
5059
|
closePositionedPopper(this[popperElementSymbol]);
|
|
4958
5060
|
unlockControlBarSelectHostWidthAfterOpenPopper.call(this);
|
|
4959
|
-
|
|
5061
|
+
if (updateStatus) {
|
|
5062
|
+
setStatusOrRemoveBadges.call(this, "closed");
|
|
5063
|
+
}
|
|
4960
5064
|
removeAttributeToken(this[controlElementSymbol], "class", "open");
|
|
4961
5065
|
unregisterFromHost.call(this);
|
|
4962
5066
|
}
|
|
@@ -5080,8 +5184,10 @@ function show() {
|
|
|
5080
5184
|
* @return {boolean}
|
|
5081
5185
|
*/
|
|
5082
5186
|
function isInControlBar() {
|
|
5083
|
-
return
|
|
5084
|
-
|
|
5187
|
+
return (
|
|
5188
|
+
this.closest("monster-control-bar,monster-button-bar") instanceof
|
|
5189
|
+
HTMLElement
|
|
5190
|
+
);
|
|
5085
5191
|
}
|
|
5086
5192
|
|
|
5087
5193
|
/**
|
|
@@ -5138,6 +5244,8 @@ function unregisterFromHost() {
|
|
|
5138
5244
|
* @private
|
|
5139
5245
|
*/
|
|
5140
5246
|
function initTotal() {
|
|
5247
|
+
const lifecycleVersion = getRemoteLifecycleVersion.call(this);
|
|
5248
|
+
|
|
5141
5249
|
if (getFilterMode.call(this) !== FILTER_MODE_REMOTE) {
|
|
5142
5250
|
return;
|
|
5143
5251
|
}
|
|
@@ -5169,6 +5277,9 @@ function initTotal() {
|
|
|
5169
5277
|
const remoteInfoRequest = getGlobal()
|
|
5170
5278
|
.fetch(url, fetchOptions)
|
|
5171
5279
|
.then((response) => {
|
|
5280
|
+
if (!isRemoteLifecycleCurrent.call(this, lifecycleVersion)) {
|
|
5281
|
+
return;
|
|
5282
|
+
}
|
|
5172
5283
|
if (!response.ok) {
|
|
5173
5284
|
addErrorAttribute(
|
|
5174
5285
|
this,
|
|
@@ -5179,6 +5290,9 @@ function initTotal() {
|
|
|
5179
5290
|
return response.text();
|
|
5180
5291
|
})
|
|
5181
5292
|
.then((text) => {
|
|
5293
|
+
if (!isRemoteLifecycleCurrent.call(this, lifecycleVersion)) {
|
|
5294
|
+
return;
|
|
5295
|
+
}
|
|
5182
5296
|
if (!text) return;
|
|
5183
5297
|
try {
|
|
5184
5298
|
const data = JSON.parse(String(text));
|
|
@@ -5189,6 +5303,9 @@ function initTotal() {
|
|
|
5189
5303
|
}
|
|
5190
5304
|
})
|
|
5191
5305
|
.catch((e) => {
|
|
5306
|
+
if (!isRemoteLifecycleCurrent.call(this, lifecycleVersion)) {
|
|
5307
|
+
return;
|
|
5308
|
+
}
|
|
5192
5309
|
addErrorAttribute(this, e);
|
|
5193
5310
|
})
|
|
5194
5311
|
.finally(() => {
|
|
@@ -49,6 +49,9 @@ function enqueueFloatingLayout({
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
let job = jobs.get(popperElement);
|
|
52
|
+
if (job?.cancelled === true) {
|
|
53
|
+
return Promise.resolve();
|
|
54
|
+
}
|
|
52
55
|
if (!job) {
|
|
53
56
|
job = createJob(popperElement);
|
|
54
57
|
jobs.set(popperElement, job);
|
|
@@ -84,8 +87,12 @@ function cancelFloatingLayout(popperElement) {
|
|
|
84
87
|
}
|
|
85
88
|
|
|
86
89
|
job.cancelled = true;
|
|
90
|
+
job.pending = false;
|
|
91
|
+
job.reasons = 0;
|
|
87
92
|
job.resolve();
|
|
88
|
-
|
|
93
|
+
if (!job.running) {
|
|
94
|
+
jobs.delete(popperElement);
|
|
95
|
+
}
|
|
89
96
|
}
|
|
90
97
|
|
|
91
98
|
function flushFloatingLayoutQueueForTests() {
|
|
@@ -175,21 +182,25 @@ async function flushQueue() {
|
|
|
175
182
|
}
|
|
176
183
|
|
|
177
184
|
flushing = true;
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
for (const job of currentJobs) {
|
|
181
|
-
await flushJob(job);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
flushing = false;
|
|
185
|
+
try {
|
|
186
|
+
const currentJobs = Array.from(jobs.values());
|
|
185
187
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
188
|
+
for (const job of currentJobs) {
|
|
189
|
+
await flushJob(job);
|
|
190
|
+
}
|
|
191
|
+
} finally {
|
|
192
|
+
flushing = false;
|
|
193
|
+
|
|
194
|
+
if (
|
|
195
|
+
Array.from(jobs.values()).some((job) => job.pending && !job.cancelled)
|
|
196
|
+
) {
|
|
197
|
+
for (const job of jobs.values()) {
|
|
198
|
+
if (job.pending) {
|
|
199
|
+
job.pending = false;
|
|
200
|
+
}
|
|
190
201
|
}
|
|
202
|
+
scheduleQueueFlush();
|
|
191
203
|
}
|
|
192
|
-
scheduleQueueFlush();
|
|
193
204
|
}
|
|
194
205
|
}
|
|
195
206
|
|
|
@@ -227,14 +238,23 @@ async function flushJob(job) {
|
|
|
227
238
|
}
|
|
228
239
|
} finally {
|
|
229
240
|
job.running = false;
|
|
230
|
-
|
|
231
|
-
if (job.pending && shouldSuppressOscillatingPendingLayout(job)) {
|
|
241
|
+
if (job.cancelled) {
|
|
232
242
|
job.pending = false;
|
|
233
243
|
job.reasons = 0;
|
|
234
|
-
}
|
|
235
|
-
if (!job.pending && job.reasons === 0) {
|
|
236
244
|
job.resolve();
|
|
237
|
-
jobs.
|
|
245
|
+
if (jobs.get(job.popperElement) === job) {
|
|
246
|
+
jobs.delete(job.popperElement);
|
|
247
|
+
}
|
|
248
|
+
} else {
|
|
249
|
+
recordLayoutSignature(job);
|
|
250
|
+
if (job.pending && shouldSuppressOscillatingPendingLayout(job)) {
|
|
251
|
+
job.pending = false;
|
|
252
|
+
job.reasons = 0;
|
|
253
|
+
}
|
|
254
|
+
if (!job.pending && job.reasons === 0) {
|
|
255
|
+
job.resolve();
|
|
256
|
+
jobs.delete(job.popperElement);
|
|
257
|
+
}
|
|
238
258
|
}
|
|
239
259
|
}
|
|
240
260
|
}
|
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
|
|
34
34
|
export {
|
|
35
35
|
applyAdaptiveFloatingElementSize,
|
|
36
|
+
applyFloatingReferenceVisibility,
|
|
36
37
|
closePositionedPopper,
|
|
37
38
|
createVisibilityRecoveryConfig,
|
|
38
39
|
getFloatingVisibleRatio,
|
|
@@ -106,8 +107,7 @@ function enableFloatingPositioning(controlElement, popperElement, config) {
|
|
|
106
107
|
|
|
107
108
|
enqueueFloatingLayout({
|
|
108
109
|
popperElement,
|
|
109
|
-
reason:
|
|
110
|
-
FLOATING_LAYOUT_REASON.POSITION | FLOATING_LAYOUT_REASON.RESIZE,
|
|
110
|
+
reason: FLOATING_LAYOUT_REASON.POSITION | FLOATING_LAYOUT_REASON.RESIZE,
|
|
111
111
|
isActive: () => isPositionedPopperOpen(popperElement),
|
|
112
112
|
position: () => {
|
|
113
113
|
runFloatingUpdateHook(popperElement);
|
|
@@ -177,6 +177,13 @@ function syncFloatingPopover(
|
|
|
177
177
|
if (!isPositionedPopperOpen(popperElement)) {
|
|
178
178
|
return;
|
|
179
179
|
}
|
|
180
|
+
if (
|
|
181
|
+
config.hideWhenReferenceHidden === true &&
|
|
182
|
+
!applyFloatingReferenceVisibility(controlElement, popperElement)
|
|
183
|
+
) {
|
|
184
|
+
visibilityRecoveryMap.delete(popperElement);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
180
187
|
|
|
181
188
|
Object.assign(popperElement.style, {
|
|
182
189
|
top: "0",
|
|
@@ -202,12 +209,49 @@ function syncFloatingPopover(
|
|
|
202
209
|
});
|
|
203
210
|
}
|
|
204
211
|
|
|
212
|
+
function applyFloatingReferenceVisibility(referenceElement, popperElement) {
|
|
213
|
+
if (!(popperElement instanceof HTMLElement)) {
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const referenceRect =
|
|
218
|
+
referenceElement instanceof HTMLElement
|
|
219
|
+
? referenceElement.getBoundingClientRect()
|
|
220
|
+
: null;
|
|
221
|
+
const referenceArea = referenceRect
|
|
222
|
+
? referenceRect.width * referenceRect.height
|
|
223
|
+
: 0;
|
|
224
|
+
let referenceHidden = false;
|
|
225
|
+
if (referenceElement instanceof HTMLElement && referenceArea > 0) {
|
|
226
|
+
referenceHidden = !getVisibleElementRect(referenceElement, referenceRect);
|
|
227
|
+
} else if (referenceElement instanceof HTMLElement) {
|
|
228
|
+
const referenceStyle = getComputedStyle(referenceElement);
|
|
229
|
+
referenceHidden =
|
|
230
|
+
referenceStyle.display === "none" ||
|
|
231
|
+
referenceStyle.visibility === "hidden";
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (referenceHidden) {
|
|
235
|
+
popperElement.dataset.monsterReferenceHidden = "true";
|
|
236
|
+
popperElement.style.visibility = "hidden";
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (popperElement.dataset.monsterReferenceHidden === "true") {
|
|
241
|
+
delete popperElement.dataset.monsterReferenceHidden;
|
|
242
|
+
popperElement.style.removeProperty("visibility");
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return true;
|
|
246
|
+
}
|
|
247
|
+
|
|
205
248
|
function closePositionedPopper(popperElement) {
|
|
206
249
|
cancelFloatingLayout(popperElement);
|
|
207
250
|
stopAutoUpdate(popperElement);
|
|
208
251
|
cancelFloatingAppearanceFrame(popperElement);
|
|
209
252
|
visibilityRecoveryMap.delete(popperElement);
|
|
210
253
|
delete popperElement.dataset.monsterAppearance;
|
|
254
|
+
delete popperElement.dataset.monsterReferenceHidden;
|
|
211
255
|
popperElement.style.display = "none";
|
|
212
256
|
popperElement.style.removeProperty("visibility");
|
|
213
257
|
popperElement.style.removeProperty("position");
|
|
@@ -226,6 +270,7 @@ function normalizePopperConfig(options, controlElement, popperElement) {
|
|
|
226
270
|
engine: "floating",
|
|
227
271
|
strategy: "absolute",
|
|
228
272
|
adaptiveSize: true,
|
|
273
|
+
hideWhenReferenceHidden: false,
|
|
229
274
|
},
|
|
230
275
|
options,
|
|
231
276
|
);
|
|
@@ -423,7 +468,7 @@ function getFloatingVisibleRatio(floatingElement) {
|
|
|
423
468
|
return 1;
|
|
424
469
|
}
|
|
425
470
|
|
|
426
|
-
const visibleRect =
|
|
471
|
+
const visibleRect = getVisibleElementRect(floatingElement, rect);
|
|
427
472
|
if (!visibleRect) {
|
|
428
473
|
return 0;
|
|
429
474
|
}
|
|
@@ -432,13 +477,26 @@ function getFloatingVisibleRatio(floatingElement) {
|
|
|
432
477
|
return Math.max(0, Math.min(1, visibleArea / area));
|
|
433
478
|
}
|
|
434
479
|
|
|
435
|
-
function
|
|
480
|
+
function getVisibleElementRect(element, rect) {
|
|
436
481
|
let visibleRect = normalizeRect(rect);
|
|
482
|
+
const visualViewport = window.visualViewport;
|
|
483
|
+
const viewportLeft = visualViewport?.offsetLeft || 0;
|
|
484
|
+
const viewportTop = visualViewport?.offsetTop || 0;
|
|
485
|
+
const viewportWidth =
|
|
486
|
+
visualViewport?.width ||
|
|
487
|
+
window.innerWidth ||
|
|
488
|
+
document.documentElement.clientWidth ||
|
|
489
|
+
0;
|
|
490
|
+
const viewportHeight =
|
|
491
|
+
visualViewport?.height ||
|
|
492
|
+
window.innerHeight ||
|
|
493
|
+
document.documentElement.clientHeight ||
|
|
494
|
+
0;
|
|
437
495
|
const viewportRect = {
|
|
438
|
-
top:
|
|
439
|
-
left:
|
|
440
|
-
right:
|
|
441
|
-
bottom:
|
|
496
|
+
top: viewportTop,
|
|
497
|
+
left: viewportLeft,
|
|
498
|
+
right: viewportLeft + viewportWidth,
|
|
499
|
+
bottom: viewportTop + viewportHeight,
|
|
442
500
|
};
|
|
443
501
|
viewportRect.width = Math.max(0, viewportRect.right - viewportRect.left);
|
|
444
502
|
viewportRect.height = Math.max(0, viewportRect.bottom - viewportRect.top);
|
|
@@ -448,7 +506,7 @@ function getVisibleFloatingRect(floatingElement, rect) {
|
|
|
448
506
|
return null;
|
|
449
507
|
}
|
|
450
508
|
|
|
451
|
-
for (const clippingContainer of getFloatingClippingContainers(
|
|
509
|
+
for (const clippingContainer of getFloatingClippingContainers(element)) {
|
|
452
510
|
visibleRect = intersectRects(
|
|
453
511
|
visibleRect,
|
|
454
512
|
normalizeRect(clippingContainer.getBoundingClientRect()),
|
|
@@ -483,9 +541,7 @@ function normalizeRect(rect) {
|
|
|
483
541
|
const left = Number.isFinite(rect.left) ? rect.left : rect.x || 0;
|
|
484
542
|
const top = Number.isFinite(rect.top) ? rect.top : rect.y || 0;
|
|
485
543
|
const right = Number.isFinite(rect.right) ? rect.right : left + rect.width;
|
|
486
|
-
const bottom = Number.isFinite(rect.bottom)
|
|
487
|
-
? rect.bottom
|
|
488
|
-
: top + rect.height;
|
|
544
|
+
const bottom = Number.isFinite(rect.bottom) ? rect.bottom : top + rect.height;
|
|
489
545
|
|
|
490
546
|
return {
|
|
491
547
|
top,
|
|
@@ -5,6 +5,7 @@ let expect = chai.expect;
|
|
|
5
5
|
|
|
6
6
|
let resolveClippingBoundaryElement;
|
|
7
7
|
let applyAdaptiveFloatingElementSize;
|
|
8
|
+
let applyFloatingReferenceVisibility;
|
|
8
9
|
let createVisibilityRecoveryConfig;
|
|
9
10
|
let getFloatingVisibleRatio;
|
|
10
11
|
|
|
@@ -19,6 +20,7 @@ describe("form floating-ui boundary resolution", function () {
|
|
|
19
20
|
.then((m) => {
|
|
20
21
|
resolveClippingBoundaryElement = m.resolveClippingBoundaryElement;
|
|
21
22
|
applyAdaptiveFloatingElementSize = m.applyAdaptiveFloatingElementSize;
|
|
23
|
+
applyFloatingReferenceVisibility = m.applyFloatingReferenceVisibility;
|
|
22
24
|
createVisibilityRecoveryConfig = m.createVisibilityRecoveryConfig;
|
|
23
25
|
getFloatingVisibleRatio = m.getFloatingVisibleRatio;
|
|
24
26
|
done();
|
|
@@ -169,7 +171,8 @@ describe("form floating-ui boundary resolution", function () {
|
|
|
169
171
|
|
|
170
172
|
popper.style.maxHeight = "300px";
|
|
171
173
|
content.setAttribute("part", "content");
|
|
172
|
-
content.textContent =
|
|
174
|
+
content.textContent =
|
|
175
|
+
"A long help text that still needs one readable line.";
|
|
173
176
|
content.style.fontSize = "16px";
|
|
174
177
|
content.style.lineHeight = "24px";
|
|
175
178
|
popper.appendChild(content);
|
|
@@ -476,4 +479,48 @@ describe("form floating-ui boundary resolution", function () {
|
|
|
476
479
|
"arrow",
|
|
477
480
|
]);
|
|
478
481
|
});
|
|
482
|
+
|
|
483
|
+
it("should hide a floating element while its reference is clipped", function () {
|
|
484
|
+
const reference = document.createElement("div");
|
|
485
|
+
const popper = document.createElement("div");
|
|
486
|
+
reference.getBoundingClientRect = () => ({
|
|
487
|
+
top: -100,
|
|
488
|
+
left: 10,
|
|
489
|
+
right: 110,
|
|
490
|
+
bottom: -50,
|
|
491
|
+
width: 100,
|
|
492
|
+
height: 50,
|
|
493
|
+
x: 10,
|
|
494
|
+
y: -100,
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
const visible = applyFloatingReferenceVisibility(reference, popper);
|
|
498
|
+
|
|
499
|
+
expect(visible).to.equal(false);
|
|
500
|
+
expect(popper.style.visibility).to.equal("hidden");
|
|
501
|
+
expect(popper.dataset.monsterReferenceHidden).to.equal("true");
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
it("should restore a floating element when its reference is visible again", function () {
|
|
505
|
+
const reference = document.createElement("div");
|
|
506
|
+
const popper = document.createElement("div");
|
|
507
|
+
reference.getBoundingClientRect = () => ({
|
|
508
|
+
top: 10,
|
|
509
|
+
left: 10,
|
|
510
|
+
right: 110,
|
|
511
|
+
bottom: 60,
|
|
512
|
+
width: 100,
|
|
513
|
+
height: 50,
|
|
514
|
+
x: 10,
|
|
515
|
+
y: 10,
|
|
516
|
+
});
|
|
517
|
+
popper.dataset.monsterReferenceHidden = "true";
|
|
518
|
+
popper.style.visibility = "hidden";
|
|
519
|
+
|
|
520
|
+
const visible = applyFloatingReferenceVisibility(reference, popper);
|
|
521
|
+
|
|
522
|
+
expect(visible).to.equal(true);
|
|
523
|
+
expect(popper.style.visibility).to.equal("");
|
|
524
|
+
expect(popper.dataset.monsterReferenceHidden).to.equal(undefined);
|
|
525
|
+
});
|
|
479
526
|
});
|
|
@@ -86,6 +86,17 @@ function waitForCondition(check, {timeout = 4000, interval = 25} = {}) {
|
|
|
86
86
|
});
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
function configureRemotePaginatedSelect(select) {
|
|
90
|
+
select.setOption('url', 'https://example.com/items?filter={filter}&page={page}');
|
|
91
|
+
select.setOption('filter.mode', 'remote');
|
|
92
|
+
select.setOption('mapping.selector', 'items.*');
|
|
93
|
+
select.setOption('mapping.labelTemplate', '${name}');
|
|
94
|
+
select.setOption('mapping.valueTemplate', '${id}');
|
|
95
|
+
select.setOption('mapping.total', 'pagination.total');
|
|
96
|
+
select.setOption('mapping.currentPage', 'pagination.page');
|
|
97
|
+
select.setOption('mapping.objectsPerPage', 'pagination.perPage');
|
|
98
|
+
}
|
|
99
|
+
|
|
89
100
|
let Select,
|
|
90
101
|
SelectStyleSheet,
|
|
91
102
|
getDefaultSelectPopperPositionProfile,
|
|
@@ -397,6 +408,46 @@ describe('Select', function () {
|
|
|
397
408
|
}
|
|
398
409
|
});
|
|
399
410
|
|
|
411
|
+
it('should keep the floating layout queue usable after cancelling a running reentrant job', async function () {
|
|
412
|
+
const cancelledPopper = document.createElement('div');
|
|
413
|
+
const nextPopper = document.createElement('div');
|
|
414
|
+
const releasePosition = createDeferred();
|
|
415
|
+
let positionStarted = false;
|
|
416
|
+
let positionFinished = false;
|
|
417
|
+
let nextMutationCount = 0;
|
|
418
|
+
|
|
419
|
+
enqueueFloatingLayout({
|
|
420
|
+
popperElement: cancelledPopper,
|
|
421
|
+
reason: FLOATING_LAYOUT_REASON.POSITION,
|
|
422
|
+
position: async () => {
|
|
423
|
+
positionStarted = true;
|
|
424
|
+
await releasePosition.promise;
|
|
425
|
+
await enqueueFloatingLayout({
|
|
426
|
+
popperElement: cancelledPopper,
|
|
427
|
+
reason: FLOATING_LAYOUT_REASON.SETTLE
|
|
428
|
+
});
|
|
429
|
+
positionFinished = true;
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
await waitForCondition(() => positionStarted === true);
|
|
434
|
+
cancelFloatingLayout(cancelledPopper);
|
|
435
|
+
releasePosition.resolve();
|
|
436
|
+
await waitForCondition(() => positionFinished === true);
|
|
437
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
438
|
+
|
|
439
|
+
enqueueFloatingLayout({
|
|
440
|
+
popperElement: nextPopper,
|
|
441
|
+
reason: FLOATING_LAYOUT_REASON.POSITION,
|
|
442
|
+
mutate: () => {
|
|
443
|
+
nextMutationCount += 1;
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
await flushFloatingLayoutQueueForTests();
|
|
447
|
+
|
|
448
|
+
expect(nextMutationCount).to.equal(1);
|
|
449
|
+
});
|
|
450
|
+
|
|
400
451
|
it('should flush reentrant floating layout queue jobs through the watchdog', async function () {
|
|
401
452
|
const originalRequestAnimationFrame = global.requestAnimationFrame;
|
|
402
453
|
const originalCancelAnimationFrame = global.cancelAnimationFrame;
|
|
@@ -738,7 +789,7 @@ describe('Select', function () {
|
|
|
738
789
|
expect(popper.style.display).to.equal('block');
|
|
739
790
|
});
|
|
740
791
|
|
|
741
|
-
it('should allow the popper to become wider than a narrow control', function (
|
|
792
|
+
it('should allow the popper to become wider than a narrow control', async function () {
|
|
742
793
|
const mocks = document.getElementById('mocks');
|
|
743
794
|
const select = document.createElement('monster-select');
|
|
744
795
|
|
|
@@ -764,23 +815,12 @@ describe('Select', function () {
|
|
|
764
815
|
y: 100
|
|
765
816
|
});
|
|
766
817
|
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
expect(popper.dataset.monsterWidthBehavior).to.equal('preferred');
|
|
774
|
-
expect(popper.dataset.monsterPreferredWidth).to.equal('240');
|
|
775
|
-
done();
|
|
776
|
-
} catch (e) {
|
|
777
|
-
done(e);
|
|
778
|
-
}
|
|
779
|
-
}, 80);
|
|
780
|
-
} catch (e) {
|
|
781
|
-
done(e);
|
|
782
|
-
}
|
|
783
|
-
}, 20);
|
|
818
|
+
await new Promise(resolve => setTimeout(resolve, 20));
|
|
819
|
+
shadowRoot.querySelector('[data-monster-role=container]').click();
|
|
820
|
+
await waitForCondition(() => popper.style.minWidth === '240px');
|
|
821
|
+
|
|
822
|
+
expect(popper.dataset.monsterWidthBehavior).to.equal('preferred');
|
|
823
|
+
expect(popper.dataset.monsterPreferredWidth).to.equal('240');
|
|
784
824
|
});
|
|
785
825
|
|
|
786
826
|
it('should use fixed positioning inside a control bar', function (done) {
|
|
@@ -1136,12 +1176,48 @@ describe('Select', function () {
|
|
|
1136
1176
|
]);
|
|
1137
1177
|
});
|
|
1138
1178
|
|
|
1139
|
-
it('should
|
|
1179
|
+
it('should use the reduced visual viewport while a soft keyboard is open', function () {
|
|
1180
|
+
const result = resolveSelectViewportMetrics({
|
|
1181
|
+
layoutWidth: 390,
|
|
1182
|
+
layoutHeight: 844,
|
|
1183
|
+
visualWidth: 390,
|
|
1184
|
+
visualHeight: 480,
|
|
1185
|
+
offsetLeft: 0,
|
|
1186
|
+
offsetTop: 0,
|
|
1187
|
+
padding: 12
|
|
1188
|
+
});
|
|
1189
|
+
|
|
1190
|
+
expect(result.width).to.equal(390);
|
|
1191
|
+
expect(result.height).to.equal(480);
|
|
1192
|
+
expect(result.left).to.equal(0);
|
|
1193
|
+
expect(result.top).to.equal(0);
|
|
1194
|
+
expect(result.padding).to.equal(12);
|
|
1195
|
+
});
|
|
1196
|
+
|
|
1197
|
+
it('should preserve visual viewport offsets for zoomed mobile layouts', function () {
|
|
1198
|
+
const result = resolveSelectViewportMetrics({
|
|
1199
|
+
layoutWidth: 1200,
|
|
1200
|
+
layoutHeight: 900,
|
|
1201
|
+
visualWidth: 600,
|
|
1202
|
+
visualHeight: 450,
|
|
1203
|
+
offsetLeft: 20,
|
|
1204
|
+
offsetTop: 30,
|
|
1205
|
+
padding: 12
|
|
1206
|
+
});
|
|
1207
|
+
|
|
1208
|
+
expect(result.width).to.equal(600);
|
|
1209
|
+
expect(result.height).to.equal(450);
|
|
1210
|
+
expect(result.left).to.equal(20);
|
|
1211
|
+
expect(result.top).to.equal(30);
|
|
1212
|
+
expect(result.padding).to.equal(12);
|
|
1213
|
+
});
|
|
1214
|
+
|
|
1215
|
+
it('should fall back to layout viewport metrics without a visual viewport', function () {
|
|
1140
1216
|
const result = resolveSelectViewportMetrics({
|
|
1141
1217
|
layoutWidth: 1400,
|
|
1142
1218
|
layoutHeight: 900,
|
|
1143
|
-
visualWidth:
|
|
1144
|
-
visualHeight:
|
|
1219
|
+
visualWidth: 0,
|
|
1220
|
+
visualHeight: 0,
|
|
1145
1221
|
offsetLeft: 20,
|
|
1146
1222
|
offsetTop: 30,
|
|
1147
1223
|
padding: 12
|
|
@@ -1149,8 +1225,8 @@ describe('Select', function () {
|
|
|
1149
1225
|
|
|
1150
1226
|
expect(result.width).to.equal(1400);
|
|
1151
1227
|
expect(result.height).to.equal(900);
|
|
1152
|
-
expect(result.left).to.equal(
|
|
1153
|
-
expect(result.top).to.equal(
|
|
1228
|
+
expect(result.left).to.equal(0);
|
|
1229
|
+
expect(result.top).to.equal(0);
|
|
1154
1230
|
expect(result.padding).to.equal(12);
|
|
1155
1231
|
});
|
|
1156
1232
|
|
|
@@ -1487,6 +1563,142 @@ describe('Select', function () {
|
|
|
1487
1563
|
.catch((e) => done(e));
|
|
1488
1564
|
}, 50);
|
|
1489
1565
|
});
|
|
1566
|
+
|
|
1567
|
+
it('should ignore a remote page response that settles after reset', async function () {
|
|
1568
|
+
this.timeout(5000);
|
|
1569
|
+
|
|
1570
|
+
const deferredResponse = createDeferred();
|
|
1571
|
+
let requestStarted = false;
|
|
1572
|
+
global['fetch'] = function () {
|
|
1573
|
+
requestStarted = true;
|
|
1574
|
+
return deferredResponse.promise;
|
|
1575
|
+
};
|
|
1576
|
+
|
|
1577
|
+
const mocks = document.getElementById('mocks');
|
|
1578
|
+
const select = document.createElement('monster-select');
|
|
1579
|
+
configureRemotePaginatedSelect(select);
|
|
1580
|
+
mocks.appendChild(select);
|
|
1581
|
+
|
|
1582
|
+
const request = select.fetch('https://example.com/items?filter=old&page=3');
|
|
1583
|
+
await waitForCondition(() => requestStarted === true);
|
|
1584
|
+
|
|
1585
|
+
select.reset();
|
|
1586
|
+
await waitForCondition(() => {
|
|
1587
|
+
return select.getOption('options').length === 0 && select.getOption('total') === null;
|
|
1588
|
+
});
|
|
1589
|
+
|
|
1590
|
+
deferredResponse.resolve(
|
|
1591
|
+
await createJsonResponse({
|
|
1592
|
+
items: [{id: 'old-3', name: 'Old page 3'}],
|
|
1593
|
+
pagination: {
|
|
1594
|
+
total: 9,
|
|
1595
|
+
page: 3,
|
|
1596
|
+
perPage: 1
|
|
1597
|
+
}
|
|
1598
|
+
})
|
|
1599
|
+
);
|
|
1600
|
+
await request;
|
|
1601
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
1602
|
+
|
|
1603
|
+
const pagination = select.shadowRoot.querySelector('[data-monster-role=pagination]');
|
|
1604
|
+
expect(select.getOption('options')).to.deep.equal([]);
|
|
1605
|
+
expect(select.getOption('total')).to.equal(null);
|
|
1606
|
+
expect(pagination.style.display).to.equal('none');
|
|
1607
|
+
expect(pagination.getOption('currentPage')).to.equal(null);
|
|
1608
|
+
expect(pagination.getOption('pages')).to.equal(null);
|
|
1609
|
+
});
|
|
1610
|
+
|
|
1611
|
+
it('should ignore a remote page response after disconnect and reconnect', async function () {
|
|
1612
|
+
this.timeout(5000);
|
|
1613
|
+
|
|
1614
|
+
const deferredResponse = createDeferred();
|
|
1615
|
+
let requestStarted = false;
|
|
1616
|
+
global['fetch'] = function () {
|
|
1617
|
+
requestStarted = true;
|
|
1618
|
+
return deferredResponse.promise;
|
|
1619
|
+
};
|
|
1620
|
+
|
|
1621
|
+
const mocks = document.getElementById('mocks');
|
|
1622
|
+
const select = document.createElement('monster-select');
|
|
1623
|
+
configureRemotePaginatedSelect(select);
|
|
1624
|
+
mocks.appendChild(select);
|
|
1625
|
+
|
|
1626
|
+
const request = select.fetch('https://example.com/items?filter=old&page=2');
|
|
1627
|
+
await waitForCondition(() => requestStarted === true);
|
|
1628
|
+
|
|
1629
|
+
select.remove();
|
|
1630
|
+
deferredResponse.resolve(
|
|
1631
|
+
await createJsonResponse({
|
|
1632
|
+
items: [{id: 'old-2', name: 'Old page 2'}],
|
|
1633
|
+
pagination: {
|
|
1634
|
+
total: 4,
|
|
1635
|
+
page: 2,
|
|
1636
|
+
perPage: 1
|
|
1637
|
+
}
|
|
1638
|
+
})
|
|
1639
|
+
);
|
|
1640
|
+
await request;
|
|
1641
|
+
mocks.appendChild(select);
|
|
1642
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
1643
|
+
|
|
1644
|
+
const pagination = select.shadowRoot.querySelector('[data-monster-role=pagination]');
|
|
1645
|
+
expect(select.getOption('options')).to.deep.equal([]);
|
|
1646
|
+
expect(select.getOption('total')).to.equal(null);
|
|
1647
|
+
expect(pagination.style.display).to.equal('none');
|
|
1648
|
+
expect(pagination.getOption('currentPage')).to.equal(null);
|
|
1649
|
+
expect(pagination.getOption('pages')).to.equal(null);
|
|
1650
|
+
});
|
|
1651
|
+
|
|
1652
|
+
it('should keep the newest remote page when responses settle out of order', async function () {
|
|
1653
|
+
this.timeout(5000);
|
|
1654
|
+
|
|
1655
|
+
const page2Response = createDeferred();
|
|
1656
|
+
const page3Response = createDeferred();
|
|
1657
|
+
const requests = [];
|
|
1658
|
+
global['fetch'] = function (url) {
|
|
1659
|
+
const requestUrl = String(url);
|
|
1660
|
+
requests.push(requestUrl);
|
|
1661
|
+
return requestUrl.includes('page=2') ? page2Response.promise : page3Response.promise;
|
|
1662
|
+
};
|
|
1663
|
+
|
|
1664
|
+
const mocks = document.getElementById('mocks');
|
|
1665
|
+
const select = document.createElement('monster-select');
|
|
1666
|
+
configureRemotePaginatedSelect(select);
|
|
1667
|
+
mocks.appendChild(select);
|
|
1668
|
+
|
|
1669
|
+
const request2 = select.fetch('https://example.com/items?filter=all&page=2');
|
|
1670
|
+
const request3 = select.fetch('https://example.com/items?filter=all&page=3');
|
|
1671
|
+
await waitForCondition(() => requests.length === 2);
|
|
1672
|
+
|
|
1673
|
+
page3Response.resolve(
|
|
1674
|
+
await createJsonResponse({
|
|
1675
|
+
items: [{id: 'page-3', name: 'Page 3'}],
|
|
1676
|
+
pagination: {
|
|
1677
|
+
total: 3,
|
|
1678
|
+
page: 3,
|
|
1679
|
+
perPage: 1
|
|
1680
|
+
}
|
|
1681
|
+
})
|
|
1682
|
+
);
|
|
1683
|
+
await request3;
|
|
1684
|
+
|
|
1685
|
+
page2Response.resolve(
|
|
1686
|
+
await createJsonResponse({
|
|
1687
|
+
items: [{id: 'page-2', name: 'Page 2'}],
|
|
1688
|
+
pagination: {
|
|
1689
|
+
total: 3,
|
|
1690
|
+
page: 2,
|
|
1691
|
+
perPage: 1
|
|
1692
|
+
}
|
|
1693
|
+
})
|
|
1694
|
+
);
|
|
1695
|
+
await request2;
|
|
1696
|
+
|
|
1697
|
+
const pagination = select.shadowRoot.querySelector('[data-monster-role=pagination]');
|
|
1698
|
+
expect(select.getOption('options').map(option => option.value)).to.deep.equal(['page-3']);
|
|
1699
|
+
expect(pagination.getOption('currentPage')).to.equal(3);
|
|
1700
|
+
expect(pagination.getOption('pages')).to.equal(3);
|
|
1701
|
+
});
|
|
1490
1702
|
});
|
|
1491
1703
|
|
|
1492
1704
|
describe('document.createElement()', function () {
|
|
@@ -2488,6 +2700,47 @@ describe('Select', function () {
|
|
|
2488
2700
|
expect(requests).to.have.length(1);
|
|
2489
2701
|
});
|
|
2490
2702
|
|
|
2703
|
+
it('should cancel a pending remote filter request on reset', async function () {
|
|
2704
|
+
this.timeout(5000);
|
|
2705
|
+
|
|
2706
|
+
const mocks = document.getElementById('mocks');
|
|
2707
|
+
const requests = [];
|
|
2708
|
+
global['fetch'] = function (url) {
|
|
2709
|
+
requests.push(String(url));
|
|
2710
|
+
return createJsonResponse({
|
|
2711
|
+
items: [{id: 'alpha', name: 'Alpha'}],
|
|
2712
|
+
pagination: {
|
|
2713
|
+
total: 1,
|
|
2714
|
+
page: 1,
|
|
2715
|
+
perPage: 1
|
|
2716
|
+
}
|
|
2717
|
+
});
|
|
2718
|
+
};
|
|
2719
|
+
|
|
2720
|
+
const select = document.createElement('monster-select');
|
|
2721
|
+
configureRemotePaginatedSelect(select);
|
|
2722
|
+
select.setOption('filter.position', 'popper');
|
|
2723
|
+
mocks.appendChild(select);
|
|
2724
|
+
|
|
2725
|
+
await waitForCondition(() => {
|
|
2726
|
+
return select.shadowRoot.querySelector('[data-monster-role=filter][name="popper-filter"]') instanceof HTMLInputElement;
|
|
2727
|
+
});
|
|
2728
|
+
|
|
2729
|
+
const filterInput = select.shadowRoot.querySelector('[data-monster-role=filter][name="popper-filter"]');
|
|
2730
|
+
filterInput.value = 'alpha';
|
|
2731
|
+
filterInput.dispatchEvent(new Event('input', {
|
|
2732
|
+
bubbles: true,
|
|
2733
|
+
composed: true
|
|
2734
|
+
}));
|
|
2735
|
+
select.reset();
|
|
2736
|
+
|
|
2737
|
+
await new Promise(resolve => setTimeout(resolve, 260));
|
|
2738
|
+
|
|
2739
|
+
expect(requests).to.deep.equal([]);
|
|
2740
|
+
expect(select.getOption('options')).to.deep.equal([]);
|
|
2741
|
+
expect(select.getOption('total')).to.equal(null);
|
|
2742
|
+
});
|
|
2743
|
+
|
|
2491
2744
|
it('should keep unresolved lookup values visible and mark their badge', function (done) {
|
|
2492
2745
|
this.timeout(3000);
|
|
2493
2746
|
|