@schukai/monster 3.79.0 → 3.80.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -12,61 +12,61 @@
12
12
  * SPDX-License-Identifier: AGPL-3.0
13
13
  */
14
14
 
15
- import {instanceSymbol} from "../../constants.mjs";
16
- import {internalSymbol} from "../../constants.mjs";
17
- import {buildMap} from "../../data/buildmap.mjs";
18
- import {DeadMansSwitch} from "../../util/deadmansswitch.mjs";
19
- import {positionPopper} from "./util/floating-ui.mjs";
15
+ import { instanceSymbol } from "../../constants.mjs";
16
+ import { internalSymbol } from "../../constants.mjs";
17
+ import { buildMap } from "../../data/buildmap.mjs";
18
+ import { DeadMansSwitch } from "../../util/deadmansswitch.mjs";
19
+ import { positionPopper } from "./util/floating-ui.mjs";
20
20
  import {
21
- addAttributeToken,
22
- findClosestByAttribute,
23
- removeAttributeToken,
21
+ addAttributeToken,
22
+ findClosestByAttribute,
23
+ removeAttributeToken,
24
24
  } from "../../dom/attributes.mjs";
25
25
  import {
26
- ATTRIBUTE_ERRORMESSAGE,
27
- ATTRIBUTE_PREFIX,
28
- ATTRIBUTE_ROLE,
26
+ ATTRIBUTE_ERRORMESSAGE,
27
+ ATTRIBUTE_PREFIX,
28
+ ATTRIBUTE_ROLE,
29
29
  } from "../../dom/constants.mjs";
30
- import {CustomControl} from "../../dom/customcontrol.mjs";
30
+ import { CustomControl } from "../../dom/customcontrol.mjs";
31
31
  import {
32
- assembleMethodSymbol,
33
- getSlottedElements,
34
- registerCustomElement,
32
+ assembleMethodSymbol,
33
+ getSlottedElements,
34
+ registerCustomElement,
35
35
  } from "../../dom/customelement.mjs";
36
36
  import {
37
- findTargetElementFromEvent,
38
- fireCustomEvent,
39
- fireEvent,
37
+ findTargetElementFromEvent,
38
+ fireCustomEvent,
39
+ fireEvent,
40
40
  } from "../../dom/events.mjs";
41
- import {getDocument} from "../../dom/util.mjs";
42
- import {Formatter} from "../../text/formatter.mjs";
43
- import {getGlobal} from "../../types/global.mjs";
44
- import {ID} from "../../types/id.mjs";
41
+ import { getDocument } from "../../dom/util.mjs";
42
+ import { Formatter } from "../../text/formatter.mjs";
43
+ import { getGlobal } from "../../types/global.mjs";
44
+ import { ID } from "../../types/id.mjs";
45
45
  import {
46
- isArray,
47
- isFunction,
48
- isInteger,
49
- isIterable,
50
- isObject,
51
- isPrimitive,
52
- isString,
46
+ isArray,
47
+ isFunction,
48
+ isInteger,
49
+ isIterable,
50
+ isObject,
51
+ isPrimitive,
52
+ isString,
53
53
  } from "../../types/is.mjs";
54
- import {Observer} from "../../types/observer.mjs";
55
- import {ProxyObserver} from "../../types/proxyobserver.mjs";
56
- import {validateArray, validateString} from "../../types/validate.mjs";
57
- import {Processing} from "../../util/processing.mjs";
58
- import {STYLE_DISPLAY_MODE_BLOCK} from "./constants.mjs";
59
- import {SelectStyleSheet} from "./stylesheet/select.mjs";
54
+ import { Observer } from "../../types/observer.mjs";
55
+ import { ProxyObserver } from "../../types/proxyobserver.mjs";
56
+ import { validateArray, validateString } from "../../types/validate.mjs";
57
+ import { Processing } from "../../util/processing.mjs";
58
+ import { STYLE_DISPLAY_MODE_BLOCK } from "./constants.mjs";
59
+ import { SelectStyleSheet } from "./stylesheet/select.mjs";
60
60
  import {
61
- getDocumentTranslations,
62
- Translations,
61
+ getDocumentTranslations,
62
+ Translations,
63
63
  } from "../../i18n/translations.mjs";
64
64
 
65
65
  export {
66
- Select,
67
- popperElementSymbol,
68
- getSummaryTemplate,
69
- getSelectionTemplate,
66
+ Select,
67
+ popperElementSymbol,
68
+ getSummaryTemplate,
69
+ getSelectionTemplate,
70
70
  };
71
71
 
72
72
  /**
@@ -199,7 +199,7 @@ const popperFilterElementSymbol = Symbol("popperFilterElement");
199
199
  * @type {Symbol}
200
200
  */
201
201
  const popperFilterContainerElementSymbol = Symbol(
202
- "popperFilterContainerElement",
202
+ "popperFilterContainerElement",
203
203
  );
204
204
 
205
205
  /**
@@ -285,585 +285,581 @@ const FILTER_POSITION_INLINE = "inline";
285
285
  * @fires monster-changed
286
286
  */
287
287
  class Select extends CustomControl {
288
-
289
- /**
290
- *
291
- */
292
- constructor() {
293
- super();
294
- initOptionObserver.call(this);
295
- }
296
-
297
- /**
298
- * This method is called by the `instanceof` operator.
299
- * @returns {Symbol}
300
- */
301
- static get [instanceSymbol]() {
302
- return Symbol.for("@schukai/monster/components/form/select@@instance");
303
- }
304
-
305
- /**
306
- * The current selection of the Select
307
- *
308
- * ```
309
- * e = document.querySelector('monster-select');
310
- * console.log(e.value)
311
- * // ↦ 1
312
- * // ↦ ['1','2']
313
- * ```
314
- *
315
- * @returns {string}
316
- */
317
- get value() {
318
- return convertSelectionToValue.call(this, this.getOption("selection"));
319
- }
320
-
321
- /**
322
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/attachInternals}
323
- * @return {boolean}
324
- */
325
- static get formAssociated() {
326
- return true;
327
- }
328
-
329
- /**
330
- * Set selection
331
- *
332
- * ```
333
- * e = document.querySelector('monster-select');
334
- * e.value=1
335
- * ```
336
- *
337
- * @property {string|array} value
338
- * @throws {Error} unsupported type
339
- */
340
- set value(value) {
341
- const result = convertValueToSelection.call(this, value);
342
- setSelection
343
- .call(this, result.selection)
344
- .then(() => {
345
- })
346
- .catch((e) => {
347
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
348
- });
349
- }
350
-
351
- /**
352
- * To set the options via the HTML tag, the attribute `data-monster-options` must be used.
353
- * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
354
- *
355
- * The individual configuration values can be found in the table.
356
- *
357
- * @property {Object} toggleEventType=click,touch List of event types to be observed for opening the dropdown
358
- * @property {boolean} delegatesFocus=false lorem [see mozilla.org](https://developer.mozilla.org/en-US/docs/Web/API/ShadowRoot/delegatesFocus)
359
- * @property {Object[]} options Selection of key identifier pairs available for selection and displayed in the dropdown.
360
- * @property {string} options[].label
361
- * @property {string} options[].value
362
- * @property {string} options[].visibility hidden or visible
363
- * @property {Array} selection Selected options
364
- * @property {Integer} showMaxOptions=10 Maximum number of visible options before a scroll bar should be displayed.
365
- * @property {string} type=radio Multiple (checkbox) or single selection (radio)
366
- * @property {string} name=(random id) Name of the form field
367
- * @property {string} url Load options from server per url
368
- * @property {string} lookup Load options from server per url
369
- * @property {boolean} lookup.url=null Load options from server per url
370
- * @property {boolean} lookup.individually=false Load options individually
371
- * @property {Object} fetch Fetch [see Using Fetch mozilla.org](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch)
372
- * @property {String} fetch.redirect=error
373
- * @property {String} fetch.method=GET
374
- * @property {String} fetch.mode=same-origin
375
- * @property {String} fetch.credentials=same-origin
376
- * @property {Object} fetch.headers={"accept":"application/json"}}
377
- * @property {Object} labels
378
- * @property {string} labels.cannot-be-loaded cannot be loaded
379
- * @property {string} labels.no-options-available no options available
380
- * @property {string} labels.select-an-option select an option
381
- * @property {string} labels.no-option no option in the list, maybe you have to change the filter
382
- * @property {Object} features List with features
383
- * @property {Boolean} features.clearAll=true Display of a delete button to delete the entire selection
384
- * @property {Boolean} features.clear=true Display of a delete key for deleting the specific selection
385
- * @property {Boolean} features.lazyLoad=false Load options when first opening the dropdown
386
- * @property {Boolean} features.closeOnSelect=false Close the dropdown when an option is selected (since 3.54.0)
387
- * @property {Boolean} features.emptyValueIfNoOptions=false If no options are available, the selection is set to an empty array
388
- * @property {Boolean} features.storeFetchedData=false Store fetched data in the object
389
- * @property {Boolean} features.useStrictValueComparison=true Use strict value comparison for the selection
390
- * @property {Boolean} filter.defaultValue=* Default filter value, if the filter is empty
391
- * @property {Boolean} filter.mode=options Filter mode, values: options, remote, disabled
392
- * @property {Object} templates Template definitions
393
- * @property {string} templates.main Main template
394
- * @property {string} templateMapping Mapping of the template placeholders
395
- * @property {string} templateMapping.selected Selected Template
396
- * @property {Object} popper [PopperJS Options](https://popper.js.org/docs/v2/)
397
- * @property {string} popper.placement=bottom PopperJS placement
398
- * @property {Object[]} modifiers={name:offset} PopperJS placement
399
- * @property {Object} mapping
400
- * @property {String} mapping.selector=* Path to select the appropriate entries
401
- * @property {String} mapping.labelTemplate="" template with the label placeholders in the form ${name}, where name is the key (**)
402
- * @property {String} mapping.valueTemplate="" template with the value placeholders in the form ${name}, where name is the key
403
- * @property {Monster.Components.Form~exampleFilterCallback|undefined} mapping.filter Filtering of values via a function
404
- * @property {Object} formatter
405
- * @property {Monster.Components.Form~formatterSelectionCallback|undefined} formatter.selection format selection label
406
- */
407
- get defaults() {
408
- return Object.assign(
409
- {},
410
- super.defaults,
411
- {
412
- toggleEventType: ["click", "touch"],
413
- delegatesFocus: false,
414
- options: [],
415
- selection: [],
416
- showMaxOptions: 10,
417
- type: "radio",
418
- name: new ID("s").toString(),
419
- features: {
420
- clearAll: true,
421
- clear: true,
422
- lazyLoad: false,
423
- closeOnSelect: false,
424
- emptyValueIfNoOptions: false,
425
- storeFetchedData: false,
426
- useStrictValueComparison: false,
427
- },
428
- url: null,
429
- lookup: {
430
- url: null,
431
- grouping: false,
432
- },
433
- labels: {
434
- "cannot-be-loaded": "Cannot be loaded",
435
- "no-options-available": noOptionsAvailableMessage,
436
- "click-to-load-options": clickToLoadOptionsMessage,
437
- "select-an-option": "Select an option",
438
- "summary-text": {
439
- zero: "No entries were selected",
440
- one: '<span class="monster-badge-primary-pill">1</span> entry was selected',
441
- other:
442
- '<span class="monster-badge-primary-pill">${count}</span> entries were selected',
443
- },
444
- "no-options":
445
- "Unfortunately, there are no options available in the list.",
446
- "no-options-found":
447
- "No options are available in the list. Please consider modifying the filter.",
448
- },
449
- messages: {
450
- control: null,
451
- selected: null,
452
- emptyOptions: null,
453
- },
454
- fetch: {
455
- redirect: "error",
456
- method: "GET",
457
- mode: "same-origin",
458
- credentials: "same-origin",
459
- headers: {
460
- accept: "application/json",
461
- },
462
- },
463
- filter: {
464
- defaultValue: "*",
465
- mode: FILTER_MODE_DISABLED,
466
- position: FILTER_POSITION_INLINE,
467
- marker: {
468
- open: "{",
469
- close: "}",
470
- },
471
- },
472
- classes: {
473
- badge: "monster-badge-primary",
474
- statusOrRemoveBadge: "empty",
475
- },
476
- mapping: {
477
- selector: "*",
478
- labelTemplate: "",
479
- valueTemplate: "",
480
- filter: null,
481
- },
482
- formatter: {
483
- selection: buildSelectionLabel,
484
- },
485
- templates: {
486
- main: getTemplate(),
487
- },
488
- templateMapping: {
489
- /** with the attribute `data-monster-selected-template` the template for the selected options can be defined. */
490
- selected: getSelectionTemplate(),
491
- },
492
-
493
- popper: {
494
- placement: "bottom",
495
- middleware: ["flip", "offset:1"],
496
- },
497
- },
498
- initOptionsFromArguments.call(this),
499
- );
500
- }
501
-
502
- /**
503
- * @return {Select}
504
- */
505
- [assembleMethodSymbol]() {
506
- const self = this;
507
- super[assembleMethodSymbol]();
508
-
509
- initControlReferences.call(self);
510
- initEventHandler.call(self);
511
-
512
- const lazyLoadFlag = self.getOption("features.lazyLoad");
513
-
514
- if (self.hasAttribute("value")) {
515
- new Processing(10, () => {
516
- this.value = this.getAttribute("value");
517
- })
518
- .run()
519
- .catch((e) => {
520
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
521
- });
522
- }
523
-
524
- if (self.getOption("url") !== null) {
525
-
526
- if (lazyLoadFlag) {
527
- lookupSelection.call(self);
528
- } else {
529
- self.fetch().catch((e) => {
530
- addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, `${e}`);
531
- });
532
- }
533
- }
534
-
535
- let lastValue = self.value;
536
- self[internalSymbol].attachObserver(
537
- new Observer(function () {
538
- if (isObject(this) && this instanceof ProxyObserver) {
539
- const n = this.getSubject()?.options?.value;
540
-
541
- if (lastValue !== n) {
542
- lastValue = n;
543
- setSelection
544
- .call(self, n)
545
- .then(() => {
546
- })
547
- .catch((e) => {
548
- addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, `${e}`);
549
- });
550
- }
551
- }
552
- }),
553
- );
554
-
555
- areOptionsAvailableAndInit.call(self);
556
-
557
- return this;
558
- }
559
-
560
- /**
561
- *
562
- * @returns {*}
563
- * @throws {Error} storeFetchedData is not enabled
564
- * @since 3.66.0
565
- */
566
- getLastFetchedData() {
567
- if (this.getOption("features.storeFetchedData") === false) {
568
- throw new Error("storeFetchedData is not enabled");
569
- }
570
-
571
- return this?.[lastFetchedDataSymbol];
572
- }
573
-
574
- /**
575
- * The Button.click() method simulates a click on the internal button element.
576
- *
577
- * @since 3.27.0
578
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/click}
579
- */
580
- click() {
581
- if (this.getOption("disabled") === true) {
582
- return;
583
- }
584
-
585
- toggle.call(this);
586
- }
587
-
588
- /**
589
- * The Button.focus() method sets focus on the internal button element.
590
- *
591
- * @since 3.27.0
592
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus}
593
- */
594
- focus(options) {
595
- if (this.getOption("disabled") === true) {
596
- return;
597
- }
598
-
599
- new Processing(() => {
600
- gatherState.call(this);
601
- focusFilter.call(this, options);
602
- })
603
- .run()
604
- .catch((e) => {
605
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, `${e}`);
606
- });
607
- }
608
-
609
- /**
610
- * The Button.blur() method removes focus from the internal button element.
611
- * @link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/blur
612
- */
613
- blur() {
614
- new Processing(() => {
615
- gatherState.call(this);
616
- blurFilter.call(this);
617
- })
618
- .run()
619
- .catch((e) => {
620
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, `${e}`);
621
- });
622
- }
623
-
624
- /**
625
- * If no url is specified, the options are taken from the Component itself.
626
- *
627
- * @param {string|URL} url URL to fetch the options
628
- * @return {Promise}
629
- */
630
- fetch(url) {
631
- if (url instanceof URL) {
632
- url = url.toString();
633
- }
634
-
635
- if (url !== undefined && url !== null) {
636
- url = validateString(url);
637
- } else {
638
- url = this.getOption("url");
639
- if (url === null) {
640
- return Promise.reject(new Error("No url defined"));
641
- }
642
- }
643
-
644
- hide.call(this);
645
-
646
- return new Promise((resolve, reject) => {
647
- setStatusOrRemoveBadges.call(this, "loading");
648
-
649
- new Processing(10, () => {
650
- this.setOption("options", []);
651
-
652
- fetchData
653
- .call(this, url)
654
- .then((map) => {
655
- if (
656
- isObject(map) ||
657
- isArray(map) ||
658
- map instanceof Set ||
659
- map instanceof Map
660
- ) {
661
-
662
- try {
663
- this.importOptions(map);
664
- } catch (e) {
665
- setStatusOrRemoveBadges.call(this, "error");
666
- reject(e);
667
- return;
668
- }
669
-
670
- this[lastFetchedDataSymbol] = map;
671
-
672
- let result;
673
- const selection = this.getOption("selection");
674
- let newValue = [];
675
- if (selection) {
676
- newValue = selection;
677
- } else if (this.hasAttribute("value")) {
678
- newValue = this.getAttribute("value");
679
- }
680
-
681
- result = setSelection.call(this, newValue);
682
- requestAnimationFrame(() => {
683
- checkOptionState.call(this);
684
- setStatusOrRemoveBadges.call(this, "closed");
685
- resolve(result);
686
- });
687
-
688
- return;
689
- }
690
-
691
- setStatusOrRemoveBadges.call(this, "error");
692
- reject(new Error("invalid response"));
693
- })
694
- .catch((e) => {
695
- setStatusOrRemoveBadges.call(this, "error");
696
- reject(e);
697
- });
698
- })
699
- .run()
700
- .catch((e) => {
701
- setStatusOrRemoveBadges.call(this, "error");
702
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
703
- reject(e);
704
- });
705
- });
706
- }
707
-
708
- /**
709
- * @return {void}
710
- */
711
- connectedCallback() {
712
- super.connectedCallback();
713
- const document = getDocument();
714
-
715
- for (const [, type] of Object.entries(["click", "touch"])) {
716
- // close on outside ui-events
717
- document.addEventListener(type, this[closeEventHandler]);
718
- }
719
-
720
- parseSlotsToOptions.call(this);
721
- attachResizeObserver.call(this);
722
- updatePopper.call(this);
723
-
724
- new Processing(() => {
725
- gatherState.call(this);
726
- focusFilter.call(this);
727
- }).run();
728
- }
729
-
730
- /**
731
- * @return {void}
732
- */
733
- disconnectedCallback() {
734
- super.disconnectedCallback();
735
- const document = getDocument();
736
-
737
- // close on outside ui-events
738
- for (const [, type] of Object.entries(["click", "touch"])) {
739
- document.removeEventListener(type, this[closeEventHandler]);
740
- }
741
-
742
- disconnectResizeObserver.call(this);
743
- }
744
-
745
- /**
746
- * Import Select Options from dataset
747
- * Not to be confused with the control defaults/options
748
- *
749
- * @param {array|object|Map|Set} data
750
- * @return {Select}
751
- * @throws {Error} map is not iterable
752
- * @throws {Error} missing label configuration
753
- */
754
- importOptions(data) {
755
- const mappingOptions = this.getOption("mapping", {});
756
- const selector = mappingOptions?.["selector"];
757
- const labelTemplate = mappingOptions?.["labelTemplate"];
758
- const valueTemplate = mappingOptions?.["valueTemplate"];
759
- const filter = mappingOptions?.["filter"];
760
-
761
- let flag = false;
762
- if (labelTemplate === "") {
763
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, "empty label template");
764
- flag = true;
765
- }
766
-
767
- if (valueTemplate === "") {
768
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, "empty value template");
769
- flag = true;
770
- }
771
-
772
- if (flag === true) {
773
- throw new Error("missing label configuration");
774
- }
775
-
776
- const map = buildMap(data, selector, labelTemplate, valueTemplate, filter);
777
-
778
- const options = [];
779
- if (!isIterable(map)) {
780
- throw new Error("map is not iterable");
781
- }
782
-
783
- const visibility = "visible";
784
-
785
- map.forEach((label, value) => {
786
- options.push({
787
- value,
788
- label,
789
- visibility,
790
- data: map.get(value),
791
- });
792
- });
793
-
794
- runAsOptionLengthChanged.call(this, map.size);
795
- this.setOption("options", options);
796
-
797
- fireCustomEvent(this, "monster-options-set", {
798
- options,
799
- });
800
-
801
- return this;
802
- }
803
-
804
- /**
805
- * @private
806
- * @return {Select}
807
- */
808
- calcAndSetOptionsDimension() {
809
- calcAndSetOptionsDimension.call(this);
810
- return this;
811
- }
812
-
813
- /**
814
- *
815
- * @return {string}
816
- */
817
- static getTag() {
818
- return "monster-select";
819
- }
820
-
821
- /**
822
- *
823
- * @return {CSSStyleSheet[]}
824
- */
825
- static getCSSStyleSheet() {
826
- return [SelectStyleSheet];
827
- }
288
+ /**
289
+ *
290
+ */
291
+ constructor() {
292
+ super();
293
+ initOptionObserver.call(this);
294
+ }
295
+
296
+ /**
297
+ * This method is called by the `instanceof` operator.
298
+ * @returns {Symbol}
299
+ */
300
+ static get [instanceSymbol]() {
301
+ return Symbol.for("@schukai/monster/components/form/select@@instance");
302
+ }
303
+
304
+ /**
305
+ * The current selection of the Select
306
+ *
307
+ * ```
308
+ * e = document.querySelector('monster-select');
309
+ * console.log(e.value)
310
+ * // ↦ 1
311
+ * // ↦ ['1','2']
312
+ * ```
313
+ *
314
+ * @returns {string}
315
+ */
316
+ get value() {
317
+ return convertSelectionToValue.call(this, this.getOption("selection"));
318
+ }
319
+
320
+ /**
321
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/attachInternals}
322
+ * @return {boolean}
323
+ */
324
+ static get formAssociated() {
325
+ return true;
326
+ }
327
+
328
+ /**
329
+ * Set selection
330
+ *
331
+ * ```
332
+ * e = document.querySelector('monster-select');
333
+ * e.value=1
334
+ * ```
335
+ *
336
+ * @property {string|array} value
337
+ * @throws {Error} unsupported type
338
+ */
339
+ set value(value) {
340
+ const result = convertValueToSelection.call(this, value);
341
+ setSelection
342
+ .call(this, result.selection)
343
+ .then(() => {})
344
+ .catch((e) => {
345
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
346
+ });
347
+ }
348
+
349
+ /**
350
+ * To set the options via the HTML tag, the attribute `data-monster-options` must be used.
351
+ * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
352
+ *
353
+ * The individual configuration values can be found in the table.
354
+ *
355
+ * @property {Object} toggleEventType=click,touch List of event types to be observed for opening the dropdown
356
+ * @property {boolean} delegatesFocus=false lorem [see mozilla.org](https://developer.mozilla.org/en-US/docs/Web/API/ShadowRoot/delegatesFocus)
357
+ * @property {Object[]} options Selection of key identifier pairs available for selection and displayed in the dropdown.
358
+ * @property {string} options[].label
359
+ * @property {string} options[].value
360
+ * @property {string} options[].visibility hidden or visible
361
+ * @property {Array} selection Selected options
362
+ * @property {Integer} showMaxOptions=10 Maximum number of visible options before a scroll bar should be displayed.
363
+ * @property {string} type=radio Multiple (checkbox) or single selection (radio)
364
+ * @property {string} name=(random id) Name of the form field
365
+ * @property {string} url Load options from server per url
366
+ * @property {string} lookup Load options from server per url
367
+ * @property {boolean} lookup.url=null Load options from server per url
368
+ * @property {boolean} lookup.individually=false Load options individually
369
+ * @property {Object} fetch Fetch [see Using Fetch mozilla.org](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch)
370
+ * @property {String} fetch.redirect=error
371
+ * @property {String} fetch.method=GET
372
+ * @property {String} fetch.mode=same-origin
373
+ * @property {String} fetch.credentials=same-origin
374
+ * @property {Object} fetch.headers={"accept":"application/json"}}
375
+ * @property {Object} labels
376
+ * @property {string} labels.cannot-be-loaded cannot be loaded
377
+ * @property {string} labels.no-options-available no options available
378
+ * @property {string} labels.select-an-option select an option
379
+ * @property {string} labels.no-option no option in the list, maybe you have to change the filter
380
+ * @property {Object} features List with features
381
+ * @property {Boolean} features.clearAll=true Display of a delete button to delete the entire selection
382
+ * @property {Boolean} features.clear=true Display of a delete key for deleting the specific selection
383
+ * @property {Boolean} features.lazyLoad=false Load options when first opening the dropdown
384
+ * @property {Boolean} features.closeOnSelect=false Close the dropdown when an option is selected (since 3.54.0)
385
+ * @property {Boolean} features.emptyValueIfNoOptions=false If no options are available, the selection is set to an empty array
386
+ * @property {Boolean} features.storeFetchedData=false Store fetched data in the object
387
+ * @property {Boolean} features.useStrictValueComparison=true Use strict value comparison for the selection
388
+ * @property {Boolean} filter.defaultValue=* Default filter value, if the filter is empty
389
+ * @property {Boolean} filter.mode=options Filter mode, values: options, remote, disabled
390
+ * @property {Object} templates Template definitions
391
+ * @property {string} templates.main Main template
392
+ * @property {string} templateMapping Mapping of the template placeholders
393
+ * @property {string} templateMapping.selected Selected Template
394
+ * @property {Object} popper [PopperJS Options](https://popper.js.org/docs/v2/)
395
+ * @property {string} popper.placement=bottom PopperJS placement
396
+ * @property {Object[]} modifiers={name:offset} PopperJS placement
397
+ * @property {Object} mapping
398
+ * @property {String} mapping.selector=* Path to select the appropriate entries
399
+ * @property {String} mapping.labelTemplate="" template with the label placeholders in the form ${name}, where name is the key (**)
400
+ * @property {String} mapping.valueTemplate="" template with the value placeholders in the form ${name}, where name is the key
401
+ * @property {Monster.Components.Form~exampleFilterCallback|undefined} mapping.filter Filtering of values via a function
402
+ * @property {Object} formatter
403
+ * @property {Monster.Components.Form~formatterSelectionCallback|undefined} formatter.selection format selection label
404
+ */
405
+ get defaults() {
406
+ return Object.assign(
407
+ {},
408
+ super.defaults,
409
+ {
410
+ toggleEventType: ["click", "touch"],
411
+ delegatesFocus: false,
412
+ options: [],
413
+ selection: [],
414
+ showMaxOptions: 10,
415
+ type: "radio",
416
+ name: new ID("s").toString(),
417
+ features: {
418
+ clearAll: true,
419
+ clear: true,
420
+ lazyLoad: false,
421
+ closeOnSelect: false,
422
+ emptyValueIfNoOptions: false,
423
+ storeFetchedData: false,
424
+ useStrictValueComparison: false,
425
+ },
426
+ url: null,
427
+ lookup: {
428
+ url: null,
429
+ grouping: false,
430
+ },
431
+ labels: {
432
+ "cannot-be-loaded": "Cannot be loaded",
433
+ "no-options-available": noOptionsAvailableMessage,
434
+ "click-to-load-options": clickToLoadOptionsMessage,
435
+ "select-an-option": "Select an option",
436
+ "summary-text": {
437
+ zero: "No entries were selected",
438
+ one: '<span class="monster-badge-primary-pill">1</span> entry was selected',
439
+ other:
440
+ '<span class="monster-badge-primary-pill">${count}</span> entries were selected',
441
+ },
442
+ "no-options":
443
+ "Unfortunately, there are no options available in the list.",
444
+ "no-options-found":
445
+ "No options are available in the list. Please consider modifying the filter.",
446
+ },
447
+ messages: {
448
+ control: null,
449
+ selected: null,
450
+ emptyOptions: null,
451
+ },
452
+ fetch: {
453
+ redirect: "error",
454
+ method: "GET",
455
+ mode: "same-origin",
456
+ credentials: "same-origin",
457
+ headers: {
458
+ accept: "application/json",
459
+ },
460
+ },
461
+ filter: {
462
+ defaultValue: "*",
463
+ mode: FILTER_MODE_DISABLED,
464
+ position: FILTER_POSITION_INLINE,
465
+ marker: {
466
+ open: "{",
467
+ close: "}",
468
+ },
469
+ },
470
+ classes: {
471
+ badge: "monster-badge-primary",
472
+ statusOrRemoveBadge: "empty",
473
+ },
474
+ mapping: {
475
+ selector: "*",
476
+ labelTemplate: "",
477
+ valueTemplate: "",
478
+ filter: null,
479
+ },
480
+ formatter: {
481
+ selection: buildSelectionLabel,
482
+ },
483
+ templates: {
484
+ main: getTemplate(),
485
+ },
486
+ templateMapping: {
487
+ /** with the attribute `data-monster-selected-template` the template for the selected options can be defined. */
488
+ selected: getSelectionTemplate(),
489
+ },
490
+
491
+ popper: {
492
+ placement: "bottom",
493
+ middleware: ["flip", "offset:1"],
494
+ },
495
+ },
496
+ initOptionsFromArguments.call(this),
497
+ );
498
+ }
499
+
500
+ /**
501
+ * @return {Select}
502
+ */
503
+ [assembleMethodSymbol]() {
504
+ const self = this;
505
+ super[assembleMethodSymbol]();
506
+
507
+ initControlReferences.call(self);
508
+ initEventHandler.call(self);
509
+
510
+ const lazyLoadFlag = self.getOption("features.lazyLoad");
511
+
512
+ if (self.hasAttribute("value")) {
513
+ new Processing(10, () => {
514
+ this.value = this.getAttribute("value");
515
+ })
516
+ .run()
517
+ .catch((e) => {
518
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
519
+ });
520
+ }
521
+
522
+ if (self.getOption("url") !== null) {
523
+ if (lazyLoadFlag) {
524
+ lookupSelection.call(self);
525
+ } else {
526
+ self.fetch().catch((e) => {
527
+ addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, `${e}`);
528
+ });
529
+ }
530
+ }
531
+
532
+ let lastValue = self.value;
533
+ self[internalSymbol].attachObserver(
534
+ new Observer(function () {
535
+ if (isObject(this) && this instanceof ProxyObserver) {
536
+ const n = this.getSubject()?.options?.value;
537
+
538
+ if (lastValue !== n) {
539
+ lastValue = n;
540
+ setSelection
541
+ .call(self, n)
542
+ .then(() => {})
543
+ .catch((e) => {
544
+ addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, `${e}`);
545
+ });
546
+ }
547
+ }
548
+ }),
549
+ );
550
+
551
+ areOptionsAvailableAndInit.call(self);
552
+
553
+ return this;
554
+ }
555
+
556
+ /**
557
+ *
558
+ * @returns {*}
559
+ * @throws {Error} storeFetchedData is not enabled
560
+ * @since 3.66.0
561
+ */
562
+ getLastFetchedData() {
563
+ if (this.getOption("features.storeFetchedData") === false) {
564
+ throw new Error("storeFetchedData is not enabled");
565
+ }
566
+
567
+ return this?.[lastFetchedDataSymbol];
568
+ }
569
+
570
+ /**
571
+ * The Button.click() method simulates a click on the internal button element.
572
+ *
573
+ * @since 3.27.0
574
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/click}
575
+ */
576
+ click() {
577
+ if (this.getOption("disabled") === true) {
578
+ return;
579
+ }
580
+
581
+ toggle.call(this);
582
+ }
583
+
584
+ /**
585
+ * The Button.focus() method sets focus on the internal button element.
586
+ *
587
+ * @since 3.27.0
588
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus}
589
+ */
590
+ focus(options) {
591
+ if (this.getOption("disabled") === true) {
592
+ return;
593
+ }
594
+
595
+ new Processing(() => {
596
+ gatherState.call(this);
597
+ focusFilter.call(this, options);
598
+ })
599
+ .run()
600
+ .catch((e) => {
601
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, `${e}`);
602
+ });
603
+ }
604
+
605
+ /**
606
+ * The Button.blur() method removes focus from the internal button element.
607
+ * @link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/blur
608
+ */
609
+ blur() {
610
+ new Processing(() => {
611
+ gatherState.call(this);
612
+ blurFilter.call(this);
613
+ })
614
+ .run()
615
+ .catch((e) => {
616
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, `${e}`);
617
+ });
618
+ }
619
+
620
+ /**
621
+ * If no url is specified, the options are taken from the Component itself.
622
+ *
623
+ * @param {string|URL} url URL to fetch the options
624
+ * @return {Promise}
625
+ */
626
+ fetch(url) {
627
+ if (url instanceof URL) {
628
+ url = url.toString();
629
+ }
630
+
631
+ if (url !== undefined && url !== null) {
632
+ url = validateString(url);
633
+ } else {
634
+ url = this.getOption("url");
635
+ if (url === null) {
636
+ return Promise.reject(new Error("No url defined"));
637
+ }
638
+ }
639
+
640
+ hide.call(this);
641
+
642
+ return new Promise((resolve, reject) => {
643
+ setStatusOrRemoveBadges.call(this, "loading");
644
+
645
+ new Processing(10, () => {
646
+ this.setOption("options", []);
647
+
648
+ fetchData
649
+ .call(this, url)
650
+ .then((map) => {
651
+ if (
652
+ isObject(map) ||
653
+ isArray(map) ||
654
+ map instanceof Set ||
655
+ map instanceof Map
656
+ ) {
657
+ try {
658
+ this.importOptions(map);
659
+ } catch (e) {
660
+ setStatusOrRemoveBadges.call(this, "error");
661
+ reject(e);
662
+ return;
663
+ }
664
+
665
+ this[lastFetchedDataSymbol] = map;
666
+
667
+ let result;
668
+ const selection = this.getOption("selection");
669
+ let newValue = [];
670
+ if (selection) {
671
+ newValue = selection;
672
+ } else if (this.hasAttribute("value")) {
673
+ newValue = this.getAttribute("value");
674
+ }
675
+
676
+ result = setSelection.call(this, newValue);
677
+ requestAnimationFrame(() => {
678
+ checkOptionState.call(this);
679
+ setStatusOrRemoveBadges.call(this, "closed");
680
+ resolve(result);
681
+ });
682
+
683
+ return;
684
+ }
685
+
686
+ setStatusOrRemoveBadges.call(this, "error");
687
+ reject(new Error("invalid response"));
688
+ })
689
+ .catch((e) => {
690
+ setStatusOrRemoveBadges.call(this, "error");
691
+ reject(e);
692
+ });
693
+ })
694
+ .run()
695
+ .catch((e) => {
696
+ setStatusOrRemoveBadges.call(this, "error");
697
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
698
+ reject(e);
699
+ });
700
+ });
701
+ }
702
+
703
+ /**
704
+ * @return {void}
705
+ */
706
+ connectedCallback() {
707
+ super.connectedCallback();
708
+ const document = getDocument();
709
+
710
+ for (const [, type] of Object.entries(["click", "touch"])) {
711
+ // close on outside ui-events
712
+ document.addEventListener(type, this[closeEventHandler]);
713
+ }
714
+
715
+ parseSlotsToOptions.call(this);
716
+ attachResizeObserver.call(this);
717
+ updatePopper.call(this);
718
+
719
+ new Processing(() => {
720
+ gatherState.call(this);
721
+ focusFilter.call(this);
722
+ }).run();
723
+ }
724
+
725
+ /**
726
+ * @return {void}
727
+ */
728
+ disconnectedCallback() {
729
+ super.disconnectedCallback();
730
+ const document = getDocument();
731
+
732
+ // close on outside ui-events
733
+ for (const [, type] of Object.entries(["click", "touch"])) {
734
+ document.removeEventListener(type, this[closeEventHandler]);
735
+ }
736
+
737
+ disconnectResizeObserver.call(this);
738
+ }
739
+
740
+ /**
741
+ * Import Select Options from dataset
742
+ * Not to be confused with the control defaults/options
743
+ *
744
+ * @param {array|object|Map|Set} data
745
+ * @return {Select}
746
+ * @throws {Error} map is not iterable
747
+ * @throws {Error} missing label configuration
748
+ */
749
+ importOptions(data) {
750
+ const mappingOptions = this.getOption("mapping", {});
751
+ const selector = mappingOptions?.["selector"];
752
+ const labelTemplate = mappingOptions?.["labelTemplate"];
753
+ const valueTemplate = mappingOptions?.["valueTemplate"];
754
+ const filter = mappingOptions?.["filter"];
755
+
756
+ let flag = false;
757
+ if (labelTemplate === "") {
758
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, "empty label template");
759
+ flag = true;
760
+ }
761
+
762
+ if (valueTemplate === "") {
763
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, "empty value template");
764
+ flag = true;
765
+ }
766
+
767
+ if (flag === true) {
768
+ throw new Error("missing label configuration");
769
+ }
770
+
771
+ const map = buildMap(data, selector, labelTemplate, valueTemplate, filter);
772
+
773
+ const options = [];
774
+ if (!isIterable(map)) {
775
+ throw new Error("map is not iterable");
776
+ }
777
+
778
+ const visibility = "visible";
779
+
780
+ map.forEach((label, value) => {
781
+ options.push({
782
+ value,
783
+ label,
784
+ visibility,
785
+ data: map.get(value),
786
+ });
787
+ });
788
+
789
+ runAsOptionLengthChanged.call(this, map.size);
790
+ this.setOption("options", options);
791
+
792
+ fireCustomEvent(this, "monster-options-set", {
793
+ options,
794
+ });
795
+
796
+ return this;
797
+ }
798
+
799
+ /**
800
+ * @private
801
+ * @return {Select}
802
+ */
803
+ calcAndSetOptionsDimension() {
804
+ calcAndSetOptionsDimension.call(this);
805
+ return this;
806
+ }
807
+
808
+ /**
809
+ *
810
+ * @return {string}
811
+ */
812
+ static getTag() {
813
+ return "monster-select";
814
+ }
815
+
816
+ /**
817
+ *
818
+ * @return {CSSStyleSheet[]}
819
+ */
820
+ static getCSSStyleSheet() {
821
+ return [SelectStyleSheet];
822
+ }
828
823
  }
829
824
 
830
825
  function lookupSelection() {
831
- const self = this;
832
-
833
- setTimeout(() => {
834
-
835
- const selection = self.getOption("selection");
836
- if (selection.length === 0) {
837
- return;
838
- }
839
-
840
- if (self[isLoadingSymbol] === true) {
841
- return;
842
- }
843
-
844
- if (self[lazyLoadDoneSymbol] === true) {
845
- return;
846
- }
847
-
848
- let url = self.getOption("url");
849
- let lookupUrl = self.getOption("lookup.url");
850
- if (lookupUrl !== null) {
851
- url = lookupUrl;
852
- }
853
-
854
- if(this.getOption("lookup.grouping") === true) {
855
- filterFromRemoteByValue.call(self, url, selection.map((s) => s?.["value"]));
856
- return;
857
- }
858
-
859
- for (const s of selection) {
860
- if (s?.["value"]) {
861
- filterFromRemoteByValue.call(self, url, s?.["value"]);
862
- }
863
- }
864
-
865
- }, 100);
866
-
826
+ const self = this;
827
+
828
+ setTimeout(() => {
829
+ const selection = self.getOption("selection");
830
+ if (selection.length === 0) {
831
+ return;
832
+ }
833
+
834
+ if (self[isLoadingSymbol] === true) {
835
+ return;
836
+ }
837
+
838
+ if (self[lazyLoadDoneSymbol] === true) {
839
+ return;
840
+ }
841
+
842
+ let url = self.getOption("url");
843
+ let lookupUrl = self.getOption("lookup.url");
844
+ if (lookupUrl !== null) {
845
+ url = lookupUrl;
846
+ }
847
+
848
+ if (this.getOption("lookup.grouping") === true) {
849
+ filterFromRemoteByValue.call(
850
+ self,
851
+ url,
852
+ selection.map((s) => s?.["value"]),
853
+ );
854
+ return;
855
+ }
856
+
857
+ for (const s of selection) {
858
+ if (s?.["value"]) {
859
+ filterFromRemoteByValue.call(self, url, s?.["value"]);
860
+ }
861
+ }
862
+ }, 100);
867
863
  }
868
864
 
869
865
  /**
@@ -878,64 +874,64 @@ function lookupSelection() {
878
874
  * @return {object}
879
875
  */
880
876
  function initOptionsFromArguments() {
881
- const options = {};
882
-
883
- const template = this.getAttribute("data-monster-selected-template");
884
- if (isString(template)) {
885
- if (!options["templateMapping"]) options["templateMapping"] = {};
886
-
887
- switch (template) {
888
- case "summary":
889
- case "default":
890
- options["templateMapping"]["selected"] = getSummaryTemplate();
891
- break;
892
- case "selected":
893
- options["templateMapping"]["selected"] = getSelectionTemplate();
894
- break;
895
- default:
896
- addAttributeToken(
897
- this,
898
- ATTRIBUTE_ERRORMESSAGE,
899
- "invalid template, use summary or selected",
900
- );
901
- }
902
- }
903
-
904
- return options;
877
+ const options = {};
878
+
879
+ const template = this.getAttribute("data-monster-selected-template");
880
+ if (isString(template)) {
881
+ if (!options["templateMapping"]) options["templateMapping"] = {};
882
+
883
+ switch (template) {
884
+ case "summary":
885
+ case "default":
886
+ options["templateMapping"]["selected"] = getSummaryTemplate();
887
+ break;
888
+ case "selected":
889
+ options["templateMapping"]["selected"] = getSelectionTemplate();
890
+ break;
891
+ default:
892
+ addAttributeToken(
893
+ this,
894
+ ATTRIBUTE_ERRORMESSAGE,
895
+ "invalid template, use summary or selected",
896
+ );
897
+ }
898
+ }
899
+
900
+ return options;
905
901
  }
906
902
 
907
903
  /**
908
904
  * @private
909
905
  */
910
906
  function attachResizeObserver() {
911
- // against flickering
912
- this[resizeObserverSymbol] = new ResizeObserver((entries) => {
913
- if (this[timerCallbackSymbol] instanceof DeadMansSwitch) {
914
- try {
915
- this[timerCallbackSymbol].touch();
916
- return;
917
- } catch (e) {
918
- delete this[timerCallbackSymbol];
919
- }
920
- }
921
-
922
- this[timerCallbackSymbol] = new DeadMansSwitch(200, () => {
923
- updatePopper.call(this);
924
- delete this[timerCallbackSymbol];
925
- });
926
- });
927
-
928
- this[resizeObserverSymbol].observe(this.parentElement);
907
+ // against flickering
908
+ this[resizeObserverSymbol] = new ResizeObserver((entries) => {
909
+ if (this[timerCallbackSymbol] instanceof DeadMansSwitch) {
910
+ try {
911
+ this[timerCallbackSymbol].touch();
912
+ return;
913
+ } catch (e) {
914
+ delete this[timerCallbackSymbol];
915
+ }
916
+ }
917
+
918
+ this[timerCallbackSymbol] = new DeadMansSwitch(200, () => {
919
+ updatePopper.call(this);
920
+ delete this[timerCallbackSymbol];
921
+ });
922
+ });
923
+
924
+ this[resizeObserverSymbol].observe(this.parentElement);
929
925
  }
930
926
 
931
927
  function disconnectResizeObserver() {
932
- if (this[resizeObserverSymbol] instanceof ResizeObserver) {
933
- this[resizeObserverSymbol].disconnect();
934
- }
928
+ if (this[resizeObserverSymbol] instanceof ResizeObserver) {
929
+ this[resizeObserverSymbol].disconnect();
930
+ }
935
931
  }
936
932
 
937
933
  function getSelectionTemplate() {
938
- return `<div data-monster-role="selection"
934
+ return `<div data-monster-role="selection"
939
935
  data-monster-insert="selection path:selection" role="search"
940
936
  ><input type="text" role="searchbox"
941
937
  part="inline-filter" name="inline-filter"
@@ -947,7 +943,7 @@ function getSelectionTemplate() {
947
943
  }
948
944
 
949
945
  function getSummaryTemplate() {
950
- return `<div data-monster-role="selection" role="search">
946
+ return `<div data-monster-role="selection" role="search">
951
947
  <input type="text" role="searchbox"
952
948
  part="inline-filter" name="inline-filter"
953
949
  data-monster-role="filter"
@@ -963,35 +959,35 @@ function getSummaryTemplate() {
963
959
  * @private
964
960
  */
965
961
  function parseSlotsToOptions() {
966
- let options = this.getOption("options");
967
- if (!isIterable(options)) {
968
- options = [];
969
- }
970
-
971
- let counter = 1;
972
- getSlottedElements.call(this, "div").forEach((node) => {
973
- let value = (counter++).toString();
974
- let visibility = "visible";
975
-
976
- if (node.hasAttribute("data-monster-value")) {
977
- value = node.getAttribute("data-monster-value");
978
- }
979
-
980
- if (node.style.display === "none") {
981
- visibility = "hidden";
982
- }
983
-
984
- const label = node.outerHTML;
985
-
986
- options.push({
987
- value,
988
- label,
989
- visibility,
990
- });
991
- });
992
-
993
- runAsOptionLengthChanged.call(this, options.length);
994
- this.setOption("options", options);
962
+ let options = this.getOption("options");
963
+ if (!isIterable(options)) {
964
+ options = [];
965
+ }
966
+
967
+ let counter = 1;
968
+ getSlottedElements.call(this, "div").forEach((node) => {
969
+ let value = (counter++).toString();
970
+ let visibility = "visible";
971
+
972
+ if (node.hasAttribute("data-monster-value")) {
973
+ value = node.getAttribute("data-monster-value");
974
+ }
975
+
976
+ if (node.style.display === "none") {
977
+ visibility = "hidden";
978
+ }
979
+
980
+ const label = node.outerHTML;
981
+
982
+ options.push({
983
+ value,
984
+ label,
985
+ visibility,
986
+ });
987
+ });
988
+
989
+ runAsOptionLengthChanged.call(this, options.length);
990
+ this.setOption("options", options);
995
991
  }
996
992
 
997
993
  /**
@@ -1001,39 +997,39 @@ function parseSlotsToOptions() {
1001
997
  * @param {int} targetLength
1002
998
  */
1003
999
  function runAsOptionLengthChanged(targetLength) {
1004
- const self = this;
1005
-
1006
- if (!self[optionsElementSymbol]) {
1007
- return;
1008
- }
1009
-
1010
- const callback = function (mutationsList, observer) {
1011
- const run = false;
1012
- for (const mutation of mutationsList) {
1013
- if (mutation.type === "childList") {
1014
- const run = true;
1015
- break;
1016
- }
1017
- }
1018
-
1019
- if (run === true) {
1020
- const nodes = self[optionsElementSymbol].querySelectorAll(
1021
- `div[${ATTRIBUTE_ROLE}=option]`,
1022
- );
1023
-
1024
- if (nodes.length === targetLength) {
1025
- checkOptionState.call(self);
1026
- observer.disconnect();
1027
- }
1028
- }
1029
- };
1030
-
1031
- const observer = new MutationObserver(callback);
1032
- observer.observe(self[optionsElementSymbol], {
1033
- attributes: false,
1034
- childList: true,
1035
- subtree: true,
1036
- });
1000
+ const self = this;
1001
+
1002
+ if (!self[optionsElementSymbol]) {
1003
+ return;
1004
+ }
1005
+
1006
+ const callback = function (mutationsList, observer) {
1007
+ const run = false;
1008
+ for (const mutation of mutationsList) {
1009
+ if (mutation.type === "childList") {
1010
+ const run = true;
1011
+ break;
1012
+ }
1013
+ }
1014
+
1015
+ if (run === true) {
1016
+ const nodes = self[optionsElementSymbol].querySelectorAll(
1017
+ `div[${ATTRIBUTE_ROLE}=option]`,
1018
+ );
1019
+
1020
+ if (nodes.length === targetLength) {
1021
+ checkOptionState.call(self);
1022
+ observer.disconnect();
1023
+ }
1024
+ }
1025
+ };
1026
+
1027
+ const observer = new MutationObserver(callback);
1028
+ observer.observe(self[optionsElementSymbol], {
1029
+ attributes: false,
1030
+ childList: true,
1031
+ subtree: true,
1032
+ });
1037
1033
  }
1038
1034
 
1039
1035
  /**
@@ -1042,38 +1038,38 @@ function runAsOptionLengthChanged(targetLength) {
1042
1038
  * @return {*}
1043
1039
  */
1044
1040
  function buildSelectionLabel(value) {
1045
- const options = this.getOption("options");
1046
-
1047
- for (let i = 0; i < options.length; i++) {
1048
- let o = options?.[i];
1049
- let l, v, v2;
1050
-
1051
- if (this.getOption("features.useStrictValueComparison") === true) {
1052
- v = value;
1053
- } else {
1054
- v = `${value}`;
1055
- }
1056
-
1057
- if (isPrimitive(o) && o === value) {
1058
- return o;
1059
- } else if (!isObject(o)) {
1060
- continue;
1061
- }
1062
-
1063
- if (this.getOption("features.useStrictValueComparison") === true) {
1064
- l = o?.["label"];
1065
- v2 = o?.["value"];
1066
- } else {
1067
- l = `${o?.["label"]}`;
1068
- v2 = `${o?.["value"]}`;
1069
- }
1070
-
1071
- if (v2 === v) {
1072
- return l;
1073
- }
1074
- }
1075
-
1076
- return undefined;
1041
+ const options = this.getOption("options");
1042
+
1043
+ for (let i = 0; i < options.length; i++) {
1044
+ let o = options?.[i];
1045
+ let l, v, v2;
1046
+
1047
+ if (this.getOption("features.useStrictValueComparison") === true) {
1048
+ v = value;
1049
+ } else {
1050
+ v = `${value}`;
1051
+ }
1052
+
1053
+ if (isPrimitive(o) && o === value) {
1054
+ return o;
1055
+ } else if (!isObject(o)) {
1056
+ continue;
1057
+ }
1058
+
1059
+ if (this.getOption("features.useStrictValueComparison") === true) {
1060
+ l = o?.["label"];
1061
+ v2 = o?.["value"];
1062
+ } else {
1063
+ l = `${o?.["label"]}`;
1064
+ v2 = `${o?.["value"]}`;
1065
+ }
1066
+
1067
+ if (v2 === v) {
1068
+ return l;
1069
+ }
1070
+ }
1071
+
1072
+ return undefined;
1077
1073
  }
1078
1074
 
1079
1075
  /**
@@ -1083,17 +1079,17 @@ function buildSelectionLabel(value) {
1083
1079
  * @throws {Error} no value found
1084
1080
  */
1085
1081
  function getSelectionLabel(value) {
1086
- const callback = this.getOption("formatter.selection");
1087
- if (isFunction(callback)) {
1088
- const label = callback.call(this, value);
1089
- if (isString(label)) return label;
1090
- }
1082
+ const callback = this.getOption("formatter.selection");
1083
+ if (isFunction(callback)) {
1084
+ const label = callback.call(this, value);
1085
+ if (isString(label)) return label;
1086
+ }
1091
1087
 
1092
- if (isString(value) || isInteger(value)) {
1093
- return `${value}`;
1094
- }
1088
+ if (isString(value) || isInteger(value)) {
1089
+ return `${value}`;
1090
+ }
1095
1091
 
1096
- return this.getOption("labels.cannot-be-loaded", value);
1092
+ return this.getOption("labels.cannot-be-loaded", value);
1097
1093
  }
1098
1094
 
1099
1095
  /**
@@ -1101,25 +1097,25 @@ function getSelectionLabel(value) {
1101
1097
  * @param {Event} event
1102
1098
  */
1103
1099
  function handleToggleKeyboardEvents(event) {
1104
- switch (event?.["code"]) {
1105
- case "Escape":
1106
- toggle.call(this);
1107
- event.preventDefault();
1108
- break;
1109
- case "Space":
1110
- toggle.call(this);
1111
- event.preventDefault();
1112
- break;
1113
- case "ArrowDown":
1114
- show.call(this);
1115
- activateCurrentOption.call(this, FOCUS_DIRECTION_DOWN);
1116
- event.preventDefault();
1117
- break;
1118
- case "ArrowUp":
1119
- hide.call(this);
1120
- event.preventDefault();
1121
- break;
1122
- }
1100
+ switch (event?.["code"]) {
1101
+ case "Escape":
1102
+ toggle.call(this);
1103
+ event.preventDefault();
1104
+ break;
1105
+ case "Space":
1106
+ toggle.call(this);
1107
+ event.preventDefault();
1108
+ break;
1109
+ case "ArrowDown":
1110
+ show.call(this);
1111
+ activateCurrentOption.call(this, FOCUS_DIRECTION_DOWN);
1112
+ event.preventDefault();
1113
+ break;
1114
+ case "ArrowUp":
1115
+ hide.call(this);
1116
+ event.preventDefault();
1117
+ break;
1118
+ }
1123
1119
  }
1124
1120
 
1125
1121
  /**
@@ -1129,46 +1125,45 @@ function handleToggleKeyboardEvents(event) {
1129
1125
  * @this CustomElement
1130
1126
  */
1131
1127
  function initOptionObserver() {
1132
- const self = this;
1133
-
1134
- self.attachObserver(
1135
- new Observer(function () {
1136
- new Processing(() => {
1137
- try {
1138
- self.updateI18n();
1139
- } catch (e) {
1140
- console.error(e);
1141
- requestAnimationFrame(() => {
1142
- setStatusOrRemoveBadges.call(self, "error");
1143
- });
1144
- }
1145
- try {
1146
- areOptionsAvailableAndInit.call(self);
1147
- } catch (e) {
1148
- console.error(e);
1149
- requestAnimationFrame(() => {
1150
- setStatusOrRemoveBadges.call(self, "error");
1151
- });
1152
- }
1153
-
1154
- setSummaryAndControlText.call(self);
1155
- }).run();
1156
- }),
1157
- );
1128
+ const self = this;
1129
+
1130
+ self.attachObserver(
1131
+ new Observer(function () {
1132
+ new Processing(() => {
1133
+ try {
1134
+ self.updateI18n();
1135
+ } catch (e) {
1136
+ console.error(e);
1137
+ requestAnimationFrame(() => {
1138
+ setStatusOrRemoveBadges.call(self, "error");
1139
+ });
1140
+ }
1141
+ try {
1142
+ areOptionsAvailableAndInit.call(self);
1143
+ } catch (e) {
1144
+ console.error(e);
1145
+ requestAnimationFrame(() => {
1146
+ setStatusOrRemoveBadges.call(self, "error");
1147
+ });
1148
+ }
1149
+
1150
+ setSummaryAndControlText.call(self);
1151
+ }).run();
1152
+ }),
1153
+ );
1158
1154
  }
1159
1155
 
1160
1156
  function getDefaultTranslation() {
1161
- const translation = new Translations("en").assignTranslations(
1162
- this.getOption("labels", {}),
1163
- );
1157
+ const translation = new Translations("en").assignTranslations(
1158
+ this.getOption("labels", {}),
1159
+ );
1164
1160
 
1165
- try {
1166
- const doc = getDocumentTranslations();
1167
- translation.locale = doc.locale;
1168
- } catch (e) {
1169
- }
1161
+ try {
1162
+ const doc = getDocumentTranslations();
1163
+ translation.locale = doc.locale;
1164
+ } catch (e) {}
1170
1165
 
1171
- return translation;
1166
+ return translation;
1172
1167
  }
1173
1168
 
1174
1169
  /**
@@ -1176,36 +1171,36 @@ function getDefaultTranslation() {
1176
1171
  * @returns {string|*}
1177
1172
  */
1178
1173
  function setSummaryAndControlText() {
1179
- const translations = getDefaultTranslation.call(this);
1180
- const selections = this.getOption("selection");
1181
-
1182
- const text = translations.getPluralRuleText(
1183
- "summary-text",
1184
- selections.length,
1185
- "",
1186
- );
1187
-
1188
- const selectedText = new Formatter({
1189
- count: String(selections.length),
1190
- }).format(text);
1191
-
1192
- this.setOption("messages.selected", selectedText);
1193
-
1194
- const current = this.getOption("messages.control");
1195
- const msg = this.getOption("labels.select-an-option");
1196
-
1197
- if (
1198
- current === "" ||
1199
- current === undefined ||
1200
- current === msg ||
1201
- current === null
1202
- ) {
1203
- if (selections === undefined || selections.length === 0) {
1204
- this.setOption("messages.control", msg);
1205
- } else {
1206
- this.setOption("messages.control", "");
1207
- }
1208
- }
1174
+ const translations = getDefaultTranslation.call(this);
1175
+ const selections = this.getOption("selection");
1176
+
1177
+ const text = translations.getPluralRuleText(
1178
+ "summary-text",
1179
+ selections.length,
1180
+ "",
1181
+ );
1182
+
1183
+ const selectedText = new Formatter({
1184
+ count: String(selections.length),
1185
+ }).format(text);
1186
+
1187
+ this.setOption("messages.selected", selectedText);
1188
+
1189
+ const current = this.getOption("messages.control");
1190
+ const msg = this.getOption("labels.select-an-option");
1191
+
1192
+ if (
1193
+ current === "" ||
1194
+ current === undefined ||
1195
+ current === msg ||
1196
+ current === null
1197
+ ) {
1198
+ if (selections === undefined || selections.length === 0) {
1199
+ this.setOption("messages.control", msg);
1200
+ } else {
1201
+ this.setOption("messages.control", "");
1202
+ }
1203
+ }
1209
1204
  }
1210
1205
 
1211
1206
  /**
@@ -1213,9 +1208,9 @@ function setSummaryAndControlText() {
1213
1208
  * @return {NodeList}
1214
1209
  */
1215
1210
  function getOptionElements() {
1216
- return this[optionsElementSymbol].querySelectorAll(
1217
- `[${ATTRIBUTE_ROLE}=option]`,
1218
- );
1211
+ return this[optionsElementSymbol].querySelectorAll(
1212
+ `[${ATTRIBUTE_ROLE}=option]`,
1213
+ );
1219
1214
  }
1220
1215
 
1221
1216
  /**
@@ -1241,82 +1236,82 @@ function getOptionElements() {
1241
1236
  * @private
1242
1237
  */
1243
1238
  function calcAndSetOptionsDimension() {
1244
- const options = getOptionElements.call(this);
1245
- const container = this[optionsElementSymbol];
1246
- if (!(container instanceof HTMLElement && options instanceof NodeList)) {
1247
- return;
1248
- }
1249
-
1250
- let visible = 0;
1251
- let optionHeight = 0;
1252
- const max = this.getOption("showMaxOptions", 10);
1253
-
1254
- let scrollFlag = false;
1255
- for (const [, option] of Object.entries(options)) {
1256
- const computedStyle = getGlobal().getComputedStyle(option);
1257
- if (computedStyle.display === "none") continue;
1258
-
1259
- let h = option.getBoundingClientRect().height;
1260
- h += parseInt(computedStyle.getPropertyValue("margin-top"), 10);
1261
- h += parseInt(computedStyle.getPropertyValue("margin-bottom"), 10);
1262
- optionHeight += h;
1263
-
1264
- visible++;
1265
-
1266
- if (visible > max) {
1267
- break;
1268
- }
1269
- }
1270
-
1271
- if (visible > max) {
1272
- visible = max;
1273
- scrollFlag = true;
1274
- }
1275
-
1276
- if (visible === 0) {
1277
- if (this.getOption("options").length === 0) {
1278
- this.setOption(
1279
- "messages.emptyOptions",
1280
- this.getOption("labels.no-options-available"),
1281
- );
1282
- } else {
1283
- if (this.getOption("filter.mode") === FILTER_MODE_DISABLED) {
1284
- this.setOption(
1285
- "messages.emptyOptions",
1286
- this.getOption("labels.no-options-available"),
1287
- );
1288
- } else {
1289
- this.setOption(
1290
- "messages.emptyOptions",
1291
- this.getOption("labels.no-options-found"),
1292
- );
1293
- }
1294
- }
1295
- this[noOptionsAvailableElementSymbol].classList.remove("d-none");
1296
- } else {
1297
- this[noOptionsAvailableElementSymbol].classList.add("d-none");
1298
- }
1299
-
1300
- const styles = getGlobal().getComputedStyle(this[optionsElementSymbol]);
1301
- let padding = parseInt(styles.getPropertyValue("padding-top"), 10);
1302
- padding += parseInt(styles.getPropertyValue("padding-bottom"), 10);
1303
-
1304
- let margin = parseInt(styles.getPropertyValue("margin-top"), 10);
1305
- margin += parseInt(styles.getPropertyValue("margin-bottom"), 10);
1306
-
1307
- const containerHeight = optionHeight + padding + margin;
1308
- container.style.height = `${containerHeight}px`;
1309
-
1310
- if (scrollFlag === true) {
1311
- container.style.overflowY = "scroll";
1312
- } else {
1313
- container.style.overflowY = "auto";
1314
- }
1315
-
1316
- const domRect = this[controlElementSymbol].getBoundingClientRect();
1317
-
1318
- this[popperElementSymbol].style.width = `${domRect.width}px`;
1319
- container.style.overflowX = "auto";
1239
+ const options = getOptionElements.call(this);
1240
+ const container = this[optionsElementSymbol];
1241
+ if (!(container instanceof HTMLElement && options instanceof NodeList)) {
1242
+ return;
1243
+ }
1244
+
1245
+ let visible = 0;
1246
+ let optionHeight = 0;
1247
+ const max = this.getOption("showMaxOptions", 10);
1248
+
1249
+ let scrollFlag = false;
1250
+ for (const [, option] of Object.entries(options)) {
1251
+ const computedStyle = getGlobal().getComputedStyle(option);
1252
+ if (computedStyle.display === "none") continue;
1253
+
1254
+ let h = option.getBoundingClientRect().height;
1255
+ h += parseInt(computedStyle.getPropertyValue("margin-top"), 10);
1256
+ h += parseInt(computedStyle.getPropertyValue("margin-bottom"), 10);
1257
+ optionHeight += h;
1258
+
1259
+ visible++;
1260
+
1261
+ if (visible > max) {
1262
+ break;
1263
+ }
1264
+ }
1265
+
1266
+ if (visible > max) {
1267
+ visible = max;
1268
+ scrollFlag = true;
1269
+ }
1270
+
1271
+ if (visible === 0) {
1272
+ if (this.getOption("options").length === 0) {
1273
+ this.setOption(
1274
+ "messages.emptyOptions",
1275
+ this.getOption("labels.no-options-available"),
1276
+ );
1277
+ } else {
1278
+ if (this.getOption("filter.mode") === FILTER_MODE_DISABLED) {
1279
+ this.setOption(
1280
+ "messages.emptyOptions",
1281
+ this.getOption("labels.no-options-available"),
1282
+ );
1283
+ } else {
1284
+ this.setOption(
1285
+ "messages.emptyOptions",
1286
+ this.getOption("labels.no-options-found"),
1287
+ );
1288
+ }
1289
+ }
1290
+ this[noOptionsAvailableElementSymbol].classList.remove("d-none");
1291
+ } else {
1292
+ this[noOptionsAvailableElementSymbol].classList.add("d-none");
1293
+ }
1294
+
1295
+ const styles = getGlobal().getComputedStyle(this[optionsElementSymbol]);
1296
+ let padding = parseInt(styles.getPropertyValue("padding-top"), 10);
1297
+ padding += parseInt(styles.getPropertyValue("padding-bottom"), 10);
1298
+
1299
+ let margin = parseInt(styles.getPropertyValue("margin-top"), 10);
1300
+ margin += parseInt(styles.getPropertyValue("margin-bottom"), 10);
1301
+
1302
+ const containerHeight = optionHeight + padding + margin;
1303
+ container.style.height = `${containerHeight}px`;
1304
+
1305
+ if (scrollFlag === true) {
1306
+ container.style.overflowY = "scroll";
1307
+ } else {
1308
+ container.style.overflowY = "auto";
1309
+ }
1310
+
1311
+ const domRect = this[controlElementSymbol].getBoundingClientRect();
1312
+
1313
+ this[popperElementSymbol].style.width = `${domRect.width}px`;
1314
+ container.style.overflowX = "auto";
1320
1315
  }
1321
1316
 
1322
1317
  /**
@@ -1325,126 +1320,126 @@ function calcAndSetOptionsDimension() {
1325
1320
  * @throws {Error} no shadow-root is defined
1326
1321
  */
1327
1322
  function activateCurrentOption(direction) {
1328
- if (!this.shadowRoot) {
1329
- throw new Error("no shadow-root is defined");
1330
- }
1331
-
1332
- let focused = this.shadowRoot.querySelector(`[${ATTRIBUTE_PREFIX}focused]`);
1333
-
1334
- if (
1335
- !(focused instanceof HTMLElement) ||
1336
- focused.matches("[data-monster-visibility=hidden]")
1337
- ) {
1338
- for (const [, e] of Object.entries(
1339
- this.shadowRoot.querySelectorAll(`[${ATTRIBUTE_ROLE}=option]`),
1340
- )) {
1341
- if (e.matches("[data-monster-visibility=visible]")) {
1342
- focused = e;
1343
- break;
1344
- }
1345
- }
1346
- } else {
1347
- if (direction === FOCUS_DIRECTION_DOWN) {
1348
- while (focused.nextSibling) {
1349
- focused = focused.nextSibling;
1350
-
1351
- if (
1352
- focused instanceof HTMLElement &&
1353
- focused.hasAttribute(ATTRIBUTE_ROLE) &&
1354
- focused.getAttribute(ATTRIBUTE_ROLE) === "option" &&
1355
- focused.matches("[data-monster-visibility=visible]") &&
1356
- focused.matches(":not([data-monster-filtered=true])")
1357
- ) {
1358
- break;
1359
- }
1360
- }
1361
- } else {
1362
- let found = false;
1363
- while (focused.previousSibling) {
1364
- focused = focused.previousSibling;
1365
- if (
1366
- focused instanceof HTMLElement &&
1367
- focused.hasAttribute(ATTRIBUTE_ROLE) &&
1368
- focused.getAttribute(ATTRIBUTE_ROLE) === "option" &&
1369
- focused.matches("[data-monster-visibility=visible]") &&
1370
- focused.matches(":not([data-monster-filtered=true])")
1371
- ) {
1372
- found = true;
1373
- break;
1374
- }
1375
- }
1376
- if (found === false) {
1377
- focusFilter.call(this);
1378
- }
1379
- }
1380
- }
1381
-
1382
- new Processing(() => {
1383
- if (focused instanceof HTMLElement) {
1384
- this.shadowRoot
1385
- .querySelectorAll(`[${ATTRIBUTE_PREFIX}focused]`)
1386
- .forEach((e) => {
1387
- e.removeAttribute(`${ATTRIBUTE_PREFIX}focused`);
1388
- });
1389
-
1390
- focused.focus();
1391
- focused.setAttribute(`${ATTRIBUTE_PREFIX}focused`, true);
1392
- }
1393
- })
1394
- .run()
1395
- .catch((e) => {
1396
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
1397
- });
1323
+ if (!this.shadowRoot) {
1324
+ throw new Error("no shadow-root is defined");
1325
+ }
1326
+
1327
+ let focused = this.shadowRoot.querySelector(`[${ATTRIBUTE_PREFIX}focused]`);
1328
+
1329
+ if (
1330
+ !(focused instanceof HTMLElement) ||
1331
+ focused.matches("[data-monster-visibility=hidden]")
1332
+ ) {
1333
+ for (const [, e] of Object.entries(
1334
+ this.shadowRoot.querySelectorAll(`[${ATTRIBUTE_ROLE}=option]`),
1335
+ )) {
1336
+ if (e.matches("[data-monster-visibility=visible]")) {
1337
+ focused = e;
1338
+ break;
1339
+ }
1340
+ }
1341
+ } else {
1342
+ if (direction === FOCUS_DIRECTION_DOWN) {
1343
+ while (focused.nextSibling) {
1344
+ focused = focused.nextSibling;
1345
+
1346
+ if (
1347
+ focused instanceof HTMLElement &&
1348
+ focused.hasAttribute(ATTRIBUTE_ROLE) &&
1349
+ focused.getAttribute(ATTRIBUTE_ROLE) === "option" &&
1350
+ focused.matches("[data-monster-visibility=visible]") &&
1351
+ focused.matches(":not([data-monster-filtered=true])")
1352
+ ) {
1353
+ break;
1354
+ }
1355
+ }
1356
+ } else {
1357
+ let found = false;
1358
+ while (focused.previousSibling) {
1359
+ focused = focused.previousSibling;
1360
+ if (
1361
+ focused instanceof HTMLElement &&
1362
+ focused.hasAttribute(ATTRIBUTE_ROLE) &&
1363
+ focused.getAttribute(ATTRIBUTE_ROLE) === "option" &&
1364
+ focused.matches("[data-monster-visibility=visible]") &&
1365
+ focused.matches(":not([data-monster-filtered=true])")
1366
+ ) {
1367
+ found = true;
1368
+ break;
1369
+ }
1370
+ }
1371
+ if (found === false) {
1372
+ focusFilter.call(this);
1373
+ }
1374
+ }
1375
+ }
1376
+
1377
+ new Processing(() => {
1378
+ if (focused instanceof HTMLElement) {
1379
+ this.shadowRoot
1380
+ .querySelectorAll(`[${ATTRIBUTE_PREFIX}focused]`)
1381
+ .forEach((e) => {
1382
+ e.removeAttribute(`${ATTRIBUTE_PREFIX}focused`);
1383
+ });
1384
+
1385
+ focused.focus();
1386
+ focused.setAttribute(`${ATTRIBUTE_PREFIX}focused`, true);
1387
+ }
1388
+ })
1389
+ .run()
1390
+ .catch((e) => {
1391
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
1392
+ });
1398
1393
  }
1399
1394
 
1400
1395
  /**
1401
1396
  * @private
1402
1397
  */
1403
1398
  function filterOptions() {
1404
- new Processing(() => {
1405
- let filterValue;
1406
-
1407
- switch (this.getOption("filter.position")) {
1408
- case FILTER_POSITION_INLINE:
1409
- if (this[inlineFilterElementSymbol] instanceof HTMLElement) {
1410
- filterValue = this[inlineFilterElementSymbol].value.toLowerCase();
1411
- } else {
1412
- return;
1413
- }
1414
-
1415
- break;
1416
- case FILTER_POSITION_POPPER:
1417
- default:
1418
- if (this[popperFilterElementSymbol] instanceof HTMLInputElement) {
1419
- filterValue = this[popperFilterElementSymbol].value.toLowerCase();
1420
- } else {
1421
- return;
1422
- }
1423
- }
1424
-
1425
- const options = this.getOption("options");
1426
- for (const [i, option] of Object.entries(options)) {
1427
- if (option.label.toLowerCase().indexOf(filterValue) === -1) {
1428
- this.setOption(`options.${i}.filtered`, "true");
1429
- } else {
1430
- this.setOption(`options.${i}.filtered`, undefined);
1431
- }
1432
- }
1433
- })
1434
- .run()
1435
- .then(() => {
1436
- new Processing(100, () => {
1437
- calcAndSetOptionsDimension.call(this);
1438
- focusFilter.call(this);
1439
- })
1440
- .run()
1441
- .catch((e) => {
1442
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
1443
- });
1444
- })
1445
- .catch((e) => {
1446
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
1447
- });
1399
+ new Processing(() => {
1400
+ let filterValue;
1401
+
1402
+ switch (this.getOption("filter.position")) {
1403
+ case FILTER_POSITION_INLINE:
1404
+ if (this[inlineFilterElementSymbol] instanceof HTMLElement) {
1405
+ filterValue = this[inlineFilterElementSymbol].value.toLowerCase();
1406
+ } else {
1407
+ return;
1408
+ }
1409
+
1410
+ break;
1411
+ case FILTER_POSITION_POPPER:
1412
+ default:
1413
+ if (this[popperFilterElementSymbol] instanceof HTMLInputElement) {
1414
+ filterValue = this[popperFilterElementSymbol].value.toLowerCase();
1415
+ } else {
1416
+ return;
1417
+ }
1418
+ }
1419
+
1420
+ const options = this.getOption("options");
1421
+ for (const [i, option] of Object.entries(options)) {
1422
+ if (option.label.toLowerCase().indexOf(filterValue) === -1) {
1423
+ this.setOption(`options.${i}.filtered`, "true");
1424
+ } else {
1425
+ this.setOption(`options.${i}.filtered`, undefined);
1426
+ }
1427
+ }
1428
+ })
1429
+ .run()
1430
+ .then(() => {
1431
+ new Processing(100, () => {
1432
+ calcAndSetOptionsDimension.call(this);
1433
+ focusFilter.call(this);
1434
+ })
1435
+ .run()
1436
+ .catch((e) => {
1437
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
1438
+ });
1439
+ })
1440
+ .catch((e) => {
1441
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
1442
+ });
1448
1443
  }
1449
1444
 
1450
1445
  /**
@@ -1452,37 +1447,37 @@ function filterOptions() {
1452
1447
  * @param {Event} event
1453
1448
  */
1454
1449
  function handleFilterKeyboardEvents(event) {
1455
- const shiftKey = event?.["shiftKey"];
1456
-
1457
- switch (event?.["code"]) {
1458
- case "Tab":
1459
- activateCurrentOption.call(this, FOCUS_DIRECTION_DOWN);
1460
- event.preventDefault();
1461
- break;
1462
- case "Escape":
1463
- toggle.call(this);
1464
- event.preventDefault();
1465
- break;
1466
- case "Tab" && shiftKey === true:
1467
- case "ArrowUp":
1468
- activateCurrentOption.call(this, FOCUS_DIRECTION_UP);
1469
- event.preventDefault();
1470
- break;
1471
- case "Tab" && !shiftKey:
1472
- case "ArrowDown":
1473
- activateCurrentOption.call(this, FOCUS_DIRECTION_DOWN);
1474
- event.preventDefault();
1475
- break;
1476
- default:
1477
- if (
1478
- this.getOption("features.lazyLoad") === true &&
1479
- this[lazyLoadDoneSymbol] !== true
1480
- ) {
1481
- this.click();
1482
- }
1483
-
1484
- handleFilterKeyEvents.call(this);
1485
- }
1450
+ const shiftKey = event?.["shiftKey"];
1451
+
1452
+ switch (event?.["code"]) {
1453
+ case "Tab":
1454
+ activateCurrentOption.call(this, FOCUS_DIRECTION_DOWN);
1455
+ event.preventDefault();
1456
+ break;
1457
+ case "Escape":
1458
+ toggle.call(this);
1459
+ event.preventDefault();
1460
+ break;
1461
+ case "Tab" && shiftKey === true:
1462
+ case "ArrowUp":
1463
+ activateCurrentOption.call(this, FOCUS_DIRECTION_UP);
1464
+ event.preventDefault();
1465
+ break;
1466
+ case "Tab" && !shiftKey:
1467
+ case "ArrowDown":
1468
+ activateCurrentOption.call(this, FOCUS_DIRECTION_DOWN);
1469
+ event.preventDefault();
1470
+ break;
1471
+ default:
1472
+ if (
1473
+ this.getOption("features.lazyLoad") === true &&
1474
+ this[lazyLoadDoneSymbol] !== true
1475
+ ) {
1476
+ this.click();
1477
+ }
1478
+
1479
+ handleFilterKeyEvents.call(this);
1480
+ }
1486
1481
  }
1487
1482
 
1488
1483
  /**
@@ -1501,85 +1496,83 @@ function handleFilterKeyboardEvents(event) {
1501
1496
  * @returns {void} This method does not return anything.
1502
1497
  */
1503
1498
  function handleFilterKeyEvents() {
1504
- if (this[keyFilterEventSymbol] instanceof DeadMansSwitch) {
1505
- try {
1506
- this[keyFilterEventSymbol].touch();
1507
- return;
1508
- } catch (e) {
1509
- delete this[keyFilterEventSymbol];
1510
- }
1511
- }
1512
-
1513
- this[keyFilterEventSymbol] = new DeadMansSwitch(200, () => {
1514
- if (this.getOption("filter.mode") !== FILTER_MODE_REMOTE) {
1515
- filterOptions.call(this);
1516
- } else {
1517
- filterFromRemote.call(this).catch((e) => {
1518
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
1519
- });
1520
- }
1521
-
1522
- delete this[keyFilterEventSymbol];
1523
- });
1499
+ if (this[keyFilterEventSymbol] instanceof DeadMansSwitch) {
1500
+ try {
1501
+ this[keyFilterEventSymbol].touch();
1502
+ return;
1503
+ } catch (e) {
1504
+ delete this[keyFilterEventSymbol];
1505
+ }
1506
+ }
1507
+
1508
+ this[keyFilterEventSymbol] = new DeadMansSwitch(200, () => {
1509
+ if (this.getOption("filter.mode") !== FILTER_MODE_REMOTE) {
1510
+ filterOptions.call(this);
1511
+ } else {
1512
+ filterFromRemote.call(this).catch((e) => {
1513
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
1514
+ });
1515
+ }
1516
+
1517
+ delete this[keyFilterEventSymbol];
1518
+ });
1524
1519
  }
1525
1520
 
1526
1521
  /**
1527
1522
  * @private
1528
1523
  */
1529
1524
  function filterFromRemote() {
1530
- if (!(this[inlineFilterElementSymbol] instanceof HTMLElement)) {
1531
- return;
1532
- }
1533
-
1534
- const optionUrl = this.getOption("url");
1535
- if (!optionUrl) {
1536
- addAttributeToken(
1537
- this,
1538
- ATTRIBUTE_ERRORMESSAGE,
1539
- "Missing URL for Remote Filter.",
1540
- );
1541
- return;
1542
- }
1543
-
1544
-
1545
- return filterFromRemoteByValue.call(this, optionUrl, this[inlineFilterElementSymbol].value);
1546
-
1525
+ if (!(this[inlineFilterElementSymbol] instanceof HTMLElement)) {
1526
+ return;
1527
+ }
1528
+
1529
+ const optionUrl = this.getOption("url");
1530
+ if (!optionUrl) {
1531
+ addAttributeToken(
1532
+ this,
1533
+ ATTRIBUTE_ERRORMESSAGE,
1534
+ "Missing URL for Remote Filter.",
1535
+ );
1536
+ return;
1537
+ }
1538
+
1539
+ return filterFromRemoteByValue.call(
1540
+ this,
1541
+ optionUrl,
1542
+ this[inlineFilterElementSymbol].value,
1543
+ );
1547
1544
  }
1548
1545
 
1549
1546
  /**
1550
1547
  * @private
1551
1548
  */
1552
1549
  function filterFromRemoteByValue(optionUrl, value) {
1553
-
1554
- return new Processing(() => {
1555
- const filterValue = encodeURI(
1556
- value
1557
- );
1558
- let url = optionUrl;
1559
- if (filterValue.length > 0) {
1560
-
1561
- const formatter = new Formatter({filter: filterValue});
1562
- const openMarker = this.getOption("filter.marker.open");
1563
- let closeMarker = this.getOption("filter.marker.close");
1564
- if (!closeMarker) {
1565
- closeMarker = openMarker;
1566
- }
1567
-
1568
- if (openMarker && closeMarker) {
1569
- formatter.setMarker(openMarker, closeMarker);
1570
- }
1571
-
1572
- url = formatter.format(optionUrl);
1573
- }
1574
-
1575
- this.fetch(url)
1576
- .then(() => {
1577
- checkOptionState.call(this);
1578
- })
1579
- .catch((e) => {
1580
- throw e;
1581
- });
1582
- }).run();
1550
+ return new Processing(() => {
1551
+ const filterValue = encodeURI(value);
1552
+ let url = optionUrl;
1553
+ if (filterValue.length > 0) {
1554
+ const formatter = new Formatter({ filter: filterValue });
1555
+ const openMarker = this.getOption("filter.marker.open");
1556
+ let closeMarker = this.getOption("filter.marker.close");
1557
+ if (!closeMarker) {
1558
+ closeMarker = openMarker;
1559
+ }
1560
+
1561
+ if (openMarker && closeMarker) {
1562
+ formatter.setMarker(openMarker, closeMarker);
1563
+ }
1564
+
1565
+ url = formatter.format(optionUrl);
1566
+ }
1567
+
1568
+ this.fetch(url)
1569
+ .then(() => {
1570
+ checkOptionState.call(this);
1571
+ })
1572
+ .catch((e) => {
1573
+ throw e;
1574
+ });
1575
+ }).run();
1583
1576
  }
1584
1577
 
1585
1578
  /**
@@ -1588,50 +1581,50 @@ function filterFromRemoteByValue(optionUrl, value) {
1588
1581
  * @private
1589
1582
  */
1590
1583
  function handleOptionKeyboardEvents(event) {
1591
- const shiftKey = event?.["shiftKey"];
1592
-
1593
- switch (event?.["code"]) {
1594
- case "Escape":
1595
- toggle.call(this);
1596
- event.preventDefault();
1597
- break;
1598
- case "Enter":
1599
- case "Space":
1600
- const path = event.composedPath();
1601
- const element = path?.[0];
1602
- if (element instanceof HTMLElement) {
1603
- const input = element.getElementsByTagName("input");
1604
- if (!input) {
1605
- return;
1606
- }
1607
- fireEvent(input, "click");
1608
- }
1609
- event.preventDefault();
1610
- break;
1611
-
1612
- case "Tab" && shiftKey === true:
1613
- case "ArrowUp":
1614
- activateCurrentOption.call(this, FOCUS_DIRECTION_UP);
1615
- event.preventDefault();
1616
- break;
1617
-
1618
- case "Tab" && !shiftKey:
1619
- case "ArrowLeft":
1620
- case "ArrowRight":
1621
- // handled by tree select
1622
- break;
1623
- case "ArrowDown":
1624
- activateCurrentOption.call(this, FOCUS_DIRECTION_DOWN);
1625
- event.preventDefault();
1626
- break;
1627
- default:
1628
- const p = event.composedPath();
1629
- if (p?.[0] instanceof HTMLInputElement) {
1630
- return;
1631
- }
1632
- focusFilter.call(this);
1633
- break;
1634
- }
1584
+ const shiftKey = event?.["shiftKey"];
1585
+
1586
+ switch (event?.["code"]) {
1587
+ case "Escape":
1588
+ toggle.call(this);
1589
+ event.preventDefault();
1590
+ break;
1591
+ case "Enter":
1592
+ case "Space":
1593
+ const path = event.composedPath();
1594
+ const element = path?.[0];
1595
+ if (element instanceof HTMLElement) {
1596
+ const input = element.getElementsByTagName("input");
1597
+ if (!input) {
1598
+ return;
1599
+ }
1600
+ fireEvent(input, "click");
1601
+ }
1602
+ event.preventDefault();
1603
+ break;
1604
+
1605
+ case "Tab" && shiftKey === true:
1606
+ case "ArrowUp":
1607
+ activateCurrentOption.call(this, FOCUS_DIRECTION_UP);
1608
+ event.preventDefault();
1609
+ break;
1610
+
1611
+ case "Tab" && !shiftKey:
1612
+ case "ArrowLeft":
1613
+ case "ArrowRight":
1614
+ // handled by tree select
1615
+ break;
1616
+ case "ArrowDown":
1617
+ activateCurrentOption.call(this, FOCUS_DIRECTION_DOWN);
1618
+ event.preventDefault();
1619
+ break;
1620
+ default:
1621
+ const p = event.composedPath();
1622
+ if (p?.[0] instanceof HTMLInputElement) {
1623
+ return;
1624
+ }
1625
+ focusFilter.call(this);
1626
+ break;
1627
+ }
1635
1628
  }
1636
1629
 
1637
1630
  /**
@@ -1639,33 +1632,33 @@ function handleOptionKeyboardEvents(event) {
1639
1632
  * @returns {string}
1640
1633
  */
1641
1634
  function getFilterMode() {
1642
- switch (this.getOption("filter.mode")) {
1643
- case FILTER_MODE_OPTIONS:
1644
- return FILTER_MODE_OPTIONS;
1645
- case FILTER_MODE_REMOTE:
1646
- return FILTER_MODE_REMOTE;
1647
- default:
1648
- return FILTER_MODE_DISABLED;
1649
- }
1635
+ switch (this.getOption("filter.mode")) {
1636
+ case FILTER_MODE_OPTIONS:
1637
+ return FILTER_MODE_OPTIONS;
1638
+ case FILTER_MODE_REMOTE:
1639
+ return FILTER_MODE_REMOTE;
1640
+ default:
1641
+ return FILTER_MODE_DISABLED;
1642
+ }
1650
1643
  }
1651
1644
 
1652
1645
  /**
1653
1646
  * @private
1654
1647
  */
1655
1648
  function blurFilter() {
1656
- if (!(this[inlineFilterElementSymbol] instanceof HTMLElement)) {
1657
- return;
1658
- }
1649
+ if (!(this[inlineFilterElementSymbol] instanceof HTMLElement)) {
1650
+ return;
1651
+ }
1659
1652
 
1660
- if (getFilterMode.call(this) === FILTER_MODE_DISABLED) {
1661
- return;
1662
- }
1653
+ if (getFilterMode.call(this) === FILTER_MODE_DISABLED) {
1654
+ return;
1655
+ }
1663
1656
 
1664
- this[popperFilterContainerElementSymbol].classList.remove("active");
1665
- this[popperFilterContainerElementSymbol].blur();
1657
+ this[popperFilterContainerElementSymbol].classList.remove("active");
1658
+ this[popperFilterContainerElementSymbol].blur();
1666
1659
 
1667
- this[inlineFilterElementSymbol].classList.remove("active");
1668
- this[inlineFilterElementSymbol].blur();
1660
+ this[inlineFilterElementSymbol].classList.remove("active");
1661
+ this[inlineFilterElementSymbol].blur();
1669
1662
  }
1670
1663
 
1671
1664
  /**
@@ -1673,29 +1666,29 @@ function blurFilter() {
1673
1666
  * @param focusOptions
1674
1667
  */
1675
1668
  function focusPopperFilter(focusOptions) {
1676
- this[popperFilterContainerElementSymbol].classList.remove("d-none");
1677
- this[popperFilterElementSymbol].classList.add("active");
1678
- this[inlineFilterElementSymbol].classList.remove("active");
1679
- this[inlineFilterElementSymbol].classList.add("d-none");
1680
-
1681
- if (!(this[popperFilterElementSymbol] instanceof HTMLElement)) {
1682
- addAttributeToken(
1683
- this,
1684
- ATTRIBUTE_ERRORMESSAGE,
1685
- "Missing Popper Filter Element.",
1686
- );
1687
- return;
1688
- }
1689
-
1690
- // visibility is set to visible, because focus() does not work on invisible elements
1691
- // and the class definition is assigned later in the processing
1692
- setTimeout(() => {
1693
- if (focusOptions === undefined || focusOptions === null) {
1694
- this[popperFilterElementSymbol].focus();
1695
- } else {
1696
- this[popperFilterElementSymbol].focus(focusOptions);
1697
- }
1698
- }, 100);
1669
+ this[popperFilterContainerElementSymbol].classList.remove("d-none");
1670
+ this[popperFilterElementSymbol].classList.add("active");
1671
+ this[inlineFilterElementSymbol].classList.remove("active");
1672
+ this[inlineFilterElementSymbol].classList.add("d-none");
1673
+
1674
+ if (!(this[popperFilterElementSymbol] instanceof HTMLElement)) {
1675
+ addAttributeToken(
1676
+ this,
1677
+ ATTRIBUTE_ERRORMESSAGE,
1678
+ "Missing Popper Filter Element.",
1679
+ );
1680
+ return;
1681
+ }
1682
+
1683
+ // visibility is set to visible, because focus() does not work on invisible elements
1684
+ // and the class definition is assigned later in the processing
1685
+ setTimeout(() => {
1686
+ if (focusOptions === undefined || focusOptions === null) {
1687
+ this[popperFilterElementSymbol].focus();
1688
+ } else {
1689
+ this[popperFilterElementSymbol].focus(focusOptions);
1690
+ }
1691
+ }, 100);
1699
1692
  }
1700
1693
 
1701
1694
  /**
@@ -1703,44 +1696,44 @@ function focusPopperFilter(focusOptions) {
1703
1696
  * @param focusOptions
1704
1697
  */
1705
1698
  function focusInlineFilter(focusOptions) {
1706
- const options = this.getOption("options");
1707
- if (
1708
- (!isArray(options) || options.length === 0) &&
1709
- this.getOption("filter.mode") !== FILTER_MODE_REMOTE
1710
- ) {
1711
- return;
1712
- }
1713
-
1714
- this[popperFilterContainerElementSymbol].classList.add("d-none");
1715
- this[inlineFilterElementSymbol].classList.add("active");
1716
- this[inlineFilterElementSymbol].classList.remove("d-none");
1717
-
1718
- // visibility is set to visible, because focus() does not work on invisible elements
1719
- // and the class definition is assigned later in the processing
1720
- setTimeout(() => {
1721
- if (focusOptions === undefined || focusOptions === null) {
1722
- this[inlineFilterElementSymbol].focus();
1723
- } else {
1724
- this[inlineFilterElementSymbol].focus(focusOptions);
1725
- }
1726
- }, 100);
1699
+ const options = this.getOption("options");
1700
+ if (
1701
+ (!isArray(options) || options.length === 0) &&
1702
+ this.getOption("filter.mode") !== FILTER_MODE_REMOTE
1703
+ ) {
1704
+ return;
1705
+ }
1706
+
1707
+ this[popperFilterContainerElementSymbol].classList.add("d-none");
1708
+ this[inlineFilterElementSymbol].classList.add("active");
1709
+ this[inlineFilterElementSymbol].classList.remove("d-none");
1710
+
1711
+ // visibility is set to visible, because focus() does not work on invisible elements
1712
+ // and the class definition is assigned later in the processing
1713
+ setTimeout(() => {
1714
+ if (focusOptions === undefined || focusOptions === null) {
1715
+ this[inlineFilterElementSymbol].focus();
1716
+ } else {
1717
+ this[inlineFilterElementSymbol].focus(focusOptions);
1718
+ }
1719
+ }, 100);
1727
1720
  }
1728
1721
 
1729
1722
  /**
1730
1723
  * @private
1731
1724
  */
1732
1725
  function focusFilter(focusOptions) {
1733
- if (getFilterMode.call(this) === FILTER_MODE_DISABLED) {
1734
- this[popperFilterContainerElementSymbol].classList.add("d-none");
1735
- this[inlineFilterElementSymbol].classList.add("d-none");
1736
- return;
1737
- }
1726
+ if (getFilterMode.call(this) === FILTER_MODE_DISABLED) {
1727
+ this[popperFilterContainerElementSymbol].classList.add("d-none");
1728
+ this[inlineFilterElementSymbol].classList.add("d-none");
1729
+ return;
1730
+ }
1738
1731
 
1739
- if (this.getOption("filter.position") === FILTER_POSITION_INLINE) {
1740
- return focusInlineFilter.call(this, focusOptions);
1741
- }
1732
+ if (this.getOption("filter.position") === FILTER_POSITION_INLINE) {
1733
+ return focusInlineFilter.call(this, focusOptions);
1734
+ }
1742
1735
 
1743
- return focusPopperFilter.call(this, focusOptions);
1736
+ return focusPopperFilter.call(this, focusOptions);
1744
1737
  }
1745
1738
 
1746
1739
  /**
@@ -1750,40 +1743,39 @@ function focusFilter(focusOptions) {
1750
1743
  * @throws {Error} unsupported type
1751
1744
  */
1752
1745
  function gatherState() {
1753
- const type = this.getOption("type");
1754
- if (["radio", "checkbox"].indexOf(type) === -1) {
1755
- throw new Error("unsupported type");
1756
- }
1757
-
1758
- if (!this.shadowRoot) {
1759
- throw new Error("no shadow-root is defined");
1760
- }
1761
-
1762
- const selection = [];
1763
- const elements = this.shadowRoot.querySelectorAll(
1764
- `input[type=${type}]:checked`,
1765
- );
1766
-
1767
- for (const e of elements) {
1768
- selection.push({
1769
- label: getSelectionLabel.call(this, e.value),
1770
- value: e.value,
1771
- });
1772
- }
1773
-
1774
- setSelection
1775
- .call(this, selection)
1776
- .then(() => {
1777
- })
1778
- .catch((e) => {
1779
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, `${e}`);
1780
- });
1781
-
1782
- if (this.getOption("features.closeOnSelect") === true) {
1783
- toggle.call(this);
1784
- }
1785
-
1786
- return this;
1746
+ const type = this.getOption("type");
1747
+ if (["radio", "checkbox"].indexOf(type) === -1) {
1748
+ throw new Error("unsupported type");
1749
+ }
1750
+
1751
+ if (!this.shadowRoot) {
1752
+ throw new Error("no shadow-root is defined");
1753
+ }
1754
+
1755
+ const selection = [];
1756
+ const elements = this.shadowRoot.querySelectorAll(
1757
+ `input[type=${type}]:checked`,
1758
+ );
1759
+
1760
+ for (const e of elements) {
1761
+ selection.push({
1762
+ label: getSelectionLabel.call(this, e.value),
1763
+ value: e.value,
1764
+ });
1765
+ }
1766
+
1767
+ setSelection
1768
+ .call(this, selection)
1769
+ .then(() => {})
1770
+ .catch((e) => {
1771
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, `${e}`);
1772
+ });
1773
+
1774
+ if (this.getOption("features.closeOnSelect") === true) {
1775
+ toggle.call(this);
1776
+ }
1777
+
1778
+ return this;
1787
1779
  }
1788
1780
 
1789
1781
  /**
@@ -1792,121 +1784,120 @@ function gatherState() {
1792
1784
  * @throws {Error} unsupported type
1793
1785
  */
1794
1786
  function clearSelection() {
1795
- const type = this.getOption("type");
1796
- if (["radio", "checkbox"].indexOf(type) === -1) {
1797
- throw new Error("unsupported type");
1798
- }
1799
-
1800
- if (!this.shadowRoot) {
1801
- throw new Error("no shadow-root is defined");
1802
- }
1803
-
1804
- setSelection
1805
- .call(this, [])
1806
- .then(() => {
1807
- })
1808
- .catch((e) => {
1809
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, `${e}`);
1810
- });
1787
+ const type = this.getOption("type");
1788
+ if (["radio", "checkbox"].indexOf(type) === -1) {
1789
+ throw new Error("unsupported type");
1790
+ }
1791
+
1792
+ if (!this.shadowRoot) {
1793
+ throw new Error("no shadow-root is defined");
1794
+ }
1795
+
1796
+ setSelection
1797
+ .call(this, [])
1798
+ .then(() => {})
1799
+ .catch((e) => {
1800
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, `${e}`);
1801
+ });
1811
1802
  }
1812
1803
 
1813
1804
  /**
1814
1805
  * @private
1815
1806
  */
1816
1807
  function areOptionsAvailableAndInit() {
1817
- // prevent multiple calls
1818
- if (this[areOptionsAvailableAndInitSymbol] === undefined) {
1819
- this[areOptionsAvailableAndInitSymbol] = 0;
1820
- }
1821
-
1822
- if (this[areOptionsAvailableAndInitSymbol] > 0) {
1823
- this[areOptionsAvailableAndInitSymbol]--;
1824
- return true;
1825
- }
1826
-
1827
- this[areOptionsAvailableAndInitSymbol]++;
1828
-
1829
- const options = this.getOption("options");
1830
-
1831
- if (
1832
- options === undefined ||
1833
- options === null ||
1834
- (isArray(options) && options.length === 0)
1835
- ) {
1836
- setStatusOrRemoveBadges.call(this, "empty");
1837
-
1838
- hide.call(this);
1839
-
1840
- let msg = this.getOption("labels.no-options-available");
1841
-
1842
- if (
1843
- this.getOption("url") !== null &&
1844
- this.getOption("features.lazyLoad") === true &&
1845
- this[lazyLoadDoneSymbol] !== true
1846
- ) {
1847
- msg = this.getOption("labels.click-to-load-options");
1848
- }
1849
-
1850
- this.setOption("messages.control", msg);
1851
- this.setOption("messages.summary", "");
1852
-
1853
- if (this.getOption("features.emptyValueIfNoOptions") === true) {
1854
- this.value = "";
1855
- }
1856
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, noOptionsAvailableMessage);
1857
- return false;
1858
- }
1859
-
1860
- const selections = this.getOption("selection");
1861
- if (
1862
- selections === undefined ||
1863
- selections === null ||
1864
- selections.length === 0
1865
- ) {
1866
- this.setOption(
1867
- "messages.control",
1868
- this.getOption("labels.select-an-option"),
1869
- );
1870
- } else {
1871
- this.setOption("messages.control", "");
1872
- }
1873
-
1874
- this.setOption("messages.summary", setSummaryAndControlText.call(this));
1875
-
1876
- let updated = false;
1877
- let valueCounter = 1;
1878
- for (const option of options) {
1879
- if (option?.visibility === undefined) {
1880
- option.visibility = "visible";
1881
- updated = true;
1882
- }
1883
-
1884
- if (option?.value === undefined && option?.label === undefined) {
1885
- option.value = `${valueCounter++}`;
1886
- option.label = option.value;
1887
- updated = true;
1888
- continue;
1889
- }
1890
-
1891
- if (option?.value === undefined) {
1892
- option.value = option.label;
1893
- updated = true;
1894
- }
1895
-
1896
- if (option?.label === undefined) {
1897
- option.label = option.value;
1898
- updated = true;
1899
- }
1900
- }
1901
-
1902
- if (updated) {
1903
- this.setOption("options", options);
1904
- }
1905
-
1906
- setStatusOrRemoveBadges.call(this);
1907
-
1908
- removeAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, noOptionsAvailableMessage);
1909
- return true;
1808
+ // prevent multiple calls
1809
+ if (this[areOptionsAvailableAndInitSymbol] === undefined) {
1810
+ this[areOptionsAvailableAndInitSymbol] = 0;
1811
+ }
1812
+
1813
+ if (this[areOptionsAvailableAndInitSymbol] > 0) {
1814
+ this[areOptionsAvailableAndInitSymbol]--;
1815
+ return true;
1816
+ }
1817
+
1818
+ this[areOptionsAvailableAndInitSymbol]++;
1819
+
1820
+ const options = this.getOption("options");
1821
+
1822
+ if (
1823
+ options === undefined ||
1824
+ options === null ||
1825
+ (isArray(options) && options.length === 0)
1826
+ ) {
1827
+ setStatusOrRemoveBadges.call(this, "empty");
1828
+
1829
+ hide.call(this);
1830
+
1831
+ let msg = this.getOption("labels.no-options-available");
1832
+
1833
+ if (
1834
+ this.getOption("url") !== null &&
1835
+ this.getOption("features.lazyLoad") === true &&
1836
+ this[lazyLoadDoneSymbol] !== true
1837
+ ) {
1838
+ msg = this.getOption("labels.click-to-load-options");
1839
+ }
1840
+
1841
+ this.setOption("messages.control", msg);
1842
+ this.setOption("messages.summary", "");
1843
+
1844
+ if (this.getOption("features.emptyValueIfNoOptions") === true) {
1845
+ this.value = "";
1846
+ }
1847
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, noOptionsAvailableMessage);
1848
+ return false;
1849
+ }
1850
+
1851
+ const selections = this.getOption("selection");
1852
+ if (
1853
+ selections === undefined ||
1854
+ selections === null ||
1855
+ selections.length === 0
1856
+ ) {
1857
+ this.setOption(
1858
+ "messages.control",
1859
+ this.getOption("labels.select-an-option"),
1860
+ );
1861
+ } else {
1862
+ this.setOption("messages.control", "");
1863
+ }
1864
+
1865
+ this.setOption("messages.summary", setSummaryAndControlText.call(this));
1866
+
1867
+ let updated = false;
1868
+ let valueCounter = 1;
1869
+ for (const option of options) {
1870
+ if (option?.visibility === undefined) {
1871
+ option.visibility = "visible";
1872
+ updated = true;
1873
+ }
1874
+
1875
+ if (option?.value === undefined && option?.label === undefined) {
1876
+ option.value = `${valueCounter++}`;
1877
+ option.label = option.value;
1878
+ updated = true;
1879
+ continue;
1880
+ }
1881
+
1882
+ if (option?.value === undefined) {
1883
+ option.value = option.label;
1884
+ updated = true;
1885
+ }
1886
+
1887
+ if (option?.label === undefined) {
1888
+ option.label = option.value;
1889
+ updated = true;
1890
+ }
1891
+ }
1892
+
1893
+ if (updated) {
1894
+ this.setOption("options", options);
1895
+ }
1896
+
1897
+ setStatusOrRemoveBadges.call(this);
1898
+
1899
+ removeAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, noOptionsAvailableMessage);
1900
+ return true;
1910
1901
  }
1911
1902
 
1912
1903
  /**
@@ -1914,30 +1905,30 @@ function areOptionsAvailableAndInit() {
1914
1905
  * @throws {Error} no shadow-root is defined
1915
1906
  */
1916
1907
  function checkOptionState() {
1917
- if (!this.shadowRoot) {
1918
- throw new Error("no shadow-root is defined");
1919
- }
1920
-
1921
- const elements = this.shadowRoot.querySelectorAll(
1922
- `[${ATTRIBUTE_ROLE}=option] input`,
1923
- );
1924
-
1925
- let selection = this.getOption("selection");
1926
- if (!isArray(selection)) {
1927
- selection = [];
1928
- }
1929
-
1930
- const checkedValues = selection.map((a) => {
1931
- return a.value;
1932
- });
1933
-
1934
- for (const e of elements) {
1935
- if (checkedValues.indexOf(e.value) !== -1) {
1936
- if (e.checked !== true) e.checked = true;
1937
- } else {
1938
- if (e.checked !== false) e.checked = false;
1939
- }
1940
- }
1908
+ if (!this.shadowRoot) {
1909
+ throw new Error("no shadow-root is defined");
1910
+ }
1911
+
1912
+ const elements = this.shadowRoot.querySelectorAll(
1913
+ `[${ATTRIBUTE_ROLE}=option] input`,
1914
+ );
1915
+
1916
+ let selection = this.getOption("selection");
1917
+ if (!isArray(selection)) {
1918
+ selection = [];
1919
+ }
1920
+
1921
+ const checkedValues = selection.map((a) => {
1922
+ return a.value;
1923
+ });
1924
+
1925
+ for (const e of elements) {
1926
+ if (checkedValues.indexOf(e.value) !== -1) {
1927
+ if (e.checked !== true) e.checked = true;
1928
+ } else {
1929
+ if (e.checked !== false) e.checked = false;
1930
+ }
1931
+ }
1941
1932
  }
1942
1933
 
1943
1934
  /**
@@ -1946,41 +1937,41 @@ function checkOptionState() {
1946
1937
  * @return {Object}
1947
1938
  */
1948
1939
  function convertValueToSelection(value) {
1949
- const selection = [];
1950
-
1951
- if (isString(value)) {
1952
- value = value
1953
- .split(",")
1954
- .map((a) => {
1955
- return a.trim();
1956
- })
1957
- .filter((a) => {
1958
- return a !== "";
1959
- });
1960
- }
1961
-
1962
- if (isString(value) || isInteger(value)) {
1963
- selection.push({
1964
- label: getSelectionLabel.call(this, value),
1965
- value: value,
1966
- });
1967
- } else if (isArray(value)) {
1968
- for (const v of value) {
1969
- selection.push({
1970
- label: getSelectionLabel.call(this, v),
1971
- value: v,
1972
- });
1973
- }
1974
-
1975
- value = value.join(",");
1976
- } else {
1977
- throw new Error("unsupported type");
1978
- }
1979
-
1980
- return {
1981
- selection: selection,
1982
- value: value,
1983
- };
1940
+ const selection = [];
1941
+
1942
+ if (isString(value)) {
1943
+ value = value
1944
+ .split(",")
1945
+ .map((a) => {
1946
+ return a.trim();
1947
+ })
1948
+ .filter((a) => {
1949
+ return a !== "";
1950
+ });
1951
+ }
1952
+
1953
+ if (isString(value) || isInteger(value)) {
1954
+ selection.push({
1955
+ label: getSelectionLabel.call(this, value),
1956
+ value: value,
1957
+ });
1958
+ } else if (isArray(value)) {
1959
+ for (const v of value) {
1960
+ selection.push({
1961
+ label: getSelectionLabel.call(this, v),
1962
+ value: v,
1963
+ });
1964
+ }
1965
+
1966
+ value = value.join(",");
1967
+ } else {
1968
+ throw new Error("unsupported type");
1969
+ }
1970
+
1971
+ return {
1972
+ selection: selection,
1973
+ value: value,
1974
+ };
1984
1975
  }
1985
1976
 
1986
1977
  /**
@@ -1989,22 +1980,22 @@ function convertValueToSelection(value) {
1989
1980
  * @return {string}
1990
1981
  */
1991
1982
  function convertSelectionToValue(selection) {
1992
- const value = [];
1993
-
1994
- if (isArray(selection)) {
1995
- for (const obj of selection) {
1996
- const v = obj?.["value"];
1997
- if (v !== undefined) value.push(v);
1998
- }
1999
- }
2000
-
2001
- if (value.length === 0) {
2002
- return "";
2003
- } else if (value.length === 1) {
2004
- return value.pop();
2005
- }
2006
-
2007
- return value.join(",");
1983
+ const value = [];
1984
+
1985
+ if (isArray(selection)) {
1986
+ for (const obj of selection) {
1987
+ const v = obj?.["value"];
1988
+ if (v !== undefined) value.push(v);
1989
+ }
1990
+ }
1991
+
1992
+ if (value.length === 0) {
1993
+ return "";
1994
+ } else if (value.length === 1) {
1995
+ return value.pop();
1996
+ }
1997
+
1998
+ return value.join(",");
2008
1999
  }
2009
2000
 
2010
2001
  /**
@@ -2014,67 +2005,67 @@ function convertSelectionToValue(selection) {
2014
2005
  * @throws {Error} no shadow-root is defined
2015
2006
  */
2016
2007
  function setSelection(selection) {
2017
- if (isString(selection)) {
2018
- const result = convertValueToSelection.call(this, selection);
2019
- selection = result?.selection;
2020
- } else if (selection === undefined) {
2021
- selection = [];
2022
- }
2023
-
2024
- validateArray(selection);
2025
-
2026
- for (let i = 0; i < selection.length; i++) {
2027
- selection[i] = {
2028
- label: getSelectionLabel.call(this, selection[i].value),
2029
- value: selection[i].value,
2030
- };
2031
- }
2032
-
2033
- this.setOption("selection", selection);
2034
- checkOptionState.call(this);
2035
-
2036
- try {
2037
- this?.setFormValue(this.value);
2038
- } catch (e) {
2039
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
2040
- }
2041
-
2042
- fireCustomEvent(this, "monster-selected", {
2043
- selection,
2044
- });
2045
-
2046
- return new Processing(() => {
2047
- const CLASSNAME = "selected";
2048
-
2049
- if (!this.shadowRoot) {
2050
- throw new Error("no shadow-root is defined");
2051
- }
2052
-
2053
- const notSelected = this.shadowRoot.querySelectorAll(":not(:checked)");
2054
-
2055
- if (notSelected) {
2056
- notSelected.forEach((node) => {
2057
- const parent = node.closest(`[${ATTRIBUTE_ROLE}=option]`);
2058
- if (parent) {
2059
- parent.classList.remove(CLASSNAME);
2060
- }
2061
- });
2062
- }
2063
-
2064
- const selected = this.shadowRoot.querySelectorAll(":checked");
2065
- if (selected) {
2066
- selected.forEach((node) => {
2067
- const parent = node.closest(`[${ATTRIBUTE_ROLE}=option]`);
2068
- if (parent) {
2069
- parent.classList.add(CLASSNAME);
2070
- }
2071
- });
2072
- }
2073
- })
2074
- .run()
2075
- .catch((e) => {
2076
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
2077
- });
2008
+ if (isString(selection)) {
2009
+ const result = convertValueToSelection.call(this, selection);
2010
+ selection = result?.selection;
2011
+ } else if (selection === undefined) {
2012
+ selection = [];
2013
+ }
2014
+
2015
+ validateArray(selection);
2016
+
2017
+ for (let i = 0; i < selection.length; i++) {
2018
+ selection[i] = {
2019
+ label: getSelectionLabel.call(this, selection[i].value),
2020
+ value: selection[i].value,
2021
+ };
2022
+ }
2023
+
2024
+ this.setOption("selection", selection);
2025
+ checkOptionState.call(this);
2026
+
2027
+ try {
2028
+ this?.setFormValue(this.value);
2029
+ } catch (e) {
2030
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
2031
+ }
2032
+
2033
+ fireCustomEvent(this, "monster-selected", {
2034
+ selection,
2035
+ });
2036
+
2037
+ return new Processing(() => {
2038
+ const CLASSNAME = "selected";
2039
+
2040
+ if (!this.shadowRoot) {
2041
+ throw new Error("no shadow-root is defined");
2042
+ }
2043
+
2044
+ const notSelected = this.shadowRoot.querySelectorAll(":not(:checked)");
2045
+
2046
+ if (notSelected) {
2047
+ notSelected.forEach((node) => {
2048
+ const parent = node.closest(`[${ATTRIBUTE_ROLE}=option]`);
2049
+ if (parent) {
2050
+ parent.classList.remove(CLASSNAME);
2051
+ }
2052
+ });
2053
+ }
2054
+
2055
+ const selected = this.shadowRoot.querySelectorAll(":checked");
2056
+ if (selected) {
2057
+ selected.forEach((node) => {
2058
+ const parent = node.closest(`[${ATTRIBUTE_ROLE}=option]`);
2059
+ if (parent) {
2060
+ parent.classList.add(CLASSNAME);
2061
+ }
2062
+ });
2063
+ }
2064
+ })
2065
+ .run()
2066
+ .catch((e) => {
2067
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
2068
+ });
2078
2069
  }
2079
2070
 
2080
2071
  /**
@@ -2085,138 +2076,137 @@ function setSelection(selection) {
2085
2076
  * @throws {TypeError} unsupported response
2086
2077
  */
2087
2078
  function fetchData(url) {
2088
- const self = this;
2089
- if (!url) url = this.getOption("url");
2090
- if (!url) return Promise.resolve();
2091
-
2092
- const fetchOptions = this.getOption("fetch", {});
2093
-
2094
- let delayWatch = false;
2095
-
2096
- // if fetch short time, do not show loading badge, because of flickering
2097
- requestAnimationFrame(() => {
2098
- if (delayWatch === true) return;
2099
- setStatusOrRemoveBadges.call(this, "loading");
2100
- delayWatch = true;
2101
- });
2102
-
2103
- url = new Formatter({filter: this.getOption("filter.defaultValue")}).format(
2104
- url,
2105
- );
2106
-
2107
- self[isLoadingSymbol] = true;
2108
- const global = getGlobal();
2109
- return global
2110
- .fetch(url, fetchOptions)
2111
- .then((response) => {
2112
- self[isLoadingSymbol] = false;
2113
- delayWatch = true;
2114
- const contentType = response.headers.get("content-type");
2115
- if (contentType && contentType.indexOf("application/json") !== -1) {
2116
- return response.text();
2117
- }
2118
-
2119
- throw new TypeError(`unsupported response ${contentType}`);
2120
- })
2121
- .then((text) => {
2122
- try {
2123
- return Promise.resolve(JSON.parse(String(text)));
2124
- } catch (e) {
2125
- throw new TypeError("the result cannot be parsed, check the URL");
2126
- }
2127
- })
2128
- .catch((e) => {
2129
- self[isLoadingSymbol] = false;
2130
- delayWatch = true;
2131
- throw e;
2132
- });
2079
+ const self = this;
2080
+ if (!url) url = this.getOption("url");
2081
+ if (!url) return Promise.resolve();
2082
+
2083
+ const fetchOptions = this.getOption("fetch", {});
2084
+
2085
+ let delayWatch = false;
2086
+
2087
+ // if fetch short time, do not show loading badge, because of flickering
2088
+ requestAnimationFrame(() => {
2089
+ if (delayWatch === true) return;
2090
+ setStatusOrRemoveBadges.call(this, "loading");
2091
+ delayWatch = true;
2092
+ });
2093
+
2094
+ url = new Formatter({ filter: this.getOption("filter.defaultValue") }).format(
2095
+ url,
2096
+ );
2097
+
2098
+ self[isLoadingSymbol] = true;
2099
+ const global = getGlobal();
2100
+ return global
2101
+ .fetch(url, fetchOptions)
2102
+ .then((response) => {
2103
+ self[isLoadingSymbol] = false;
2104
+ delayWatch = true;
2105
+ const contentType = response.headers.get("content-type");
2106
+ if (contentType && contentType.indexOf("application/json") !== -1) {
2107
+ return response.text();
2108
+ }
2109
+
2110
+ throw new TypeError(`unsupported response ${contentType}`);
2111
+ })
2112
+ .then((text) => {
2113
+ try {
2114
+ return Promise.resolve(JSON.parse(String(text)));
2115
+ } catch (e) {
2116
+ throw new TypeError("the result cannot be parsed, check the URL");
2117
+ }
2118
+ })
2119
+ .catch((e) => {
2120
+ self[isLoadingSymbol] = false;
2121
+ delayWatch = true;
2122
+ throw e;
2123
+ });
2133
2124
  }
2134
2125
 
2135
2126
  /**
2136
2127
  * @private
2137
2128
  */
2138
2129
  function hide() {
2139
- this[popperElementSymbol].style.display = "none";
2140
- setStatusOrRemoveBadges.call(this, "closed");
2141
- removeAttributeToken(this[controlElementSymbol], "class", "open");
2130
+ this[popperElementSymbol].style.display = "none";
2131
+ setStatusOrRemoveBadges.call(this, "closed");
2132
+ removeAttributeToken(this[controlElementSymbol], "class", "open");
2142
2133
  }
2143
2134
 
2144
2135
  /**
2145
2136
  * @private
2146
2137
  */
2147
2138
  function show() {
2148
- if (this.getOption("disabled", undefined) === true) {
2149
- return;
2150
- }
2151
-
2152
- if (this[popperElementSymbol].style.display === STYLE_DISPLAY_MODE_BLOCK) {
2153
- return;
2154
- }
2155
-
2156
- focusFilter.call(this);
2157
-
2158
- const lazyLoadFlag =
2159
- this.getOption("features.lazyLoad") && this[lazyLoadDoneSymbol] !== true;
2160
-
2161
- if (lazyLoadFlag === true) {
2162
- this[lazyLoadDoneSymbol] = true;
2163
- setStatusOrRemoveBadges.call(this, "loading");
2164
-
2165
- new Processing(200, () => {
2166
- this.fetch()
2167
- .then(() => {
2168
- checkOptionState.call(this);
2169
- requestAnimationFrame(() => {
2170
- show.call(this);
2171
- })
2172
- })
2173
- .catch((e) => {
2174
-
2175
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
2176
- setStatusOrRemoveBadges.call(this, "error");
2177
- });
2178
- })
2179
- .run()
2180
- .catch((e) => {
2181
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
2182
- setStatusOrRemoveBadges.call(this, "error");
2183
- });
2184
-
2185
- return;
2186
- }
2187
-
2188
- const options = getOptionElements.call(this);
2189
- if (options.length === 0) {
2190
- return;
2191
- }
2192
-
2193
- this[popperElementSymbol].style.visibility = "hidden";
2194
- this[popperElementSymbol].style.display = STYLE_DISPLAY_MODE_BLOCK;
2195
- setStatusOrRemoveBadges.call(this, "open");
2196
-
2197
- addAttributeToken(this[controlElementSymbol], "class", "open");
2198
-
2199
- new Processing(() => {
2200
- calcAndSetOptionsDimension.call(this);
2201
- focusFilter.call(this);
2202
- this[popperElementSymbol].style.removeProperty("visibility");
2203
- updatePopper.call(this);
2204
- })
2205
- .run()
2206
- .catch((e) => {
2207
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
2208
- });
2139
+ if (this.getOption("disabled", undefined) === true) {
2140
+ return;
2141
+ }
2142
+
2143
+ if (this[popperElementSymbol].style.display === STYLE_DISPLAY_MODE_BLOCK) {
2144
+ return;
2145
+ }
2146
+
2147
+ focusFilter.call(this);
2148
+
2149
+ const lazyLoadFlag =
2150
+ this.getOption("features.lazyLoad") && this[lazyLoadDoneSymbol] !== true;
2151
+
2152
+ if (lazyLoadFlag === true) {
2153
+ this[lazyLoadDoneSymbol] = true;
2154
+ setStatusOrRemoveBadges.call(this, "loading");
2155
+
2156
+ new Processing(200, () => {
2157
+ this.fetch()
2158
+ .then(() => {
2159
+ checkOptionState.call(this);
2160
+ requestAnimationFrame(() => {
2161
+ show.call(this);
2162
+ });
2163
+ })
2164
+ .catch((e) => {
2165
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
2166
+ setStatusOrRemoveBadges.call(this, "error");
2167
+ });
2168
+ })
2169
+ .run()
2170
+ .catch((e) => {
2171
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
2172
+ setStatusOrRemoveBadges.call(this, "error");
2173
+ });
2174
+
2175
+ return;
2176
+ }
2177
+
2178
+ const options = getOptionElements.call(this);
2179
+ if (options.length === 0) {
2180
+ return;
2181
+ }
2182
+
2183
+ this[popperElementSymbol].style.visibility = "hidden";
2184
+ this[popperElementSymbol].style.display = STYLE_DISPLAY_MODE_BLOCK;
2185
+ setStatusOrRemoveBadges.call(this, "open");
2186
+
2187
+ addAttributeToken(this[controlElementSymbol], "class", "open");
2188
+
2189
+ new Processing(() => {
2190
+ calcAndSetOptionsDimension.call(this);
2191
+ focusFilter.call(this);
2192
+ this[popperElementSymbol].style.removeProperty("visibility");
2193
+ updatePopper.call(this);
2194
+ })
2195
+ .run()
2196
+ .catch((e) => {
2197
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
2198
+ });
2209
2199
  }
2210
2200
 
2211
2201
  /**
2212
2202
  * @private
2213
2203
  */
2214
2204
  function toggle() {
2215
- if (this[popperElementSymbol].style.display === STYLE_DISPLAY_MODE_BLOCK) {
2216
- hide.call(this);
2217
- } else {
2218
- show.call(this);
2219
- }
2205
+ if (this[popperElementSymbol].style.display === STYLE_DISPLAY_MODE_BLOCK) {
2206
+ hide.call(this);
2207
+ } else {
2208
+ show.call(this);
2209
+ }
2220
2210
  }
2221
2211
 
2222
2212
  /**
@@ -2225,188 +2215,188 @@ function toggle() {
2225
2215
  * @fires monster-selection-cleared
2226
2216
  */
2227
2217
  function initEventHandler() {
2228
- const self = this;
2229
-
2230
- /**
2231
- * @param {Event} event
2232
- */
2233
- self[clearOptionEventHandler] = (event) => {
2234
- const element = findTargetElementFromEvent(
2235
- event,
2236
- ATTRIBUTE_ROLE,
2237
- "remove-badge",
2238
- );
2239
-
2240
- if (element instanceof HTMLElement) {
2241
- const badge = findClosestByAttribute(element, ATTRIBUTE_ROLE, "badge");
2242
- if (badge instanceof HTMLElement) {
2243
- const value = badge.getAttribute(`${ATTRIBUTE_PREFIX}value`);
2244
-
2245
- let selection = self.getOption("selection");
2246
- selection = selection.filter((b) => {
2247
- return value !== b.value;
2248
- });
2249
-
2250
- setSelection
2251
- .call(self, selection)
2252
- .then(() => {
2253
- fireCustomEvent(self, "monster-selection-removed", {
2254
- value,
2255
- });
2256
- })
2257
- .catch((e) => {
2258
- addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, e.message);
2259
- });
2260
- }
2261
- }
2262
- };
2263
-
2264
- /**
2265
- * @param {Event} event
2266
- */
2267
- self[closeEventHandler] = (event) => {
2268
- const path = event.composedPath();
2269
-
2270
- for (const [, element] of Object.entries(path)) {
2271
- if (element === self) {
2272
- return;
2273
- }
2274
- }
2275
- hide.call(self);
2276
- };
2277
-
2278
- /**
2279
- * @param {Event} event
2280
- */
2281
- self[inputEventHandler] = (event) => {
2282
- const path = event.composedPath();
2283
- const element = path?.[0];
2284
-
2285
- if (element instanceof HTMLElement) {
2286
- if (
2287
- element.hasAttribute(ATTRIBUTE_ROLE) &&
2288
- element.getAttribute(ATTRIBUTE_ROLE) === "option-control"
2289
- ) {
2290
- fireCustomEvent(self, "monster-change", {
2291
- type: event.type,
2292
- value: element.value,
2293
- checked: element.checked,
2294
- });
2295
- } else if (
2296
- element.hasAttribute(ATTRIBUTE_ROLE) &&
2297
- element.getAttribute(ATTRIBUTE_ROLE) === "filter"
2298
- ) {
2299
- }
2300
- }
2301
- };
2302
-
2303
- /**
2304
- * @param {Event} event
2305
- */
2306
- self[changeEventHandler] = (event) => {
2307
- gatherState.call(self);
2308
- fireCustomEvent(self, "monster-changed", event?.detail);
2309
- };
2310
-
2311
- self[keyEventHandler] = (event) => {
2312
- const path = event.composedPath();
2313
- const element = path.shift();
2314
-
2315
- let role;
2316
-
2317
- if (element instanceof HTMLElement) {
2318
- if (element.hasAttribute(ATTRIBUTE_ROLE)) {
2319
- role = element.getAttribute(ATTRIBUTE_ROLE);
2320
- } else if (element === this) {
2321
- show.call(this);
2322
- // focusFilter.call(self);
2323
- } else {
2324
- const e = element.closest(`[${ATTRIBUTE_ROLE}]`);
2325
- if (e instanceof HTMLElement && e.hasAttribute(ATTRIBUTE_ROLE)) {
2326
- role = e.getAttribute(ATTRIBUTE_ROLE);
2327
- }
2328
- }
2329
- } else {
2330
- return;
2331
- }
2332
-
2333
- switch (role) {
2334
- case "filter":
2335
- handleFilterKeyboardEvents.call(self, event);
2336
- break;
2337
- case "option-label":
2338
- case "option-control":
2339
- case "option":
2340
- handleOptionKeyboardEvents.call(self, event);
2341
- break;
2342
- case "control":
2343
- case "toggle":
2344
- handleToggleKeyboardEvents.call(self, event);
2345
- break;
2346
- }
2347
- };
2348
-
2349
- const types = self.getOption("toggleEventType", ["click"]);
2350
-
2351
- for (const [, type] of Object.entries(types)) {
2352
- self[controlElementSymbol]
2353
- .querySelector(`[${ATTRIBUTE_ROLE}="container"]`)
2354
- .addEventListener(type, function (event) {
2355
- const element = findTargetElementFromEvent(
2356
- event,
2357
- ATTRIBUTE_ROLE,
2358
- "remove-badge",
2359
- );
2360
- if (element instanceof HTMLElement) {
2361
- return;
2362
- }
2363
-
2364
- toggle.call(self);
2365
- });
2366
-
2367
- self[controlElementSymbol]
2368
- .querySelector(`[${ATTRIBUTE_ROLE}="status-or-remove-badges"]`)
2369
- .addEventListener(type, function (event) {
2370
- if (self.getOption("disabled", undefined) === true) {
2371
- return;
2372
- }
2373
-
2374
- const path = event.composedPath();
2375
- const element = path?.[0];
2376
- if (element instanceof HTMLElement) {
2377
- const control = element.closest(
2378
- `[${ATTRIBUTE_ROLE}="status-or-remove-badges"]`,
2379
- );
2380
- if (control instanceof HTMLElement) {
2381
- if (control.classList.contains("clear")) {
2382
- clearSelection.call(self);
2383
-
2384
- fireCustomEvent(self, "monster-selection-cleared", {});
2385
- } else {
2386
- const element = findTargetElementFromEvent(
2387
- event,
2388
- ATTRIBUTE_ROLE,
2389
- "remove-badge",
2390
- );
2391
- if (element instanceof HTMLElement) {
2392
- return;
2393
- }
2394
-
2395
- toggle.call(self);
2396
- }
2397
- }
2398
- }
2399
- });
2400
-
2401
- // badge, selection
2402
- self.addEventListener(type, self[clearOptionEventHandler]);
2403
- }
2404
-
2405
- self.addEventListener("monster-change", self[changeEventHandler]);
2406
- self.addEventListener("input", self[inputEventHandler]);
2407
- self.addEventListener("keydown", self[keyEventHandler]);
2408
-
2409
- return self;
2218
+ const self = this;
2219
+
2220
+ /**
2221
+ * @param {Event} event
2222
+ */
2223
+ self[clearOptionEventHandler] = (event) => {
2224
+ const element = findTargetElementFromEvent(
2225
+ event,
2226
+ ATTRIBUTE_ROLE,
2227
+ "remove-badge",
2228
+ );
2229
+
2230
+ if (element instanceof HTMLElement) {
2231
+ const badge = findClosestByAttribute(element, ATTRIBUTE_ROLE, "badge");
2232
+ if (badge instanceof HTMLElement) {
2233
+ const value = badge.getAttribute(`${ATTRIBUTE_PREFIX}value`);
2234
+
2235
+ let selection = self.getOption("selection");
2236
+ selection = selection.filter((b) => {
2237
+ return value !== b.value;
2238
+ });
2239
+
2240
+ setSelection
2241
+ .call(self, selection)
2242
+ .then(() => {
2243
+ fireCustomEvent(self, "monster-selection-removed", {
2244
+ value,
2245
+ });
2246
+ })
2247
+ .catch((e) => {
2248
+ addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, e.message);
2249
+ });
2250
+ }
2251
+ }
2252
+ };
2253
+
2254
+ /**
2255
+ * @param {Event} event
2256
+ */
2257
+ self[closeEventHandler] = (event) => {
2258
+ const path = event.composedPath();
2259
+
2260
+ for (const [, element] of Object.entries(path)) {
2261
+ if (element === self) {
2262
+ return;
2263
+ }
2264
+ }
2265
+ hide.call(self);
2266
+ };
2267
+
2268
+ /**
2269
+ * @param {Event} event
2270
+ */
2271
+ self[inputEventHandler] = (event) => {
2272
+ const path = event.composedPath();
2273
+ const element = path?.[0];
2274
+
2275
+ if (element instanceof HTMLElement) {
2276
+ if (
2277
+ element.hasAttribute(ATTRIBUTE_ROLE) &&
2278
+ element.getAttribute(ATTRIBUTE_ROLE) === "option-control"
2279
+ ) {
2280
+ fireCustomEvent(self, "monster-change", {
2281
+ type: event.type,
2282
+ value: element.value,
2283
+ checked: element.checked,
2284
+ });
2285
+ } else if (
2286
+ element.hasAttribute(ATTRIBUTE_ROLE) &&
2287
+ element.getAttribute(ATTRIBUTE_ROLE) === "filter"
2288
+ ) {
2289
+ }
2290
+ }
2291
+ };
2292
+
2293
+ /**
2294
+ * @param {Event} event
2295
+ */
2296
+ self[changeEventHandler] = (event) => {
2297
+ gatherState.call(self);
2298
+ fireCustomEvent(self, "monster-changed", event?.detail);
2299
+ };
2300
+
2301
+ self[keyEventHandler] = (event) => {
2302
+ const path = event.composedPath();
2303
+ const element = path.shift();
2304
+
2305
+ let role;
2306
+
2307
+ if (element instanceof HTMLElement) {
2308
+ if (element.hasAttribute(ATTRIBUTE_ROLE)) {
2309
+ role = element.getAttribute(ATTRIBUTE_ROLE);
2310
+ } else if (element === this) {
2311
+ show.call(this);
2312
+ // focusFilter.call(self);
2313
+ } else {
2314
+ const e = element.closest(`[${ATTRIBUTE_ROLE}]`);
2315
+ if (e instanceof HTMLElement && e.hasAttribute(ATTRIBUTE_ROLE)) {
2316
+ role = e.getAttribute(ATTRIBUTE_ROLE);
2317
+ }
2318
+ }
2319
+ } else {
2320
+ return;
2321
+ }
2322
+
2323
+ switch (role) {
2324
+ case "filter":
2325
+ handleFilterKeyboardEvents.call(self, event);
2326
+ break;
2327
+ case "option-label":
2328
+ case "option-control":
2329
+ case "option":
2330
+ handleOptionKeyboardEvents.call(self, event);
2331
+ break;
2332
+ case "control":
2333
+ case "toggle":
2334
+ handleToggleKeyboardEvents.call(self, event);
2335
+ break;
2336
+ }
2337
+ };
2338
+
2339
+ const types = self.getOption("toggleEventType", ["click"]);
2340
+
2341
+ for (const [, type] of Object.entries(types)) {
2342
+ self[controlElementSymbol]
2343
+ .querySelector(`[${ATTRIBUTE_ROLE}="container"]`)
2344
+ .addEventListener(type, function (event) {
2345
+ const element = findTargetElementFromEvent(
2346
+ event,
2347
+ ATTRIBUTE_ROLE,
2348
+ "remove-badge",
2349
+ );
2350
+ if (element instanceof HTMLElement) {
2351
+ return;
2352
+ }
2353
+
2354
+ toggle.call(self);
2355
+ });
2356
+
2357
+ self[controlElementSymbol]
2358
+ .querySelector(`[${ATTRIBUTE_ROLE}="status-or-remove-badges"]`)
2359
+ .addEventListener(type, function (event) {
2360
+ if (self.getOption("disabled", undefined) === true) {
2361
+ return;
2362
+ }
2363
+
2364
+ const path = event.composedPath();
2365
+ const element = path?.[0];
2366
+ if (element instanceof HTMLElement) {
2367
+ const control = element.closest(
2368
+ `[${ATTRIBUTE_ROLE}="status-or-remove-badges"]`,
2369
+ );
2370
+ if (control instanceof HTMLElement) {
2371
+ if (control.classList.contains("clear")) {
2372
+ clearSelection.call(self);
2373
+
2374
+ fireCustomEvent(self, "monster-selection-cleared", {});
2375
+ } else {
2376
+ const element = findTargetElementFromEvent(
2377
+ event,
2378
+ ATTRIBUTE_ROLE,
2379
+ "remove-badge",
2380
+ );
2381
+ if (element instanceof HTMLElement) {
2382
+ return;
2383
+ }
2384
+
2385
+ toggle.call(self);
2386
+ }
2387
+ }
2388
+ }
2389
+ });
2390
+
2391
+ // badge, selection
2392
+ self.addEventListener(type, self[clearOptionEventHandler]);
2393
+ }
2394
+
2395
+ self.addEventListener("monster-change", self[changeEventHandler]);
2396
+ self.addEventListener("input", self[inputEventHandler]);
2397
+ self.addEventListener("keydown", self[keyEventHandler]);
2398
+
2399
+ return self;
2410
2400
  }
2411
2401
 
2412
2402
  /**
@@ -2414,72 +2404,70 @@ function initEventHandler() {
2414
2404
  * @return {Select}
2415
2405
  */
2416
2406
  function setStatusOrRemoveBadges(suggestion) {
2417
-
2418
- requestAnimationFrame(() => {
2419
- const selection = this.getOption("selection");
2420
-
2421
- const clearAllFlag =
2422
- isArray(selection) &&
2423
- selection.length > 0 &&
2424
- this.getOption("features.clearAll") === true;
2425
-
2426
- const current = this.getOption("classes.statusOrRemoveBadge");
2427
-
2428
- if (suggestion === "error") {
2429
- if (current !== "error") {
2430
- this.setOption("classes.statusOrRemoveBadge", "error");
2431
- }
2432
- return;
2433
- }
2434
-
2435
- if (this[isLoadingSymbol] === true) {
2436
- if (current !== "loading") {
2437
- this.setOption("classes.statusOrRemoveBadge", "loading");
2438
- }
2439
- return;
2440
- }
2441
-
2442
- if (suggestion === "loading") {
2443
- if (current !== "loading") {
2444
- this.setOption("classes.statusOrRemoveBadge", "loading");
2445
- }
2446
- return;
2447
- }
2448
-
2449
- if (clearAllFlag) {
2450
- if (current !== "clear") {
2451
- this.setOption("classes.statusOrRemoveBadge", "clear");
2452
- }
2453
- return;
2454
- }
2455
-
2456
- if (this[controlElementSymbol].classList.contains("open")) {
2457
- if (current !== "open") {
2458
- this.setOption("classes.statusOrRemoveBadge", "open");
2459
- }
2460
- return;
2461
- }
2462
-
2463
- const options = this.getOption("options");
2464
- if (
2465
- options === undefined ||
2466
- options === null ||
2467
- (isArray(options) && options.length === 0)
2468
- ) {
2469
- if (current !== "empty") {
2470
- this.setOption("classes.statusOrRemoveBadge", "empty");
2471
- }
2472
- return;
2473
- }
2474
-
2475
- if (suggestion) {
2476
- if (current !== suggestion) {
2477
- this.setOption("classes.statusOrRemoveBadge", suggestion);
2478
- }
2479
- return;
2480
- }
2481
- })
2482
-
2407
+ requestAnimationFrame(() => {
2408
+ const selection = this.getOption("selection");
2409
+
2410
+ const clearAllFlag =
2411
+ isArray(selection) &&
2412
+ selection.length > 0 &&
2413
+ this.getOption("features.clearAll") === true;
2414
+
2415
+ const current = this.getOption("classes.statusOrRemoveBadge");
2416
+
2417
+ if (suggestion === "error") {
2418
+ if (current !== "error") {
2419
+ this.setOption("classes.statusOrRemoveBadge", "error");
2420
+ }
2421
+ return;
2422
+ }
2423
+
2424
+ if (this[isLoadingSymbol] === true) {
2425
+ if (current !== "loading") {
2426
+ this.setOption("classes.statusOrRemoveBadge", "loading");
2427
+ }
2428
+ return;
2429
+ }
2430
+
2431
+ if (suggestion === "loading") {
2432
+ if (current !== "loading") {
2433
+ this.setOption("classes.statusOrRemoveBadge", "loading");
2434
+ }
2435
+ return;
2436
+ }
2437
+
2438
+ if (clearAllFlag) {
2439
+ if (current !== "clear") {
2440
+ this.setOption("classes.statusOrRemoveBadge", "clear");
2441
+ }
2442
+ return;
2443
+ }
2444
+
2445
+ if (this[controlElementSymbol].classList.contains("open")) {
2446
+ if (current !== "open") {
2447
+ this.setOption("classes.statusOrRemoveBadge", "open");
2448
+ }
2449
+ return;
2450
+ }
2451
+
2452
+ const options = this.getOption("options");
2453
+ if (
2454
+ options === undefined ||
2455
+ options === null ||
2456
+ (isArray(options) && options.length === 0)
2457
+ ) {
2458
+ if (current !== "empty") {
2459
+ this.setOption("classes.statusOrRemoveBadge", "empty");
2460
+ }
2461
+ return;
2462
+ }
2463
+
2464
+ if (suggestion) {
2465
+ if (current !== suggestion) {
2466
+ this.setOption("classes.statusOrRemoveBadge", suggestion);
2467
+ }
2468
+ return;
2469
+ }
2470
+ });
2483
2471
  }
2484
2472
 
2485
2473
  /**
@@ -2488,68 +2476,68 @@ function setStatusOrRemoveBadges(suggestion) {
2488
2476
  * @throws {Error} no shadow-root is defined
2489
2477
  */
2490
2478
  function initControlReferences() {
2491
- if (!this.shadowRoot) {
2492
- throw new Error("no shadow-root is defined");
2493
- }
2494
-
2495
- this[controlElementSymbol] = this.shadowRoot.querySelector(
2496
- `[${ATTRIBUTE_ROLE}=control]`,
2497
- );
2498
- this[selectionElementSymbol] = this.shadowRoot.querySelector(
2499
- `[${ATTRIBUTE_ROLE}=selection]`,
2500
- );
2501
- this[containerElementSymbol] = this.shadowRoot.querySelector(
2502
- `[${ATTRIBUTE_ROLE}=container]`,
2503
- );
2504
- this[popperElementSymbol] = this.shadowRoot.querySelector(
2505
- `[${ATTRIBUTE_ROLE}=popper]`,
2506
- );
2507
- this[inlineFilterElementSymbol] = this.shadowRoot.querySelector(
2508
- `[${ATTRIBUTE_ROLE}=filter][name="inline-filter"]`,
2509
- );
2510
- this[popperFilterElementSymbol] = this.shadowRoot.querySelector(
2511
- `[${ATTRIBUTE_ROLE}=filter][name="popper-filter"]`,
2512
- );
2513
- this[popperFilterContainerElementSymbol] =
2514
- this[popperFilterElementSymbol].parentElement;
2515
- this[optionsElementSymbol] = this.shadowRoot.querySelector(
2516
- `[${ATTRIBUTE_ROLE}=options]`,
2517
- );
2518
- this[noOptionsAvailableElementSymbol] = this.shadowRoot.querySelector(
2519
- `[${ATTRIBUTE_ROLE}="no-options"]`,
2520
- );
2521
- this[statusOrRemoveBadgesElementSymbol] = this.shadowRoot.querySelector(
2522
- `[${ATTRIBUTE_ROLE}=status-or-remove-badges]`,
2523
- );
2479
+ if (!this.shadowRoot) {
2480
+ throw new Error("no shadow-root is defined");
2481
+ }
2482
+
2483
+ this[controlElementSymbol] = this.shadowRoot.querySelector(
2484
+ `[${ATTRIBUTE_ROLE}=control]`,
2485
+ );
2486
+ this[selectionElementSymbol] = this.shadowRoot.querySelector(
2487
+ `[${ATTRIBUTE_ROLE}=selection]`,
2488
+ );
2489
+ this[containerElementSymbol] = this.shadowRoot.querySelector(
2490
+ `[${ATTRIBUTE_ROLE}=container]`,
2491
+ );
2492
+ this[popperElementSymbol] = this.shadowRoot.querySelector(
2493
+ `[${ATTRIBUTE_ROLE}=popper]`,
2494
+ );
2495
+ this[inlineFilterElementSymbol] = this.shadowRoot.querySelector(
2496
+ `[${ATTRIBUTE_ROLE}=filter][name="inline-filter"]`,
2497
+ );
2498
+ this[popperFilterElementSymbol] = this.shadowRoot.querySelector(
2499
+ `[${ATTRIBUTE_ROLE}=filter][name="popper-filter"]`,
2500
+ );
2501
+ this[popperFilterContainerElementSymbol] =
2502
+ this[popperFilterElementSymbol].parentElement;
2503
+ this[optionsElementSymbol] = this.shadowRoot.querySelector(
2504
+ `[${ATTRIBUTE_ROLE}=options]`,
2505
+ );
2506
+ this[noOptionsAvailableElementSymbol] = this.shadowRoot.querySelector(
2507
+ `[${ATTRIBUTE_ROLE}="no-options"]`,
2508
+ );
2509
+ this[statusOrRemoveBadgesElementSymbol] = this.shadowRoot.querySelector(
2510
+ `[${ATTRIBUTE_ROLE}=status-or-remove-badges]`,
2511
+ );
2524
2512
  }
2525
2513
 
2526
2514
  /**
2527
2515
  * @private
2528
2516
  */
2529
2517
  function updatePopper() {
2530
- if (this[popperElementSymbol].style.display !== STYLE_DISPLAY_MODE_BLOCK) {
2531
- return;
2532
- }
2533
-
2534
- if (this.getOption("disabled", false) === true) {
2535
- return;
2536
- }
2537
-
2538
- new Processing(() => {
2539
- calcAndSetOptionsDimension.call(this);
2540
- positionPopper.call(
2541
- this,
2542
- this[controlElementSymbol],
2543
- this[popperElementSymbol],
2544
- this.getOption("popper", {}),
2545
- );
2546
- })
2547
- .run()
2548
- .catch((e) => {
2549
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
2550
- });
2551
-
2552
- return this;
2518
+ if (this[popperElementSymbol].style.display !== STYLE_DISPLAY_MODE_BLOCK) {
2519
+ return;
2520
+ }
2521
+
2522
+ if (this.getOption("disabled", false) === true) {
2523
+ return;
2524
+ }
2525
+
2526
+ new Processing(() => {
2527
+ calcAndSetOptionsDimension.call(this);
2528
+ positionPopper.call(
2529
+ this,
2530
+ this[controlElementSymbol],
2531
+ this[popperElementSymbol],
2532
+ this.getOption("popper", {}),
2533
+ );
2534
+ })
2535
+ .run()
2536
+ .catch((e) => {
2537
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
2538
+ });
2539
+
2540
+ return this;
2553
2541
  }
2554
2542
 
2555
2543
  /**
@@ -2557,8 +2545,8 @@ function updatePopper() {
2557
2545
  * @return {string}
2558
2546
  */
2559
2547
  function getTemplate() {
2560
- // language=HTML
2561
- return `
2548
+ // language=HTML
2549
+ return `
2562
2550
  <template id="options">
2563
2551
  <div data-monster-role="option" tabindex="-1"
2564
2552
  data-monster-attributes="