@nanahoshi/mona-ui 0.24.4 → 0.24.5

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 (66) hide show
  1. package/README.md +1 -0
  2. package/fesm2022/nanahoshi-mona-ui-auto-complete.mjs +95 -70
  3. package/fesm2022/nanahoshi-mona-ui-combo-box.mjs +136 -89
  4. package/fesm2022/nanahoshi-mona-ui-dropdown-list.mjs +108 -56
  5. package/fesm2022/nanahoshi-mona-ui-dropdowns.mjs +68 -59
  6. package/fesm2022/nanahoshi-mona-ui-editor.mjs +3 -3
  7. package/fesm2022/nanahoshi-mona-ui-filter.mjs +1 -1
  8. package/fesm2022/{nanahoshi-mona-ui-grid-grid-footer-row.component-DLOPRuFv.mjs → nanahoshi-mona-ui-grid-grid-footer-row.component-BMCM0W8s.mjs} +2 -2
  9. package/fesm2022/{nanahoshi-mona-ui-grid-grid-list.component-Dw5U9H0a.mjs → nanahoshi-mona-ui-grid-grid-list.component-BSlV8Sua.mjs} +4 -4
  10. package/fesm2022/{nanahoshi-mona-ui-grid-grid-row.component-D1kyAzD8.mjs → nanahoshi-mona-ui-grid-grid-row.component-CTOdnH29.mjs} +2 -2
  11. package/fesm2022/{nanahoshi-mona-ui-grid-grid-virtual-list.component-ECK1_smx.mjs → nanahoshi-mona-ui-grid-grid-virtual-list.component-C2kf5MwO.mjs} +4 -4
  12. package/fesm2022/{nanahoshi-mona-ui-grid-nanahoshi-mona-ui-grid-D4lUDbfx.mjs → nanahoshi-mona-ui-grid-nanahoshi-mona-ui-grid-Dh42tUui.mjs} +8 -8
  13. package/fesm2022/nanahoshi-mona-ui-grid.mjs +1 -1
  14. package/fesm2022/nanahoshi-mona-ui-i18n-icu.mjs +2 -1
  15. package/fesm2022/nanahoshi-mona-ui-internal-indicator-icon.mjs +2 -2
  16. package/fesm2022/nanahoshi-mona-ui-internal.mjs +4 -2
  17. package/fesm2022/nanahoshi-mona-ui-locales-ar-sa.mjs +2 -1
  18. package/fesm2022/nanahoshi-mona-ui-locales-de-de.mjs +4 -1
  19. package/fesm2022/nanahoshi-mona-ui-locales-es-es.mjs +4 -1
  20. package/fesm2022/nanahoshi-mona-ui-locales-fr-fr.mjs +4 -1
  21. package/fesm2022/nanahoshi-mona-ui-locales-id-id.mjs +2 -1
  22. package/fesm2022/nanahoshi-mona-ui-locales-it-it.mjs +2 -1
  23. package/fesm2022/nanahoshi-mona-ui-locales-ja-jp.mjs +2 -1
  24. package/fesm2022/nanahoshi-mona-ui-locales-ko-kr.mjs +2 -1
  25. package/fesm2022/nanahoshi-mona-ui-locales-pt-br.mjs +2 -1
  26. package/fesm2022/nanahoshi-mona-ui-locales-ru-ru.mjs +2 -1
  27. package/fesm2022/nanahoshi-mona-ui-locales-tr-tr.mjs +2 -1
  28. package/fesm2022/nanahoshi-mona-ui-locales-zh-cn.mjs +2 -1
  29. package/fesm2022/nanahoshi-mona-ui-locales-zh-tw.mjs +2 -1
  30. package/fesm2022/nanahoshi-mona-ui-multi-select.mjs +118 -66
  31. package/fesm2022/nanahoshi-mona-ui-pager.mjs +1 -1
  32. package/i18n/catalogs/en-US.messages.json +2 -1
  33. package/locales/ar-sa/ar-sa.messages.json +2 -1
  34. package/locales/de-de/de-de.messages.json +2 -1
  35. package/locales/es-es/es-es.messages.json +2 -1
  36. package/locales/fr-fr/fr-fr.messages.json +2 -1
  37. package/locales/id-id/id-id.messages.json +2 -1
  38. package/locales/it-it/it-it.messages.json +2 -1
  39. package/locales/ja-jp/ja-jp.messages.json +2 -1
  40. package/locales/ko-kr/ko-kr.messages.json +2 -1
  41. package/locales/pt-br/pt-br.messages.json +2 -1
  42. package/locales/ru-ru/ru-ru.messages.json +2 -1
  43. package/locales/tr-tr/tr-tr.messages.json +2 -1
  44. package/locales/zh-cn/zh-cn.messages.json +2 -1
  45. package/locales/zh-tw/zh-tw.messages.json +2 -1
  46. package/package.json +1 -1
  47. package/types/nanahoshi-mona-ui-auto-complete.d.ts +34 -20
  48. package/types/nanahoshi-mona-ui-combo-box.d.ts +42 -23
  49. package/types/nanahoshi-mona-ui-dropdown-list.d.ts +29 -15
  50. package/types/nanahoshi-mona-ui-dropdowns.d.ts +30 -19
  51. package/types/nanahoshi-mona-ui-i18n.d.ts +1 -0
  52. package/types/nanahoshi-mona-ui-internal.d.ts +3 -1
  53. package/types/nanahoshi-mona-ui-locales-ar-sa.d.ts +1 -0
  54. package/types/nanahoshi-mona-ui-locales-de-de.d.ts +1 -0
  55. package/types/nanahoshi-mona-ui-locales-es-es.d.ts +1 -0
  56. package/types/nanahoshi-mona-ui-locales-fr-fr.d.ts +1 -0
  57. package/types/nanahoshi-mona-ui-locales-id-id.d.ts +1 -0
  58. package/types/nanahoshi-mona-ui-locales-it-it.d.ts +1 -0
  59. package/types/nanahoshi-mona-ui-locales-ja-jp.d.ts +1 -0
  60. package/types/nanahoshi-mona-ui-locales-ko-kr.d.ts +1 -0
  61. package/types/nanahoshi-mona-ui-locales-pt-br.d.ts +1 -0
  62. package/types/nanahoshi-mona-ui-locales-ru-ru.d.ts +1 -0
  63. package/types/nanahoshi-mona-ui-locales-tr-tr.d.ts +1 -0
  64. package/types/nanahoshi-mona-ui-locales-zh-cn.d.ts +1 -0
  65. package/types/nanahoshi-mona-ui-locales-zh-tw.d.ts +1 -0
  66. package/types/nanahoshi-mona-ui-multi-select.d.ts +45 -21
@@ -1,18 +1,16 @@
1
1
  import { NgTemplateOutlet } from '@angular/common';
2
2
  import * as i0 from '@angular/core';
3
- import { inject, DestroyRef, ElementRef, signal, computed, contentChild, TemplateRef, viewChild, input, output, model, effect, untracked, afterNextRender, forwardRef, Component } from '@angular/core';
3
+ import { inject, DestroyRef, ElementRef, signal, computed, contentChild, TemplateRef, viewChild, input, booleanAttribute, output, model, effect, untracked, afterNextRender, forwardRef, Component } from '@angular/core';
4
4
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
5
- import * as i2 from '@angular/forms';
6
- import { FormsModule } from '@angular/forms';
7
5
  import { FilterChangeEvent } from '@nanahoshi/mona-ui/common';
8
6
  import * as i1 from '@nanahoshi/mona-ui/dropdowns';
9
- import { DropdownListService, DropdownService, DropdownFooterTemplateDirective, DropdownGroupHeaderTemplateDirective, DropdownHeaderTemplateDirective, DropdownItemTemplateDirective, dropdownPopupThemeVariants, DropdownNoDataTemplateDirective, DropdownPrefixTemplateDirective, DropdownLiveRegionDirective, DropdownDataInputToken, DROPDOWN_POPUP_INPUT_TOKEN, DropdownDataHandlerDirective, DropdownListPopupHandlerDirective } from '@nanahoshi/mona-ui/dropdowns';
10
- import { themeControlSurfaceClasses, createElementControlId, rxTimeout } from '@nanahoshi/mona-ui/internal';
7
+ import { DropdownListService, DropdownService, DropdownFooterTemplateDirective, DropdownGroupHeaderTemplateDirective, DropdownHeaderTemplateDirective, DropdownItemTemplateDirective, dropdownPopupThemeVariants, DropdownNoDataTemplateDirective, DropdownPrefixTemplateDirective, DROPDOWN_DATA_INPUT_TOKEN, DROPDOWN_POPUP_INPUT_TOKEN, DropdownDataHandlerDirective, DropdownListPopupHandlerDirective, DropdownLiveRegionDirective } from '@nanahoshi/mona-ui/dropdowns';
8
+ import { themeControlSurfaceClasses, createElementControlId } from '@nanahoshi/mona-ui/internal';
11
9
  import { IndicatorIconComponent } from '@nanahoshi/mona-ui/internal/indicator-icon';
12
10
  import { ListService, ListComponent, ListGroupHeaderTemplateDirective, ListFooterTemplateDirective, ListHeaderTemplateDirective, ListNoDataTemplateDirective, ListItemTemplateDirective } from '@nanahoshi/mona-ui/internal/list';
13
11
  import { TextBoxDirective } from '@nanahoshi/mona-ui/text-box';
14
12
  import { MonaI18nService } from '@nanahoshi/mona-ui/i18n';
15
- import { Subject, fromEvent, filter, debounceTime, take, tap } from 'rxjs';
13
+ import { Subject, debounceTime, filter, take, tap } from 'rxjs';
16
14
  import { twMerge } from 'tailwind-merge';
17
15
  import { cva } from 'class-variance-authority';
18
16
 
@@ -99,7 +97,6 @@ class ComboBoxComponent {
99
97
  #hostElementRef;
100
98
  #i18n;
101
99
  #listService;
102
- #navigatedItem;
103
100
  #popupRef;
104
101
  #userNavigatedViaArrows;
105
102
  constructor() {
@@ -109,8 +106,6 @@ class ComboBoxComponent {
109
106
  this.#hostElementRef = inject(ElementRef);
110
107
  this.#i18n = inject(MonaI18nService);
111
108
  this.#listService = inject(ListService);
112
- this.#navigatedItem = signal(null, /* @ts-ignore */
113
- ...(ngDevMode ? [{ debugName: "#navigatedItem" }] : /* istanbul ignore next */ []));
114
109
  this.#popupRef = this.#dropdownService.popupRef;
115
110
  this.#userNavigatedViaArrows = signal(false, /* @ts-ignore */
116
111
  ...(ngDevMode ? [{ debugName: "#userNavigatedViaArrows" }] : /* istanbul ignore next */ []));
@@ -134,17 +129,20 @@ class ComboBoxComponent {
134
129
  return twMerge(variantClass, userClass);
135
130
  }, /* @ts-ignore */
136
131
  ...(ngDevMode ? [{ debugName: "baseClass" }] : /* istanbul ignore next */ []));
132
+ this.clearButtonVisible = computed(() => this.showClearButton() &&
133
+ this.selectedListItem() != null &&
134
+ !this.loading() &&
135
+ !this.disabled() &&
136
+ !this.readonly(), /* @ts-ignore */
137
+ ...(ngDevMode ? [{ debugName: "clearButtonVisible" }] : /* istanbul ignore next */ []));
137
138
  this.comboBoxValue = signal("", /* @ts-ignore */
138
139
  ...(ngDevMode ? [{ debugName: "comboBoxValue" }] : /* istanbul ignore next */ []));
139
140
  this.comboBoxValue$ = new Subject();
140
- this.effectiveAriaLabel = computed(() => this.ariaLabel() || (this.ariaLabelledBy() ? "" : this.placeholder()), /* @ts-ignore */
141
- ...(ngDevMode ? [{ debugName: "effectiveAriaLabel" }] : /* istanbul ignore next */ []));
142
141
  this.expanded = computed(() => this.#popupRef() !== null, /* @ts-ignore */
143
142
  ...(ngDevMode ? [{ debugName: "expanded" }] : /* istanbul ignore next */ []));
144
143
  this.footerTemplate = contentChild(DropdownFooterTemplateDirective, { ...(ngDevMode ? { debugName: "footerTemplate" } : /* istanbul ignore next */ {}), read: TemplateRef });
145
144
  this.groupHeaderTemplate = contentChild(DropdownGroupHeaderTemplateDirective, { ...(ngDevMode ? { debugName: "groupHeaderTemplate" } : /* istanbul ignore next */ {}), read: TemplateRef });
146
145
  this.headerTemplate = contentChild(DropdownHeaderTemplateDirective, { ...(ngDevMode ? { debugName: "headerTemplate" } : /* istanbul ignore next */ {}), read: TemplateRef });
147
- this.id = createElementControlId();
148
146
  this.inputClass = computed(() => {
149
147
  const rounded = this.rounded();
150
148
  return comboBoxTextInputThemeVariants({ rounded });
@@ -159,7 +157,9 @@ class ComboBoxComponent {
159
157
  const size = this.size();
160
158
  const userClass = this.popupClass();
161
159
  const variantClass = dropdownPopupThemeVariants({ rounded, size });
162
- return twMerge(variantClass, userClass);
160
+ // With a popupHeight, the overlay sets the limit; the default max-h-64 would cut a taller popup short.
161
+ const heightClass = this.popupHeight() ? "max-h-none" : "";
162
+ return twMerge(variantClass, heightClass, userClass);
163
163
  }, /* @ts-ignore */
164
164
  ...(ngDevMode ? [{ debugName: "listPopupClass" }] : /* istanbul ignore next */ []));
165
165
  this.messages = this.#i18n.componentMessages("comboBox", COMBO_BOX_DEFAULT_MESSAGES);
@@ -188,16 +188,16 @@ class ComboBoxComponent {
188
188
  * @description Allows a custom value to be committed when it doesn't match any item in {@link data}.
189
189
  * @default false
190
190
  */
191
- this.allowCustomValue = input(false, /* @ts-ignore */
192
- ...(ngDevMode ? [{ debugName: "allowCustomValue" }] : /* istanbul ignore next */ []));
191
+ this.allowCustomValue = input(false, { ...(ngDevMode ? { debugName: "allowCustomValue" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
193
192
  /**
194
193
  * @description ID of an element that provides an extended description for the input.
195
194
  * @default ""
196
195
  */
197
196
  this.ariaDescribedBy = input("", { ...(ngDevMode ? { debugName: "ariaDescribedBy" } : /* istanbul ignore next */ {}), alias: "aria-describedby" });
198
197
  /**
199
- * @description Accessible name for the input. Describe what the combo box represents.
200
- * Falls back to {@link placeholder} when neither this nor {@link ariaLabelledBy} is set.
198
+ * @description Accessible name for the input. Describe what the combo box represents. Prefer a visible label,
199
+ * linked through {@link inputId} or {@link ariaLabelledBy}; without any of them, browsers fall back to the
200
+ * {@link placeholder}.
201
201
  * @default ""
202
202
  */
203
203
  this.ariaLabel = input("", { ...(ngDevMode ? { debugName: "ariaLabel" } : /* istanbul ignore next */ {}), alias: "aria-label" });
@@ -209,6 +209,7 @@ class ComboBoxComponent {
209
209
  /**
210
210
  * @description Emitted when the popup is about to close. This event is preventable.
211
211
  */
212
+ // eslint-disable-next-line @angular-eslint/no-output-native -- renaming the output would break existing (close) bindings
212
213
  this.close = output();
213
214
  /**
214
215
  * @description Emitted after the popup is closed.
@@ -224,15 +225,20 @@ class ComboBoxComponent {
224
225
  * @description Renders the component with reduced visual emphasis and removes pointer interaction.
225
226
  * @default false
226
227
  */
227
- this.disabled = model(false, /* @ts-ignore */
228
- ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
228
+ this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
229
+ /**
230
+ * @description Id of the inner `<input>` element. Point a `<label for>` or `<mona-label for>` at it to label the
231
+ * combo box. A unique id is generated when it isn't set.
232
+ * @default generated
233
+ */
234
+ this.inputId = input(createElementControlId(), /* @ts-ignore */
235
+ ...(ngDevMode ? [{ debugName: "inputId" }] : /* istanbul ignore next */ []));
229
236
  /**
230
237
  * @description Marks the combo box as invalid. When bound to a signal form field via `[formField]`,
231
238
  * this is written by the `FormField` directive.
232
239
  * @default false
233
240
  */
234
- this.invalid = input(false, /* @ts-ignore */
235
- ...(ngDevMode ? [{ debugName: "invalid" }] : /* istanbul ignore next */ []));
241
+ this.invalid = input(false, { ...(ngDevMode ? { debugName: "invalid" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
236
242
  /**
237
243
  * @description A predicate function or the name of the field that determines whether an item is disabled.
238
244
  * @default undefined
@@ -240,11 +246,11 @@ class ComboBoxComponent {
240
246
  this.itemDisabled = input(/* @ts-ignore */
241
247
  ...(ngDevMode ? [undefined, { debugName: "itemDisabled" }] : /* istanbul ignore next */ []));
242
248
  /**
243
- * @description Displays a loading indicator and prevents interaction while an operation is in progress.
249
+ * @description Shows a loading indicator, in place of the clear button, and marks the field busy. It doesn't
250
+ * block interaction: the field stays usable while data loads.
244
251
  * @default false
245
252
  */
246
- this.loading = input(false, /* @ts-ignore */
247
- ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
253
+ this.loading = input(false, { ...(ngDevMode ? { debugName: "loading" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
248
254
  /**
249
255
  * @description Emitted when the popup is about to open. This event is preventable.
250
256
  */
@@ -266,7 +272,8 @@ class ComboBoxComponent {
266
272
  this.popupClass = input("", /* @ts-ignore */
267
273
  ...(ngDevMode ? [{ debugName: "popupClass" }] : /* istanbul ignore next */ []));
268
274
  /**
269
- * @description Sets the height of the popup element.
275
+ * @description Maximum height of the popup, in pixels or as a CSS length. Shorter lists shrink to fit. When
276
+ * unset, the popup is at most 200 pixels tall.
270
277
  * @default null
271
278
  */
272
279
  this.popupHeight = input(null, /* @ts-ignore */
@@ -281,14 +288,12 @@ class ComboBoxComponent {
281
288
  * @description Prevents value changes while preserving the component's visual state.
282
289
  * @default false
283
290
  */
284
- this.readonly = input(false, /* @ts-ignore */
285
- ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
291
+ this.readonly = input(false, { ...(ngDevMode ? { debugName: "readonly" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
286
292
  /**
287
293
  * @description Sets the required state of the combo box component.
288
294
  * @default false
289
295
  */
290
- this.required = input(false, /* @ts-ignore */
291
- ...(ngDevMode ? [{ debugName: "required" }] : /* istanbul ignore next */ []));
296
+ this.required = input(false, { ...(ngDevMode ? { debugName: "required" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
292
297
  /**
293
298
  * @description Border-radius preset applied to the component.
294
299
  * @default "medium"
@@ -299,8 +304,7 @@ class ComboBoxComponent {
299
304
  * @description Displays a button that resets the value to empty when an item is selected.
300
305
  * @default false
301
306
  */
302
- this.showClearButton = input(false, /* @ts-ignore */
303
- ...(ngDevMode ? [{ debugName: "showClearButton" }] : /* istanbul ignore next */ []));
307
+ this.showClearButton = input(false, { ...(ngDevMode ? { debugName: "showClearButton" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
304
308
  /**
305
309
  * @description Size preset controlling the component's dimensions.
306
310
  * @default "medium"
@@ -315,8 +319,8 @@ class ComboBoxComponent {
315
319
  this.textField = input(/* @ts-ignore */
316
320
  ...(ngDevMode ? [undefined, { debugName: "textField" }] : /* istanbul ignore next */ []));
317
321
  /**
318
- * @description Emitted when the combo box is interacted with on blur, selection, clear, or committed input.
319
- * The `FormField` directive listens to this to mark the field as touched.
322
+ * @description Emitted when focus leaves the combo box. Moving between its input, clear button and popup doesn't
323
+ * count. The `FormField` directive listens to this to mark the field as touched.
320
324
  */
321
325
  this.touch = output();
322
326
  /**
@@ -324,8 +328,7 @@ class ComboBoxComponent {
324
328
  * this is written by the `FormField` directive.
325
329
  * @default false
326
330
  */
327
- this.touched = input(false, /* @ts-ignore */
328
- ...(ngDevMode ? [{ debugName: "touched" }] : /* istanbul ignore next */ []));
331
+ this.touched = input(false, { ...(ngDevMode ? { debugName: "touched" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
329
332
  /**
330
333
  * @description Additional CSS classes merged onto the host element via `tailwind-merge`.
331
334
  * @default ""
@@ -341,8 +344,8 @@ class ComboBoxComponent {
341
344
  this.value = model(null, /* @ts-ignore */
342
345
  ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
343
346
  /**
344
- * @description Emitted with the entered text when the user presses Enter on unmatched input.
345
- * Only emitted when {@link allowCustomValue} is true.
347
+ * @description Emitted with the entered text when the user presses Enter on text that matches no item, or leaves
348
+ * the field with it. Only emitted when {@link allowCustomValue} is true.
346
349
  */
347
350
  this.valueAdd = output();
348
351
  /**
@@ -364,8 +367,7 @@ class ComboBoxComponent {
364
367
  *
365
368
  * @default false
366
369
  */
367
- this.valuePrimitive = input(false, /* @ts-ignore */
368
- ...(ngDevMode ? [{ debugName: "valuePrimitive" }] : /* istanbul ignore next */ []));
370
+ this.valuePrimitive = input(false, { ...(ngDevMode ? { debugName: "valuePrimitive" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
369
371
  effect(() => {
370
372
  const popupTemplate = this.popupTemplate();
371
373
  untracked(() => this.#dropdownService.popupTemplate.set(popupTemplate));
@@ -387,34 +389,58 @@ class ComboBoxComponent {
387
389
  this.setSubscriptions();
388
390
  }
389
391
  });
390
- fromEvent(this.#hostElementRef.nativeElement, "focusin")
391
- .pipe(takeUntilDestroyed(), filter(() => !this.readonly()))
392
- .subscribe(() => this.focus());
393
392
  }
394
- focus() {
395
- rxTimeout(this.#destroyRef, () => {
396
- const input = this.#hostElementRef.nativeElement.querySelector("input");
397
- if (input && !this.readonly()) {
398
- input.focus();
399
- input.setSelectionRange(input.value.length, input.value.length);
400
- }
401
- });
393
+ /**
394
+ * @description Moves focus to the inner input. The caret goes to the end of the text.
395
+ * @param options Passed to `HTMLElement.focus()`, for example `{ preventScroll: true }`.
396
+ */
397
+ focus(options) {
398
+ const input = this.#hostElementRef.nativeElement.querySelector("input");
399
+ if (input) {
400
+ input.focus(options);
401
+ input.setSelectionRange(input.value.length, input.value.length);
402
+ }
402
403
  }
403
- onInputBlur(event) {
404
- const popupElement = this.#dropdownService.popupRef()?.overlayRef.overlayElement;
405
- if (popupElement && event?.relatedTarget instanceof Node && popupElement.contains(event.relatedTarget)) {
404
+ onFocusOut(event) {
405
+ // Removing a focused dropdown from the page fires focusout after it is destroyed.
406
+ if (this.#destroyRef.destroyed) {
406
407
  return;
407
408
  }
409
+ const nextTarget = event.relatedTarget;
410
+ if (nextTarget instanceof Node) {
411
+ const popupElement = this.#dropdownService.popupRef()?.overlayRef.overlayElement;
412
+ if (this.#hostElementRef.nativeElement.contains(nextTarget) || popupElement?.contains(nextTarget)) {
413
+ return;
414
+ }
415
+ }
416
+ this.resolveText();
408
417
  this.touch.emit();
409
418
  }
419
+ onHostPointerDown(event) {
420
+ // A press on the padding, the affixes or the arrow focuses the input instead of the host. Controls of
421
+ // their own (the input, the clear button, links or buttons in a prefix) keep the press.
422
+ const host = this.#hostElementRef.nativeElement;
423
+ const target = event.target;
424
+ const control = target?.closest("a, button, input, select, textarea, [tabindex]");
425
+ if (this.disabled() || (control != null && control !== host)) {
426
+ return;
427
+ }
428
+ event.preventDefault();
429
+ host.querySelector("input")?.focus();
430
+ }
431
+ onInput(text) {
432
+ // Keep the signal in step with the input on every keystroke, so that writing the same text back (a revert to
433
+ // the selected item's text, say) still reaches the DOM through the [value] binding.
434
+ this.comboBoxValue.set(text);
435
+ this.comboBoxValue$.next(text);
436
+ }
410
437
  onItemSelect(event) {
411
438
  if (this.#listService.filterableOptions().enabled &&
412
439
  event.source.via === "keyboard" &&
413
440
  event.source.key !== "Enter") {
414
- this.#navigatedItem.set(event.item.data);
415
441
  return;
416
442
  }
417
- this.updateValue(this.getControlValue(event.item.data), true);
443
+ this.updateValue(this.getControlValue(event.item.data));
418
444
  this.closePopup();
419
445
  }
420
446
  onPopupPointerDown(event) {
@@ -424,7 +450,9 @@ class ComboBoxComponent {
424
450
  }
425
451
  }
426
452
  onValueClear(event) {
427
- if (this.readonly() || (event instanceof KeyboardEvent && event.key !== "Enter" && event.key !== " ")) {
453
+ if (this.disabled() ||
454
+ this.readonly() ||
455
+ (event instanceof KeyboardEvent && event.key !== "Enter" && event.key !== " ")) {
428
456
  return;
429
457
  }
430
458
  event.preventDefault();
@@ -435,7 +463,6 @@ class ComboBoxComponent {
435
463
  this.focus();
436
464
  }
437
465
  clear() {
438
- this.#navigatedItem.set(null);
439
466
  this.updateValue(null);
440
467
  this.comboBoxValue.set("");
441
468
  this.#listService.clearSelections();
@@ -507,6 +534,36 @@ class ComboBoxComponent {
507
534
  this.#listService.filterChange$.next(event);
508
535
  return event;
509
536
  }
537
+ /**
538
+ * Settles typed text when focus leaves the combo box, so the field never shows text that isn't its value:
539
+ * an item whose text matches is selected, empty text clears the value, other text is offered through
540
+ * `valueAdd` when custom values are allowed, and otherwise the selected item's text comes back.
541
+ */
542
+ resolveText() {
543
+ // Read the input itself: with a filter debounce, the last keystrokes may not have reached comboBoxValue yet.
544
+ const input = this.#hostElementRef.nativeElement.querySelector("input");
545
+ const text = input?.value ?? this.comboBoxValue();
546
+ this.comboBoxValue.set(text);
547
+ const selectedItem = this.selectedListItem();
548
+ const selectedText = selectedItem ? this.#listService.getItemText(selectedItem) : "";
549
+ if (text === selectedText) {
550
+ return;
551
+ }
552
+ if (!text) {
553
+ this.clear();
554
+ return;
555
+ }
556
+ const matchingItem = this.#listService.getMatchingFilteredItem(text);
557
+ if (matchingItem && this.#listService.getItemText(matchingItem).toLowerCase() === text.toLowerCase()) {
558
+ this.selectItem(matchingItem);
559
+ return;
560
+ }
561
+ if (this.allowCustomValue()) {
562
+ this.valueAdd.emit(text);
563
+ return;
564
+ }
565
+ this.comboBoxValue.set(selectedText);
566
+ }
510
567
  selectItem(item) {
511
568
  this.#listService.selectItem(item);
512
569
  this.updateValue(this.getControlValue(item.data));
@@ -515,10 +572,9 @@ class ComboBoxComponent {
515
572
  this.#dropdownListService.navigate$.pipe(takeUntilDestroyed(this.#destroyRef)).subscribe(({ item }) => {
516
573
  if (!this.expanded()) {
517
574
  this.#listService.selectItem(item);
518
- this.updateValue(this.getControlValue(item.data), true);
575
+ this.updateValue(this.getControlValue(item.data));
519
576
  }
520
577
  else {
521
- this.#navigatedItem.set(item.data);
522
578
  this.#userNavigatedViaArrows.set(true);
523
579
  }
524
580
  });
@@ -546,7 +602,6 @@ class ComboBoxComponent {
546
602
  this.comboBoxValue.set(value ?? "");
547
603
  const item = this.#listService.getMatchingFilteredItem(value);
548
604
  if (item) {
549
- this.#navigatedItem.set(item.data);
550
605
  this.#listService.highlightedItem.set(item);
551
606
  if (this.#popupRef()) {
552
607
  this.#listService.scrollToItem$.next({ item, focus: false });
@@ -562,19 +617,20 @@ class ComboBoxComponent {
562
617
  setEscapeKeySubscription() {
563
618
  this.#dropdownService.beforeKeydown$
564
619
  .pipe(takeUntilDestroyed(this.#destroyRef), filter(e => e.originalEvent?.key === "Escape"), tap(e => e.preventDefault()))
565
- .subscribe(() => {
620
+ .subscribe(event => {
566
621
  if (this.#popupRef()) {
567
622
  const selectedItem = this.selectedListItem();
568
623
  if (selectedItem) {
569
624
  this.comboBoxValue.set(this.#listService.getItemText(selectedItem));
570
- this.#navigatedItem.set(selectedItem.data);
571
625
  }
572
626
  else {
573
627
  this.clear();
574
628
  }
575
629
  this.closePopup();
576
630
  }
577
- else {
631
+ else if (this.comboBoxValue() || this.value() != null) {
632
+ // This Escape was used to clear the field; a dialog or window around it waits for the next.
633
+ event.originalEvent?.stopPropagation();
578
634
  this.clear();
579
635
  this.focus();
580
636
  }
@@ -592,11 +648,6 @@ class ComboBoxComponent {
592
648
  }
593
649
  });
594
650
  }
595
- setNavigatedItemSubscription() {
596
- this.#listService.navigatedItem$.pipe(takeUntilDestroyed(this.#destroyRef)).subscribe(e => {
597
- this.#navigatedItem.set(e?.data ?? null);
598
- });
599
- }
600
651
  setPopupCloseSubscriptions() {
601
652
  this.#dropdownService.popupCloseComplete$.pipe(takeUntilDestroyed(this.#destroyRef)).subscribe(() => {
602
653
  this.#userNavigatedViaArrows.set(false);
@@ -618,7 +669,6 @@ class ComboBoxComponent {
618
669
  this.setComboboxValueSubscription();
619
670
  this.setEscapeKeySubscription();
620
671
  this.setKeydownSubscription();
621
- this.setNavigatedItemSubscription();
622
672
  this.setPopupCloseSubscriptions();
623
673
  this.setSpaceKeySubscription();
624
674
  }
@@ -634,23 +684,19 @@ class ComboBoxComponent {
634
684
  this.#listService.setSelectedDataItems([value]);
635
685
  }
636
686
  }
637
- updateValue(value, notify = true) {
638
- const oldValue = this.value();
639
- if (oldValue !== value) {
687
+ updateValue(value) {
688
+ if (this.value() !== value) {
640
689
  this.value.set(value);
641
690
  }
642
691
  this.comboBoxValue.set(this.valueText());
643
- if (notify && oldValue !== value) {
644
- this.touch.emit();
645
- }
646
692
  }
647
693
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: ComboBoxComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
648
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.6", type: ComboBoxComponent, isStandalone: true, selector: "mona-combo-box", inputs: { allowCustomValue: { classPropertyName: "allowCustomValue", publicName: "allowCustomValue", isSignal: true, isRequired: false, transformFunction: null }, ariaDescribedBy: { classPropertyName: "ariaDescribedBy", publicName: "aria-describedby", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelledBy: { classPropertyName: "ariaLabelledBy", publicName: "aria-labelledby", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, itemDisabled: { classPropertyName: "itemDisabled", publicName: "itemDisabled", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, popupClass: { classPropertyName: "popupClass", publicName: "popupClass", isSignal: true, isRequired: false, transformFunction: null }, popupHeight: { classPropertyName: "popupHeight", publicName: "popupHeight", isSignal: true, isRequired: false, transformFunction: null }, popupWidth: { classPropertyName: "popupWidth", publicName: "popupWidth", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, rounded: { classPropertyName: "rounded", publicName: "rounded", isSignal: true, isRequired: false, transformFunction: null }, showClearButton: { classPropertyName: "showClearButton", publicName: "showClearButton", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, textField: { classPropertyName: "textField", publicName: "textField", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, valueField: { classPropertyName: "valueField", publicName: "valueField", isSignal: true, isRequired: false, transformFunction: null }, valuePrimitive: { classPropertyName: "valuePrimitive", publicName: "valuePrimitive", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { close: "close", closed: "closed", disabled: "disabledChange", open: "open", opened: "opened", touch: "touch", value: "valueChange", valueAdd: "valueAdd" }, host: { properties: { "attr.aria-disabled": "disabled() ? true : undefined", "attr.aria-invalid": "invalidState() ? true : undefined", "attr.aria-readonly": "readonly() ? true : undefined", "attr.aria-required": "required() ? true : undefined", "attr.data-disabled": "disabled() || null", "attr.data-invalid": "invalidState() || null", "attr.data-readonly": "readonly() || null", "attr.tabindex": "-1", "class": "baseClass()" } }, providers: [
694
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.6", type: ComboBoxComponent, isStandalone: true, selector: "mona-combo-box", inputs: { allowCustomValue: { classPropertyName: "allowCustomValue", publicName: "allowCustomValue", isSignal: true, isRequired: false, transformFunction: null }, ariaDescribedBy: { classPropertyName: "ariaDescribedBy", publicName: "aria-describedby", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "aria-label", isSignal: true, isRequired: false, transformFunction: null }, ariaLabelledBy: { classPropertyName: "ariaLabelledBy", publicName: "aria-labelledby", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, inputId: { classPropertyName: "inputId", publicName: "inputId", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, itemDisabled: { classPropertyName: "itemDisabled", publicName: "itemDisabled", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, popupClass: { classPropertyName: "popupClass", publicName: "popupClass", isSignal: true, isRequired: false, transformFunction: null }, popupHeight: { classPropertyName: "popupHeight", publicName: "popupHeight", isSignal: true, isRequired: false, transformFunction: null }, popupWidth: { classPropertyName: "popupWidth", publicName: "popupWidth", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, rounded: { classPropertyName: "rounded", publicName: "rounded", isSignal: true, isRequired: false, transformFunction: null }, showClearButton: { classPropertyName: "showClearButton", publicName: "showClearButton", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, textField: { classPropertyName: "textField", publicName: "textField", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, valueField: { classPropertyName: "valueField", publicName: "valueField", isSignal: true, isRequired: false, transformFunction: null }, valuePrimitive: { classPropertyName: "valuePrimitive", publicName: "valuePrimitive", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { close: "close", closed: "closed", open: "open", opened: "opened", touch: "touch", value: "valueChange", valueAdd: "valueAdd" }, host: { listeners: { "focusout": "onFocusOut($event)", "pointerdown": "onHostPointerDown($event)" }, properties: { "attr.aria-describedby": "null", "attr.aria-label": "null", "attr.aria-labelledby": "null", "attr.data-disabled": "disabled() || null", "attr.data-invalid": "invalidState() || null", "attr.data-readonly": "readonly() || null", "attr.tabindex": "-1", "class": "baseClass()" } }, providers: [
649
695
  ListService,
650
696
  DropdownService,
651
697
  DropdownListService,
652
698
  {
653
- provide: DropdownDataInputToken,
699
+ provide: DROPDOWN_DATA_INPUT_TOKEN,
654
700
  useExisting: forwardRef(() => ComboBoxComponent),
655
701
  multi: false
656
702
  },
@@ -659,7 +705,7 @@ class ComboBoxComponent {
659
705
  useExisting: forwardRef(() => ComboBoxComponent),
660
706
  multi: false
661
707
  }
662
- ], queries: [{ propertyName: "footerTemplate", first: true, predicate: DropdownFooterTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "groupHeaderTemplate", first: true, predicate: DropdownGroupHeaderTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "headerTemplate", first: true, predicate: DropdownHeaderTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "itemTemplate", first: true, predicate: DropdownItemTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "noDataTemplate", first: true, predicate: DropdownNoDataTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "prefixTemplate", first: true, predicate: DropdownPrefixTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }], viewQueries: [{ propertyName: "popupTemplate", first: true, predicate: ["popupTemplate"], descendants: true, isSignal: true }], hostDirectives: [{ directive: i1.DropdownDataHandlerDirective }, { directive: i1.DropdownListPopupHandlerDirective }], ngImport: i0, template: "@if (prefixTemplate()) {\n <span [class]=\"affixClass()\">\n <ng-container [ngTemplateOutlet]=\"prefixTemplate()\"></ng-container>\n </span>\n}\n\n<input\n monaTextBox\n [class]=\"inputClass()\"\n [disabled]=\"disabled()\"\n [id]=\"id\"\n [placeholder]=\"placeholder()\"\n [readonly]=\"readonly()\"\n [required]=\"required()\"\n [ngModel]=\"comboBoxValue()\"\n [attr.aria-activedescendant]=\"activeDescendant()\"\n [attr.aria-autocomplete]=\"'list'\"\n [attr.aria-busy]=\"loading()\"\n [attr.aria-controls]=\"listId\"\n [attr.aria-describedby]=\"ariaDescribedBy() || undefined\"\n [attr.aria-expanded]=\"expanded()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-invalid]=\"invalidState() || undefined\"\n [attr.aria-label]=\"effectiveAriaLabel() || undefined\"\n [attr.aria-labelledby]=\"ariaLabelledBy() || undefined\"\n role=\"combobox\"\n (ngModelChange)=\"comboBoxValue$.next($event)\"\n (blur)=\"onInputBlur($event)\" />\n\n@if (showClearButton() && selectedListItem() && !loading()) {\n <mona-indicator-icon\n preset=\"clear\"\n [interactive]=\"true\"\n [label]=\"messages().clear\"\n (activate)=\"onValueClear($event)\"></mona-indicator-icon>\n}\n\n@if (loading()) {\n <mona-indicator-icon preset=\"loading\" [size]=\"12\"></mona-indicator-icon>\n}\n\n<mona-indicator-icon preset=\"dropdown\" [size]=\"18\"></mona-indicator-icon>\n\n<span monaDropdownLiveRegion></span>\n\n<ng-template #popupTemplate>\n <div [class]=\"listPopupClass()\" (pointerdown)=\"onPopupPointerDown($event)\">\n <mona-list (itemSelect)=\"onItemSelect($event)\" [focusOnMount]=\"false\" [size]=\"size()\" [listId]=\"listId\">\n @if (footerTemplate()) {\n <ng-template monaListFooterTemplate>\n <ng-container [ngTemplateOutlet]=\"footerTemplate()\"></ng-container>\n </ng-template>\n }\n @if (groupHeaderTemplate()) {\n <ng-template monaListGroupHeaderTemplate let-group>\n <ng-container\n [ngTemplateOutlet]=\"groupHeaderTemplate()\"\n [ngTemplateOutletContext]=\"{ $implicit: group }\"></ng-container>\n </ng-template>\n }\n @if (headerTemplate()) {\n <ng-template monaListHeaderTemplate>\n <ng-container [ngTemplateOutlet]=\"headerTemplate()\"></ng-container>\n </ng-template>\n }\n @if (itemTemplate()) {\n <ng-template monaListItemTemplate let-dataItem>\n <ng-container\n [ngTemplateOutlet]=\"itemTemplate()\"\n [ngTemplateOutletContext]=\"{ $implicit: dataItem }\"></ng-container>\n </ng-template>\n }\n @if (noDataTemplate()) {\n <ng-template monaListNoDataTemplate>\n <ng-container [ngTemplateOutlet]=\"noDataTemplate()\"></ng-container>\n </ng-template>\n }\n </mona-list>\n </div>\n</ng-template>\n", dependencies: [{ kind: "directive", type: TextBoxDirective, selector: "input[monaTextBox]", inputs: ["invalid", "rounded", "size", "touched", "class"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: ListComponent, selector: "mona-list", inputs: ["ariaLabel", "data", "focusOnMount", "height", "listClass", "listId", "listItemClass", "listItemStyle", "listStyle", "maxHeight", "maxWidth", "size", "textField", "width"], outputs: ["itemSelect"] }, { kind: "directive", type: ListGroupHeaderTemplateDirective, selector: "ng-template[monaListGroupHeaderTemplate]" }, { kind: "directive", type: ListFooterTemplateDirective, selector: "ng-template[monaListFooterTemplate]" }, { kind: "directive", type: ListHeaderTemplateDirective, selector: "ng-template[monaListHeaderTemplate]" }, { kind: "directive", type: ListNoDataTemplateDirective, selector: "ng-template[monaListNoDataTemplate]" }, { kind: "directive", type: ListItemTemplateDirective, selector: "ng-template[monaListItemTemplate]" }, { kind: "directive", type: DropdownLiveRegionDirective, selector: "span[monaDropdownLiveRegion]" }, { kind: "component", type: IndicatorIconComponent, selector: "mona-indicator-icon", inputs: ["icon", "interactive", "label", "preset", "size", "class"], outputs: ["activate"] }] }); }
708
+ ], queries: [{ propertyName: "footerTemplate", first: true, predicate: DropdownFooterTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "groupHeaderTemplate", first: true, predicate: DropdownGroupHeaderTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "headerTemplate", first: true, predicate: DropdownHeaderTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "itemTemplate", first: true, predicate: DropdownItemTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "noDataTemplate", first: true, predicate: DropdownNoDataTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "prefixTemplate", first: true, predicate: DropdownPrefixTemplateDirective, descendants: true, read: TemplateRef, isSignal: true }], viewQueries: [{ propertyName: "popupTemplate", first: true, predicate: ["popupTemplate"], descendants: true, isSignal: true }], hostDirectives: [{ directive: i1.DropdownDataHandlerDirective }, { directive: i1.DropdownListPopupHandlerDirective }, { directive: i1.DropdownLiveRegionDirective }], ngImport: i0, template: "@if (prefixTemplate()) {\n <span [class]=\"affixClass()\">\n <ng-container [ngTemplateOutlet]=\"prefixTemplate()\"></ng-container>\n </span>\n}\n\n<input\n monaTextBox\n [class]=\"inputClass()\"\n [disabled]=\"disabled()\"\n [id]=\"inputId()\"\n [placeholder]=\"placeholder()\"\n [readonly]=\"readonly()\"\n [required]=\"required()\"\n [value]=\"comboBoxValue()\"\n [attr.aria-activedescendant]=\"activeDescendant()\"\n [attr.aria-autocomplete]=\"'list'\"\n [attr.aria-busy]=\"loading()\"\n [attr.aria-controls]=\"listId\"\n [attr.aria-describedby]=\"ariaDescribedBy() || undefined\"\n [attr.aria-expanded]=\"expanded()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-invalid]=\"invalidState() || undefined\"\n [attr.aria-label]=\"ariaLabel() || undefined\"\n [attr.aria-labelledby]=\"ariaLabelledBy() || undefined\"\n role=\"combobox\"\n (input)=\"onInput(textInput.value)\"\n #textInput />\n\n@if (clearButtonVisible()) {\n <mona-indicator-icon\n preset=\"clear\"\n [interactive]=\"true\"\n [label]=\"messages().clear\"\n (activate)=\"onValueClear($event)\"></mona-indicator-icon>\n}\n\n@if (loading()) {\n <mona-indicator-icon preset=\"loading\" [size]=\"12\"></mona-indicator-icon>\n}\n\n<mona-indicator-icon preset=\"dropdown\" [size]=\"18\"></mona-indicator-icon>\n\n<ng-template #popupTemplate>\n <div [class]=\"listPopupClass()\" (pointerdown)=\"onPopupPointerDown($event)\">\n <mona-list (itemSelect)=\"onItemSelect($event)\" [focusOnMount]=\"false\" [size]=\"size()\" [listId]=\"listId\">\n @if (footerTemplate()) {\n <ng-template monaListFooterTemplate>\n <ng-container [ngTemplateOutlet]=\"footerTemplate()\"></ng-container>\n </ng-template>\n }\n @if (groupHeaderTemplate()) {\n <ng-template monaListGroupHeaderTemplate let-group>\n <ng-container\n [ngTemplateOutlet]=\"groupHeaderTemplate()\"\n [ngTemplateOutletContext]=\"{ $implicit: group }\"></ng-container>\n </ng-template>\n }\n @if (headerTemplate()) {\n <ng-template monaListHeaderTemplate>\n <ng-container [ngTemplateOutlet]=\"headerTemplate()\"></ng-container>\n </ng-template>\n }\n @if (itemTemplate()) {\n <ng-template monaListItemTemplate let-dataItem>\n <ng-container\n [ngTemplateOutlet]=\"itemTemplate()\"\n [ngTemplateOutletContext]=\"{ $implicit: dataItem }\"></ng-container>\n </ng-template>\n }\n @if (noDataTemplate()) {\n <ng-template monaListNoDataTemplate>\n <ng-container [ngTemplateOutlet]=\"noDataTemplate()\"></ng-container>\n </ng-template>\n }\n </mona-list>\n </div>\n</ng-template>\n", dependencies: [{ kind: "directive", type: TextBoxDirective, selector: "input[monaTextBox]", inputs: ["invalid", "rounded", "size", "touched", "class"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: ListComponent, selector: "mona-list", inputs: ["ariaLabel", "data", "focusOnMount", "height", "listClass", "listId", "listItemClass", "listItemStyle", "listStyle", "maxHeight", "maxWidth", "size", "textField", "width"], outputs: ["itemSelect"] }, { kind: "directive", type: ListGroupHeaderTemplateDirective, selector: "ng-template[monaListGroupHeaderTemplate]" }, { kind: "directive", type: ListFooterTemplateDirective, selector: "ng-template[monaListFooterTemplate]" }, { kind: "directive", type: ListHeaderTemplateDirective, selector: "ng-template[monaListHeaderTemplate]" }, { kind: "directive", type: ListNoDataTemplateDirective, selector: "ng-template[monaListNoDataTemplate]" }, { kind: "directive", type: ListItemTemplateDirective, selector: "ng-template[monaListItemTemplate]" }, { kind: "component", type: IndicatorIconComponent, selector: "mona-indicator-icon", inputs: ["icon", "interactive", "label", "preset", "size", "class"], outputs: ["activate"] }] }); }
663
709
  }
664
710
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: ComboBoxComponent, decorators: [{
665
711
  type: Component,
@@ -668,7 +714,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImpor
668
714
  DropdownService,
669
715
  DropdownListService,
670
716
  {
671
- provide: DropdownDataInputToken,
717
+ provide: DROPDOWN_DATA_INPUT_TOKEN,
672
718
  useExisting: forwardRef(() => ComboBoxComponent),
673
719
  multi: false
674
720
  },
@@ -679,7 +725,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImpor
679
725
  }
680
726
  ], imports: [
681
727
  TextBoxDirective,
682
- FormsModule,
683
728
  NgTemplateOutlet,
684
729
  ListComponent,
685
730
  ListGroupHeaderTemplateDirective,
@@ -687,20 +732,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImpor
687
732
  ListHeaderTemplateDirective,
688
733
  ListNoDataTemplateDirective,
689
734
  ListItemTemplateDirective,
690
- DropdownLiveRegionDirective,
691
735
  IndicatorIconComponent
692
- ], hostDirectives: [DropdownDataHandlerDirective, DropdownListPopupHandlerDirective], host: {
693
- "[attr.aria-disabled]": "disabled() ? true : undefined",
694
- "[attr.aria-invalid]": "invalidState() ? true : undefined",
695
- "[attr.aria-readonly]": "readonly() ? true : undefined",
696
- "[attr.aria-required]": "required() ? true : undefined",
736
+ ], hostDirectives: [DropdownDataHandlerDirective, DropdownListPopupHandlerDirective, DropdownLiveRegionDirective], host: {
737
+ // The host has no role: the inner input is the combobox and carries the name, description and state. A static
738
+ // aria-* attribute written on <mona-combo-box> would otherwise stay on the host as well.
739
+ "[attr.aria-describedby]": "null",
740
+ "[attr.aria-label]": "null",
741
+ "[attr.aria-labelledby]": "null",
697
742
  "[attr.data-disabled]": "disabled() || null",
698
743
  "[attr.data-invalid]": "invalidState() || null",
699
744
  "[attr.data-readonly]": "readonly() || null",
700
745
  "[attr.tabindex]": "-1",
701
- "[class]": "baseClass()"
702
- }, template: "@if (prefixTemplate()) {\n <span [class]=\"affixClass()\">\n <ng-container [ngTemplateOutlet]=\"prefixTemplate()\"></ng-container>\n </span>\n}\n\n<input\n monaTextBox\n [class]=\"inputClass()\"\n [disabled]=\"disabled()\"\n [id]=\"id\"\n [placeholder]=\"placeholder()\"\n [readonly]=\"readonly()\"\n [required]=\"required()\"\n [ngModel]=\"comboBoxValue()\"\n [attr.aria-activedescendant]=\"activeDescendant()\"\n [attr.aria-autocomplete]=\"'list'\"\n [attr.aria-busy]=\"loading()\"\n [attr.aria-controls]=\"listId\"\n [attr.aria-describedby]=\"ariaDescribedBy() || undefined\"\n [attr.aria-expanded]=\"expanded()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-invalid]=\"invalidState() || undefined\"\n [attr.aria-label]=\"effectiveAriaLabel() || undefined\"\n [attr.aria-labelledby]=\"ariaLabelledBy() || undefined\"\n role=\"combobox\"\n (ngModelChange)=\"comboBoxValue$.next($event)\"\n (blur)=\"onInputBlur($event)\" />\n\n@if (showClearButton() && selectedListItem() && !loading()) {\n <mona-indicator-icon\n preset=\"clear\"\n [interactive]=\"true\"\n [label]=\"messages().clear\"\n (activate)=\"onValueClear($event)\"></mona-indicator-icon>\n}\n\n@if (loading()) {\n <mona-indicator-icon preset=\"loading\" [size]=\"12\"></mona-indicator-icon>\n}\n\n<mona-indicator-icon preset=\"dropdown\" [size]=\"18\"></mona-indicator-icon>\n\n<span monaDropdownLiveRegion></span>\n\n<ng-template #popupTemplate>\n <div [class]=\"listPopupClass()\" (pointerdown)=\"onPopupPointerDown($event)\">\n <mona-list (itemSelect)=\"onItemSelect($event)\" [focusOnMount]=\"false\" [size]=\"size()\" [listId]=\"listId\">\n @if (footerTemplate()) {\n <ng-template monaListFooterTemplate>\n <ng-container [ngTemplateOutlet]=\"footerTemplate()\"></ng-container>\n </ng-template>\n }\n @if (groupHeaderTemplate()) {\n <ng-template monaListGroupHeaderTemplate let-group>\n <ng-container\n [ngTemplateOutlet]=\"groupHeaderTemplate()\"\n [ngTemplateOutletContext]=\"{ $implicit: group }\"></ng-container>\n </ng-template>\n }\n @if (headerTemplate()) {\n <ng-template monaListHeaderTemplate>\n <ng-container [ngTemplateOutlet]=\"headerTemplate()\"></ng-container>\n </ng-template>\n }\n @if (itemTemplate()) {\n <ng-template monaListItemTemplate let-dataItem>\n <ng-container\n [ngTemplateOutlet]=\"itemTemplate()\"\n [ngTemplateOutletContext]=\"{ $implicit: dataItem }\"></ng-container>\n </ng-template>\n }\n @if (noDataTemplate()) {\n <ng-template monaListNoDataTemplate>\n <ng-container [ngTemplateOutlet]=\"noDataTemplate()\"></ng-container>\n </ng-template>\n }\n </mona-list>\n </div>\n</ng-template>\n" }]
703
- }], ctorParameters: () => [], propDecorators: { footerTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => DropdownFooterTemplateDirective), { ...{ read: TemplateRef }, isSignal: true }] }], groupHeaderTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => DropdownGroupHeaderTemplateDirective), { ...{ read: TemplateRef }, isSignal: true }] }], headerTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => DropdownHeaderTemplateDirective), { ...{ read: TemplateRef }, isSignal: true }] }], itemTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => DropdownItemTemplateDirective), { ...{ read: TemplateRef }, isSignal: true }] }], noDataTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => DropdownNoDataTemplateDirective), { ...{ read: TemplateRef }, isSignal: true }] }], popupTemplate: [{ type: i0.ViewChild, args: ["popupTemplate", { isSignal: true }] }], prefixTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => DropdownPrefixTemplateDirective), { ...{ read: TemplateRef }, isSignal: true }] }], allowCustomValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowCustomValue", required: false }] }], ariaDescribedBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-describedby", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], ariaLabelledBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-labelledby", required: false }] }], close: [{ type: i0.Output, args: ["close"] }], closed: [{ type: i0.Output, args: ["closed"] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], itemDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemDisabled", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], open: [{ type: i0.Output, args: ["open"] }], opened: [{ type: i0.Output, args: ["opened"] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], popupClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "popupClass", required: false }] }], popupHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "popupHeight", required: false }] }], popupWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "popupWidth", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], rounded: [{ type: i0.Input, args: [{ isSignal: true, alias: "rounded", required: false }] }], showClearButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showClearButton", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], textField: [{ type: i0.Input, args: [{ isSignal: true, alias: "textField", required: false }] }], touch: [{ type: i0.Output, args: ["touch"] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], valueAdd: [{ type: i0.Output, args: ["valueAdd"] }], valueField: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueField", required: false }] }], valuePrimitive: [{ type: i0.Input, args: [{ isSignal: true, alias: "valuePrimitive", required: false }] }] } });
746
+ "[class]": "baseClass()",
747
+ "(focusout)": "onFocusOut($event)",
748
+ "(pointerdown)": "onHostPointerDown($event)"
749
+ }, template: "@if (prefixTemplate()) {\n <span [class]=\"affixClass()\">\n <ng-container [ngTemplateOutlet]=\"prefixTemplate()\"></ng-container>\n </span>\n}\n\n<input\n monaTextBox\n [class]=\"inputClass()\"\n [disabled]=\"disabled()\"\n [id]=\"inputId()\"\n [placeholder]=\"placeholder()\"\n [readonly]=\"readonly()\"\n [required]=\"required()\"\n [value]=\"comboBoxValue()\"\n [attr.aria-activedescendant]=\"activeDescendant()\"\n [attr.aria-autocomplete]=\"'list'\"\n [attr.aria-busy]=\"loading()\"\n [attr.aria-controls]=\"listId\"\n [attr.aria-describedby]=\"ariaDescribedBy() || undefined\"\n [attr.aria-expanded]=\"expanded()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-invalid]=\"invalidState() || undefined\"\n [attr.aria-label]=\"ariaLabel() || undefined\"\n [attr.aria-labelledby]=\"ariaLabelledBy() || undefined\"\n role=\"combobox\"\n (input)=\"onInput(textInput.value)\"\n #textInput />\n\n@if (clearButtonVisible()) {\n <mona-indicator-icon\n preset=\"clear\"\n [interactive]=\"true\"\n [label]=\"messages().clear\"\n (activate)=\"onValueClear($event)\"></mona-indicator-icon>\n}\n\n@if (loading()) {\n <mona-indicator-icon preset=\"loading\" [size]=\"12\"></mona-indicator-icon>\n}\n\n<mona-indicator-icon preset=\"dropdown\" [size]=\"18\"></mona-indicator-icon>\n\n<ng-template #popupTemplate>\n <div [class]=\"listPopupClass()\" (pointerdown)=\"onPopupPointerDown($event)\">\n <mona-list (itemSelect)=\"onItemSelect($event)\" [focusOnMount]=\"false\" [size]=\"size()\" [listId]=\"listId\">\n @if (footerTemplate()) {\n <ng-template monaListFooterTemplate>\n <ng-container [ngTemplateOutlet]=\"footerTemplate()\"></ng-container>\n </ng-template>\n }\n @if (groupHeaderTemplate()) {\n <ng-template monaListGroupHeaderTemplate let-group>\n <ng-container\n [ngTemplateOutlet]=\"groupHeaderTemplate()\"\n [ngTemplateOutletContext]=\"{ $implicit: group }\"></ng-container>\n </ng-template>\n }\n @if (headerTemplate()) {\n <ng-template monaListHeaderTemplate>\n <ng-container [ngTemplateOutlet]=\"headerTemplate()\"></ng-container>\n </ng-template>\n }\n @if (itemTemplate()) {\n <ng-template monaListItemTemplate let-dataItem>\n <ng-container\n [ngTemplateOutlet]=\"itemTemplate()\"\n [ngTemplateOutletContext]=\"{ $implicit: dataItem }\"></ng-container>\n </ng-template>\n }\n @if (noDataTemplate()) {\n <ng-template monaListNoDataTemplate>\n <ng-container [ngTemplateOutlet]=\"noDataTemplate()\"></ng-container>\n </ng-template>\n }\n </mona-list>\n </div>\n</ng-template>\n" }]
750
+ }], ctorParameters: () => [], propDecorators: { footerTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => DropdownFooterTemplateDirective), { ...{ read: TemplateRef }, isSignal: true }] }], groupHeaderTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => DropdownGroupHeaderTemplateDirective), { ...{ read: TemplateRef }, isSignal: true }] }], headerTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => DropdownHeaderTemplateDirective), { ...{ read: TemplateRef }, isSignal: true }] }], itemTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => DropdownItemTemplateDirective), { ...{ read: TemplateRef }, isSignal: true }] }], noDataTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => DropdownNoDataTemplateDirective), { ...{ read: TemplateRef }, isSignal: true }] }], popupTemplate: [{ type: i0.ViewChild, args: ["popupTemplate", { isSignal: true }] }], prefixTemplate: [{ type: i0.ContentChild, args: [i0.forwardRef(() => DropdownPrefixTemplateDirective), { ...{ read: TemplateRef }, isSignal: true }] }], allowCustomValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowCustomValue", required: false }] }], ariaDescribedBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-describedby", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-label", required: false }] }], ariaLabelledBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-labelledby", required: false }] }], close: [{ type: i0.Output, args: ["close"] }], closed: [{ type: i0.Output, args: ["closed"] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], inputId: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputId", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], itemDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemDisabled", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], open: [{ type: i0.Output, args: ["open"] }], opened: [{ type: i0.Output, args: ["opened"] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], popupClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "popupClass", required: false }] }], popupHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "popupHeight", required: false }] }], popupWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "popupWidth", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], rounded: [{ type: i0.Input, args: [{ isSignal: true, alias: "rounded", required: false }] }], showClearButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showClearButton", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], textField: [{ type: i0.Input, args: [{ isSignal: true, alias: "textField", required: false }] }], touch: [{ type: i0.Output, args: ["touch"] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], valueAdd: [{ type: i0.Output, args: ["valueAdd"] }], valueField: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueField", required: false }] }], valuePrimitive: [{ type: i0.Input, args: [{ isSignal: true, alias: "valuePrimitive", required: false }] }] } });
704
751
 
705
752
  /*
706
753
  * Public API Surface of @nanahoshi/mona-ui/combo-box