quang 21.1.2 → 21.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/fesm2022/quang-auth.mjs +25 -25
  2. package/fesm2022/quang-auth.mjs.map +1 -1
  3. package/fesm2022/quang-components-autocomplete.mjs +92 -51
  4. package/fesm2022/quang-components-autocomplete.mjs.map +1 -1
  5. package/fesm2022/quang-components-checkbox.mjs +8 -8
  6. package/fesm2022/quang-components-checkbox.mjs.map +1 -1
  7. package/fesm2022/quang-components-date.mjs +38 -38
  8. package/fesm2022/quang-components-date.mjs.map +1 -1
  9. package/fesm2022/quang-components-input.mjs +16 -16
  10. package/fesm2022/quang-components-input.mjs.map +1 -1
  11. package/fesm2022/quang-components-paginator.mjs +28 -28
  12. package/fesm2022/quang-components-paginator.mjs.map +1 -1
  13. package/fesm2022/quang-components-radio-group.mjs +10 -9
  14. package/fesm2022/quang-components-radio-group.mjs.map +1 -1
  15. package/fesm2022/quang-components-select.mjs +15 -15
  16. package/fesm2022/quang-components-select.mjs.map +1 -1
  17. package/fesm2022/quang-components-shared.mjs +56 -54
  18. package/fesm2022/quang-components-shared.mjs.map +1 -1
  19. package/fesm2022/quang-components-table.mjs +19 -19
  20. package/fesm2022/quang-components-table.mjs.map +1 -1
  21. package/fesm2022/quang-components-tabs.mjs +5 -5
  22. package/fesm2022/quang-components-tabs.mjs.map +1 -1
  23. package/fesm2022/quang-components-wysiwyg.mjs +33 -33
  24. package/fesm2022/quang-components-wysiwyg.mjs.map +1 -1
  25. package/fesm2022/quang-device.mjs +3 -3
  26. package/fesm2022/quang-loader.mjs +8 -8
  27. package/fesm2022/quang-loader.mjs.map +1 -1
  28. package/fesm2022/quang-overlay-modal.mjs +21 -21
  29. package/fesm2022/quang-overlay-modal.mjs.map +1 -1
  30. package/fesm2022/quang-overlay-popover.mjs +12 -12
  31. package/fesm2022/quang-overlay-popover.mjs.map +1 -1
  32. package/fesm2022/quang-overlay-shared.mjs +33 -33
  33. package/fesm2022/quang-overlay-shared.mjs.map +1 -1
  34. package/fesm2022/quang-overlay-toast.mjs +10 -10
  35. package/fesm2022/quang-overlay-toast.mjs.map +1 -1
  36. package/fesm2022/quang-overlay-tooltip.mjs +14 -14
  37. package/fesm2022/quang-overlay-tooltip.mjs.map +1 -1
  38. package/fesm2022/quang-translation.mjs +6 -6
  39. package/package.json +3 -2
  40. package/types/quang-components-autocomplete.d.ts +5 -0
  41. package/types/quang-components-radio-group.d.ts +1 -1
  42. package/types/quang-components-shared.d.ts +3 -1
  43. package/types/quang-forms.d.ts +1 -1
@@ -39,21 +39,21 @@ class QuangAutocompleteComponent extends QuangBaseComponent {
39
39
  /**
40
40
  * The list of options to display in the autocomplete dropdown.
41
41
  */
42
- this.selectOptions = input.required(...(ngDevMode ? [{ debugName: "selectOptions" }] : []));
42
+ this.selectOptions = input.required(...(ngDevMode ? [{ debugName: "selectOptions" }] : /* istanbul ignore next */ []));
43
43
  /**
44
44
  * When true, allows any text input as a valid form value, not just option values.
45
45
  * The form value will sync with whatever text the user types.
46
46
  * When false (default), the form value must match one of the option values.
47
47
  * @default false
48
48
  */
49
- this.allowFreeText = input(false, ...(ngDevMode ? [{ debugName: "allowFreeText" }] : []));
49
+ this.allowFreeText = input(false, ...(ngDevMode ? [{ debugName: "allowFreeText" }] : /* istanbul ignore next */ []));
50
50
  /**
51
51
  * When true and allowFreeText is false, automatically selects an option if the user's
52
52
  * input text matches an option's label exactly (case-insensitive, trimmed).
53
53
  * This provides a better UX by auto-selecting when users type a complete option label.
54
54
  * @default true
55
55
  */
56
- this.autoSelectOnExactMatch = input(true, ...(ngDevMode ? [{ debugName: "autoSelectOnExactMatch" }] : []));
56
+ this.autoSelectOnExactMatch = input(true, ...(ngDevMode ? [{ debugName: "autoSelectOnExactMatch" }] : /* istanbul ignore next */ []));
57
57
  /**
58
58
  * When true, updates the form value as the user types (after debounce).
59
59
  * When false (default), the form value is only updated when:
@@ -61,7 +61,7 @@ class QuangAutocompleteComponent extends QuangBaseComponent {
61
61
  * - User stops typing and the input loses focus (blur)
62
62
  * @default false
63
63
  */
64
- this.updateValueOnType = input(false, ...(ngDevMode ? [{ debugName: "updateValueOnType" }] : []));
64
+ this.updateValueOnType = input(false, ...(ngDevMode ? [{ debugName: "updateValueOnType" }] : /* istanbul ignore next */ []));
65
65
  /**
66
66
  * Whether the form value can be any text or must match one of the options.
67
67
  * When true, the form value syncs with the input text.
@@ -69,61 +69,61 @@ class QuangAutocompleteComponent extends QuangBaseComponent {
69
69
  * @default false
70
70
  * @deprecated Use `allowFreeText` instead. This input will be removed in a future version.
71
71
  */
72
- this.syncFormWithText = input(false, ...(ngDevMode ? [{ debugName: "syncFormWithText" }] : []));
72
+ this.syncFormWithText = input(false, ...(ngDevMode ? [{ debugName: "syncFormWithText" }] : /* istanbul ignore next */ []));
73
73
  /**
74
74
  * Maximum height of the option list before scrolling.
75
75
  * @default '200px'
76
76
  */
77
- this.optionListMaxHeight = input('200px', ...(ngDevMode ? [{ debugName: "optionListMaxHeight" }] : []));
77
+ this.optionListMaxHeight = input('200px', ...(ngDevMode ? [{ debugName: "optionListMaxHeight" }] : /* istanbul ignore next */ []));
78
78
  /**
79
79
  * Whether to translate option labels.
80
80
  * @default true
81
81
  */
82
- this.translateValue = input(true, ...(ngDevMode ? [{ debugName: "translateValue" }] : []));
82
+ this.translateValue = input(true, ...(ngDevMode ? [{ debugName: "translateValue" }] : /* istanbul ignore next */ []));
83
83
  /**
84
84
  * Scroll behavior when the option list opens.
85
85
  * @default 'smooth'
86
86
  */
87
- this.scrollBehaviorOnOpen = input('smooth', ...(ngDevMode ? [{ debugName: "scrollBehaviorOnOpen" }] : []));
87
+ this.scrollBehaviorOnOpen = input('smooth', ...(ngDevMode ? [{ debugName: "scrollBehaviorOnOpen" }] : /* istanbul ignore next */ []));
88
88
  /**
89
89
  * When true, only emits the value without saving it to ngControl.
90
90
  * @default false
91
91
  */
92
- this.emitOnly = input(false, ...(ngDevMode ? [{ debugName: "emitOnly" }] : []));
92
+ this.emitOnly = input(false, ...(ngDevMode ? [{ debugName: "emitOnly" }] : /* istanbul ignore next */ []));
93
93
  /**
94
94
  * Enable multiple selection mode with chips.
95
95
  * @default false
96
96
  */
97
- this.multiple = input(false, ...(ngDevMode ? [{ debugName: "multiple" }] : []));
97
+ this.multiple = input(false, ...(ngDevMode ? [{ debugName: "multiple" }] : /* istanbul ignore next */ []));
98
98
  /**
99
99
  * Maximum length in characters for chip display text.
100
100
  * When set, chips will be truncated and show a tooltip with full text.
101
101
  * @default 0 (no limit)
102
102
  */
103
- this.chipMaxLength = input(0, ...(ngDevMode ? [{ debugName: "chipMaxLength" }] : []));
103
+ this.chipMaxLength = input(0, ...(ngDevMode ? [{ debugName: "chipMaxLength" }] : /* istanbul ignore next */ []));
104
104
  /**
105
105
  * Layout direction for chips in multiple selection mode.
106
106
  * @default 'vertical'
107
107
  */
108
- this.multiSelectDisplayMode = input('vertical', ...(ngDevMode ? [{ debugName: "multiSelectDisplayMode" }] : []));
108
+ this.multiSelectDisplayMode = input('vertical', ...(ngDevMode ? [{ debugName: "multiSelectDisplayMode" }] : /* istanbul ignore next */ []));
109
109
  /**
110
110
  * Position of chips relative to the input in multiple selection mode.
111
111
  * - 'top': Chips are displayed above the input (default)
112
112
  * - 'bottom': Chips are displayed below the input
113
113
  * @default 'top'
114
114
  */
115
- this.chipsPosition = input('top', ...(ngDevMode ? [{ debugName: "chipsPosition" }] : []));
115
+ this.chipsPosition = input('top', ...(ngDevMode ? [{ debugName: "chipsPosition" }] : /* istanbul ignore next */ []));
116
116
  /**
117
117
  * Debounce time in milliseconds for search text changes.
118
118
  * @default 300
119
119
  */
120
- this.searchTextDebounce = input(300, ...(ngDevMode ? [{ debugName: "searchTextDebounce" }] : []));
120
+ this.searchTextDebounce = input(300, ...(ngDevMode ? [{ debugName: "searchTextDebounce" }] : /* istanbul ignore next */ []));
121
121
  /**
122
122
  * Whether to filter options internally based on input text.
123
123
  * When false, filtering should be handled externally via searchTextChange event.
124
124
  * @default true
125
125
  */
126
- this.internalFilterOptions = input(true, ...(ngDevMode ? [{ debugName: "internalFilterOptions" }] : []));
126
+ this.internalFilterOptions = input(true, ...(ngDevMode ? [{ debugName: "internalFilterOptions" }] : /* istanbul ignore next */ []));
127
127
  // ============================================
128
128
  // OUTPUTS - Event emitters
129
129
  // ============================================
@@ -141,20 +141,20 @@ class QuangAutocompleteComponent extends QuangBaseComponent {
141
141
  // VIEW CHILDREN - Template references
142
142
  // ============================================
143
143
  /** Reference to the option list component */
144
- this.optionList = viewChild('optionList', ...(ngDevMode ? [{ debugName: "optionList" }] : []));
144
+ this.optionList = viewChild('optionList', ...(ngDevMode ? [{ debugName: "optionList" }] : /* istanbul ignore next */ []));
145
145
  /** Reference to the input element */
146
- this.selectInput = viewChild('selectInput', ...(ngDevMode ? [{ debugName: "selectInput" }] : []));
146
+ this.selectInput = viewChild('selectInput', ...(ngDevMode ? [{ debugName: "selectInput" }] : /* istanbul ignore next */ []));
147
147
  /** Reference to the chip container element */
148
- this.chipContainer = viewChild('chipContainer', ...(ngDevMode ? [{ debugName: "chipContainer" }] : []));
148
+ this.chipContainer = viewChild('chipContainer', ...(ngDevMode ? [{ debugName: "chipContainer" }] : /* istanbul ignore next */ []));
149
149
  /** Reference to the main autocomplete container */
150
- this.autocompleteContainer = viewChild('autocompleteContainer', ...(ngDevMode ? [{ debugName: "autocompleteContainer" }] : []));
150
+ this.autocompleteContainer = viewChild('autocompleteContainer', ...(ngDevMode ? [{ debugName: "autocompleteContainer" }] : /* istanbul ignore next */ []));
151
151
  // ============================================
152
152
  // PUBLIC STATE - Used in template
153
153
  // ============================================
154
154
  /** Constant for option list parent type */
155
155
  this.ParentType = OptionListParentType.AUTOCOMPLETE;
156
156
  /** Height of the input element (used for positioning) */
157
- this.inputHeight = signal(0, ...(ngDevMode ? [{ debugName: "inputHeight" }] : []));
157
+ this.inputHeight = signal(0, ...(ngDevMode ? [{ debugName: "inputHeight" }] : /* istanbul ignore next */ []));
158
158
  /**
159
159
  * The display text for the input field.
160
160
  * - When searching: shows what the user typed
@@ -177,21 +177,21 @@ class QuangAutocompleteComponent extends QuangBaseComponent {
177
177
  return String(value);
178
178
  }
179
179
  return option?.label ?? '';
180
- }, ...(ngDevMode ? [{ debugName: "_inputValue" }] : []));
180
+ }, ...(ngDevMode ? [{ debugName: "_inputValue" }] : /* istanbul ignore next */ []));
181
181
  /** Whether the option list is currently visible */
182
- this._showOptions = signal(null, ...(ngDevMode ? [{ debugName: "_showOptions" }] : []));
182
+ this._showOptions = signal(null, ...(ngDevMode ? [{ debugName: "_showOptions" }] : /* istanbul ignore next */ []));
183
183
  /** List of selected chip values (for multiple mode) */
184
- this._chipList = signal([], ...(ngDevMode ? [{ debugName: "_chipList" }] : []));
184
+ this._chipList = signal([], ...(ngDevMode ? [{ debugName: "_chipList" }] : /* istanbul ignore next */ []));
185
185
  /** List of selected option objects (for multiple mode) */
186
- this._selectedOptions = signal([], ...(ngDevMode ? [{ debugName: "_selectedOptions" }] : []));
186
+ this._selectedOptions = signal([], ...(ngDevMode ? [{ debugName: "_selectedOptions" }] : /* istanbul ignore next */ []));
187
187
  /** Filtered options based on search text and chip selection */
188
188
  this._filteredOptions = computed(() => {
189
189
  const searchText = this._isSearching() ? this._userSearchText() : '';
190
190
  if (this.multiple()) {
191
- return this.filterOptions(searchText).filter((x) => !this._chipList().some((chip) => chip === x.value));
191
+ return this.filterOptions(searchText).filter((x) => !this._chipList().some((chip) => chip?.toString() === x.value?.toString()));
192
192
  }
193
193
  return searchText?.length ? this.filterOptions(searchText) : this.selectOptions();
194
- }, ...(ngDevMode ? [{ debugName: "_filteredOptions" }] : []));
194
+ }, ...(ngDevMode ? [{ debugName: "_filteredOptions" }] : /* istanbul ignore next */ []));
195
195
  /**
196
196
  * The value to use for highlighting in the option list.
197
197
  * When searching: shows the matched option (if any) based on exact label match
@@ -216,21 +216,21 @@ class QuangAutocompleteComponent extends QuangBaseComponent {
216
216
  }
217
217
  // When not searching, use the current value
218
218
  return this._value();
219
- }, ...(ngDevMode ? [{ debugName: "_highlightedValue" }] : []));
219
+ }, ...(ngDevMode ? [{ debugName: "_highlightedValue" }] : /* istanbul ignore next */ []));
220
220
  // ============================================
221
221
  // PROTECTED STATE - Internal but accessible to subclasses
222
222
  // ============================================
223
223
  /** Whether the user is actively typing/searching */
224
- this._isSearching = signal(false, ...(ngDevMode ? [{ debugName: "_isSearching" }] : []));
224
+ this._isSearching = signal(false, ...(ngDevMode ? [{ debugName: "_isSearching" }] : /* istanbul ignore next */ []));
225
225
  /** The text the user is currently typing while searching */
226
- this._userSearchText = signal('', ...(ngDevMode ? [{ debugName: "_userSearchText" }] : []));
226
+ this._userSearchText = signal('', ...(ngDevMode ? [{ debugName: "_userSearchText" }] : /* istanbul ignore next */ []));
227
227
  /**
228
228
  * Internal computed that returns true if free text input is allowed.
229
229
  * Combines both `allowFreeText` and deprecated `syncFormWithText` inputs.
230
230
  */
231
231
  this._allowFreeTextInternal = computed(() => {
232
232
  return this.allowFreeText() || this.syncFormWithText();
233
- }, ...(ngDevMode ? [{ debugName: "_allowFreeTextInternal" }] : []));
233
+ }, ...(ngDevMode ? [{ debugName: "_allowFreeTextInternal" }] : /* istanbul ignore next */ []));
234
234
  // ============================================
235
235
  // PRIVATE STATE - Internal implementation details
236
236
  // ============================================
@@ -254,7 +254,7 @@ class QuangAutocompleteComponent extends QuangBaseComponent {
254
254
  selectInput.nativeElement.addEventListener('keydown', (e) => {
255
255
  this.handleInputKeydown(e, selectInput.nativeElement);
256
256
  });
257
- }, ...(ngDevMode ? [{ debugName: "onChangeSelectInputEffect" }] : []));
257
+ }, ...(ngDevMode ? [{ debugName: "onChangeSelectInputEffect" }] : /* istanbul ignore next */ []));
258
258
  /** Subscription to options changes */
259
259
  this.selectOptionsChangeSubscription = toObservable(this.selectOptions)
260
260
  .pipe(takeUntilDestroyed())
@@ -372,6 +372,22 @@ class QuangAutocompleteComponent extends QuangBaseComponent {
372
372
  * @param hideOptions Whether to hide the dropdown after selection
373
373
  */
374
374
  onValueChange(value, hideOptions = true) {
375
+ if (this.multiple()) {
376
+ const valueToHandle = this.resolveValueForMultipleMode(value);
377
+ if (!valueToHandle?.toString().trim()) {
378
+ return;
379
+ }
380
+ this.handleSelectValue(valueToHandle);
381
+ this.onChangedHandler(this._chipList());
382
+ if (hideOptions) {
383
+ this.hideOptionVisibility();
384
+ this.focusInput();
385
+ }
386
+ this._userSearchText.set('');
387
+ this._isSearching.set(false);
388
+ this.selectedOption.emit(valueToHandle);
389
+ return;
390
+ }
375
391
  // When allowFreeText is true and a null/undefined value is received (e.g., from selecting
376
392
  // a non-existent option in the dropdown), use the typed text as the value instead of clearing
377
393
  if ((value === null || value === undefined) && this._allowFreeTextInternal()) {
@@ -386,15 +402,6 @@ class QuangAutocompleteComponent extends QuangBaseComponent {
386
402
  return;
387
403
  }
388
404
  }
389
- if (this.multiple()) {
390
- this.handleSelectValue(value);
391
- this.onChangedHandler(this._chipList());
392
- if (this._chipList().some((x) => x === value)) {
393
- this._userSearchText.set('');
394
- this._isSearching.set(false);
395
- }
396
- return;
397
- }
398
405
  // Update _userSearchText to the selected option's label
399
406
  // This enables processTextToFormValue to match correctly on blur
400
407
  const selectedOption = this.selectOptions().find((x) => x.value === value);
@@ -437,12 +444,22 @@ class QuangAutocompleteComponent extends QuangBaseComponent {
437
444
  }
438
445
  break;
439
446
  case 'Enter':
440
- // When allowFreeText is true and dropdown is open, handle Enter specially
447
+ // In multiple+freeText mode, Enter should add either an exact matching option
448
+ // or the typed custom text as a chip.
449
+ if (this._showOptions() && this._allowFreeTextInternal() && this.multiple()) {
450
+ const searchText = this._userSearchText()?.trim();
451
+ if (!searchText) {
452
+ break;
453
+ }
454
+ event.preventDefault();
455
+ const matchingOption = this.findMatchingOption(searchText);
456
+ this.onValueChange(matchingOption?.value ?? null);
457
+ break;
458
+ }
459
+ // When allowFreeText is true and dropdown is open, handle Enter specially in single mode
441
460
  if (this._showOptions() && this._allowFreeTextInternal()) {
442
- // Check if there are any filtered options
443
461
  const filteredOptions = this._filteredOptions();
444
462
  if (filteredOptions.length === 0) {
445
- // No options to select - use the typed text as the value
446
463
  event.preventDefault();
447
464
  this.processTextToFormValue(this._userSearchText(), {
448
465
  exitSearchMode: true,
@@ -451,7 +468,6 @@ class QuangAutocompleteComponent extends QuangBaseComponent {
451
468
  });
452
469
  this.hideOptionVisibility();
453
470
  }
454
- // If there are filtered options, let option-list handle the selection
455
471
  }
456
472
  break;
457
473
  }
@@ -514,7 +530,7 @@ class QuangAutocompleteComponent extends QuangBaseComponent {
514
530
  */
515
531
  getDescription(chipValue) {
516
532
  const option = this.selectOptions().find((x) => x.value?.toString() === chipValue?.toString());
517
- return option?.label?.toString() ?? '';
533
+ return option?.label?.toString() ?? chipValue?.toString() ?? '';
518
534
  }
519
535
  getOptionByValue(value) {
520
536
  return this.selectOptions().find((x) => x.value?.toString() === value?.toString());
@@ -679,11 +695,36 @@ class QuangAutocompleteComponent extends QuangBaseComponent {
679
695
  * Handles selecting a value (adding to chip list in multiple mode).
680
696
  */
681
697
  handleSelectValue(value) {
698
+ const stringValue = value?.toString();
699
+ if (!stringValue) {
700
+ return;
701
+ }
702
+ if (this._chipList().some((x) => x.toString() === stringValue)) {
703
+ return;
704
+ }
682
705
  const option = this.selectOptions().find((x) => x.value === value);
683
- if (option && !this._chipList().some((x) => x === option.value)) {
706
+ if (option) {
684
707
  this._chipList.update((list) => [...list, option.value]);
685
708
  this._selectedOptions.update((list) => [...list, option]);
709
+ return;
710
+ }
711
+ if (this._allowFreeTextInternal()) {
712
+ this._chipList.update((list) => [...list, stringValue]);
713
+ }
714
+ }
715
+ /**
716
+ * Resolves the value to add in multiple mode.
717
+ * If no option value is provided and free text is enabled, use the currently typed text.
718
+ */
719
+ resolveValueForMultipleMode(value) {
720
+ if (value !== null && value !== undefined) {
721
+ return value;
722
+ }
723
+ if (!this._allowFreeTextInternal()) {
724
+ return null;
686
725
  }
726
+ const typedText = this._userSearchText()?.trim();
727
+ return typedText || null;
687
728
  }
688
729
  /**
689
730
  * Emits search text change after debounce.
@@ -724,8 +765,8 @@ class QuangAutocompleteComponent extends QuangBaseComponent {
724
765
  this.onChange(value);
725
766
  }
726
767
  }
727
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: QuangAutocompleteComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
728
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: QuangAutocompleteComponent, isStandalone: true, selector: "quang-autocomplete", inputs: { selectOptions: { classPropertyName: "selectOptions", publicName: "selectOptions", isSignal: true, isRequired: true, transformFunction: null }, allowFreeText: { classPropertyName: "allowFreeText", publicName: "allowFreeText", isSignal: true, isRequired: false, transformFunction: null }, autoSelectOnExactMatch: { classPropertyName: "autoSelectOnExactMatch", publicName: "autoSelectOnExactMatch", isSignal: true, isRequired: false, transformFunction: null }, updateValueOnType: { classPropertyName: "updateValueOnType", publicName: "updateValueOnType", isSignal: true, isRequired: false, transformFunction: null }, syncFormWithText: { classPropertyName: "syncFormWithText", publicName: "syncFormWithText", isSignal: true, isRequired: false, transformFunction: null }, optionListMaxHeight: { classPropertyName: "optionListMaxHeight", publicName: "optionListMaxHeight", isSignal: true, isRequired: false, transformFunction: null }, translateValue: { classPropertyName: "translateValue", publicName: "translateValue", isSignal: true, isRequired: false, transformFunction: null }, scrollBehaviorOnOpen: { classPropertyName: "scrollBehaviorOnOpen", publicName: "scrollBehaviorOnOpen", isSignal: true, isRequired: false, transformFunction: null }, emitOnly: { classPropertyName: "emitOnly", publicName: "emitOnly", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, chipMaxLength: { classPropertyName: "chipMaxLength", publicName: "chipMaxLength", isSignal: true, isRequired: false, transformFunction: null }, multiSelectDisplayMode: { classPropertyName: "multiSelectDisplayMode", publicName: "multiSelectDisplayMode", isSignal: true, isRequired: false, transformFunction: null }, chipsPosition: { classPropertyName: "chipsPosition", publicName: "chipsPosition", isSignal: true, isRequired: false, transformFunction: null }, searchTextDebounce: { classPropertyName: "searchTextDebounce", publicName: "searchTextDebounce", isSignal: true, isRequired: false, transformFunction: null }, internalFilterOptions: { classPropertyName: "internalFilterOptions", publicName: "internalFilterOptions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedOption: "selectedOption", searchTextChange: "searchTextChange" }, providers: [
768
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: QuangAutocompleteComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
769
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.7", type: QuangAutocompleteComponent, isStandalone: true, selector: "quang-autocomplete", inputs: { selectOptions: { classPropertyName: "selectOptions", publicName: "selectOptions", isSignal: true, isRequired: true, transformFunction: null }, allowFreeText: { classPropertyName: "allowFreeText", publicName: "allowFreeText", isSignal: true, isRequired: false, transformFunction: null }, autoSelectOnExactMatch: { classPropertyName: "autoSelectOnExactMatch", publicName: "autoSelectOnExactMatch", isSignal: true, isRequired: false, transformFunction: null }, updateValueOnType: { classPropertyName: "updateValueOnType", publicName: "updateValueOnType", isSignal: true, isRequired: false, transformFunction: null }, syncFormWithText: { classPropertyName: "syncFormWithText", publicName: "syncFormWithText", isSignal: true, isRequired: false, transformFunction: null }, optionListMaxHeight: { classPropertyName: "optionListMaxHeight", publicName: "optionListMaxHeight", isSignal: true, isRequired: false, transformFunction: null }, translateValue: { classPropertyName: "translateValue", publicName: "translateValue", isSignal: true, isRequired: false, transformFunction: null }, scrollBehaviorOnOpen: { classPropertyName: "scrollBehaviorOnOpen", publicName: "scrollBehaviorOnOpen", isSignal: true, isRequired: false, transformFunction: null }, emitOnly: { classPropertyName: "emitOnly", publicName: "emitOnly", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, chipMaxLength: { classPropertyName: "chipMaxLength", publicName: "chipMaxLength", isSignal: true, isRequired: false, transformFunction: null }, multiSelectDisplayMode: { classPropertyName: "multiSelectDisplayMode", publicName: "multiSelectDisplayMode", isSignal: true, isRequired: false, transformFunction: null }, chipsPosition: { classPropertyName: "chipsPosition", publicName: "chipsPosition", isSignal: true, isRequired: false, transformFunction: null }, searchTextDebounce: { classPropertyName: "searchTextDebounce", publicName: "searchTextDebounce", isSignal: true, isRequired: false, transformFunction: null }, internalFilterOptions: { classPropertyName: "internalFilterOptions", publicName: "internalFilterOptions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedOption: "selectedOption", searchTextChange: "searchTextChange" }, providers: [
729
770
  {
730
771
  provide: NG_VALUE_ACCESSOR,
731
772
  useExisting: forwardRef(() => QuangAutocompleteComponent),
@@ -735,9 +776,9 @@ class QuangAutocompleteComponent extends QuangBaseComponent {
735
776
  provide: QuangOptionListComponent,
736
777
  multi: false,
737
778
  },
738
- ], viewQueries: [{ propertyName: "optionList", first: true, predicate: ["optionList"], descendants: true, isSignal: true }, { propertyName: "selectInput", first: true, predicate: ["selectInput"], descendants: true, isSignal: true }, { propertyName: "chipContainer", first: true, predicate: ["chipContainer"], descendants: true, isSignal: true }, { propertyName: "autocompleteContainer", first: true, predicate: ["autocompleteContainer"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div\n [ngStyle]=\"{ '--chip-max-length': chipMaxLength() ? chipMaxLength() + 'ch' : 'none' }\"\n #autocompleteContainer\n class=\"autocomplete-container\"\n>\n @if (componentLabel()) {\n <label\n [htmlFor]=\"componentId()\"\n class=\"form-label d-flex gap-2\"\n >\n <div>\n <span>{{ componentLabel() | transloco }}</span>\n <span [hidden]=\"!_isRequired()\">*</span>\n </div>\n @if (helpMessage() && helpMessageTooltip()) {\n <div [quangTooltip]=\"helpMessage() | transloco\">\n <ng-content select=\"[help-icon]\" />\n </div>\n }\n </label>\n }\n <div\n [ngClass]=\"{\n horizontal: multiSelectDisplayMode() === 'horizontal',\n 'form-control': multiSelectDisplayMode() === 'horizontal',\n 'chips-bottom': chipsPosition() === 'bottom',\n }\"\n #chipContainer\n class=\"container-wrap\"\n >\n @if (multiple() && _chipList().length > 0 && chipsPosition() === 'top') {\n <ng-container *ngTemplateOutlet=\"chipsTemplate\" />\n }\n\n <input\n [attr.aria-activedescendant]=\"_showOptions() ? optionList()?.getActiveDescendantId() : null\"\n [attr.aria-controls]=\"_showOptions() ? 'optionList' : null\"\n [attr.aria-expanded]=\"_showOptions()\"\n [attr.required]=\"getIsRequiredControl()\"\n [class.form-control]=\"multiSelectDisplayMode() !== 'horizontal'\"\n [class.is-invalid]=\"_showErrors()\"\n [class.is-valid]=\"_showSuccess()\"\n [disabled]=\"_isDisabled() || isReadonly()\"\n [id]=\"componentId()\"\n [ngClass]=\"componentClass()\"\n [placeholder]=\"componentPlaceholder() | transloco\"\n [tabIndex]=\"componentTabIndex()\"\n [value]=\"_inputValue()\"\n (blur)=\"onBlurInput($event)\"\n (input)=\"onChangeInput($event)\"\n (keydown)=\"onInputKeydown($event)\"\n (mousedown)=\"showOptionVisibility()\"\n #selectInput\n aria-autocomplete=\"list\"\n aria-haspopup=\"listbox\"\n autocomplete=\"off\"\n role=\"combobox\"\n type=\"text\"\n />\n\n @if (multiple() && _chipList().length > 0 && chipsPosition() === 'bottom') {\n <ng-container *ngTemplateOutlet=\"chipsTemplate\" />\n }\n </div>\n @if (_showOptions()) {\n <quang-option-list\n [_isDisabled]=\"_isDisabled()\"\n [_value]=\"_highlightedValue()\"\n [componentClass]=\"componentClass()\"\n [componentLabel]=\"componentLabel()\"\n [componentTabIndex]=\"componentTabIndex()\"\n [nullOption]=\"false\"\n [optionListMaxHeight]=\"optionListMaxHeight()\"\n [parentID]=\"componentId()\"\n [parentType]=\"ParentType\"\n [scrollBehaviorOnOpen]=\"scrollBehaviorOnOpen()\"\n [selectButtonRef]=\"autocompleteContainer\"\n [selectOptions]=\"_filteredOptions()\"\n [translateValue]=\"translateValue()\"\n (blurHandler)=\"onBlurOptionList($event)\"\n (changedHandler)=\"onValueChange($event)\"\n (escapePressed)=\"onEscapePressed()\"\n (tabPressed)=\"onTabPressed($event)\"\n #optionList\n selectionMode=\"single\"\n />\n }\n <div\n [class.d-block]=\"_showSuccess()\"\n class=\"valid-feedback\"\n >\n {{ successMessage() | transloco }}\n </div>\n <div\n [class.d-block]=\"_showErrors()\"\n class=\"invalid-feedback\"\n >\n {{ _currentErrorMessage() | transloco: _currentErrorMessageExtraData() }}\n </div>\n @if (helpMessage() && !helpMessageTooltip()) {\n <small\n [hidden]=\"_showSuccess() || _showErrors()\"\n aria-live=\"assertive\"\n class=\"form-text text-muted\"\n >\n {{ helpMessage() | transloco }}\n </small>\n }\n</div>\n\n<!-- Chips template for reuse in top/bottom positions -->\n<ng-template #chipsTemplate>\n <div class=\"chips-container\">\n @for (chip of _chipList(); track chip) {\n @if (getDescription(chip)) {\n <div\n [ngClass]=\"{ 'chip-truncate': chipMaxLength() }\"\n [quangTooltip]=\"chipMaxLength() ? getDescription(chip) : ''\"\n class=\"chip chip-hover\"\n >\n <p [ngClass]=\"{ 'm-0': isReadonly() || _isDisabled() }\">\n @if (getOptionByValue(chip); as opt) {\n @if (opt.renderer) {\n <ng-container\n [ngTemplateOutlet]=\"opt.renderer\"\n [ngTemplateOutletContext]=\"{ $implicit: opt, selected: true, index: getOptionIndex(opt) }\"\n ></ng-container>\n } @else {\n {{ getDescription(chip) }}\n }\n } @else {\n {{ getDescription(chip) }}\n }\n </p>\n @if (!isReadonly() && !_isDisabled()) {\n <button\n [tabIndex]=\"$index + 1\"\n (click)=\"deleteChip(chip)\"\n class=\"btn btn-chip\"\n type=\"button\"\n >\n <svg\n class=\"ionicon\"\n fill=\"currentColor\"\n height=\"24\"\n viewBox=\"0 0 512 512\"\n width=\"24\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M368 368L144 144M368 144L144 368\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"32\"\n />\n </svg>\n </button>\n }\n </div>\n }\n }\n </div>\n</ng-template>\n", styles: [":host{display:block;--chip-max-length: none}.autocomplete-container{margin-bottom:1rem;position:relative}.chip:has(.btn-chip:disabled):hover{filter:unset;cursor:unset}.container-wrap,.container-wrap .chips-container{display:flex;flex-wrap:wrap;gap:.5rem}.container-wrap.chips-bottom{flex-direction:column}.container-wrap.chips-bottom input{order:-1}.container-wrap.horizontal{display:flex}.container-wrap.horizontal .chip-container{max-width:70%;margin-bottom:0;margin-left:.5rem;flex-wrap:nowrap;white-space:nowrap;overflow-x:auto;position:absolute;align-items:center}.container-wrap.horizontal .chip-container .chip{white-space:nowrap}.container-wrap.horizontal input{min-width:30%;flex:1 1 0;width:auto;border:none}.container-wrap.horizontal input:focus-visible{outline:none}.chip{display:flex;justify-content:space-between;align-items:center;gap:.25rem;padding:.25rem .5rem;border-radius:16px;color:var(--bs-btn-color);background-color:rgba(var(--bs-primary-rgb),.1);border-width:1px;border-style:solid;border-color:var(--bs-primary-border-subtle);min-height:2rem;max-width:100%}.chip p{margin:0;max-width:var(--chip-max-length);overflow:hidden;overflow-wrap:break-word;word-break:break-word}.chip.chip-truncate p{white-space:nowrap;text-overflow:ellipsis}.chip .btn-chip{text-align:end;padding:0;min-width:unset}.chip .btn-chip:hover{opacity:80%}.chip .btn-chip:active{border-color:transparent}.chip .btn-chip svg{color:var(--bs-primary);vertical-align:sub}.chip:has(.btn-chip:focus-visible){border-width:2px;filter:brightness(80%)}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: QuangOptionListComponent, selector: "quang-option-list", inputs: ["selectionMode", "optionListMaxHeight", "selectOptions", "selectButtonRef", "_value", "_isDisabled", "componentClass", "componentLabel", "componentTabIndex", "translateValue", "nullOption", "scrollBehaviorOnOpen", "parentType", "parentID"], outputs: ["changedHandler", "blurHandler", "escapePressed", "tabPressed"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: QuangTooltipDirective, selector: "[quangTooltip]", inputs: ["quangTooltip", "showMethod"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
779
+ ], viewQueries: [{ propertyName: "optionList", first: true, predicate: ["optionList"], descendants: true, isSignal: true }, { propertyName: "selectInput", first: true, predicate: ["selectInput"], descendants: true, isSignal: true }, { propertyName: "chipContainer", first: true, predicate: ["chipContainer"], descendants: true, isSignal: true }, { propertyName: "autocompleteContainer", first: true, predicate: ["autocompleteContainer"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div\n [ngStyle]=\"{ '--chip-max-length': chipMaxLength() ? chipMaxLength() + 'ch' : 'none' }\"\n #autocompleteContainer\n class=\"autocomplete-container\"\n>\n @if (componentLabel()) {\n <label\n [htmlFor]=\"componentId()\"\n class=\"form-label d-flex gap-2\"\n >\n <div>\n <span>{{ componentLabel() | transloco }}</span>\n <span [hidden]=\"!_isRequired()\">*</span>\n </div>\n @if (helpMessage() && helpMessageTooltip()) {\n <div\n [overlayPosition]=\"helpTooltipPosition()\"\n [quangTooltip]=\"helpMessage() | transloco\"\n [showMethod]=\"showHelpTooltipMethod()\"\n class=\"pointer\"\n >\n <ng-content select=\"[help-icon]\" />\n </div>\n }\n </label>\n }\n <div\n [ngClass]=\"{\n horizontal: multiSelectDisplayMode() === 'horizontal',\n 'form-control': multiSelectDisplayMode() === 'horizontal',\n 'chips-bottom': chipsPosition() === 'bottom',\n }\"\n #chipContainer\n class=\"container-wrap\"\n >\n @if (multiple() && _chipList().length > 0 && chipsPosition() === 'top') {\n <ng-container *ngTemplateOutlet=\"chipsTemplate\" />\n }\n\n <input\n [attr.aria-activedescendant]=\"_showOptions() ? optionList()?.getActiveDescendantId() : null\"\n [attr.aria-controls]=\"_showOptions() ? 'optionList' : null\"\n [attr.aria-expanded]=\"_showOptions()\"\n [attr.required]=\"getIsRequiredControl()\"\n [class.form-control]=\"multiSelectDisplayMode() !== 'horizontal'\"\n [class.is-invalid]=\"_showErrors()\"\n [class.is-valid]=\"_showSuccess()\"\n [disabled]=\"_isDisabled() || isReadonly()\"\n [id]=\"componentId()\"\n [ngClass]=\"componentClass()\"\n [placeholder]=\"componentPlaceholder() | transloco\"\n [tabIndex]=\"componentTabIndex()\"\n [value]=\"_inputValue()\"\n (blur)=\"onBlurInput($event)\"\n (input)=\"onChangeInput($event)\"\n (keydown)=\"onInputKeydown($event)\"\n (mousedown)=\"showOptionVisibility()\"\n #selectInput\n aria-autocomplete=\"list\"\n aria-haspopup=\"listbox\"\n autocomplete=\"off\"\n role=\"combobox\"\n type=\"text\"\n />\n\n @if (multiple() && _chipList().length > 0 && chipsPosition() === 'bottom') {\n <ng-container *ngTemplateOutlet=\"chipsTemplate\" />\n }\n </div>\n @if (_showOptions()) {\n <quang-option-list\n [_isDisabled]=\"_isDisabled()\"\n [_value]=\"_highlightedValue()\"\n [componentClass]=\"componentClass()\"\n [componentLabel]=\"componentLabel()\"\n [componentTabIndex]=\"componentTabIndex()\"\n [nullOption]=\"false\"\n [optionListMaxHeight]=\"optionListMaxHeight()\"\n [parentID]=\"componentId()\"\n [parentType]=\"ParentType\"\n [scrollBehaviorOnOpen]=\"scrollBehaviorOnOpen()\"\n [selectButtonRef]=\"autocompleteContainer\"\n [selectOptions]=\"_filteredOptions()\"\n [translateValue]=\"translateValue()\"\n (blurHandler)=\"onBlurOptionList($event)\"\n (changedHandler)=\"onValueChange($event)\"\n (escapePressed)=\"onEscapePressed()\"\n (tabPressed)=\"onTabPressed($event)\"\n #optionList\n selectionMode=\"single\"\n />\n }\n <div\n [class.d-block]=\"_showSuccess()\"\n class=\"valid-feedback\"\n >\n {{ successMessage() | transloco }}\n </div>\n <div\n [class.d-block]=\"_showErrors()\"\n class=\"invalid-feedback\"\n >\n {{ _currentErrorMessage() | transloco: _currentErrorMessageExtraData() }}\n </div>\n @if (helpMessage() && !helpMessageTooltip()) {\n <small\n [hidden]=\"_showSuccess() || _showErrors()\"\n aria-live=\"assertive\"\n class=\"form-text text-muted\"\n >\n {{ helpMessage() | transloco }}\n </small>\n }\n</div>\n\n<!-- Chips template for reuse in top/bottom positions -->\n<ng-template #chipsTemplate>\n <div class=\"chips-container\">\n @for (chip of _chipList(); track chip) {\n @if (getDescription(chip)) {\n <div\n [ngClass]=\"{ 'chip-truncate': chipMaxLength() }\"\n [quangTooltip]=\"chipMaxLength() ? getDescription(chip) : ''\"\n class=\"chip chip-hover\"\n >\n <p [ngClass]=\"{ 'm-0': isReadonly() || _isDisabled() }\">\n @if (getOptionByValue(chip); as opt) {\n @if (opt.renderer) {\n <ng-container\n [ngTemplateOutlet]=\"opt.renderer\"\n [ngTemplateOutletContext]=\"{ $implicit: opt, selected: true, index: getOptionIndex(opt) }\"\n ></ng-container>\n } @else {\n {{ getDescription(chip) }}\n }\n } @else {\n {{ getDescription(chip) }}\n }\n </p>\n @if (!isReadonly() && !_isDisabled()) {\n <button\n [tabIndex]=\"$index + 1\"\n (click)=\"deleteChip(chip)\"\n class=\"btn btn-chip\"\n type=\"button\"\n >\n <svg\n class=\"ionicon\"\n fill=\"currentColor\"\n height=\"24\"\n viewBox=\"0 0 512 512\"\n width=\"24\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M368 368L144 144M368 144L144 368\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"32\"\n />\n </svg>\n </button>\n }\n </div>\n }\n }\n </div>\n</ng-template>\n", styles: [":host{display:block;--chip-max-length: none}.pointer{cursor:pointer}.autocomplete-container{margin-bottom:1rem;position:relative}.chip:has(.btn-chip:disabled):hover{filter:unset;cursor:unset}.container-wrap,.container-wrap .chips-container{display:flex;flex-wrap:wrap;gap:.5rem}.container-wrap.chips-bottom{flex-direction:column}.container-wrap.chips-bottom input{order:-1}.container-wrap.horizontal{display:flex}.container-wrap.horizontal .chip-container{max-width:70%;margin-bottom:0;margin-left:.5rem;flex-wrap:nowrap;white-space:nowrap;overflow-x:auto;position:absolute;align-items:center}.container-wrap.horizontal .chip-container .chip{white-space:nowrap}.container-wrap.horizontal input{min-width:30%;flex:1 1 0;width:auto;border:none}.container-wrap.horizontal input:focus-visible{outline:none}.chip{display:flex;justify-content:space-between;align-items:center;gap:.25rem;padding:.25rem .5rem;border-radius:16px;color:var(--bs-btn-color);background-color:rgba(var(--bs-primary-rgb),.1);border-width:1px;border-style:solid;border-color:var(--bs-primary-border-subtle);min-height:2rem;max-width:100%}.chip p{margin:0;max-width:var(--chip-max-length);overflow:hidden;overflow-wrap:break-word;word-break:break-word}.chip.chip-truncate p{white-space:nowrap;text-overflow:ellipsis}.chip .btn-chip{text-align:end;padding:0;min-width:unset}.chip .btn-chip:hover{opacity:80%}.chip .btn-chip:active{border-color:transparent}.chip .btn-chip svg{color:var(--bs-primary);vertical-align:sub}.chip:has(.btn-chip:focus-visible){border-width:2px;filter:brightness(80%)}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: QuangOptionListComponent, selector: "quang-option-list", inputs: ["selectionMode", "optionListMaxHeight", "selectOptions", "selectButtonRef", "_value", "_isDisabled", "componentClass", "componentLabel", "componentTabIndex", "translateValue", "nullOption", "scrollBehaviorOnOpen", "parentType", "parentID"], outputs: ["changedHandler", "blurHandler", "escapePressed", "tabPressed"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: QuangTooltipDirective, selector: "[quangTooltip]", inputs: ["quangTooltip", "showMethod"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
739
780
  }
740
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: QuangAutocompleteComponent, decorators: [{
781
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: QuangAutocompleteComponent, decorators: [{
741
782
  type: Component,
742
783
  args: [{ selector: 'quang-autocomplete', imports: [TranslocoPipe, NgClass, NgTemplateOutlet, QuangOptionListComponent, NgStyle, QuangTooltipDirective], changeDetection: ChangeDetectionStrategy.OnPush, providers: [
743
784
  {
@@ -749,7 +790,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImpor
749
790
  provide: QuangOptionListComponent,
750
791
  multi: false,
751
792
  },
752
- ], template: "<div\n [ngStyle]=\"{ '--chip-max-length': chipMaxLength() ? chipMaxLength() + 'ch' : 'none' }\"\n #autocompleteContainer\n class=\"autocomplete-container\"\n>\n @if (componentLabel()) {\n <label\n [htmlFor]=\"componentId()\"\n class=\"form-label d-flex gap-2\"\n >\n <div>\n <span>{{ componentLabel() | transloco }}</span>\n <span [hidden]=\"!_isRequired()\">*</span>\n </div>\n @if (helpMessage() && helpMessageTooltip()) {\n <div [quangTooltip]=\"helpMessage() | transloco\">\n <ng-content select=\"[help-icon]\" />\n </div>\n }\n </label>\n }\n <div\n [ngClass]=\"{\n horizontal: multiSelectDisplayMode() === 'horizontal',\n 'form-control': multiSelectDisplayMode() === 'horizontal',\n 'chips-bottom': chipsPosition() === 'bottom',\n }\"\n #chipContainer\n class=\"container-wrap\"\n >\n @if (multiple() && _chipList().length > 0 && chipsPosition() === 'top') {\n <ng-container *ngTemplateOutlet=\"chipsTemplate\" />\n }\n\n <input\n [attr.aria-activedescendant]=\"_showOptions() ? optionList()?.getActiveDescendantId() : null\"\n [attr.aria-controls]=\"_showOptions() ? 'optionList' : null\"\n [attr.aria-expanded]=\"_showOptions()\"\n [attr.required]=\"getIsRequiredControl()\"\n [class.form-control]=\"multiSelectDisplayMode() !== 'horizontal'\"\n [class.is-invalid]=\"_showErrors()\"\n [class.is-valid]=\"_showSuccess()\"\n [disabled]=\"_isDisabled() || isReadonly()\"\n [id]=\"componentId()\"\n [ngClass]=\"componentClass()\"\n [placeholder]=\"componentPlaceholder() | transloco\"\n [tabIndex]=\"componentTabIndex()\"\n [value]=\"_inputValue()\"\n (blur)=\"onBlurInput($event)\"\n (input)=\"onChangeInput($event)\"\n (keydown)=\"onInputKeydown($event)\"\n (mousedown)=\"showOptionVisibility()\"\n #selectInput\n aria-autocomplete=\"list\"\n aria-haspopup=\"listbox\"\n autocomplete=\"off\"\n role=\"combobox\"\n type=\"text\"\n />\n\n @if (multiple() && _chipList().length > 0 && chipsPosition() === 'bottom') {\n <ng-container *ngTemplateOutlet=\"chipsTemplate\" />\n }\n </div>\n @if (_showOptions()) {\n <quang-option-list\n [_isDisabled]=\"_isDisabled()\"\n [_value]=\"_highlightedValue()\"\n [componentClass]=\"componentClass()\"\n [componentLabel]=\"componentLabel()\"\n [componentTabIndex]=\"componentTabIndex()\"\n [nullOption]=\"false\"\n [optionListMaxHeight]=\"optionListMaxHeight()\"\n [parentID]=\"componentId()\"\n [parentType]=\"ParentType\"\n [scrollBehaviorOnOpen]=\"scrollBehaviorOnOpen()\"\n [selectButtonRef]=\"autocompleteContainer\"\n [selectOptions]=\"_filteredOptions()\"\n [translateValue]=\"translateValue()\"\n (blurHandler)=\"onBlurOptionList($event)\"\n (changedHandler)=\"onValueChange($event)\"\n (escapePressed)=\"onEscapePressed()\"\n (tabPressed)=\"onTabPressed($event)\"\n #optionList\n selectionMode=\"single\"\n />\n }\n <div\n [class.d-block]=\"_showSuccess()\"\n class=\"valid-feedback\"\n >\n {{ successMessage() | transloco }}\n </div>\n <div\n [class.d-block]=\"_showErrors()\"\n class=\"invalid-feedback\"\n >\n {{ _currentErrorMessage() | transloco: _currentErrorMessageExtraData() }}\n </div>\n @if (helpMessage() && !helpMessageTooltip()) {\n <small\n [hidden]=\"_showSuccess() || _showErrors()\"\n aria-live=\"assertive\"\n class=\"form-text text-muted\"\n >\n {{ helpMessage() | transloco }}\n </small>\n }\n</div>\n\n<!-- Chips template for reuse in top/bottom positions -->\n<ng-template #chipsTemplate>\n <div class=\"chips-container\">\n @for (chip of _chipList(); track chip) {\n @if (getDescription(chip)) {\n <div\n [ngClass]=\"{ 'chip-truncate': chipMaxLength() }\"\n [quangTooltip]=\"chipMaxLength() ? getDescription(chip) : ''\"\n class=\"chip chip-hover\"\n >\n <p [ngClass]=\"{ 'm-0': isReadonly() || _isDisabled() }\">\n @if (getOptionByValue(chip); as opt) {\n @if (opt.renderer) {\n <ng-container\n [ngTemplateOutlet]=\"opt.renderer\"\n [ngTemplateOutletContext]=\"{ $implicit: opt, selected: true, index: getOptionIndex(opt) }\"\n ></ng-container>\n } @else {\n {{ getDescription(chip) }}\n }\n } @else {\n {{ getDescription(chip) }}\n }\n </p>\n @if (!isReadonly() && !_isDisabled()) {\n <button\n [tabIndex]=\"$index + 1\"\n (click)=\"deleteChip(chip)\"\n class=\"btn btn-chip\"\n type=\"button\"\n >\n <svg\n class=\"ionicon\"\n fill=\"currentColor\"\n height=\"24\"\n viewBox=\"0 0 512 512\"\n width=\"24\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M368 368L144 144M368 144L144 368\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"32\"\n />\n </svg>\n </button>\n }\n </div>\n }\n }\n </div>\n</ng-template>\n", styles: [":host{display:block;--chip-max-length: none}.autocomplete-container{margin-bottom:1rem;position:relative}.chip:has(.btn-chip:disabled):hover{filter:unset;cursor:unset}.container-wrap,.container-wrap .chips-container{display:flex;flex-wrap:wrap;gap:.5rem}.container-wrap.chips-bottom{flex-direction:column}.container-wrap.chips-bottom input{order:-1}.container-wrap.horizontal{display:flex}.container-wrap.horizontal .chip-container{max-width:70%;margin-bottom:0;margin-left:.5rem;flex-wrap:nowrap;white-space:nowrap;overflow-x:auto;position:absolute;align-items:center}.container-wrap.horizontal .chip-container .chip{white-space:nowrap}.container-wrap.horizontal input{min-width:30%;flex:1 1 0;width:auto;border:none}.container-wrap.horizontal input:focus-visible{outline:none}.chip{display:flex;justify-content:space-between;align-items:center;gap:.25rem;padding:.25rem .5rem;border-radius:16px;color:var(--bs-btn-color);background-color:rgba(var(--bs-primary-rgb),.1);border-width:1px;border-style:solid;border-color:var(--bs-primary-border-subtle);min-height:2rem;max-width:100%}.chip p{margin:0;max-width:var(--chip-max-length);overflow:hidden;overflow-wrap:break-word;word-break:break-word}.chip.chip-truncate p{white-space:nowrap;text-overflow:ellipsis}.chip .btn-chip{text-align:end;padding:0;min-width:unset}.chip .btn-chip:hover{opacity:80%}.chip .btn-chip:active{border-color:transparent}.chip .btn-chip svg{color:var(--bs-primary);vertical-align:sub}.chip:has(.btn-chip:focus-visible){border-width:2px;filter:brightness(80%)}\n"] }]
793
+ ], template: "<div\n [ngStyle]=\"{ '--chip-max-length': chipMaxLength() ? chipMaxLength() + 'ch' : 'none' }\"\n #autocompleteContainer\n class=\"autocomplete-container\"\n>\n @if (componentLabel()) {\n <label\n [htmlFor]=\"componentId()\"\n class=\"form-label d-flex gap-2\"\n >\n <div>\n <span>{{ componentLabel() | transloco }}</span>\n <span [hidden]=\"!_isRequired()\">*</span>\n </div>\n @if (helpMessage() && helpMessageTooltip()) {\n <div\n [overlayPosition]=\"helpTooltipPosition()\"\n [quangTooltip]=\"helpMessage() | transloco\"\n [showMethod]=\"showHelpTooltipMethod()\"\n class=\"pointer\"\n >\n <ng-content select=\"[help-icon]\" />\n </div>\n }\n </label>\n }\n <div\n [ngClass]=\"{\n horizontal: multiSelectDisplayMode() === 'horizontal',\n 'form-control': multiSelectDisplayMode() === 'horizontal',\n 'chips-bottom': chipsPosition() === 'bottom',\n }\"\n #chipContainer\n class=\"container-wrap\"\n >\n @if (multiple() && _chipList().length > 0 && chipsPosition() === 'top') {\n <ng-container *ngTemplateOutlet=\"chipsTemplate\" />\n }\n\n <input\n [attr.aria-activedescendant]=\"_showOptions() ? optionList()?.getActiveDescendantId() : null\"\n [attr.aria-controls]=\"_showOptions() ? 'optionList' : null\"\n [attr.aria-expanded]=\"_showOptions()\"\n [attr.required]=\"getIsRequiredControl()\"\n [class.form-control]=\"multiSelectDisplayMode() !== 'horizontal'\"\n [class.is-invalid]=\"_showErrors()\"\n [class.is-valid]=\"_showSuccess()\"\n [disabled]=\"_isDisabled() || isReadonly()\"\n [id]=\"componentId()\"\n [ngClass]=\"componentClass()\"\n [placeholder]=\"componentPlaceholder() | transloco\"\n [tabIndex]=\"componentTabIndex()\"\n [value]=\"_inputValue()\"\n (blur)=\"onBlurInput($event)\"\n (input)=\"onChangeInput($event)\"\n (keydown)=\"onInputKeydown($event)\"\n (mousedown)=\"showOptionVisibility()\"\n #selectInput\n aria-autocomplete=\"list\"\n aria-haspopup=\"listbox\"\n autocomplete=\"off\"\n role=\"combobox\"\n type=\"text\"\n />\n\n @if (multiple() && _chipList().length > 0 && chipsPosition() === 'bottom') {\n <ng-container *ngTemplateOutlet=\"chipsTemplate\" />\n }\n </div>\n @if (_showOptions()) {\n <quang-option-list\n [_isDisabled]=\"_isDisabled()\"\n [_value]=\"_highlightedValue()\"\n [componentClass]=\"componentClass()\"\n [componentLabel]=\"componentLabel()\"\n [componentTabIndex]=\"componentTabIndex()\"\n [nullOption]=\"false\"\n [optionListMaxHeight]=\"optionListMaxHeight()\"\n [parentID]=\"componentId()\"\n [parentType]=\"ParentType\"\n [scrollBehaviorOnOpen]=\"scrollBehaviorOnOpen()\"\n [selectButtonRef]=\"autocompleteContainer\"\n [selectOptions]=\"_filteredOptions()\"\n [translateValue]=\"translateValue()\"\n (blurHandler)=\"onBlurOptionList($event)\"\n (changedHandler)=\"onValueChange($event)\"\n (escapePressed)=\"onEscapePressed()\"\n (tabPressed)=\"onTabPressed($event)\"\n #optionList\n selectionMode=\"single\"\n />\n }\n <div\n [class.d-block]=\"_showSuccess()\"\n class=\"valid-feedback\"\n >\n {{ successMessage() | transloco }}\n </div>\n <div\n [class.d-block]=\"_showErrors()\"\n class=\"invalid-feedback\"\n >\n {{ _currentErrorMessage() | transloco: _currentErrorMessageExtraData() }}\n </div>\n @if (helpMessage() && !helpMessageTooltip()) {\n <small\n [hidden]=\"_showSuccess() || _showErrors()\"\n aria-live=\"assertive\"\n class=\"form-text text-muted\"\n >\n {{ helpMessage() | transloco }}\n </small>\n }\n</div>\n\n<!-- Chips template for reuse in top/bottom positions -->\n<ng-template #chipsTemplate>\n <div class=\"chips-container\">\n @for (chip of _chipList(); track chip) {\n @if (getDescription(chip)) {\n <div\n [ngClass]=\"{ 'chip-truncate': chipMaxLength() }\"\n [quangTooltip]=\"chipMaxLength() ? getDescription(chip) : ''\"\n class=\"chip chip-hover\"\n >\n <p [ngClass]=\"{ 'm-0': isReadonly() || _isDisabled() }\">\n @if (getOptionByValue(chip); as opt) {\n @if (opt.renderer) {\n <ng-container\n [ngTemplateOutlet]=\"opt.renderer\"\n [ngTemplateOutletContext]=\"{ $implicit: opt, selected: true, index: getOptionIndex(opt) }\"\n ></ng-container>\n } @else {\n {{ getDescription(chip) }}\n }\n } @else {\n {{ getDescription(chip) }}\n }\n </p>\n @if (!isReadonly() && !_isDisabled()) {\n <button\n [tabIndex]=\"$index + 1\"\n (click)=\"deleteChip(chip)\"\n class=\"btn btn-chip\"\n type=\"button\"\n >\n <svg\n class=\"ionicon\"\n fill=\"currentColor\"\n height=\"24\"\n viewBox=\"0 0 512 512\"\n width=\"24\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M368 368L144 144M368 144L144 368\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"32\"\n />\n </svg>\n </button>\n }\n </div>\n }\n }\n </div>\n</ng-template>\n", styles: [":host{display:block;--chip-max-length: none}.pointer{cursor:pointer}.autocomplete-container{margin-bottom:1rem;position:relative}.chip:has(.btn-chip:disabled):hover{filter:unset;cursor:unset}.container-wrap,.container-wrap .chips-container{display:flex;flex-wrap:wrap;gap:.5rem}.container-wrap.chips-bottom{flex-direction:column}.container-wrap.chips-bottom input{order:-1}.container-wrap.horizontal{display:flex}.container-wrap.horizontal .chip-container{max-width:70%;margin-bottom:0;margin-left:.5rem;flex-wrap:nowrap;white-space:nowrap;overflow-x:auto;position:absolute;align-items:center}.container-wrap.horizontal .chip-container .chip{white-space:nowrap}.container-wrap.horizontal input{min-width:30%;flex:1 1 0;width:auto;border:none}.container-wrap.horizontal input:focus-visible{outline:none}.chip{display:flex;justify-content:space-between;align-items:center;gap:.25rem;padding:.25rem .5rem;border-radius:16px;color:var(--bs-btn-color);background-color:rgba(var(--bs-primary-rgb),.1);border-width:1px;border-style:solid;border-color:var(--bs-primary-border-subtle);min-height:2rem;max-width:100%}.chip p{margin:0;max-width:var(--chip-max-length);overflow:hidden;overflow-wrap:break-word;word-break:break-word}.chip.chip-truncate p{white-space:nowrap;text-overflow:ellipsis}.chip .btn-chip{text-align:end;padding:0;min-width:unset}.chip .btn-chip:hover{opacity:80%}.chip .btn-chip:active{border-color:transparent}.chip .btn-chip svg{color:var(--bs-primary);vertical-align:sub}.chip:has(.btn-chip:focus-visible){border-width:2px;filter:brightness(80%)}\n"] }]
753
794
  }], ctorParameters: () => [], propDecorators: { selectOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectOptions", required: true }] }], allowFreeText: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowFreeText", required: false }] }], autoSelectOnExactMatch: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoSelectOnExactMatch", required: false }] }], updateValueOnType: [{ type: i0.Input, args: [{ isSignal: true, alias: "updateValueOnType", required: false }] }], syncFormWithText: [{ type: i0.Input, args: [{ isSignal: true, alias: "syncFormWithText", required: false }] }], optionListMaxHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionListMaxHeight", required: false }] }], translateValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "translateValue", required: false }] }], scrollBehaviorOnOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollBehaviorOnOpen", required: false }] }], emitOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "emitOnly", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], chipMaxLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "chipMaxLength", required: false }] }], multiSelectDisplayMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiSelectDisplayMode", required: false }] }], chipsPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "chipsPosition", required: false }] }], searchTextDebounce: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchTextDebounce", required: false }] }], internalFilterOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "internalFilterOptions", required: false }] }], selectedOption: [{ type: i0.Output, args: ["selectedOption"] }], searchTextChange: [{ type: i0.Output, args: ["searchTextChange"] }], optionList: [{ type: i0.ViewChild, args: ['optionList', { isSignal: true }] }], selectInput: [{ type: i0.ViewChild, args: ['selectInput', { isSignal: true }] }], chipContainer: [{ type: i0.ViewChild, args: ['chipContainer', { isSignal: true }] }], autocompleteContainer: [{ type: i0.ViewChild, args: ['autocompleteContainer', { isSignal: true }] }] } });
754
795
 
755
796
  /**