@schukai/monster 3.55.2 → 3.55.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/package.json +4 -3
  3. package/source/components/datatable/filter/select.mjs +2 -3
  4. package/source/components/datatable/stylesheet/column-bar.mjs +8 -14
  5. package/source/components/datatable/stylesheet/dataset.mjs +8 -14
  6. package/source/components/datatable/stylesheet/datasource.mjs +7 -13
  7. package/source/components/datatable/stylesheet/datatable.mjs +8 -14
  8. package/source/components/datatable/stylesheet/embedded-pagination.mjs +8 -14
  9. package/source/components/datatable/stylesheet/filter-button.mjs +7 -13
  10. package/source/components/datatable/stylesheet/filter-controls-defaults.mjs +7 -13
  11. package/source/components/datatable/stylesheet/filter-date-range.mjs +7 -13
  12. package/source/components/datatable/stylesheet/filter-range.mjs +7 -13
  13. package/source/components/datatable/stylesheet/filter.mjs +8 -14
  14. package/source/components/datatable/stylesheet/pagination.mjs +8 -14
  15. package/source/components/datatable/stylesheet/select-filter.mjs +8 -14
  16. package/source/components/datatable/stylesheet/status.mjs +8 -14
  17. package/source/components/form/select.mjs +1812 -1638
  18. package/source/components/form/state-button.mjs +3 -1
  19. package/source/components/form/style/state-button.pcss +6 -0
  20. package/source/components/form/stylesheet/action-button.mjs +7 -13
  21. package/source/components/form/stylesheet/api-button.mjs +7 -13
  22. package/source/components/form/stylesheet/button-bar.mjs +7 -13
  23. package/source/components/form/stylesheet/button.mjs +7 -13
  24. package/source/components/form/stylesheet/confirm-button.mjs +7 -13
  25. package/source/components/form/stylesheet/context-error.mjs +7 -13
  26. package/source/components/form/stylesheet/context-help.mjs +7 -13
  27. package/source/components/form/stylesheet/form.mjs +7 -13
  28. package/source/components/form/stylesheet/message-state-button.mjs +7 -13
  29. package/source/components/form/stylesheet/popper-button.mjs +7 -13
  30. package/source/components/form/stylesheet/popper.mjs +7 -13
  31. package/source/components/form/stylesheet/select.mjs +7 -13
  32. package/source/components/form/stylesheet/state-button.mjs +8 -14
  33. package/source/components/form/stylesheet/tabs.mjs +7 -13
  34. package/source/components/form/stylesheet/tree-select.mjs +7 -13
  35. package/source/components/form/tree-select.mjs +12 -3
  36. package/source/components/host/stylesheet/call-button.mjs +8 -14
  37. package/source/components/host/stylesheet/collapse.mjs +8 -14
  38. package/source/components/host/stylesheet/config-manager.mjs +7 -13
  39. package/source/components/host/stylesheet/details.mjs +8 -14
  40. package/source/components/host/stylesheet/host.mjs +8 -14
  41. package/source/components/host/stylesheet/overlay.mjs +8 -14
  42. package/source/components/host/stylesheet/toggle-button.mjs +8 -14
  43. package/source/components/host/stylesheet/viewer.mjs +8 -14
  44. package/source/components/notify/stylesheet/message.mjs +8 -14
  45. package/source/components/notify/stylesheet/notify.mjs +7 -13
  46. package/source/components/state/stylesheet/log.mjs +7 -13
  47. package/source/components/state/stylesheet/state.mjs +7 -13
  48. package/source/components/style/normalize.pcss +3 -3
  49. package/source/components/stylesheet/badge.mjs +7 -13
  50. package/source/components/stylesheet/border.mjs +7 -13
  51. package/source/components/stylesheet/button.mjs +7 -13
  52. package/source/components/stylesheet/card.mjs +7 -13
  53. package/source/components/stylesheet/color.mjs +7 -13
  54. package/source/components/stylesheet/common.mjs +7 -13
  55. package/source/components/stylesheet/control.mjs +7 -13
  56. package/source/components/stylesheet/data-grid.mjs +7 -13
  57. package/source/components/stylesheet/display.mjs +7 -13
  58. package/source/components/stylesheet/floating-ui.mjs +7 -13
  59. package/source/components/stylesheet/form.mjs +7 -13
  60. package/source/components/stylesheet/host.mjs +7 -13
  61. package/source/components/stylesheet/icons.mjs +7 -13
  62. package/source/components/stylesheet/link.mjs +7 -13
  63. package/source/components/stylesheet/normalize.mjs +8 -14
  64. package/source/components/stylesheet/popper.mjs +7 -13
  65. package/source/components/stylesheet/property.mjs +7 -13
  66. package/source/components/stylesheet/ripple.mjs +7 -13
  67. package/source/components/stylesheet/skeleton.mjs +7 -13
  68. package/source/components/stylesheet/space.mjs +7 -13
  69. package/source/components/stylesheet/spinner.mjs +7 -13
  70. package/source/components/stylesheet/table.mjs +7 -13
  71. package/source/components/stylesheet/theme.mjs +7 -13
  72. package/source/components/stylesheet/typography.mjs +7 -13
  73. package/source/components/tree-menu/stylesheet/tree-menu.mjs +7 -13
  74. package/source/data/extend.mjs +56 -47
  75. package/source/dom/customelement.mjs +8 -3
  76. package/source/dom/util/init-options-from-attributes.mjs +0 -1
  77. package/source/monster.mjs +1 -0
  78. package/source/types/version.mjs +1 -1
  79. package/test/cases/components/form/button.mjs +1 -1
  80. package/test/cases/components/form/confirm-button.mjs +1 -1
  81. package/test/cases/components/form/reload.mjs +1 -1
  82. package/test/cases/components/form/select.mjs +25 -29
  83. package/test/cases/components/form/state-button.mjs +1 -1
  84. package/test/cases/components/form/tabs.mjs +1 -1
  85. package/test/cases/components/form/template.mjs +1 -1
  86. package/test/cases/components/form/tree-select.mjs +26 -18
  87. package/test/cases/data/extend.mjs +43 -2
  88. package/test/cases/monster.mjs +1 -1
  89. package/test/web/test.html +2 -2
  90. package/test/web/tests.js +10 -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="