@schukai/monster 3.120.0 → 3.121.0
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 +19 -0
- package/package.json +1 -1
- package/source/components/form/select.mjs +284 -217
- package/source/components/form/style/select.pcss +27 -11
- package/source/components/form/stylesheet/select.mjs +1 -1
- package/source/types/version.mjs +1 -1
- package/test/cases/components/form/select.mjs +5 -3
- package/test/cases/components/form/tree-select.mjs +5 -6
- package/test/cases/monster.mjs +1 -1
- package/test/web/test.html +2 -2
- package/test/web/tests.js +293 -243
@@ -12,13 +12,11 @@
|
|
12
12
|
* SPDX-License-Identifier: AGPL-3.0
|
13
13
|
*/
|
14
14
|
|
15
|
-
import { instanceSymbol } from "../../constants.mjs";
|
16
|
-
import { internalSymbol } from "../../constants.mjs";
|
15
|
+
import { instanceSymbol, internalSymbol } from "../../constants.mjs";
|
17
16
|
import { buildMap, build as buildValue } from "../../data/buildmap.mjs";
|
18
|
-
import { DeadMansSwitch } from "../../util/deadmansswitch.mjs";
|
19
|
-
import { positionPopper } from "./util/floating-ui.mjs";
|
20
17
|
import {
|
21
18
|
addAttributeToken,
|
19
|
+
containsAttributeToken,
|
22
20
|
findClosestByAttribute,
|
23
21
|
removeAttributeToken,
|
24
22
|
} from "../../dom/attributes.mjs";
|
@@ -29,12 +27,18 @@ import {
|
|
29
27
|
getSlottedElements,
|
30
28
|
registerCustomElement,
|
31
29
|
} from "../../dom/customelement.mjs";
|
30
|
+
import { addErrorAttribute, removeErrorAttribute } from "../../dom/error.mjs";
|
32
31
|
import {
|
33
32
|
findTargetElementFromEvent,
|
34
33
|
fireCustomEvent,
|
35
34
|
fireEvent,
|
36
35
|
} from "../../dom/events.mjs";
|
36
|
+
import { getLocaleOfDocument } from "../../dom/locale.mjs";
|
37
37
|
import { getDocument } from "../../dom/util.mjs";
|
38
|
+
import {
|
39
|
+
getDocumentTranslations,
|
40
|
+
Translations,
|
41
|
+
} from "../../i18n/translations.mjs";
|
38
42
|
import { Formatter } from "../../text/formatter.mjs";
|
39
43
|
import { getGlobal } from "../../types/global.mjs";
|
40
44
|
import { ID } from "../../types/id.mjs";
|
@@ -50,21 +54,17 @@ import {
|
|
50
54
|
import { Observer } from "../../types/observer.mjs";
|
51
55
|
import { ProxyObserver } from "../../types/proxyobserver.mjs";
|
52
56
|
import { validateArray, validateString } from "../../types/validate.mjs";
|
57
|
+
import { DeadMansSwitch } from "../../util/deadmansswitch.mjs";
|
53
58
|
import { Processing } from "../../util/processing.mjs";
|
54
59
|
import { STYLE_DISPLAY_MODE_BLOCK } from "./constants.mjs";
|
55
60
|
import { SelectStyleSheet } from "./stylesheet/select.mjs";
|
56
|
-
import {
|
57
|
-
getDocumentTranslations,
|
58
|
-
Translations,
|
59
|
-
} from "../../i18n/translations.mjs";
|
60
|
-
import { getLocaleOfDocument } from "../../dom/locale.mjs";
|
61
|
-
import { addErrorAttribute, removeErrorAttribute } from "../../dom/error.mjs";
|
61
|
+
import { positionPopper } from "./util/floating-ui.mjs";
|
62
62
|
|
63
63
|
export {
|
64
|
-
Select,
|
65
|
-
popperElementSymbol,
|
66
|
-
getSummaryTemplate,
|
67
64
|
getSelectionTemplate,
|
65
|
+
getSummaryTemplate,
|
66
|
+
popperElementSymbol,
|
67
|
+
Select,
|
68
68
|
};
|
69
69
|
|
70
70
|
/**
|
@@ -227,6 +227,20 @@ const disabledRequestMarker = Symbol("@@disabledRequestMarker");
|
|
227
227
|
*/
|
228
228
|
const runLookupOnceSymbol = Symbol("runLookupOnce");
|
229
229
|
|
230
|
+
/**
|
231
|
+
* @private
|
232
|
+
* @type {symbol}
|
233
|
+
*/
|
234
|
+
const cleanupOptionsListSymbol = Symbol("cleanupOptionsList");
|
235
|
+
|
236
|
+
/**
|
237
|
+
* @private
|
238
|
+
* @type {symbol}
|
239
|
+
*/
|
240
|
+
const debounceOptionsMutationObserverSymbol = Symbol(
|
241
|
+
"debounceOptionsMutationObserver",
|
242
|
+
);
|
243
|
+
|
230
244
|
/**
|
231
245
|
* @private
|
232
246
|
* @type {number}
|
@@ -361,59 +375,55 @@ class Select extends CustomControl {
|
|
361
375
|
*
|
362
376
|
* The individual configuration values can be found in the table.
|
363
377
|
*
|
364
|
-
* @property {
|
365
|
-
* @property {boolean} delegatesFocus
|
366
|
-
* @property {Object
|
367
|
-
* @property {string}
|
368
|
-
* @property {
|
369
|
-
* @property {
|
370
|
-
* @property {
|
371
|
-
* @property {
|
372
|
-
* @property {string}
|
373
|
-
* @property {
|
374
|
-
* @property {string}
|
375
|
-
* @property {
|
376
|
-
* @property {string}
|
377
|
-
* @property {
|
378
|
-
* @property {Object} fetch
|
379
|
-
* @property {
|
380
|
-
* @property {
|
381
|
-
* @property {
|
382
|
-
* @property {
|
383
|
-
* @property {
|
384
|
-
* @property {
|
385
|
-
* @property {string} labels.
|
386
|
-
* @property {string} labels.
|
387
|
-
* @property {string} labels.
|
388
|
-
* @property {
|
389
|
-
* @property {
|
390
|
-
* @property {
|
391
|
-
* @property {
|
392
|
-
* @property {
|
393
|
-
* @property {
|
394
|
-
* @property {
|
395
|
-
* @property {
|
396
|
-
* @property {
|
397
|
-
* @property {
|
398
|
-
* @property {
|
399
|
-
* @property {
|
400
|
-
* @property {string}
|
401
|
-
* @property {string}
|
402
|
-
* @property {string} templateMapping.selected
|
403
|
-
* @property {
|
404
|
-
* @property {string} popper.
|
405
|
-
* @property {
|
406
|
-
* @property {
|
407
|
-
* @property {
|
408
|
-
* @property {
|
409
|
-
* @property {
|
410
|
-
* @property {
|
411
|
-
* @property {
|
412
|
-
* @property {
|
413
|
-
* @property {Array} empty.defaultValueCheckbox Default value if you use checkboxes
|
414
|
-
* @property {Array} empty.equivalents Equivalents for empty values
|
415
|
-
* @property {Object} formatter
|
416
|
-
* @property {function|undefined} formatter.selection format selection label
|
378
|
+
* @property {string[]} toggleEventType Array of DOM event names (e.g. ["click","touch"]) to toggle the dropdown.
|
379
|
+
* @property {boolean} delegatesFocus Whether the element delegates focus to its internal control (e.g. the filter input).
|
380
|
+
* @property {Array<Object>} options Array of option objects {label,value,visibility?,data?} for static option list.
|
381
|
+
* @property {string|string[]} selection Initial selected value(s) as string, comma-separated string, or array of strings.
|
382
|
+
* @property {number} showMaxOptions Maximum visible options before the dropdown scrolls.
|
383
|
+
* @property {"radio"|"checkbox"} type Selection mode: "radio" for single, "checkbox" for multiple.
|
384
|
+
* @property {string} name Name of the hidden form field for form submission.
|
385
|
+
* @property {string|null} url URL to dynamically fetch options via HTTP when opening or filtering.
|
386
|
+
* @property {string} lookup.url URL template with ${filter} placeholder to fetch only selected entries on init when `url` is set and either `features.lazyLoad` or `filter.mode==="remote"`.
|
387
|
+
* @property {boolean} lookup.grouping Group lookup requests: true to fetch all selected values in one request, false to fetch each individually.
|
388
|
+
* @property {string} fetch.redirect Fetch redirect mode (e.g. "error").
|
389
|
+
* @property {string} fetch.method HTTP method for fetching options (e.g. "GET").
|
390
|
+
* @property {string} fetch.mode Fetch mode (e.g. "same-origin").
|
391
|
+
* @property {string} fetch.credentials Credentials policy for fetch (e.g. "same-origin").
|
392
|
+
* @property {Object.<string,string>} fetch.headers HTTP headers for fetch requests.
|
393
|
+
* @property {string} labels.cannot-be-loaded Message when options cannot be loaded.
|
394
|
+
* @property {string} labels.no-options-available Message when no static options are available.
|
395
|
+
* @property {string} labels.click-to-load-options Message prompting user to click to load options when `features.lazyLoad` is enabled.
|
396
|
+
* @property {string} labels.select-an-option Placeholder text when no selection is made.
|
397
|
+
* @property {string} labels.no-options Message when neither slots nor fetched options exist.
|
398
|
+
* @property {string} labels.no-options-found Message when filter yields no matching options.
|
399
|
+
* @property {string} labels.summary-text.zero Plural template for zero selected entries (e.g. "No entries were selected").
|
400
|
+
* @property {string} labels.summary-text.one Plural template for one selected entry.
|
401
|
+
* @property {string} labels.summary-text.other Plural template for multiple selected entries.
|
402
|
+
* @property {boolean} features.clearAll Show a "clear all" badge to reset selection.
|
403
|
+
* @property {boolean} features.clear Show remove icon on individual selection badges.
|
404
|
+
* @property {boolean} features.lazyLoad Lazy-load options on first open (initial fetch on show and triggers `lookup.url` preload; automatically disabled if `filter.mode==="remote"`).
|
405
|
+
* @property {boolean} features.closeOnSelect Automatically close dropdown after selection.
|
406
|
+
* @property {boolean} features.emptyValueIfNoOptions Set value to empty when no options are available.
|
407
|
+
* @property {boolean} features.storeFetchedData Persist raw fetched data for later retrieval via `getLastFetchedData()`.
|
408
|
+
* @property {boolean} features.useStrictValueComparison Use strict (`===`) comparison when matching option values.
|
409
|
+
* @property {string|null} filter.defaultValue Default filter value for remote requests; if unset or empty, disabled marker prevents request.
|
410
|
+
* @property {"options"|"remote"|"disabled"} filter.mode Client-side ("options"), server-side ("remote"; disables `features.lazyLoad`), or disabled filtering.
|
411
|
+
* @property {"inline"|"popper"} filter.position Position of filter input: inline within control or inside popper dropdown.
|
412
|
+
* @property {string} filter.marker.open Opening marker for embedding filter value in `filter.mode==="remote"` URLs.
|
413
|
+
* @property {string} filter.marker.close Closing marker for embedding filter value in URLs.
|
414
|
+
* @property {string|null} filter.defaultOptionsUrl URL for default options when `filter.mode==="remote"` is set and no filter value is provided.
|
415
|
+
* @property {string} templates.main HTML template string for rendering options and selection badges.
|
416
|
+
* @property {string} templateMapping.selected Template variant for selected items (e.g. badge vs summary view).
|
417
|
+
* @property {string} popper.placement Popper.js placement strategy for dropdown (e.g. "bottom").
|
418
|
+
* @property {Array<string|Object>} popper.middleware Popper.js middleware or offset configurations.
|
419
|
+
* @property {string} mapping.selector Data path or selector to identify entries in imported data.
|
420
|
+
* @property {string} mapping.labelTemplate Template for option labels using placeholders like `${name}`.
|
421
|
+
* @property {string} mapping.valueTemplate Template for option values using placeholders like `${name}`.
|
422
|
+
* @property {Function} mapping.filter Optional callback to filter imported map entries before building `options[]`.
|
423
|
+
* @property {string} empty.defaultValueRadio Default radio-value when no selection exists.
|
424
|
+
* @property {Array} empty.defaultValueCheckbox Default checkbox-values array when no selection exists.
|
425
|
+
* @property {Array} empty.equivalents Values considered empty (e.g. `undefined`, `null`, `""`, `NaN`) and normalized to defaults.
|
426
|
+
* @property {Function} formatter.selection Callback `(value)=>string` to format the display label of each selected value.
|
417
427
|
*/
|
418
428
|
get defaults() {
|
419
429
|
return Object.assign(
|
@@ -464,6 +474,7 @@ class Select extends CustomControl {
|
|
464
474
|
open: "{",
|
465
475
|
close: "}",
|
466
476
|
},
|
477
|
+
defaultOptionsUrl: null,
|
467
478
|
},
|
468
479
|
classes: {
|
469
480
|
badge: "monster-badge-primary",
|
@@ -514,11 +525,8 @@ class Select extends CustomControl {
|
|
514
525
|
let remoteFilterFlag = getFilterMode.call(this) === FILTER_MODE_REMOTE;
|
515
526
|
|
516
527
|
if (getFilterMode.call(this) === FILTER_MODE_REMOTE) {
|
517
|
-
self.
|
518
|
-
|
519
|
-
addErrorAttribute(this, "lazyLoad is not supported with remote filter");
|
520
|
-
lazyLoadFlag = false;
|
521
|
-
}
|
528
|
+
self.setOption("features.lazyLoad", false);
|
529
|
+
lazyLoadFlag = false;
|
522
530
|
}
|
523
531
|
|
524
532
|
if (self.hasAttribute("value")) {
|
@@ -697,95 +705,8 @@ class Select extends CustomControl {
|
|
697
705
|
* @fires monster-options-set this event is fired when the options are set
|
698
706
|
*/
|
699
707
|
importOptions(data) {
|
700
|
-
|
701
|
-
|
702
|
-
const selector = mappingOptions?.["selector"];
|
703
|
-
const labelTemplate = mappingOptions?.["labelTemplate"];
|
704
|
-
const valueTemplate = mappingOptions?.["valueTemplate"];
|
705
|
-
let filter = mappingOptions?.["filter"];
|
706
|
-
|
707
|
-
let flag = false;
|
708
|
-
if (labelTemplate === "") {
|
709
|
-
addErrorAttribute(this, "empty label template");
|
710
|
-
flag = true;
|
711
|
-
}
|
712
|
-
|
713
|
-
if (valueTemplate === "") {
|
714
|
-
addErrorAttribute(this, "empty value template");
|
715
|
-
flag = true;
|
716
|
-
}
|
717
|
-
|
718
|
-
if (flag === true) {
|
719
|
-
throw new Error("missing label configuration");
|
720
|
-
}
|
721
|
-
if (isString(filter)) {
|
722
|
-
if (0 === filter.indexOf("run:")) {
|
723
|
-
const code = filter.replace("run:", "");
|
724
|
-
filter = (m, v, k) => {
|
725
|
-
const fkt = new Function("m", "v", "k", "control", code);
|
726
|
-
return fkt(m, v, k, self);
|
727
|
-
};
|
728
|
-
} else if (0 === filter.indexOf("call:")) {
|
729
|
-
const parts = filter.split(":");
|
730
|
-
parts.shift(); // remove prefix
|
731
|
-
const fkt = parts.shift();
|
732
|
-
|
733
|
-
switch (fkt) {
|
734
|
-
case "filterValueOfAttribute":
|
735
|
-
const attribute = parts.shift();
|
736
|
-
const attrValue = self.getAttribute(attribute);
|
737
|
-
|
738
|
-
filter = (m, v, k) => {
|
739
|
-
const mm = buildValue(m, valueTemplate);
|
740
|
-
return mm != attrValue; // no type check, no !==
|
741
|
-
};
|
742
|
-
break;
|
743
|
-
|
744
|
-
default:
|
745
|
-
addErrorAttribute(
|
746
|
-
this,
|
747
|
-
new Error(`Unknown filter function ${fkt}`),
|
748
|
-
);
|
749
|
-
}
|
750
|
-
}
|
751
|
-
}
|
752
|
-
|
753
|
-
const map = buildMap(data, selector, labelTemplate, valueTemplate, filter);
|
754
|
-
|
755
|
-
const options = [];
|
756
|
-
|
757
|
-
if (!isIterable(map)) {
|
758
|
-
throw new Error("map is not iterable");
|
759
|
-
}
|
760
|
-
|
761
|
-
const visibility = "visible";
|
762
|
-
|
763
|
-
map.forEach((label, value) => {
|
764
|
-
options.push({
|
765
|
-
value,
|
766
|
-
label,
|
767
|
-
visibility,
|
768
|
-
data: map.get(value),
|
769
|
-
});
|
770
|
-
});
|
771
|
-
|
772
|
-
runAsOptionLengthChanged.call(this, map.size);
|
773
|
-
this.setOption("options", options);
|
774
|
-
|
775
|
-
fireCustomEvent(this, "monster-options-set", {
|
776
|
-
options,
|
777
|
-
});
|
778
|
-
|
779
|
-
setTimeout(() => {
|
780
|
-
setSelection
|
781
|
-
.call(this, this.getOption("selection"))
|
782
|
-
.then(() => {})
|
783
|
-
.catch((e) => {
|
784
|
-
addErrorAttribute(this, e);
|
785
|
-
});
|
786
|
-
}, 10);
|
787
|
-
|
788
|
-
return this;
|
708
|
+
this[cleanupOptionsListSymbol] = true;
|
709
|
+
return importOptionsIntern.call(this, data);
|
789
710
|
}
|
790
711
|
|
791
712
|
/**
|
@@ -814,6 +735,102 @@ class Select extends CustomControl {
|
|
814
735
|
}
|
815
736
|
}
|
816
737
|
|
738
|
+
/**
|
739
|
+
* @private
|
740
|
+
* @param data
|
741
|
+
* @returns {any}
|
742
|
+
*/
|
743
|
+
function importOptionsIntern(data) {
|
744
|
+
const self = this;
|
745
|
+
const mappingOptions = this.getOption("mapping", {});
|
746
|
+
const selector = mappingOptions?.["selector"];
|
747
|
+
const labelTemplate = mappingOptions?.["labelTemplate"];
|
748
|
+
const valueTemplate = mappingOptions?.["valueTemplate"];
|
749
|
+
let filter = mappingOptions?.["filter"];
|
750
|
+
|
751
|
+
let flag = false;
|
752
|
+
if (labelTemplate === "") {
|
753
|
+
addErrorAttribute(this, "empty label template");
|
754
|
+
flag = true;
|
755
|
+
}
|
756
|
+
|
757
|
+
if (valueTemplate === "") {
|
758
|
+
addErrorAttribute(this, "empty value template");
|
759
|
+
flag = true;
|
760
|
+
}
|
761
|
+
|
762
|
+
if (flag === true) {
|
763
|
+
throw new Error("missing label configuration");
|
764
|
+
}
|
765
|
+
if (isString(filter)) {
|
766
|
+
if (0 === filter.indexOf("run:")) {
|
767
|
+
const code = filter.replace("run:", "");
|
768
|
+
filter = (m, v, k) => {
|
769
|
+
const fkt = new Function("m", "v", "k", "control", code);
|
770
|
+
return fkt(m, v, k, self);
|
771
|
+
};
|
772
|
+
} else if (0 === filter.indexOf("call:")) {
|
773
|
+
const parts = filter.split(":");
|
774
|
+
parts.shift(); // remove prefix
|
775
|
+
const fkt = parts.shift();
|
776
|
+
|
777
|
+
switch (fkt) {
|
778
|
+
case "filterValueOfAttribute":
|
779
|
+
const attribute = parts.shift();
|
780
|
+
const attrValue = self.getAttribute(attribute);
|
781
|
+
|
782
|
+
filter = (m, v, k) => {
|
783
|
+
const mm = buildValue(m, valueTemplate);
|
784
|
+
return mm != attrValue; // no type check, no !==
|
785
|
+
};
|
786
|
+
break;
|
787
|
+
|
788
|
+
default:
|
789
|
+
addErrorAttribute(this, new Error(`Unknown filter function ${fkt}`));
|
790
|
+
}
|
791
|
+
}
|
792
|
+
}
|
793
|
+
|
794
|
+
const map = buildMap(data, selector, labelTemplate, valueTemplate, filter);
|
795
|
+
|
796
|
+
let options = [];
|
797
|
+
if (this[cleanupOptionsListSymbol] !== true) {
|
798
|
+
options = this.getOption("options", []);
|
799
|
+
}
|
800
|
+
|
801
|
+
if (!isIterable(map)) {
|
802
|
+
throw new Error("map is not iterable");
|
803
|
+
}
|
804
|
+
|
805
|
+
const visibility = "visible";
|
806
|
+
|
807
|
+
map.forEach((label, value) => {
|
808
|
+
options.push({
|
809
|
+
value,
|
810
|
+
label,
|
811
|
+
visibility,
|
812
|
+
data: map.get(value),
|
813
|
+
});
|
814
|
+
});
|
815
|
+
|
816
|
+
this.setOption("options", options);
|
817
|
+
|
818
|
+
fireCustomEvent(this, "monster-options-set", {
|
819
|
+
options,
|
820
|
+
});
|
821
|
+
|
822
|
+
setTimeout(() => {
|
823
|
+
setSelection
|
824
|
+
.call(this, this.getOption("selection"))
|
825
|
+
.then(() => {})
|
826
|
+
.catch((e) => {
|
827
|
+
addErrorAttribute(this, e);
|
828
|
+
});
|
829
|
+
}, 10);
|
830
|
+
|
831
|
+
return this;
|
832
|
+
}
|
833
|
+
|
817
834
|
/**
|
818
835
|
* @private
|
819
836
|
* @returns {object}
|
@@ -1003,6 +1020,8 @@ function lookupSelection() {
|
|
1003
1020
|
url = lookupUrl;
|
1004
1021
|
}
|
1005
1022
|
|
1023
|
+
this[cleanupOptionsListSymbol] = false;
|
1024
|
+
|
1006
1025
|
if (this.getOption("lookup.grouping") === true) {
|
1007
1026
|
filterFromRemoteByValue
|
1008
1027
|
.call(
|
@@ -1027,6 +1046,8 @@ function lookupSelection() {
|
|
1027
1046
|
}
|
1028
1047
|
|
1029
1048
|
function fetchIt(url, controlOptions) {
|
1049
|
+
const self = this;
|
1050
|
+
|
1030
1051
|
if (url instanceof URL) {
|
1031
1052
|
url = url.toString();
|
1032
1053
|
}
|
@@ -1054,7 +1075,7 @@ function fetchIt(url, controlOptions) {
|
|
1054
1075
|
map instanceof Map
|
1055
1076
|
) {
|
1056
1077
|
try {
|
1057
|
-
|
1078
|
+
importOptionsIntern.call(self, map);
|
1058
1079
|
} catch (e) {
|
1059
1080
|
setStatusOrRemoveBadges.call(this, "error");
|
1060
1081
|
reject(e);
|
@@ -1074,7 +1095,7 @@ function fetchIt(url, controlOptions) {
|
|
1074
1095
|
|
1075
1096
|
result = setSelection.call(this, newValue);
|
1076
1097
|
|
1077
|
-
|
1098
|
+
queueMicrotask(() => {
|
1078
1099
|
checkOptionState.call(this);
|
1079
1100
|
setStatusOrRemoveBadges.call(this, "closed");
|
1080
1101
|
updatePopper.call(this);
|
@@ -1203,7 +1224,7 @@ function getSummaryTemplate() {
|
|
1203
1224
|
autocomplete="off"
|
1204
1225
|
tabindex="0"
|
1205
1226
|
>
|
1206
|
-
<div data-monster-replace="path:messages.selected"></div>
|
1227
|
+
<div data-monster-replace="path:messages.selected"></div>
|
1207
1228
|
</div>`;
|
1208
1229
|
}
|
1209
1230
|
|
@@ -1239,52 +1260,9 @@ function parseSlotsToOptions() {
|
|
1239
1260
|
});
|
1240
1261
|
});
|
1241
1262
|
|
1242
|
-
runAsOptionLengthChanged.call(this, options.length);
|
1243
1263
|
this.setOption("options", options);
|
1244
1264
|
}
|
1245
1265
|
|
1246
|
-
/**
|
1247
|
-
* wait until all options are finished rendering
|
1248
|
-
*
|
1249
|
-
* @private
|
1250
|
-
* @param {int} targetLength
|
1251
|
-
*/
|
1252
|
-
function runAsOptionLengthChanged(targetLength) {
|
1253
|
-
const self = this;
|
1254
|
-
|
1255
|
-
if (!self[optionsElementSymbol]) {
|
1256
|
-
return;
|
1257
|
-
}
|
1258
|
-
|
1259
|
-
const callback = function (mutationsList, observer) {
|
1260
|
-
const run = false;
|
1261
|
-
for (const mutation of mutationsList) {
|
1262
|
-
if (mutation.type === "childList") {
|
1263
|
-
const run = true;
|
1264
|
-
break;
|
1265
|
-
}
|
1266
|
-
}
|
1267
|
-
|
1268
|
-
if (run === true) {
|
1269
|
-
const nodes = self[optionsElementSymbol].querySelectorAll(
|
1270
|
-
`div[${ATTRIBUTE_ROLE}=option]`,
|
1271
|
-
);
|
1272
|
-
|
1273
|
-
if (nodes.length === targetLength) {
|
1274
|
-
checkOptionState.call(self);
|
1275
|
-
observer.disconnect();
|
1276
|
-
}
|
1277
|
-
}
|
1278
|
-
};
|
1279
|
-
|
1280
|
-
const observer = new MutationObserver(callback);
|
1281
|
-
observer.observe(self[optionsElementSymbol], {
|
1282
|
-
attributes: false,
|
1283
|
-
childList: true,
|
1284
|
-
subtree: true,
|
1285
|
-
});
|
1286
|
-
}
|
1287
|
-
|
1288
1266
|
/**
|
1289
1267
|
* @private
|
1290
1268
|
* @param {*} value
|
@@ -1748,6 +1726,8 @@ function handleFilterKeyEvents() {
|
|
1748
1726
|
if (getFilterMode.call(this) !== FILTER_MODE_REMOTE) {
|
1749
1727
|
filterOptions.call(this);
|
1750
1728
|
} else {
|
1729
|
+
this[cleanupOptionsListSymbol] = true;
|
1730
|
+
|
1751
1731
|
filterFromRemote.call(this).catch((e) => {
|
1752
1732
|
addErrorAttribute(this, e);
|
1753
1733
|
});
|
@@ -2075,10 +2055,29 @@ function clearSelection() {
|
|
2075
2055
|
});
|
2076
2056
|
}
|
2077
2057
|
|
2058
|
+
const optionAvailableDeadManSymbol = Symbol("optionAvailableDeadManSymbol");
|
2059
|
+
|
2078
2060
|
/**
|
2079
2061
|
* @private
|
2080
2062
|
*/
|
2081
2063
|
function areOptionsAvailableAndInit() {
|
2064
|
+
// against flickering
|
2065
|
+
if (this[optionAvailableDeadManSymbol] instanceof DeadMansSwitch) {
|
2066
|
+
try {
|
2067
|
+
this[optionAvailableDeadManSymbol].touch();
|
2068
|
+
return;
|
2069
|
+
} catch (e) {
|
2070
|
+
delete this[optionAvailableDeadManSymbol];
|
2071
|
+
}
|
2072
|
+
}
|
2073
|
+
|
2074
|
+
this[optionAvailableDeadManSymbol] = new DeadMansSwitch(200, () => {
|
2075
|
+
areOptionsAvailableAndInitInternal.call(this);
|
2076
|
+
delete this[timerCallbackSymbol];
|
2077
|
+
});
|
2078
|
+
}
|
2079
|
+
|
2080
|
+
function areOptionsAvailableAndInitInternal() {
|
2082
2081
|
// prevent multiple calls
|
2083
2082
|
if (this[areOptionsAvailableAndInitSymbol] === undefined) {
|
2084
2083
|
this[areOptionsAvailableAndInitSymbol] = 0;
|
@@ -2112,6 +2111,17 @@ function areOptionsAvailableAndInit() {
|
|
2112
2111
|
msg = this.getOption("labels.click-to-load-options");
|
2113
2112
|
}
|
2114
2113
|
|
2114
|
+
if (this.getOption("filter.mode") === FILTER_MODE_REMOTE) {
|
2115
|
+
msg = "";
|
2116
|
+
}
|
2117
|
+
|
2118
|
+
if (
|
2119
|
+
containsAttributeToken(this[controlElementSymbol], "class", "open") ===
|
2120
|
+
true
|
2121
|
+
) {
|
2122
|
+
msg = "";
|
2123
|
+
}
|
2124
|
+
|
2115
2125
|
this.setOption("messages.control", msg);
|
2116
2126
|
this.setOption("messages.summary", "");
|
2117
2127
|
|
@@ -2443,6 +2453,11 @@ function fetchData(url) {
|
|
2443
2453
|
.then((response) => {
|
2444
2454
|
self[isLoadingSymbol] = false;
|
2445
2455
|
delayWatch = true;
|
2456
|
+
|
2457
|
+
if (response.status >= 200 && response.status < 300) {
|
2458
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
2459
|
+
}
|
2460
|
+
|
2446
2461
|
const contentType = response.headers.get("content-type");
|
2447
2462
|
if (contentType && contentType.indexOf("application/json") !== -1) {
|
2448
2463
|
return response.text();
|
@@ -2516,6 +2531,8 @@ function show() {
|
|
2516
2531
|
return;
|
2517
2532
|
}
|
2518
2533
|
|
2534
|
+
initDefaultOptionsFromUrl.call(this);
|
2535
|
+
|
2519
2536
|
const hasPopperFilterFlag =
|
2520
2537
|
this.getOption("filter.position") === FILTER_POSITION_POPPER &&
|
2521
2538
|
getFilterMode.call(this) !== FILTER_MODE_DISABLED;
|
@@ -2543,6 +2560,27 @@ function show() {
|
|
2543
2560
|
});
|
2544
2561
|
}
|
2545
2562
|
|
2563
|
+
function initDefaultOptionsFromUrl() {
|
2564
|
+
const url = this.getOption("filter.defaultOptionsUrl");
|
2565
|
+
if (!url) {
|
2566
|
+
return;
|
2567
|
+
}
|
2568
|
+
|
2569
|
+
this.setOption("filter.defaultOptionsUrl", null);
|
2570
|
+
|
2571
|
+
fetchData
|
2572
|
+
.call(this, url)
|
2573
|
+
.then((data) => {
|
2574
|
+
this[cleanupOptionsListSymbol] = false;
|
2575
|
+
importOptionsIntern.call(this, data);
|
2576
|
+
setStatusOrRemoveBadges.call(this, "open");
|
2577
|
+
})
|
2578
|
+
.catch((e) => {
|
2579
|
+
addErrorAttribute(this, e);
|
2580
|
+
setStatusOrRemoveBadges.call(this, "error");
|
2581
|
+
});
|
2582
|
+
}
|
2583
|
+
|
2546
2584
|
/**
|
2547
2585
|
* @private
|
2548
2586
|
*/
|
@@ -2692,6 +2730,7 @@ function initEventHandler() {
|
|
2692
2730
|
ATTRIBUTE_ROLE,
|
2693
2731
|
"remove-badge",
|
2694
2732
|
);
|
2733
|
+
|
2695
2734
|
if (element instanceof HTMLElement) {
|
2696
2735
|
return;
|
2697
2736
|
}
|
@@ -2741,6 +2780,34 @@ function initEventHandler() {
|
|
2741
2780
|
self.addEventListener("input", self[inputEventHandler]);
|
2742
2781
|
self.addEventListener("keydown", self[keyEventHandler]);
|
2743
2782
|
|
2783
|
+
const callback = () => {
|
2784
|
+
if (this[debounceOptionsMutationObserverSymbol] instanceof DeadMansSwitch) {
|
2785
|
+
try {
|
2786
|
+
this[debounceOptionsMutationObserverSymbol].touch();
|
2787
|
+
return;
|
2788
|
+
} catch (e) {
|
2789
|
+
delete this[debounceOptionsMutationObserverSymbol];
|
2790
|
+
}
|
2791
|
+
}
|
2792
|
+
|
2793
|
+
this[debounceOptionsMutationObserverSymbol] = new DeadMansSwitch(
|
2794
|
+
100,
|
2795
|
+
() => {
|
2796
|
+
checkOptionState.call(self);
|
2797
|
+
calcAndSetOptionsDimension.call(self);
|
2798
|
+
updatePopper.call(self);
|
2799
|
+
delete this[debounceOptionsMutationObserverSymbol];
|
2800
|
+
},
|
2801
|
+
);
|
2802
|
+
};
|
2803
|
+
|
2804
|
+
const observer = new MutationObserver(callback);
|
2805
|
+
observer.observe(self[optionsElementSymbol], {
|
2806
|
+
attributes: false,
|
2807
|
+
childList: true,
|
2808
|
+
subtree: true,
|
2809
|
+
});
|
2810
|
+
|
2744
2811
|
return self;
|
2745
2812
|
}
|
2746
2813
|
|
@@ -2780,16 +2847,16 @@ function setStatusOrRemoveBadges(suggestion) {
|
|
2780
2847
|
return;
|
2781
2848
|
}
|
2782
2849
|
|
2783
|
-
if (
|
2784
|
-
if (current !== "
|
2785
|
-
this.setOption("classes.statusOrRemoveBadge", "
|
2850
|
+
if (this[controlElementSymbol].classList.contains("open")) {
|
2851
|
+
if (current !== "open") {
|
2852
|
+
this.setOption("classes.statusOrRemoveBadge", "open");
|
2786
2853
|
}
|
2787
2854
|
return;
|
2788
2855
|
}
|
2789
2856
|
|
2790
|
-
if (
|
2791
|
-
if (current !== "
|
2792
|
-
this.setOption("classes.statusOrRemoveBadge", "
|
2857
|
+
if (clearAllFlag) {
|
2858
|
+
if (current !== "clear") {
|
2859
|
+
this.setOption("classes.statusOrRemoveBadge", "clear");
|
2793
2860
|
}
|
2794
2861
|
return;
|
2795
2862
|
}
|
@@ -2902,10 +2969,10 @@ function getTemplate() {
|
|
2902
2969
|
data-monster-attributes="
|
2903
2970
|
type path:type,
|
2904
2971
|
role path:role,
|
2905
|
-
value path:options.value,
|
2906
|
-
name path:name,
|
2972
|
+
value path:options.value,
|
2973
|
+
name path:name,
|
2907
2974
|
part path:type | prefix:option- | suffix: form,
|
2908
|
-
class path:options.class
|
2975
|
+
class path:options.class
|
2909
2976
|
" tabindex="-1">
|
2910
2977
|
<div data-monster-replace="path:options.label"
|
2911
2978
|
part="option-label"></div>
|
@@ -2917,8 +2984,8 @@ function getTemplate() {
|
|
2917
2984
|
<div data-monster-role="badge"
|
2918
2985
|
part="badge"
|
2919
2986
|
data-monster-attributes="
|
2920
|
-
data-monster-value path:selection | index:value,
|
2921
|
-
class path:classes | index:badge,
|
2987
|
+
data-monster-value path:selection | index:value,
|
2988
|
+
class path:classes | index:badge,
|
2922
2989
|
part path:type | suffix:-option | prefix: form-" tabindex="-1">
|
2923
2990
|
<div data-monster-replace="path:selection | index:label" part="badge-label"
|
2924
2991
|
data-monster-role="badge-label"></div>
|