@schukai/monster 3.79.0 → 3.80.0

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