@schukai/monster 3.55.2 → 3.55.3

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