@schukai/monster 3.56.0 → 3.57.0

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