@radix-ng/primitives 1.0.0-beta.0 → 1.0.0-beta.2

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 (117) hide show
  1. package/fesm2022/radix-ng-primitives-accordion.mjs +2 -2
  2. package/fesm2022/radix-ng-primitives-accordion.mjs.map +1 -1
  3. package/fesm2022/radix-ng-primitives-calendar.mjs +109 -84
  4. package/fesm2022/radix-ng-primitives-calendar.mjs.map +1 -1
  5. package/fesm2022/radix-ng-primitives-checkbox.mjs +2 -2
  6. package/fesm2022/radix-ng-primitives-checkbox.mjs.map +1 -1
  7. package/fesm2022/radix-ng-primitives-collapsible.mjs +1 -1
  8. package/fesm2022/radix-ng-primitives-collapsible.mjs.map +1 -1
  9. package/fesm2022/radix-ng-primitives-combobox.mjs +1923 -0
  10. package/fesm2022/radix-ng-primitives-combobox.mjs.map +1 -0
  11. package/fesm2022/radix-ng-primitives-context-menu.mjs +1 -1
  12. package/fesm2022/radix-ng-primitives-context-menu.mjs.map +1 -1
  13. package/fesm2022/radix-ng-primitives-core.mjs +591 -470
  14. package/fesm2022/radix-ng-primitives-core.mjs.map +1 -1
  15. package/fesm2022/radix-ng-primitives-cropper.mjs +287 -308
  16. package/fesm2022/radix-ng-primitives-cropper.mjs.map +1 -1
  17. package/fesm2022/radix-ng-primitives-date-field.mjs +66 -15
  18. package/fesm2022/radix-ng-primitives-date-field.mjs.map +1 -1
  19. package/fesm2022/radix-ng-primitives-dialog.mjs +1 -1
  20. package/fesm2022/radix-ng-primitives-dialog.mjs.map +1 -1
  21. package/fesm2022/radix-ng-primitives-drawer.mjs +7 -106
  22. package/fesm2022/radix-ng-primitives-drawer.mjs.map +1 -1
  23. package/fesm2022/radix-ng-primitives-editable.mjs +305 -24
  24. package/fesm2022/radix-ng-primitives-editable.mjs.map +1 -1
  25. package/fesm2022/radix-ng-primitives-field.mjs +86 -6
  26. package/fesm2022/radix-ng-primitives-field.mjs.map +1 -1
  27. package/fesm2022/radix-ng-primitives-fieldset.mjs +1 -1
  28. package/fesm2022/radix-ng-primitives-fieldset.mjs.map +1 -1
  29. package/fesm2022/radix-ng-primitives-focus-scope.mjs +1 -1
  30. package/fesm2022/radix-ng-primitives-focus-scope.mjs.map +1 -1
  31. package/fesm2022/radix-ng-primitives-form.mjs +207 -0
  32. package/fesm2022/radix-ng-primitives-form.mjs.map +1 -0
  33. package/fesm2022/radix-ng-primitives-input.mjs +85 -4
  34. package/fesm2022/radix-ng-primitives-input.mjs.map +1 -1
  35. package/fesm2022/radix-ng-primitives-menu.mjs +413 -5
  36. package/fesm2022/radix-ng-primitives-menu.mjs.map +1 -1
  37. package/fesm2022/radix-ng-primitives-menubar.mjs +1 -1
  38. package/fesm2022/radix-ng-primitives-menubar.mjs.map +1 -1
  39. package/fesm2022/radix-ng-primitives-meter.mjs +1 -1
  40. package/fesm2022/radix-ng-primitives-meter.mjs.map +1 -1
  41. package/fesm2022/radix-ng-primitives-navigation-menu.mjs +1 -1
  42. package/fesm2022/radix-ng-primitives-navigation-menu.mjs.map +1 -1
  43. package/fesm2022/radix-ng-primitives-number-field.mjs +2 -2
  44. package/fesm2022/radix-ng-primitives-number-field.mjs.map +1 -1
  45. package/fesm2022/radix-ng-primitives-popover.mjs +1 -1
  46. package/fesm2022/radix-ng-primitives-popover.mjs.map +1 -1
  47. package/fesm2022/radix-ng-primitives-popper.mjs +22 -5
  48. package/fesm2022/radix-ng-primitives-popper.mjs.map +1 -1
  49. package/fesm2022/radix-ng-primitives-portal.mjs.map +1 -1
  50. package/fesm2022/radix-ng-primitives-preview-card.mjs +1 -1
  51. package/fesm2022/radix-ng-primitives-preview-card.mjs.map +1 -1
  52. package/fesm2022/radix-ng-primitives-progress.mjs +1 -1
  53. package/fesm2022/radix-ng-primitives-progress.mjs.map +1 -1
  54. package/fesm2022/radix-ng-primitives-roving-focus.mjs +1 -1
  55. package/fesm2022/radix-ng-primitives-roving-focus.mjs.map +1 -1
  56. package/fesm2022/radix-ng-primitives-scroll-area.mjs +923 -0
  57. package/fesm2022/radix-ng-primitives-scroll-area.mjs.map +1 -0
  58. package/fesm2022/radix-ng-primitives-select.mjs +421 -224
  59. package/fesm2022/radix-ng-primitives-select.mjs.map +1 -1
  60. package/fesm2022/radix-ng-primitives-slider.mjs +1 -1
  61. package/fesm2022/radix-ng-primitives-slider.mjs.map +1 -1
  62. package/fesm2022/radix-ng-primitives-stepper.mjs.map +1 -1
  63. package/fesm2022/radix-ng-primitives-switch.mjs +3 -2
  64. package/fesm2022/radix-ng-primitives-switch.mjs.map +1 -1
  65. package/fesm2022/radix-ng-primitives-tabs.mjs +12 -3
  66. package/fesm2022/radix-ng-primitives-tabs.mjs.map +1 -1
  67. package/fesm2022/radix-ng-primitives-time-field.mjs +27 -3
  68. package/fesm2022/radix-ng-primitives-time-field.mjs.map +1 -1
  69. package/fesm2022/radix-ng-primitives-toast.mjs +839 -0
  70. package/fesm2022/radix-ng-primitives-toast.mjs.map +1 -0
  71. package/fesm2022/radix-ng-primitives-toggle-group.mjs +1 -1
  72. package/fesm2022/radix-ng-primitives-toggle-group.mjs.map +1 -1
  73. package/fesm2022/radix-ng-primitives-toolbar.mjs +2 -2
  74. package/fesm2022/radix-ng-primitives-toolbar.mjs.map +1 -1
  75. package/fesm2022/radix-ng-primitives-tooltip.mjs +11 -3
  76. package/fesm2022/radix-ng-primitives-tooltip.mjs.map +1 -1
  77. package/package.json +18 -2
  78. package/schematics/ng-add/index.js +57 -0
  79. package/schematics/ng-add/index.js.map +1 -1
  80. package/schematics/ng-add/schema.d.ts +1 -0
  81. package/schematics/ng-add/schema.json +6 -0
  82. package/types/radix-ng-primitives-accordion.d.ts +3 -2
  83. package/types/radix-ng-primitives-calendar.d.ts +38 -18
  84. package/types/radix-ng-primitives-checkbox.d.ts +5 -5
  85. package/types/radix-ng-primitives-collapsible.d.ts +2 -1
  86. package/types/radix-ng-primitives-combobox.d.ts +1265 -0
  87. package/types/radix-ng-primitives-context-menu.d.ts +3 -2
  88. package/types/radix-ng-primitives-core.d.ts +187 -56
  89. package/types/radix-ng-primitives-cropper.d.ts +89 -56
  90. package/types/radix-ng-primitives-date-field.d.ts +11 -5
  91. package/types/radix-ng-primitives-dialog.d.ts +2 -1
  92. package/types/radix-ng-primitives-drawer.d.ts +5 -27
  93. package/types/radix-ng-primitives-editable.d.ts +90 -13
  94. package/types/radix-ng-primitives-field.d.ts +74 -4
  95. package/types/radix-ng-primitives-fieldset.d.ts +3 -2
  96. package/types/radix-ng-primitives-focus-scope.d.ts +2 -1
  97. package/types/radix-ng-primitives-form.d.ts +124 -0
  98. package/types/radix-ng-primitives-input.d.ts +75 -5
  99. package/types/radix-ng-primitives-menu.d.ts +16 -4
  100. package/types/radix-ng-primitives-menubar.d.ts +2 -1
  101. package/types/radix-ng-primitives-meter.d.ts +3 -2
  102. package/types/radix-ng-primitives-navigation-menu.d.ts +1 -1
  103. package/types/radix-ng-primitives-number-field.d.ts +6 -6
  104. package/types/radix-ng-primitives-popover.d.ts +2 -1
  105. package/types/radix-ng-primitives-popper.d.ts +19 -2
  106. package/types/radix-ng-primitives-preview-card.d.ts +1 -1
  107. package/types/radix-ng-primitives-progress.d.ts +3 -2
  108. package/types/radix-ng-primitives-roving-focus.d.ts +4 -3
  109. package/types/radix-ng-primitives-scroll-area.d.ts +253 -0
  110. package/types/radix-ng-primitives-select.d.ts +296 -136
  111. package/types/radix-ng-primitives-slider.d.ts +1 -1
  112. package/types/radix-ng-primitives-switch.d.ts +1 -1
  113. package/types/radix-ng-primitives-tabs.d.ts +1 -1
  114. package/types/radix-ng-primitives-toast.d.ts +378 -0
  115. package/types/radix-ng-primitives-toggle-group.d.ts +2 -1
  116. package/types/radix-ng-primitives-toolbar.d.ts +3 -2
  117. package/types/radix-ng-primitives-tooltip.d.ts +3 -2
@@ -0,0 +1,1923 @@
1
+ import * as i0 from '@angular/core';
2
+ import { Directive, inject, Injector, model, input, booleanAttribute, computed, numberAttribute, output, signal, effect, untracked, isDevMode, ElementRef, DestroyRef, afterNextRender, NgModule } from '@angular/core';
3
+ import * as i1 from '@radix-ng/primitives/popper';
4
+ import { RdxPopperAnchor, RdxPopperArrow, RdxPopper, injectPopperContentWrapperContext, RdxPopperContent, RdxPopperContentWrapper } from '@radix-ng/primitives/popper';
5
+ import { NG_VALUE_ACCESSOR } from '@angular/forms';
6
+ import { createContext, useTransitionStatus, injectId, useFilter, useListHighlight, isItemEqualToValue, isNullish, itemToStringLabel, useScrollLock } from '@radix-ng/primitives/core';
7
+ import * as i1$1 from '@radix-ng/primitives/dismissable-layer';
8
+ import { RdxDismissableLayerBranch, RdxDismissableLayer, provideRdxDismissableLayerConfig } from '@radix-ng/primitives/dismissable-layer';
9
+ import { injectFieldRootContext } from '@radix-ng/primitives/field';
10
+ import * as i1$2 from '@radix-ng/primitives/portal';
11
+ import { RdxPortal } from '@radix-ng/primitives/portal';
12
+ import * as i1$3 from '@radix-ng/primitives/presence';
13
+ import { provideRdxPresenceContext, RdxPresenceDirective } from '@radix-ng/primitives/presence';
14
+
15
+ /**
16
+ * Optional positioning anchor for the popup. Put it on the element the popup should align to — for
17
+ * example a control that wraps chips + input in `multiple` mode. When present it takes precedence
18
+ * over the input (the popper resolves the first `RdxPopperAnchor` in DOM order, and this part sits
19
+ * above the input). When absent, the input itself is the anchor, which is ideal when the input fills
20
+ * the control.
21
+ *
22
+ * @group Components
23
+ */
24
+ class RdxComboboxAnchor {
25
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxAnchor, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
26
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxComboboxAnchor, isStandalone: true, selector: "[rdxComboboxAnchor]", exportAs: ["rdxComboboxAnchor"], hostDirectives: [{ directive: i1.RdxPopperAnchor }], ngImport: i0 }); }
27
+ }
28
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxAnchor, decorators: [{
29
+ type: Directive,
30
+ args: [{
31
+ selector: '[rdxComboboxAnchor]',
32
+ exportAs: 'rdxComboboxAnchor',
33
+ hostDirectives: [RdxPopperAnchor]
34
+ }]
35
+ }] });
36
+
37
+ /**
38
+ * An optional arrow that points from the popup to the anchor. Composes the popper arrow, which keeps
39
+ * it aligned as the popup flips sides.
40
+ *
41
+ * @group Components
42
+ */
43
+ class RdxComboboxArrow {
44
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxArrow, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
45
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxComboboxArrow, isStandalone: true, selector: "[rdxComboboxArrow]", exportAs: ["rdxComboboxArrow"], hostDirectives: [{ directive: i1.RdxPopperArrow, inputs: ["width", "width", "height", "height"] }], ngImport: i0 }); }
46
+ }
47
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxArrow, decorators: [{
48
+ type: Directive,
49
+ args: [{
50
+ selector: '[rdxComboboxArrow]',
51
+ exportAs: 'rdxComboboxArrow',
52
+ hostDirectives: [{ directive: RdxPopperArrow, inputs: ['width', 'height'] }]
53
+ }]
54
+ }] });
55
+
56
+ const context = () => {
57
+ const root = inject(RdxComboboxRoot);
58
+ return {
59
+ listId: root.listId,
60
+ labelId: root.labelId,
61
+ setLabelId: (id) => root.labelId.set(id),
62
+ dir: root.dir,
63
+ value: root.value,
64
+ inputValue: root.inputValue,
65
+ open: root.open,
66
+ multiple: root.multiple,
67
+ selectionMode: root.mode,
68
+ disabledState: root.disabledState,
69
+ readonly: root.readonly,
70
+ requiredState: root.requiredState,
71
+ openOnInputClick: root.openOnInputClick,
72
+ modal: root.modal,
73
+ virtualized: root.virtualized,
74
+ filteredItems: root.filteredItems,
75
+ highlightedItem: root.highlightedItem,
76
+ highlightedIndex: root.highlightedIndex.asReadonly(),
77
+ activeId: root.activeId,
78
+ itemId: (index) => root.itemId(index),
79
+ isKeyboardActive: () => root.isKeyboardActive(),
80
+ setKeyboardActive: (value) => root.setKeyboardActive(value),
81
+ transitionStatus: root.transitionStatus,
82
+ registerTransitionElement: root.registerTransitionElement,
83
+ visibleCount: root.visibleCount,
84
+ isVisible: (item) => root.isVisible(item),
85
+ isSelected: (value) => root.isSelected(value),
86
+ registerItem: (item) => root.registerItem(item),
87
+ unregisterItem: (item) => root.unregisterItem(item),
88
+ highlight: root.highlight,
89
+ highlightNext: () => root.highlightNext('keyboard'),
90
+ highlightPrevious: () => root.highlightPrevious('keyboard'),
91
+ highlightFirst: () => root.highlightFirst('keyboard'),
92
+ highlightLast: () => root.highlightLast('keyboard'),
93
+ highlightIndex: (index, reason) => root.highlightIndex(index, reason),
94
+ setHighlight: (item, reason) => root.setHighlight(item, reason),
95
+ clearHighlight: () => root.clearHighlightState(),
96
+ inputElement: root.inputElement.asReadonly(),
97
+ setInputElement: (el) => root.inputElement.set(el),
98
+ registerTrigger: (el) => (root.triggerElement = el),
99
+ focusInput: () => root.focusInput(),
100
+ openPopup: () => root.setOpen(true),
101
+ openForBrowse: () => root.openForBrowse(),
102
+ closePopup: (revert = true) => root.closePopup(revert),
103
+ setInputValue: (value) => root.setInputValue(value),
104
+ openAndHighlight: (edge) => root.openAndHighlight(edge),
105
+ select: (item) => root.handleSelect(item),
106
+ selectIndex: (index) => root.selectIndex(index),
107
+ selectHighlighted: () => root.selectHighlighted(),
108
+ clearSelection: () => root.clearSelection(),
109
+ removeValue: (value) => root.removeValue(value),
110
+ removeLastValue: () => root.removeLastValue(),
111
+ registerChipsNav: (fn) => root.registerChipsNav(fn),
112
+ focusLastChip: () => root.focusLastChip(),
113
+ labelFor: (value) => root.labelFor(value),
114
+ markAsTouched: () => root.markAsTouched()
115
+ };
116
+ };
117
+ const [injectComboboxRootContext, provideComboboxRootContext] = createContext('RdxComboboxRootContext', 'components/combobox');
118
+ /**
119
+ * Root of a Combobox — a filterable select. Owns selection, input text, open state, filtering, and
120
+ * highlight-model navigation, and exposes them to the parts through {@link RdxComboboxRootContext}.
121
+ * Implements `ControlValueAccessor` for forms.
122
+ *
123
+ * @group Components
124
+ */
125
+ class RdxComboboxRoot {
126
+ constructor() {
127
+ this.injector = inject(Injector);
128
+ /** Selected value(s). A single value in single mode, an array in `multiple` mode. */
129
+ this.value = model(null, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
130
+ /** Initial value when uncontrolled. */
131
+ this.defaultValue = input(...(ngDevMode ? [undefined, { debugName: "defaultValue" }] : /* istanbul ignore next */ []));
132
+ /** The text currently in the input. */
133
+ this.inputValue = model('', ...(ngDevMode ? [{ debugName: "inputValue" }] : /* istanbul ignore next */ []));
134
+ /** Whether the popup is open. */
135
+ this.open = model(false, ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
136
+ /** Initial open state when uncontrolled. */
137
+ this.defaultOpen = input(false, { ...(ngDevMode ? { debugName: "defaultOpen" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
138
+ /** Whether multiple values can be selected. Shorthand for `selectionMode="multiple"`. */
139
+ this.multipleInput = input(false, { ...(ngDevMode ? { debugName: "multipleInput" } : /* istanbul ignore next */ {}), alias: 'multiple', transform: booleanAttribute });
140
+ /**
141
+ * Selection behavior. `'single'` / `'multiple'` commit a value; `'none'` filters without
142
+ * selecting (a pure search/command UI). Defaults from the `multiple` shorthand.
143
+ */
144
+ this.selectionMode = input(...(ngDevMode ? [undefined, { debugName: "selectionMode" }] : /* istanbul ignore next */ []));
145
+ /** Resolved selection mode. */
146
+ this.mode = computed(() => this.selectionMode() ?? (this.multipleInput() ? 'multiple' : 'single'), ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
147
+ /** Whether the combobox is in multiple-selection mode. */
148
+ this.multiple = computed(() => this.mode() === 'multiple', ...(ngDevMode ? [{ debugName: "multiple" }] : /* istanbul ignore next */ []));
149
+ /** In `'none'` mode, whether pressing an item fills the input with its label. */
150
+ this.fillInputOnItemPress = input(true, { ...(ngDevMode ? { debugName: "fillInputOnItemPress" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
151
+ /** Text direction. */
152
+ this.dir = input('ltr', ...(ngDevMode ? [{ debugName: "dir" }] : /* istanbul ignore next */ []));
153
+ /** Whether the combobox is disabled. */
154
+ this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
155
+ /** Whether the combobox is read-only. */
156
+ this.readonly = input(false, { ...(ngDevMode ? { debugName: "readonly" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
157
+ /** Whether a value is required (for forms). */
158
+ this.required = input(false, { ...(ngDevMode ? { debugName: "required" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
159
+ /** Whether keyboard navigation wraps at the list boundaries. */
160
+ this.loopFocus = input(true, { ...(ngDevMode ? { debugName: "loopFocus" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
161
+ /**
162
+ * Auto-highlight behavior:
163
+ * - `false` (default): never auto-highlight;
164
+ * - `true` / `'input-change'`: highlight the first match as the query changes;
165
+ * - `'always'`: keep the first navigable item highlighted whenever the popup is open.
166
+ */
167
+ this.autoHighlight = input(false, ...(ngDevMode ? [{ debugName: "autoHighlight" }] : /* istanbul ignore next */ []));
168
+ /** Resolved auto-highlight mode. */
169
+ this.autoHighlightMode = computed(() => {
170
+ const value = this.autoHighlight();
171
+ if (value === 'always') {
172
+ return 'always';
173
+ }
174
+ if (value === true || value === 'input-change') {
175
+ return 'input-change';
176
+ }
177
+ return 'off';
178
+ }, ...(ngDevMode ? [{ debugName: "autoHighlightMode" }] : /* istanbul ignore next */ []));
179
+ /** Whether clicking the input opens the popup. */
180
+ this.openOnInputClick = input(true, { ...(ngDevMode ? { debugName: "openOnInputClick" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
181
+ /** Whether the popup is modal: locks page scroll and makes outside content inert while open. */
182
+ this.modal = input(false, { ...(ngDevMode ? { debugName: "modal" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
183
+ /** Whether selecting an item requests submit of the closest form. */
184
+ this.submitOnItemClick = input(false, { ...(ngDevMode ? { debugName: "submitOnItemClick" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
185
+ /**
186
+ * Filter applied to items against the input query.
187
+ * - `undefined` (default): locale-aware "contains" via {@link useFilter};
188
+ * - a function: custom matching;
189
+ * - `null`: built-in filtering disabled (the consumer controls which items exist).
190
+ */
191
+ this.filter = input(undefined, ...(ngDevMode ? [{ debugName: "filter" }] : /* istanbul ignore next */ []));
192
+ /** Maximum number of matching items to show. `-1` (default) means no limit. */
193
+ this.limit = input(-1, { ...(ngDevMode ? { debugName: "limit" } : /* istanbul ignore next */ {}), transform: numberAttribute });
194
+ /**
195
+ * The full set of item values, used as the source of truth for filtering and navigation in
196
+ * {@link virtualized} mode (where only a window of `RdxComboboxItem` is mounted). The filtered
197
+ * subset is exposed as {@link filteredItems} for an external virtualizer to render.
198
+ */
199
+ this.items = input(...(ngDevMode ? [undefined, { debugName: "items" }] : /* istanbul ignore next */ []));
200
+ /**
201
+ * Whether the list is externally virtualized — only a window of items is rendered, so navigation
202
+ * runs over {@link items}/{@link filteredItems} by index instead of over mounted DOM elements.
203
+ * Requires {@link items}, and {@link itemToStringLabel} for selection labels/filter text. Disabled
204
+ * items outside the rendered window are not skipped by keyboard navigation.
205
+ */
206
+ this.virtualized = input(false, { ...(ngDevMode ? { debugName: "virtualized" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
207
+ /** How item values are compared for equality (function or object key). */
208
+ this.by = input(...(ngDevMode ? [undefined, { debugName: "by" }] : /* istanbul ignore next */ []));
209
+ /** Converts a value to its display label. Defaults to the matching item's text. */
210
+ this.itemToStringLabel = input(...(ngDevMode ? [undefined, { debugName: "itemToStringLabel" }] : /* istanbul ignore next */ []));
211
+ /** Emits when the selection changes. */
212
+ this.onValueChange = output();
213
+ /** Emits when the input text changes. */
214
+ this.onInputValueChange = output();
215
+ /** Emits when the popup opens or closes. */
216
+ this.onOpenChange = output();
217
+ /**
218
+ * Emits as the highlight moves, with the item's value, its index in {@link filteredItems}, and the
219
+ * reason. In virtualized mode, use `index` to call the virtualizer's `scrollToIndex`.
220
+ */
221
+ this.onItemHighlighted = output();
222
+ /** Emits after the open/close transition (including any exit animation) finishes. */
223
+ this.onOpenChangeComplete = output();
224
+ this.transition = useTransitionStatus((open) => this.onOpenChangeComplete.emit(open));
225
+ /** Open/close transition phase, for `data-starting-style` / `data-ending-style`. */
226
+ this.transitionStatus = this.transition.status;
227
+ /** Registers the popup element whose animation determines transition completion. */
228
+ this.registerTransitionElement = this.transition.registerElement;
229
+ this.listId = injectId('rdx-combobox-list-');
230
+ this.labelId = signal(undefined, ...(ngDevMode ? [{ debugName: "labelId" }] : /* istanbul ignore next */ []));
231
+ this.inputElement = signal(null, ...(ngDevMode ? [{ debugName: "inputElement" }] : /* istanbul ignore next */ []));
232
+ this.cvaDisabled = signal(false, ...(ngDevMode ? [{ debugName: "cvaDisabled" }] : /* istanbul ignore next */ []));
233
+ this.disabledState = computed(() => this.disabled() || this.cvaDisabled(), ...(ngDevMode ? [{ debugName: "disabledState" }] : /* istanbul ignore next */ []));
234
+ this.requiredState = computed(() => this.required(), ...(ngDevMode ? [{ debugName: "requiredState" }] : /* istanbul ignore next */ []));
235
+ this.defaultFilter = useFilter();
236
+ /**
237
+ * Whether the input text is a fresh user query rather than the current selection's label. While
238
+ * `false` (just opened, or showing a selected label), the list is unfiltered so the user can
239
+ * browse; it flips `true` on the first keystroke.
240
+ */
241
+ this.typed = signal(false, ...(ngDevMode ? [{ debugName: "typed" }] : /* istanbul ignore next */ []));
242
+ this._items = signal([], ...(ngDevMode ? [{ debugName: "_items" }] : /* istanbul ignore next */ []));
243
+ /** Registered items, sorted into DOM order. */
244
+ this.orderedItems = computed(() => {
245
+ const items = [...this._items()];
246
+ return items.sort((a, b) => {
247
+ const position = a.element.compareDocumentPosition(b.element);
248
+ if (position & Node.DOCUMENT_POSITION_FOLLOWING) {
249
+ return -1;
250
+ }
251
+ if (position & Node.DOCUMENT_POSITION_PRECEDING) {
252
+ return 1;
253
+ }
254
+ return 0;
255
+ });
256
+ }, ...(ngDevMode ? [{ debugName: "orderedItems" }] : /* istanbul ignore next */ []));
257
+ /** Matching items in DOM order, capped at `limit`. The set of items the list shows. */
258
+ this.visibleItems = computed(() => {
259
+ const matching = this.orderedItems().filter((item) => this.matchesFilter(item));
260
+ const limit = this.limit();
261
+ return limit >= 0 ? matching.slice(0, limit) : matching;
262
+ }, ...(ngDevMode ? [{ debugName: "visibleItems" }] : /* istanbul ignore next */ []));
263
+ this.visibleSet = computed(() => new Set(this.visibleItems()), ...(ngDevMode ? [{ debugName: "visibleSet" }] : /* istanbul ignore next */ []));
264
+ /**
265
+ * The filtered item values an external virtualizer should render (the analogue of Base UI's
266
+ * `useFilteredItems`). Driven by {@link items} when provided (virtualized mode), capped by
267
+ * {@link limit}; otherwise mirrors the values of the mounted {@link visibleItems}.
268
+ */
269
+ this.filteredItems = computed(() => {
270
+ const data = this.items();
271
+ if (data === undefined) {
272
+ return this.visibleItems().map((item) => item.value());
273
+ }
274
+ const limit = this.limit();
275
+ const cap = (arr) => (limit >= 0 ? arr.slice(0, limit) : arr);
276
+ const filter = this.filter();
277
+ if (filter === null) {
278
+ return cap(data);
279
+ }
280
+ const query = this.typed() ? (this.inputValue() ?? '') : '';
281
+ if (!query) {
282
+ return cap(data);
283
+ }
284
+ const matcher = filter ?? this.defaultFilter.contains;
285
+ return cap(data.filter((value) => matcher(this.textFor(value), query)));
286
+ }, ...(ngDevMode ? [{ debugName: "filteredItems" }] : /* istanbul ignore next */ []));
287
+ this.visibleCount = computed(() => this.virtualized() ? this.filteredItems().length : this.visibleItems().length, ...(ngDevMode ? [{ debugName: "visibleCount" }] : /* istanbul ignore next */ []));
288
+ this.highlight = useListHighlight({
289
+ items: this.orderedItems,
290
+ isNavigable: (item) => this.isVisible(item) && !item.disabled(),
291
+ getId: (item) => item.id,
292
+ loop: this.loopFocus,
293
+ injector: this.injector
294
+ });
295
+ this.highlightedItem = this.highlight.highlightedItem;
296
+ /** Highlighted index into {@link filteredItems} in virtualized mode (`-1` when cleared). */
297
+ this.highlightedIndex = signal(-1, ...(ngDevMode ? [{ debugName: "highlightedIndex" }] : /* istanbul ignore next */ []));
298
+ /** Why the highlight last moved; read when emitting {@link onItemHighlighted}. */
299
+ this.highlightReason = signal('none', ...(ngDevMode ? [{ debugName: "highlightReason" }] : /* istanbul ignore next */ []));
300
+ this.activeId = computed(() => {
301
+ if (this.virtualized()) {
302
+ const index = this.highlightedIndex();
303
+ return index >= 0 ? this.itemId(index) : undefined;
304
+ }
305
+ return this.highlight.activeId();
306
+ }, ...(ngDevMode ? [{ debugName: "activeId" }] : /* istanbul ignore next */ []));
307
+ /** Edge to highlight once the list has mounted (items register asynchronously after opening). */
308
+ this.pendingHighlightEdge = signal(null, ...(ngDevMode ? [{ debugName: "pendingHighlightEdge" }] : /* istanbul ignore next */ []));
309
+ // Tracks whether the last interaction was the keyboard, so the highlight doesn't jump to an item
310
+ // the cursor happens to rest on when arrow-key navigation scrolls the list under a still pointer.
311
+ this.keyboardActive = false;
312
+ /** The trigger element, used as a focus fallback when the input lives inside the popup. */
313
+ this.triggerElement = null;
314
+ this.chipsFocusLast = null;
315
+ // Apply uncontrolled defaults once.
316
+ effect(() => {
317
+ const initial = this.defaultValue();
318
+ if (initial !== undefined && untracked(this.value) === null) {
319
+ this.value.set(initial);
320
+ }
321
+ });
322
+ effect(() => {
323
+ if (untracked(this.open) === false && this.defaultOpen()) {
324
+ this.open.set(true);
325
+ }
326
+ });
327
+ // Emit open changes and drive the open/close transition (skip the initial run).
328
+ let previousOpen = untracked(this.open);
329
+ effect(() => {
330
+ const open = this.open();
331
+ if (open === previousOpen) {
332
+ return;
333
+ }
334
+ previousOpen = open;
335
+ untracked(() => {
336
+ this.onOpenChange.emit(open);
337
+ this.transition.start(open);
338
+ });
339
+ });
340
+ // Emit highlight changes (skip the initial run). Tracks both the DOM-ref highlight and the
341
+ // virtualized index; only one is active per mode, so the other never fires spuriously.
342
+ let highlightInitialized = false;
343
+ effect(() => {
344
+ const item = this.highlightedItem();
345
+ const index = this.highlightedIndex();
346
+ if (!highlightInitialized) {
347
+ highlightInitialized = true;
348
+ return;
349
+ }
350
+ untracked(() => {
351
+ const reason = this.highlightReason();
352
+ if (this.virtualized()) {
353
+ const value = index >= 0 ? (this.filteredItems()[index] ?? null) : null;
354
+ this.onItemHighlighted.emit({ value, index, reason });
355
+ }
356
+ else {
357
+ const value = item ? item.value() : null;
358
+ const itemIndex = item ? this.visibleItems().indexOf(item) : -1;
359
+ this.onItemHighlighted.emit({ value, index: itemIndex, reason });
360
+ }
361
+ });
362
+ });
363
+ // Apply a deferred open-edge highlight once items (DOM refs) or filtered data have registered.
364
+ effect(() => {
365
+ const edge = this.pendingHighlightEdge();
366
+ const count = this.virtualized() ? this.filteredItems().length : this.orderedItems().length;
367
+ if (!this.open() || edge === null || count === 0) {
368
+ return;
369
+ }
370
+ untracked(() => {
371
+ // Programmatic move — reset the reason in both modes so the emit reports 'none', not a
372
+ // stale 'keyboard'/'pointer' left by the previous user interaction.
373
+ this.highlightReason.set('none');
374
+ if (this.virtualized()) {
375
+ this.highlightedIndex.set(edge === 'first' ? 0 : count - 1);
376
+ }
377
+ else if (edge === 'first') {
378
+ this.highlight.first();
379
+ }
380
+ else {
381
+ this.highlight.last();
382
+ }
383
+ this.pendingHighlightEdge.set(null);
384
+ });
385
+ });
386
+ // autoHighlight 'always': keep the first navigable item highlighted whenever the popup is
387
+ // open. `visibleCount` re-runs this when filtering changes; the current highlight is read
388
+ // untracked to re-establish a highlight only after the self-heal clears it (no loop).
389
+ effect(() => {
390
+ this.orderedItems();
391
+ this.visibleCount();
392
+ if (this.autoHighlightMode() === 'always' && this.open()) {
393
+ untracked(() => {
394
+ if (this.virtualized()) {
395
+ // Re-seed when the index is cleared OR has fallen out of range, so this works
396
+ // regardless of whether the self-heal effect ran first (no ordering dependency).
397
+ const length = this.filteredItems().length;
398
+ const index = this.highlightedIndex();
399
+ if ((index < 0 || index >= length) && length > 0) {
400
+ this.highlightReason.set('none');
401
+ this.highlightedIndex.set(0);
402
+ }
403
+ }
404
+ else if (this.highlightedItem() === null) {
405
+ this.highlightReason.set('none');
406
+ this.highlight.first();
407
+ }
408
+ });
409
+ }
410
+ });
411
+ // Virtualized self-heal: clear a highlight that filtering has pushed out of range, so
412
+ // `activeId` never references an index past the end of the filtered list.
413
+ effect(() => {
414
+ if (!this.virtualized()) {
415
+ return;
416
+ }
417
+ const length = this.filteredItems().length;
418
+ untracked(() => {
419
+ const index = this.highlightedIndex();
420
+ if (index >= length && index !== -1) {
421
+ this.highlightReason.set('none');
422
+ this.highlightedIndex.set(-1);
423
+ }
424
+ });
425
+ });
426
+ // Virtualized object values can't be labelled from the DOM (items aren't registered) — without
427
+ // `itemToStringLabel`, selection/revert fall back to a generic label. Warn once in dev.
428
+ if (isDevMode()) {
429
+ let warned = false;
430
+ effect(() => {
431
+ if (warned || !this.virtualized() || this.itemToStringLabel()) {
432
+ return;
433
+ }
434
+ if (this.items()?.some((value) => value !== null && typeof value === 'object')) {
435
+ warned = true;
436
+ console.warn('[rdxComboboxRoot] `virtualized` with object item values needs `itemToStringLabel` ' +
437
+ 'to render correct selection labels; falling back to a generic label.');
438
+ }
439
+ });
440
+ }
441
+ }
442
+ /** Opens the popup for browsing (resets the query to "pristine" and selects the input text). */
443
+ openForBrowse() {
444
+ if (!this.open()) {
445
+ this.typed.set(false);
446
+ }
447
+ this.setOpen(true);
448
+ this.selectInputText();
449
+ if (this.autoHighlightMode() === 'always') {
450
+ this.pendingHighlightEdge.set('first');
451
+ }
452
+ }
453
+ /** Opens the popup and highlights the given edge once the list mounts. */
454
+ openAndHighlight(edge) {
455
+ if (!this.open()) {
456
+ this.typed.set(false);
457
+ }
458
+ this.setOpen(true);
459
+ this.selectInputText();
460
+ this.pendingHighlightEdge.set(edge);
461
+ }
462
+ /** Whether the item matches the active query (ignores the `limit` cap). */
463
+ matchesFilter(item) {
464
+ const filter = this.filter();
465
+ if (filter === null) {
466
+ return true;
467
+ }
468
+ // Until the user types a fresh query, show the whole list (the input may still hold the
469
+ // selected item's label, which must not filter everything down to just that item).
470
+ const query = this.typed() ? (this.inputValue() ?? '') : '';
471
+ const matcher = filter ?? this.defaultFilter.contains;
472
+ return matcher(item.textValue(), query);
473
+ }
474
+ /** Whether the item is shown in the list (matches the query and is within `limit`). */
475
+ isVisible(item) {
476
+ return this.visibleSet().has(item);
477
+ }
478
+ isKeyboardActive() {
479
+ return this.keyboardActive;
480
+ }
481
+ setKeyboardActive(value) {
482
+ this.keyboardActive = value;
483
+ }
484
+ isSelected(value) {
485
+ if (this.mode() === 'none') {
486
+ return false;
487
+ }
488
+ const current = this.value();
489
+ if (this.multiple()) {
490
+ return Array.isArray(current) && current.some((v) => isItemEqualToValue(v, value, this.by()));
491
+ }
492
+ return !isNullish(current) && isItemEqualToValue(current, value, this.by());
493
+ }
494
+ registerItem(item) {
495
+ this._items.update((items) => [...items, item]);
496
+ }
497
+ unregisterItem(item) {
498
+ this._items.update((items) => items.filter((i) => i !== item));
499
+ }
500
+ setOpen(open) {
501
+ if (this.disabledState() || this.readonly()) {
502
+ return;
503
+ }
504
+ this.open.set(open);
505
+ }
506
+ closePopup(revert = true) {
507
+ if (!this.open()) {
508
+ return;
509
+ }
510
+ this.open.set(false);
511
+ this.clearHighlightState();
512
+ if (revert) {
513
+ this.revertInputValue();
514
+ }
515
+ this.markAsTouched();
516
+ }
517
+ /** Updates the input text from user typing (marks the query as a fresh user query). */
518
+ setInputValue(value) {
519
+ this.inputValue.set(value);
520
+ this.typed.set(true);
521
+ this.onInputValueChange.emit(value);
522
+ // Auto-highlight the first match as the query changes (deferred so it lands after items mount).
523
+ if (this.autoHighlightMode() !== 'off') {
524
+ this.pendingHighlightEdge.set('first');
525
+ }
526
+ }
527
+ /** Sets the input text programmatically (a selection label / revert) — not a user query. */
528
+ setLabel(value) {
529
+ this.inputValue.set(value);
530
+ this.typed.set(false);
531
+ this.onInputValueChange.emit(value);
532
+ }
533
+ /** Selects all input text so the next keystroke replaces a stale selection label. */
534
+ selectInputText() {
535
+ this.inputElement()?.select();
536
+ }
537
+ /** Resets the input text to the current selection's label (single mode) or empty. */
538
+ revertInputValue() {
539
+ if (this.multiple()) {
540
+ this.setLabel('');
541
+ return;
542
+ }
543
+ const current = this.value();
544
+ this.setLabel(isNullish(current) ? '' : this.labelFor(current));
545
+ }
546
+ labelFor(value) {
547
+ const custom = this.itemToStringLabel();
548
+ if (custom) {
549
+ return custom(value);
550
+ }
551
+ const item = this.orderedItems().find((i) => isItemEqualToValue(i.value(), value, this.by()));
552
+ return item ? item.textValue() : itemToStringLabel(value);
553
+ }
554
+ /** Filter/label text for a raw item value (virtualized mode, no DOM element to read from). */
555
+ textFor(value) {
556
+ const custom = this.itemToStringLabel();
557
+ return custom ? custom(value) : itemToStringLabel(value);
558
+ }
559
+ /** Deterministic id for the item at `index` in virtualized mode (matches `aria-activedescendant`). */
560
+ itemId(index) {
561
+ return `${this.listId}-item-${index}`;
562
+ }
563
+ handleSelect(item) {
564
+ if (this.disabledState() || this.readonly() || item.disabled()) {
565
+ return;
566
+ }
567
+ this.handleSelectValue(item.value(), item.textValue() || this.labelFor(item.value()));
568
+ }
569
+ /** Selects the filtered item at `index` (virtualized mode). The label comes from {@link labelFor}. */
570
+ selectIndex(index) {
571
+ if (this.disabledState() || this.readonly()) {
572
+ return;
573
+ }
574
+ const value = this.filteredItems()[index];
575
+ if (value === undefined) {
576
+ return;
577
+ }
578
+ this.handleSelectValue(value, this.labelFor(value));
579
+ }
580
+ /** Commits a selection from a resolved value/label, independent of whether a DOM item exists. */
581
+ handleSelectValue(value, textValue) {
582
+ if (this.mode() === 'none') {
583
+ // No value is committed; `onValueChange` fires as a pointer/keyboard activation signal so
584
+ // command-palette consumers can react. Optionally fill the input, then close.
585
+ this.onValueChange.emit(value);
586
+ if (this.fillInputOnItemPress()) {
587
+ this.setLabel(textValue);
588
+ }
589
+ this.open.set(false);
590
+ this.clearHighlightState();
591
+ this.restoreFocusAfterSelect();
592
+ this.maybeSubmit();
593
+ return;
594
+ }
595
+ if (this.multiple()) {
596
+ const current = Array.isArray(this.value()) ? [...this.value()] : [];
597
+ const index = current.findIndex((v) => isItemEqualToValue(v, value, this.by()));
598
+ if (index === -1) {
599
+ current.push(value);
600
+ }
601
+ else {
602
+ current.splice(index, 1);
603
+ }
604
+ this.commitValue(current);
605
+ this.setLabel('');
606
+ this.focusInput();
607
+ }
608
+ else {
609
+ this.commitValue(value);
610
+ this.setLabel(textValue);
611
+ this.open.set(false);
612
+ this.clearHighlightState();
613
+ this.restoreFocusAfterSelect();
614
+ }
615
+ this.maybeSubmit();
616
+ }
617
+ /** Requests submit of the closest form when `submitOnItemClick` is enabled. */
618
+ maybeSubmit() {
619
+ if (this.submitOnItemClick()) {
620
+ this.inputElement()?.form?.requestSubmit?.();
621
+ }
622
+ }
623
+ selectHighlighted() {
624
+ if (this.virtualized()) {
625
+ const index = this.highlightedIndex();
626
+ if (index >= 0) {
627
+ this.selectIndex(index);
628
+ }
629
+ return;
630
+ }
631
+ const item = this.highlightedItem();
632
+ if (item) {
633
+ this.handleSelect(item);
634
+ }
635
+ }
636
+ // --- Highlight navigation facade (mode-aware: index-based when virtualized, else DOM-ref) ---
637
+ highlightNext(reason = 'keyboard') {
638
+ this.highlightReason.set(reason);
639
+ if (this.virtualized()) {
640
+ this.stepIndex(1);
641
+ }
642
+ else {
643
+ this.highlight.next();
644
+ }
645
+ }
646
+ highlightPrevious(reason = 'keyboard') {
647
+ this.highlightReason.set(reason);
648
+ if (this.virtualized()) {
649
+ this.stepIndex(-1);
650
+ }
651
+ else {
652
+ this.highlight.previous();
653
+ }
654
+ }
655
+ highlightFirst(reason = 'keyboard') {
656
+ this.highlightReason.set(reason);
657
+ if (this.virtualized()) {
658
+ this.highlightedIndex.set(this.filteredItems().length > 0 ? 0 : -1);
659
+ }
660
+ else {
661
+ this.highlight.first();
662
+ }
663
+ }
664
+ highlightLast(reason = 'keyboard') {
665
+ this.highlightReason.set(reason);
666
+ if (this.virtualized()) {
667
+ const length = this.filteredItems().length;
668
+ this.highlightedIndex.set(length > 0 ? length - 1 : -1);
669
+ }
670
+ else {
671
+ this.highlight.last();
672
+ }
673
+ }
674
+ /** Highlights a specific index in virtualized mode (e.g. pointer hover). Ignored if out of range. */
675
+ highlightIndex(index, reason) {
676
+ if (index < 0 || index >= this.filteredItems().length) {
677
+ return;
678
+ }
679
+ this.highlightReason.set(reason);
680
+ this.highlightedIndex.set(index);
681
+ }
682
+ /** Highlights a DOM-ref item (non-virtualized pointer hover). */
683
+ setHighlight(item, reason) {
684
+ this.highlightReason.set(reason);
685
+ this.highlight.set(item);
686
+ }
687
+ /** Clears whichever highlight model is active. */
688
+ clearHighlightState() {
689
+ this.highlight.clear();
690
+ this.highlightedIndex.set(-1);
691
+ }
692
+ /** Steps the virtualized highlight index by `direction`, wrapping when {@link loopFocus}. */
693
+ stepIndex(direction) {
694
+ const length = this.filteredItems().length;
695
+ if (length === 0) {
696
+ this.highlightedIndex.set(-1);
697
+ return;
698
+ }
699
+ const current = this.highlightedIndex();
700
+ if (current < 0) {
701
+ this.highlightedIndex.set(direction === 1 ? 0 : length - 1);
702
+ return;
703
+ }
704
+ let next = current + direction;
705
+ const loop = this.loopFocus();
706
+ if (next < 0) {
707
+ next = loop ? length - 1 : 0;
708
+ }
709
+ else if (next >= length) {
710
+ next = loop ? 0 : length - 1;
711
+ }
712
+ this.highlightedIndex.set(next);
713
+ }
714
+ clearSelection() {
715
+ this.commitValue(this.multiple() ? [] : null);
716
+ this.setLabel('');
717
+ this.focusInput();
718
+ }
719
+ removeValue(value) {
720
+ if (!this.multiple() || !Array.isArray(this.value())) {
721
+ return;
722
+ }
723
+ const next = this.value().filter((v) => !isItemEqualToValue(v, value, this.by()));
724
+ this.commitValue(next);
725
+ }
726
+ removeLastValue() {
727
+ if (!this.multiple() || !Array.isArray(this.value())) {
728
+ return;
729
+ }
730
+ const current = this.value();
731
+ if (current.length > 0) {
732
+ this.commitValue(current.slice(0, -1));
733
+ }
734
+ }
735
+ focusInput() {
736
+ this.inputElement()?.focus();
737
+ }
738
+ /**
739
+ * Restores focus after a selection closes the popup, so the keyboard can reopen it. When the
740
+ * input lives inside the popup it is about to unmount, so focus goes to the trigger instead;
741
+ * otherwise it returns to the input. Done synchronously while the input is still in the DOM.
742
+ */
743
+ restoreFocusAfterSelect() {
744
+ const input = this.inputElement();
745
+ if (input && !input.closest('[rdxComboboxPopup]')) {
746
+ input.focus();
747
+ }
748
+ else {
749
+ this.triggerElement?.focus();
750
+ }
751
+ }
752
+ /** Registered by `RdxComboboxChips` so the input can hand keyboard focus to the chips. */
753
+ registerChipsNav(fn) {
754
+ this.chipsFocusLast = fn;
755
+ }
756
+ /** Moves focus to the last chip, if any. Returns whether a chip was focused. */
757
+ focusLastChip() {
758
+ return this.chipsFocusLast?.() ?? false;
759
+ }
760
+ markAsTouched() {
761
+ this.onTouched?.();
762
+ }
763
+ commitValue(value) {
764
+ this.value.set(value);
765
+ this.onValueChange.emit(value);
766
+ this.onChange?.(value);
767
+ }
768
+ // ControlValueAccessor
769
+ writeValue(value) {
770
+ untracked(() => this.value.set(value));
771
+ }
772
+ registerOnChange(fn) {
773
+ this.onChange = fn;
774
+ }
775
+ registerOnTouched(fn) {
776
+ this.onTouched = fn;
777
+ }
778
+ setDisabledState(isDisabled) {
779
+ this.cvaDisabled.set(isDisabled);
780
+ }
781
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxRoot, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
782
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxComboboxRoot, isStandalone: true, selector: "[rdxComboboxRoot]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, defaultValue: { classPropertyName: "defaultValue", publicName: "defaultValue", isSignal: true, isRequired: false, transformFunction: null }, inputValue: { classPropertyName: "inputValue", publicName: "inputValue", isSignal: true, isRequired: false, transformFunction: null }, open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, defaultOpen: { classPropertyName: "defaultOpen", publicName: "defaultOpen", isSignal: true, isRequired: false, transformFunction: null }, multipleInput: { classPropertyName: "multipleInput", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, selectionMode: { classPropertyName: "selectionMode", publicName: "selectionMode", isSignal: true, isRequired: false, transformFunction: null }, fillInputOnItemPress: { classPropertyName: "fillInputOnItemPress", publicName: "fillInputOnItemPress", isSignal: true, isRequired: false, transformFunction: null }, dir: { classPropertyName: "dir", publicName: "dir", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", 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 }, loopFocus: { classPropertyName: "loopFocus", publicName: "loopFocus", isSignal: true, isRequired: false, transformFunction: null }, autoHighlight: { classPropertyName: "autoHighlight", publicName: "autoHighlight", isSignal: true, isRequired: false, transformFunction: null }, openOnInputClick: { classPropertyName: "openOnInputClick", publicName: "openOnInputClick", isSignal: true, isRequired: false, transformFunction: null }, modal: { classPropertyName: "modal", publicName: "modal", isSignal: true, isRequired: false, transformFunction: null }, submitOnItemClick: { classPropertyName: "submitOnItemClick", publicName: "submitOnItemClick", isSignal: true, isRequired: false, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null }, limit: { classPropertyName: "limit", publicName: "limit", isSignal: true, isRequired: false, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, virtualized: { classPropertyName: "virtualized", publicName: "virtualized", isSignal: true, isRequired: false, transformFunction: null }, by: { classPropertyName: "by", publicName: "by", isSignal: true, isRequired: false, transformFunction: null }, itemToStringLabel: { classPropertyName: "itemToStringLabel", publicName: "itemToStringLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", inputValue: "inputValueChange", open: "openChange", onValueChange: "onValueChange", onInputValueChange: "onInputValueChange", onOpenChange: "onOpenChange", onItemHighlighted: "onItemHighlighted", onOpenChangeComplete: "onOpenChangeComplete" }, host: { properties: { "attr.data-disabled": "disabledState() ? \"\" : undefined" } }, providers: [
783
+ provideComboboxRootContext(context),
784
+ { provide: NG_VALUE_ACCESSOR, useExisting: RdxComboboxRoot, multi: true }
785
+ ], exportAs: ["rdxComboboxRoot"], hostDirectives: [{ directive: i1.RdxPopper }], ngImport: i0 }); }
786
+ }
787
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxRoot, decorators: [{
788
+ type: Directive,
789
+ args: [{
790
+ selector: '[rdxComboboxRoot]',
791
+ exportAs: 'rdxComboboxRoot',
792
+ providers: [
793
+ provideComboboxRootContext(context),
794
+ { provide: NG_VALUE_ACCESSOR, useExisting: RdxComboboxRoot, multi: true }
795
+ ],
796
+ hostDirectives: [RdxPopper],
797
+ host: {
798
+ '[attr.data-disabled]': 'disabledState() ? "" : undefined'
799
+ }
800
+ }]
801
+ }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], defaultValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultValue", required: false }] }], inputValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputValue", required: false }] }, { type: i0.Output, args: ["inputValueChange"] }], open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], defaultOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultOpen", required: false }] }], multipleInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], selectionMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionMode", required: false }] }], fillInputOnItemPress: [{ type: i0.Input, args: [{ isSignal: true, alias: "fillInputOnItemPress", required: false }] }], dir: [{ type: i0.Input, args: [{ isSignal: true, alias: "dir", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], loopFocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "loopFocus", required: false }] }], autoHighlight: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoHighlight", required: false }] }], openOnInputClick: [{ type: i0.Input, args: [{ isSignal: true, alias: "openOnInputClick", required: false }] }], modal: [{ type: i0.Input, args: [{ isSignal: true, alias: "modal", required: false }] }], submitOnItemClick: [{ type: i0.Input, args: [{ isSignal: true, alias: "submitOnItemClick", required: false }] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: false }] }], limit: [{ type: i0.Input, args: [{ isSignal: true, alias: "limit", required: false }] }], items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], virtualized: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtualized", required: false }] }], by: [{ type: i0.Input, args: [{ isSignal: true, alias: "by", required: false }] }], itemToStringLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemToStringLabel", required: false }] }], onValueChange: [{ type: i0.Output, args: ["onValueChange"] }], onInputValueChange: [{ type: i0.Output, args: ["onInputValueChange"] }], onOpenChange: [{ type: i0.Output, args: ["onOpenChange"] }], onItemHighlighted: [{ type: i0.Output, args: ["onItemHighlighted"] }], onOpenChangeComplete: [{ type: i0.Output, args: ["onOpenChangeComplete"] }] } });
802
+
803
+ /**
804
+ * An overlay rendered beneath the popup in `modal` mode. Place it inside the portal/presence; style
805
+ * it `position: fixed; inset: 0`. Give it `pointer-events: auto` so a click on it dismisses the popup
806
+ * (with `modal`, the rest of the page is inert). Exposes `data-open` / `data-closed` for animation.
807
+ *
808
+ * @group Components
809
+ */
810
+ class RdxComboboxBackdrop {
811
+ constructor() {
812
+ this.rootContext = injectComboboxRootContext();
813
+ }
814
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxBackdrop, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
815
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxComboboxBackdrop, isStandalone: true, selector: "[rdxComboboxBackdrop]", host: { attributes: { "aria-hidden": "true" }, properties: { "attr.data-state": "rootContext.open() ? \"open\" : \"closed\"", "attr.data-open": "rootContext.open() ? \"\" : undefined", "attr.data-closed": "rootContext.open() ? undefined : \"\"" } }, exportAs: ["rdxComboboxBackdrop"], ngImport: i0 }); }
816
+ }
817
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxBackdrop, decorators: [{
818
+ type: Directive,
819
+ args: [{
820
+ selector: '[rdxComboboxBackdrop]',
821
+ exportAs: 'rdxComboboxBackdrop',
822
+ host: {
823
+ 'aria-hidden': 'true',
824
+ '[attr.data-state]': 'rootContext.open() ? "open" : "closed"',
825
+ '[attr.data-open]': 'rootContext.open() ? "" : undefined',
826
+ '[attr.data-closed]': 'rootContext.open() ? undefined : ""'
827
+ }
828
+ }]
829
+ }] });
830
+
831
+ /**
832
+ * Container for the selected-value chips in `multiple` mode. Sits before the input and coordinates
833
+ * arrow-key navigation across the chips (the chips themselves handle the key events).
834
+ *
835
+ * @group Components
836
+ */
837
+ class RdxComboboxChips {
838
+ constructor() {
839
+ this.host = inject(ElementRef).nativeElement;
840
+ this.rootContext = injectComboboxRootContext();
841
+ this.rootContext.registerChipsNav(() => this.focusLast());
842
+ inject(DestroyRef).onDestroy(() => this.rootContext.registerChipsNav(null));
843
+ }
844
+ /** The chip elements in DOM order. */
845
+ getChips() {
846
+ return Array.from(this.host.querySelectorAll('[rdxComboboxChip]'));
847
+ }
848
+ /** Focuses the last chip. Returns whether there was one. */
849
+ focusLast() {
850
+ const chips = this.getChips();
851
+ if (chips.length === 0) {
852
+ return false;
853
+ }
854
+ chips[chips.length - 1].focus();
855
+ return true;
856
+ }
857
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxChips, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
858
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxComboboxChips, isStandalone: true, selector: "[rdxComboboxChips]", host: { attributes: { "role": "list" } }, exportAs: ["rdxComboboxChips"], hostDirectives: [{ directive: i1$1.RdxDismissableLayerBranch }], ngImport: i0 }); }
859
+ }
860
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxChips, decorators: [{
861
+ type: Directive,
862
+ args: [{
863
+ selector: '[rdxComboboxChips]',
864
+ exportAs: 'rdxComboboxChips',
865
+ hostDirectives: [RdxDismissableLayerBranch],
866
+ host: {
867
+ role: 'list'
868
+ }
869
+ }]
870
+ }], ctorParameters: () => [] });
871
+
872
+ const chipContext = () => {
873
+ const chip = inject(RdxComboboxChip);
874
+ return { value: chip.value };
875
+ };
876
+ const [injectComboboxChipContext, provideComboboxChipContext] = createContext('RdxComboboxChipContext', 'components/combobox');
877
+ /**
878
+ * A single selected-value chip. Provide its `value` so {@link RdxComboboxChipRemove} can deselect it.
879
+ * Chips are focusable and navigable with the arrow keys; Backspace / Delete removes the chip.
880
+ *
881
+ * @group Components
882
+ */
883
+ class RdxComboboxChip {
884
+ constructor() {
885
+ this.element = inject(ElementRef).nativeElement;
886
+ this.chips = inject(RdxComboboxChips, { optional: true });
887
+ this.rootContext = injectComboboxRootContext();
888
+ /** The value this chip represents. */
889
+ this.value = input.required(...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
890
+ }
891
+ onKeydown(event) {
892
+ const list = this.chips?.getChips() ?? [];
893
+ const index = list.indexOf(this.element);
894
+ switch (event.key) {
895
+ case 'ArrowLeft':
896
+ if (index > 0) {
897
+ event.preventDefault();
898
+ list[index - 1].focus();
899
+ }
900
+ break;
901
+ case 'ArrowRight':
902
+ event.preventDefault();
903
+ if (index < list.length - 1) {
904
+ list[index + 1].focus();
905
+ }
906
+ else {
907
+ this.rootContext.focusInput();
908
+ }
909
+ break;
910
+ case 'Home':
911
+ if (list.length) {
912
+ event.preventDefault();
913
+ list[0].focus();
914
+ }
915
+ break;
916
+ case 'End':
917
+ event.preventDefault();
918
+ this.rootContext.focusInput();
919
+ break;
920
+ case 'Backspace':
921
+ case 'Delete': {
922
+ event.preventDefault();
923
+ // The @for tracks by value, so the previous/next chip nodes survive this removal.
924
+ const prev = list[index - 1];
925
+ const next = list[index + 1];
926
+ this.rootContext.removeValue(this.value());
927
+ if (prev) {
928
+ prev.focus();
929
+ }
930
+ else if (next) {
931
+ next.focus();
932
+ }
933
+ else {
934
+ this.rootContext.focusInput();
935
+ }
936
+ break;
937
+ }
938
+ case 'Escape':
939
+ this.rootContext.focusInput();
940
+ break;
941
+ }
942
+ }
943
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxChip, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
944
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxComboboxChip, isStandalone: true, selector: "[rdxComboboxChip]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null } }, host: { attributes: { "role": "listitem", "tabindex": "-1" }, listeners: { "keydown": "onKeydown($event)" } }, providers: [provideComboboxChipContext(chipContext)], exportAs: ["rdxComboboxChip"], ngImport: i0 }); }
945
+ }
946
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxChip, decorators: [{
947
+ type: Directive,
948
+ args: [{
949
+ selector: '[rdxComboboxChip]',
950
+ exportAs: 'rdxComboboxChip',
951
+ providers: [provideComboboxChipContext(chipContext)],
952
+ host: {
953
+ role: 'listitem',
954
+ tabindex: '-1',
955
+ '(keydown)': 'onKeydown($event)'
956
+ }
957
+ }]
958
+ }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }] } });
959
+
960
+ /**
961
+ * Removes its chip's value from the selection, keeping focus in the input.
962
+ *
963
+ * @group Components
964
+ */
965
+ class RdxComboboxChipRemove {
966
+ constructor() {
967
+ this.rootContext = injectComboboxRootContext();
968
+ this.chipContext = injectComboboxChipContext();
969
+ }
970
+ onClick() {
971
+ this.rootContext.removeValue(this.chipContext.value());
972
+ this.rootContext.focusInput();
973
+ }
974
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxChipRemove, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
975
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxComboboxChipRemove, isStandalone: true, selector: "button[rdxComboboxChipRemove]", host: { attributes: { "type": "button", "tabindex": "-1", "aria-label": "Remove" }, listeners: { "click": "onClick()" } }, exportAs: ["rdxComboboxChipRemove"], ngImport: i0 }); }
976
+ }
977
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxChipRemove, decorators: [{
978
+ type: Directive,
979
+ args: [{
980
+ selector: 'button[rdxComboboxChipRemove]',
981
+ exportAs: 'rdxComboboxChipRemove',
982
+ host: {
983
+ type: 'button',
984
+ tabindex: '-1',
985
+ 'aria-label': 'Remove',
986
+ '(click)': 'onClick()'
987
+ }
988
+ }]
989
+ }] });
990
+
991
+ /**
992
+ * Clears the current selection and the input text. Hidden when there is nothing to clear.
993
+ *
994
+ * @group Components
995
+ */
996
+ class RdxComboboxClear {
997
+ constructor() {
998
+ this.rootContext = injectComboboxRootContext();
999
+ this.isEmpty = computed(() => {
1000
+ const value = this.rootContext.value();
1001
+ if (Array.isArray(value)) {
1002
+ return value.length === 0;
1003
+ }
1004
+ return value === null || value === undefined;
1005
+ }, ...(ngDevMode ? [{ debugName: "isEmpty" }] : /* istanbul ignore next */ []));
1006
+ }
1007
+ onClick() {
1008
+ this.rootContext.clearSelection();
1009
+ }
1010
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxClear, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1011
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxComboboxClear, isStandalone: true, selector: "button[rdxComboboxClear]", host: { attributes: { "type": "button", "tabindex": "-1", "aria-label": "Clear" }, listeners: { "click": "onClick()" }, properties: { "hidden": "isEmpty()", "attr.disabled": "rootContext.disabledState() ? \"\" : undefined" } }, exportAs: ["rdxComboboxClear"], hostDirectives: [{ directive: i1$1.RdxDismissableLayerBranch }], ngImport: i0 }); }
1012
+ }
1013
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxClear, decorators: [{
1014
+ type: Directive,
1015
+ args: [{
1016
+ selector: 'button[rdxComboboxClear]',
1017
+ exportAs: 'rdxComboboxClear',
1018
+ hostDirectives: [RdxDismissableLayerBranch],
1019
+ host: {
1020
+ type: 'button',
1021
+ tabindex: '-1',
1022
+ 'aria-label': 'Clear',
1023
+ '[hidden]': 'isEmpty()',
1024
+ '[attr.disabled]': 'rootContext.disabledState() ? "" : undefined',
1025
+ '(click)': 'onClick()'
1026
+ }
1027
+ }]
1028
+ }] });
1029
+
1030
+ /**
1031
+ * Shown only when no items match the current query.
1032
+ *
1033
+ * @group Components
1034
+ */
1035
+ class RdxComboboxEmpty {
1036
+ constructor() {
1037
+ this.rootContext = injectComboboxRootContext();
1038
+ }
1039
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxEmpty, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1040
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxComboboxEmpty, isStandalone: true, selector: "[rdxComboboxEmpty]", host: { attributes: { "role": "presentation" }, properties: { "hidden": "rootContext.visibleCount() > 0" } }, exportAs: ["rdxComboboxEmpty"], ngImport: i0 }); }
1041
+ }
1042
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxEmpty, decorators: [{
1043
+ type: Directive,
1044
+ args: [{
1045
+ selector: '[rdxComboboxEmpty]',
1046
+ exportAs: 'rdxComboboxEmpty',
1047
+ host: {
1048
+ role: 'presentation',
1049
+ '[hidden]': 'rootContext.visibleCount() > 0'
1050
+ }
1051
+ }]
1052
+ }] });
1053
+
1054
+ const groupContext = () => {
1055
+ const group = inject(RdxComboboxGroup);
1056
+ return {
1057
+ labelId: group.labelId,
1058
+ registerItem: (item) => group.registerItem(item),
1059
+ unregisterItem: (item) => group.unregisterItem(item)
1060
+ };
1061
+ };
1062
+ const [injectComboboxGroupContext, provideComboboxGroupContext] = createContext('RdxComboboxGroupContext', 'components/combobox');
1063
+ /**
1064
+ * Groups related options under a shared label. Hides itself when all of its items are filtered out,
1065
+ * so a group heading never lingers above an empty section.
1066
+ *
1067
+ * @group Components
1068
+ */
1069
+ class RdxComboboxGroup {
1070
+ constructor() {
1071
+ this.rootContext = injectComboboxRootContext();
1072
+ this.labelId = signal(undefined, ...(ngDevMode ? [{ debugName: "labelId" }] : /* istanbul ignore next */ []));
1073
+ this.items = signal([], ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
1074
+ this.hasItems = computed(() => this.items().length > 0, ...(ngDevMode ? [{ debugName: "hasItems" }] : /* istanbul ignore next */ []));
1075
+ this.hasVisibleItems = computed(() => this.items().some((item) => this.rootContext.isVisible(item)), ...(ngDevMode ? [{ debugName: "hasVisibleItems" }] : /* istanbul ignore next */ []));
1076
+ }
1077
+ registerItem(item) {
1078
+ this.items.update((items) => [...items, item]);
1079
+ }
1080
+ unregisterItem(item) {
1081
+ this.items.update((items) => items.filter((i) => i !== item));
1082
+ }
1083
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxGroup, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1084
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxComboboxGroup, isStandalone: true, selector: "[rdxComboboxGroup]", host: { attributes: { "role": "group" }, properties: { "attr.aria-labelledby": "labelId()", "hidden": "hasItems() && !hasVisibleItems()" } }, providers: [provideComboboxGroupContext(groupContext)], exportAs: ["rdxComboboxGroup"], ngImport: i0 }); }
1085
+ }
1086
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxGroup, decorators: [{
1087
+ type: Directive,
1088
+ args: [{
1089
+ selector: '[rdxComboboxGroup]',
1090
+ exportAs: 'rdxComboboxGroup',
1091
+ providers: [provideComboboxGroupContext(groupContext)],
1092
+ host: {
1093
+ role: 'group',
1094
+ '[attr.aria-labelledby]': 'labelId()',
1095
+ '[hidden]': 'hasItems() && !hasVisibleItems()'
1096
+ }
1097
+ }]
1098
+ }] });
1099
+
1100
+ /**
1101
+ * Accessible label for a {@link RdxComboboxGroup}. Wires itself up via `aria-labelledby`.
1102
+ *
1103
+ * @group Components
1104
+ */
1105
+ class RdxComboboxGroupLabel {
1106
+ constructor() {
1107
+ this.groupContext = injectComboboxGroupContext();
1108
+ this.id = injectId('rdx-combobox-group-label-');
1109
+ this.groupContext.labelId.set(this.id);
1110
+ }
1111
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxGroupLabel, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1112
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxComboboxGroupLabel, isStandalone: true, selector: "[rdxComboboxGroupLabel]", host: { properties: { "attr.id": "id" } }, exportAs: ["rdxComboboxGroupLabel"], ngImport: i0 }); }
1113
+ }
1114
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxGroupLabel, decorators: [{
1115
+ type: Directive,
1116
+ args: [{
1117
+ selector: '[rdxComboboxGroupLabel]',
1118
+ exportAs: 'rdxComboboxGroupLabel',
1119
+ host: {
1120
+ '[attr.id]': 'id'
1121
+ }
1122
+ }]
1123
+ }], ctorParameters: () => [] });
1124
+
1125
+ /**
1126
+ * Decorative icon inside the trigger. Hidden from assistive technology.
1127
+ *
1128
+ * @group Components
1129
+ */
1130
+ class RdxComboboxIcon {
1131
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxIcon, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1132
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxComboboxIcon, isStandalone: true, selector: "[rdxComboboxIcon]", host: { attributes: { "aria-hidden": "true" } }, exportAs: ["rdxComboboxIcon"], ngImport: i0 }); }
1133
+ }
1134
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxIcon, decorators: [{
1135
+ type: Directive,
1136
+ args: [{
1137
+ selector: '[rdxComboboxIcon]',
1138
+ exportAs: 'rdxComboboxIcon',
1139
+ host: {
1140
+ 'aria-hidden': 'true'
1141
+ }
1142
+ }]
1143
+ }] });
1144
+
1145
+ const attr = (value) => (value ? '' : undefined);
1146
+ /**
1147
+ * The combobox text input. Holds DOM focus at all times; the highlighted option is referenced via
1148
+ * `aria-activedescendant`. Integrates with Field for labeling, description, and validation state.
1149
+ *
1150
+ * @group Components
1151
+ */
1152
+ class RdxComboboxInput {
1153
+ constructor() {
1154
+ this.rootContext = injectComboboxRootContext();
1155
+ this.element = inject(ElementRef).nativeElement;
1156
+ this.fieldRootContext = injectFieldRootContext(true);
1157
+ /** The input id; Field labels and descriptions reference it for accessible relationships. */
1158
+ this.id = input(injectId('rdx-combobox-input-'), ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
1159
+ /** Marks the input as invalid independently of any Field state. */
1160
+ this.invalid = input(false, { ...(ngDevMode ? { debugName: "invalid" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1161
+ this.invalidState = computed(() => this.invalid() || Boolean(this.fieldRootContext?.invalidState()), ...(ngDevMode ? [{ debugName: "invalidState" }] : /* istanbul ignore next */ []));
1162
+ this.disabledState = computed(() => this.rootContext.disabledState() || Boolean(this.fieldRootContext?.disabledState()), ...(ngDevMode ? [{ debugName: "disabledState" }] : /* istanbul ignore next */ []));
1163
+ this.requiredState = computed(() => this.rootContext.requiredState() || Boolean(this.fieldRootContext?.requiredState()), ...(ngDevMode ? [{ debugName: "requiredState" }] : /* istanbul ignore next */ []));
1164
+ this.filledState = computed(() => !this.isEmptyValue() || Boolean(this.fieldRootContext?.filledState()), ...(ngDevMode ? [{ debugName: "filledState" }] : /* istanbul ignore next */ []));
1165
+ this.focusedState = computed(() => Boolean(this.fieldRootContext?.focusedState()), ...(ngDevMode ? [{ debugName: "focusedState" }] : /* istanbul ignore next */ []));
1166
+ this.isEmptyValue = computed(() => {
1167
+ const value = this.rootContext.value();
1168
+ if (Array.isArray(value)) {
1169
+ return value.length === 0;
1170
+ }
1171
+ return value === null || value === undefined;
1172
+ }, ...(ngDevMode ? [{ debugName: "isEmptyValue" }] : /* istanbul ignore next */ []));
1173
+ this.describedBy = computed(() => {
1174
+ if (!this.fieldRootContext) {
1175
+ return undefined;
1176
+ }
1177
+ const ids = [
1178
+ ...this.fieldRootContext.descriptionIds(),
1179
+ ...(this.fieldRootContext.invalidState() ? this.fieldRootContext.errorIds() : [])
1180
+ ];
1181
+ return ids.length ? ids.join(' ') : undefined;
1182
+ }, ...(ngDevMode ? [{ debugName: "describedBy" }] : /* istanbul ignore next */ []));
1183
+ /** Whether an IME composition is in progress (CJK). While composing, don't filter or select. */
1184
+ this.composing = false;
1185
+ this.dataAttr = attr;
1186
+ this.rootContext.setInputElement(this.element);
1187
+ afterNextRender(() => {
1188
+ this.fieldRootContext?.setControlId(this.id());
1189
+ });
1190
+ inject(DestroyRef).onDestroy(() => {
1191
+ if (this.rootContext.inputElement() === this.element) {
1192
+ this.rootContext.setInputElement(null);
1193
+ }
1194
+ });
1195
+ }
1196
+ onInput(event) {
1197
+ // Defer filtering until the composition ends so intermediate IME text doesn't filter/select.
1198
+ if (this.composing || event.isComposing) {
1199
+ return;
1200
+ }
1201
+ this.commitInput(event.target.value);
1202
+ }
1203
+ onCompositionEnd(event) {
1204
+ this.composing = false;
1205
+ this.commitInput(event.target.value);
1206
+ }
1207
+ commitInput(value) {
1208
+ if (!this.rootContext.open()) {
1209
+ this.rootContext.openPopup();
1210
+ }
1211
+ // setInputValue applies any autoHighlight (deferred until items mount).
1212
+ this.rootContext.setInputValue(value);
1213
+ }
1214
+ onClick() {
1215
+ if (this.rootContext.openOnInputClick()) {
1216
+ this.rootContext.openForBrowse();
1217
+ }
1218
+ }
1219
+ onFocus() {
1220
+ this.fieldRootContext?.setFocused(true);
1221
+ }
1222
+ onBlur() {
1223
+ this.fieldRootContext?.setFocused(false);
1224
+ this.fieldRootContext?.setTouched(true);
1225
+ }
1226
+ onKeydown(event) {
1227
+ // Don't interfere with IME composition or text-editing shortcuts / range selection. Home/End
1228
+ // and Shift+Arrows must keep moving the caret, Ctrl/Meta combos stay browser shortcuts.
1229
+ if (event.isComposing || this.composing) {
1230
+ return;
1231
+ }
1232
+ if (event.ctrlKey || event.metaKey || event.altKey || event.shiftKey) {
1233
+ return;
1234
+ }
1235
+ const open = this.rootContext.open();
1236
+ switch (event.key) {
1237
+ case 'ArrowDown':
1238
+ event.preventDefault();
1239
+ this.rootContext.setKeyboardActive(true);
1240
+ if (!open) {
1241
+ this.rootContext.openAndHighlight('first');
1242
+ }
1243
+ else {
1244
+ this.rootContext.highlightNext();
1245
+ }
1246
+ break;
1247
+ case 'ArrowUp':
1248
+ event.preventDefault();
1249
+ this.rootContext.setKeyboardActive(true);
1250
+ if (!open) {
1251
+ this.rootContext.openAndHighlight('last');
1252
+ }
1253
+ else {
1254
+ this.rootContext.highlightPrevious();
1255
+ }
1256
+ break;
1257
+ case 'Enter':
1258
+ if (open) {
1259
+ const hasHighlight = this.rootContext.virtualized()
1260
+ ? this.rootContext.highlightedIndex() >= 0
1261
+ : this.rootContext.highlightedItem() !== null;
1262
+ if (hasHighlight) {
1263
+ // Select the highlighted item (and prevent an accidental form submit).
1264
+ event.preventDefault();
1265
+ this.rootContext.selectHighlighted();
1266
+ }
1267
+ else {
1268
+ // Nothing highlighted: just close, and let the form submit.
1269
+ this.rootContext.closePopup(true);
1270
+ }
1271
+ }
1272
+ break;
1273
+ case 'Escape':
1274
+ // Just close the popup (reverting the in-progress query); never clear the selection.
1275
+ if (open) {
1276
+ event.preventDefault();
1277
+ this.rootContext.closePopup(true);
1278
+ }
1279
+ break;
1280
+ case 'Tab':
1281
+ if (open) {
1282
+ this.rootContext.closePopup(true);
1283
+ }
1284
+ break;
1285
+ case 'ArrowLeft':
1286
+ // From the very start of the input in multiple mode, step into the chips.
1287
+ if (this.rootContext.multiple() &&
1288
+ this.element.selectionStart === 0 &&
1289
+ this.element.selectionEnd === 0 &&
1290
+ this.rootContext.focusLastChip()) {
1291
+ event.preventDefault();
1292
+ }
1293
+ break;
1294
+ case 'Backspace':
1295
+ if (this.rootContext.multiple() && this.element.value === '') {
1296
+ this.rootContext.removeLastValue();
1297
+ }
1298
+ break;
1299
+ }
1300
+ }
1301
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxInput, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1302
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxComboboxInput, isStandalone: true, selector: "input[rdxComboboxInput]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "combobox", "autocomplete": "off", "aria-autocomplete": "list" }, listeners: { "input": "onInput($event)", "click": "onClick()", "focus": "onFocus()", "blur": "onBlur()", "keydown": "onKeydown($event)", "compositionstart": "composing = true", "compositionend": "onCompositionEnd($event)" }, properties: { "attr.id": "id()", "attr.aria-expanded": "rootContext.open()", "attr.aria-controls": "rootContext.listId", "attr.aria-labelledby": "rootContext.labelId()", "attr.aria-activedescendant": "rootContext.activeId()", "attr.aria-describedby": "describedBy()", "attr.aria-invalid": "invalidState() ? \"true\" : undefined", "attr.aria-required": "requiredState() ? \"true\" : undefined", "attr.aria-disabled": "disabledState() ? \"true\" : undefined", "attr.disabled": "disabledState() ? \"\" : undefined", "attr.readonly": "rootContext.readonly() ? \"\" : undefined", "attr.required": "requiredState() ? \"\" : undefined", "value": "rootContext.inputValue()", "attr.data-popup-open": "dataAttr(rootContext.open())", "attr.data-list-empty": "dataAttr(rootContext.visibleCount() === 0)", "attr.data-placeholder": "dataAttr(isEmptyValue())", "attr.data-invalid": "dataAttr(invalidState())", "attr.data-valid": "dataAttr(!invalidState())", "attr.data-disabled": "dataAttr(disabledState())", "attr.data-required": "dataAttr(requiredState())", "attr.data-filled": "dataAttr(filledState())", "attr.data-focused": "dataAttr(focusedState())" } }, exportAs: ["rdxComboboxInput"], hostDirectives: [{ directive: i1.RdxPopperAnchor }, { directive: i1$1.RdxDismissableLayerBranch }], ngImport: i0 }); }
1303
+ }
1304
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxInput, decorators: [{
1305
+ type: Directive,
1306
+ args: [{
1307
+ selector: 'input[rdxComboboxInput]',
1308
+ exportAs: 'rdxComboboxInput',
1309
+ hostDirectives: [RdxPopperAnchor, RdxDismissableLayerBranch],
1310
+ host: {
1311
+ role: 'combobox',
1312
+ autocomplete: 'off',
1313
+ 'aria-autocomplete': 'list',
1314
+ '[attr.id]': 'id()',
1315
+ '[attr.aria-expanded]': 'rootContext.open()',
1316
+ '[attr.aria-controls]': 'rootContext.listId',
1317
+ '[attr.aria-labelledby]': 'rootContext.labelId()',
1318
+ '[attr.aria-activedescendant]': 'rootContext.activeId()',
1319
+ '[attr.aria-describedby]': 'describedBy()',
1320
+ '[attr.aria-invalid]': 'invalidState() ? "true" : undefined',
1321
+ '[attr.aria-required]': 'requiredState() ? "true" : undefined',
1322
+ '[attr.aria-disabled]': 'disabledState() ? "true" : undefined',
1323
+ '[attr.disabled]': 'disabledState() ? "" : undefined',
1324
+ '[attr.readonly]': 'rootContext.readonly() ? "" : undefined',
1325
+ '[attr.required]': 'requiredState() ? "" : undefined',
1326
+ '[value]': 'rootContext.inputValue()',
1327
+ '[attr.data-popup-open]': 'dataAttr(rootContext.open())',
1328
+ '[attr.data-list-empty]': 'dataAttr(rootContext.visibleCount() === 0)',
1329
+ '[attr.data-placeholder]': 'dataAttr(isEmptyValue())',
1330
+ '[attr.data-invalid]': 'dataAttr(invalidState())',
1331
+ '[attr.data-valid]': 'dataAttr(!invalidState())',
1332
+ '[attr.data-disabled]': 'dataAttr(disabledState())',
1333
+ '[attr.data-required]': 'dataAttr(requiredState())',
1334
+ '[attr.data-filled]': 'dataAttr(filledState())',
1335
+ '[attr.data-focused]': 'dataAttr(focusedState())',
1336
+ '(input)': 'onInput($event)',
1337
+ '(click)': 'onClick()',
1338
+ '(focus)': 'onFocus()',
1339
+ '(blur)': 'onBlur()',
1340
+ '(keydown)': 'onKeydown($event)',
1341
+ '(compositionstart)': 'composing = true',
1342
+ '(compositionend)': 'onCompositionEnd($event)'
1343
+ }
1344
+ }]
1345
+ }], ctorParameters: () => [], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }] } });
1346
+
1347
+ const itemContext = () => {
1348
+ const item = inject(RdxComboboxItem);
1349
+ return {
1350
+ isSelected: item.isSelected,
1351
+ isHighlighted: item.isHighlighted,
1352
+ disabled: item.disabled,
1353
+ value: item.value
1354
+ };
1355
+ };
1356
+ const [injectComboboxItemContext, provideComboboxItemContext] = createContext('RdxComboboxItemContext', 'components/combobox');
1357
+ /**
1358
+ * A selectable option. Registers itself with the root for filtering and navigation. Highlight is
1359
+ * virtual (`data-highlighted` + `aria-activedescendant` on the input) — items never take DOM focus.
1360
+ *
1361
+ * @group Components
1362
+ */
1363
+ class RdxComboboxItem {
1364
+ constructor() {
1365
+ this.rootContext = injectComboboxRootContext();
1366
+ this.element = inject(ElementRef).nativeElement;
1367
+ this.id = injectId('rdx-combobox-item-');
1368
+ /** The option's value. */
1369
+ this.value = input.required(...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
1370
+ /** Explicit text matched against the query. Defaults to the element's text content. */
1371
+ this.textValueInput = input('', { ...(ngDevMode ? { debugName: "textValueInput" } : /* istanbul ignore next */ {}), alias: 'textValue' });
1372
+ /** Whether the option is disabled. */
1373
+ this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1374
+ /**
1375
+ * The option's index in the list. Required in virtualized mode — it drives the deterministic id
1376
+ * (for `aria-activedescendant`), highlight matching, and selection when only a window is mounted.
1377
+ */
1378
+ this.index = input(...(ngDevMode ? [undefined, { debugName: "index" }] : /* istanbul ignore next */ []));
1379
+ this.virtualized = this.rootContext.virtualized;
1380
+ this.autoTextValue = signal('', ...(ngDevMode ? [{ debugName: "autoTextValue" }] : /* istanbul ignore next */ []));
1381
+ this.textValue = computed(() => this.textValueInput() || this.autoTextValue(), ...(ngDevMode ? [{ debugName: "textValue" }] : /* istanbul ignore next */ []));
1382
+ /** Host id: deterministic per-index in virtualized mode, otherwise a generated unique id. */
1383
+ this.elementId = computed(() => this.virtualized() ? this.rootContext.itemId(this.index() ?? -1) : this.id, ...(ngDevMode ? [{ debugName: "elementId" }] : /* istanbul ignore next */ []));
1384
+ this.ariaSetSize = computed(() => this.virtualized() ? this.rootContext.filteredItems().length : undefined, ...(ngDevMode ? [{ debugName: "ariaSetSize" }] : /* istanbul ignore next */ []));
1385
+ this.ariaPosInSet = computed(() => (this.virtualized() ? (this.index() ?? -1) + 1 : undefined), ...(ngDevMode ? [{ debugName: "ariaPosInSet" }] : /* istanbul ignore next */ []));
1386
+ // Virtualized items are always rendered (the consumer only mounts the filtered window).
1387
+ this.isVisible = computed(() => (this.virtualized() ? true : this.rootContext.isVisible(this)), ...(ngDevMode ? [{ debugName: "isVisible" }] : /* istanbul ignore next */ []));
1388
+ this.isSelected = computed(() => this.rootContext.isSelected(this.value()), ...(ngDevMode ? [{ debugName: "isSelected" }] : /* istanbul ignore next */ []));
1389
+ this.isHighlighted = computed(() => this.virtualized()
1390
+ ? this.rootContext.highlightedIndex() === this.index()
1391
+ : this.rootContext.highlightedItem() === this, ...(ngDevMode ? [{ debugName: "isHighlighted" }] : /* istanbul ignore next */ []));
1392
+ this.group = injectComboboxGroupContext(true);
1393
+ const destroyRef = inject(DestroyRef);
1394
+ afterNextRender(() => {
1395
+ // Virtualized items are not registered: the root navigates over `items` data by index, and
1396
+ // a windowed item mounting/unmounting must not churn the DOM-ordered registry or `labelFor`.
1397
+ if (this.virtualized()) {
1398
+ return;
1399
+ }
1400
+ if (!this.textValueInput()) {
1401
+ // Derive the match/label text from the element, excluding decorative indicator text
1402
+ // (e.g. a checkmark) so it doesn't pollute filtering or the input label.
1403
+ const clone = this.element.cloneNode(true);
1404
+ clone.querySelectorAll('[rdxComboboxItemIndicator]').forEach((node) => node.remove());
1405
+ this.autoTextValue.set(clone.textContent?.trim() ?? '');
1406
+ }
1407
+ this.rootContext.registerItem(this);
1408
+ this.group?.registerItem(this);
1409
+ });
1410
+ destroyRef.onDestroy(() => {
1411
+ if (this.virtualized()) {
1412
+ return;
1413
+ }
1414
+ this.rootContext.unregisterItem(this);
1415
+ this.group?.unregisterItem(this);
1416
+ });
1417
+ }
1418
+ onPointerDown(event) {
1419
+ // Keep focus on the input; prevent the item from stealing focus on pointer/mouse down.
1420
+ event.preventDefault();
1421
+ this.rootContext.setKeyboardActive(false);
1422
+ }
1423
+ onPointerUp() {
1424
+ if (this.virtualized()) {
1425
+ this.rootContext.selectIndex(this.index() ?? -1);
1426
+ }
1427
+ else {
1428
+ this.rootContext.select(this);
1429
+ }
1430
+ }
1431
+ onPointerMove() {
1432
+ // Ignore the first move after keyboard navigation: arrow keys scroll the list under a still
1433
+ // cursor, and the resulting pointer event must not yank the highlight off the keyboard target.
1434
+ if (this.rootContext.isKeyboardActive()) {
1435
+ this.rootContext.setKeyboardActive(false);
1436
+ return;
1437
+ }
1438
+ if (this.disabled()) {
1439
+ return;
1440
+ }
1441
+ if (this.virtualized()) {
1442
+ this.rootContext.highlightIndex(this.index() ?? -1, 'pointer');
1443
+ }
1444
+ else if (this.isVisible()) {
1445
+ this.rootContext.setHighlight(this, 'pointer');
1446
+ }
1447
+ }
1448
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxItem, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1449
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxComboboxItem, isStandalone: true, selector: "[rdxComboboxItem]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, textValueInput: { classPropertyName: "textValueInput", publicName: "textValue", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, index: { classPropertyName: "index", publicName: "index", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "option" }, listeners: { "pointerdown": "onPointerDown($event)", "mousedown": "onPointerDown($event)", "pointerup": "onPointerUp()", "pointermove": "onPointerMove()" }, properties: { "attr.id": "elementId()", "attr.aria-selected": "isSelected()", "attr.aria-disabled": "disabled() ? \"true\" : undefined", "attr.aria-setsize": "ariaSetSize()", "attr.aria-posinset": "ariaPosInSet()", "attr.data-selected": "isSelected() ? \"\" : undefined", "attr.data-highlighted": "isHighlighted() ? \"\" : undefined", "attr.data-disabled": "disabled() ? \"\" : undefined", "hidden": "!isVisible()", "attr.data-hidden": "isVisible() ? undefined : \"\"" } }, providers: [provideComboboxItemContext(itemContext)], exportAs: ["rdxComboboxItem"], ngImport: i0 }); }
1450
+ }
1451
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxItem, decorators: [{
1452
+ type: Directive,
1453
+ args: [{
1454
+ selector: '[rdxComboboxItem]',
1455
+ exportAs: 'rdxComboboxItem',
1456
+ providers: [provideComboboxItemContext(itemContext)],
1457
+ host: {
1458
+ role: 'option',
1459
+ '[attr.id]': 'elementId()',
1460
+ '[attr.aria-selected]': 'isSelected()',
1461
+ '[attr.aria-disabled]': 'disabled() ? "true" : undefined',
1462
+ '[attr.aria-setsize]': 'ariaSetSize()',
1463
+ '[attr.aria-posinset]': 'ariaPosInSet()',
1464
+ '[attr.data-selected]': 'isSelected() ? "" : undefined',
1465
+ '[attr.data-highlighted]': 'isHighlighted() ? "" : undefined',
1466
+ '[attr.data-disabled]': 'disabled() ? "" : undefined',
1467
+ '[hidden]': '!isVisible()',
1468
+ '[attr.data-hidden]': 'isVisible() ? undefined : ""',
1469
+ '(pointerdown)': 'onPointerDown($event)',
1470
+ '(mousedown)': 'onPointerDown($event)',
1471
+ '(pointerup)': 'onPointerUp()',
1472
+ '(pointermove)': 'onPointerMove()'
1473
+ }
1474
+ }]
1475
+ }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], textValueInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "textValue", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], index: [{ type: i0.Input, args: [{ isSignal: true, alias: "index", required: false }] }] } });
1476
+
1477
+ /**
1478
+ * Renders only when its item is selected (e.g. a checkmark).
1479
+ *
1480
+ * @group Components
1481
+ */
1482
+ class RdxComboboxItemIndicator {
1483
+ constructor() {
1484
+ this.itemContext = injectComboboxItemContext();
1485
+ }
1486
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxItemIndicator, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1487
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxComboboxItemIndicator, isStandalone: true, selector: "[rdxComboboxItemIndicator]", host: { attributes: { "aria-hidden": "true" }, properties: { "hidden": "!itemContext.isSelected()" } }, exportAs: ["rdxComboboxItemIndicator"], ngImport: i0 }); }
1488
+ }
1489
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxItemIndicator, decorators: [{
1490
+ type: Directive,
1491
+ args: [{
1492
+ selector: '[rdxComboboxItemIndicator]',
1493
+ exportAs: 'rdxComboboxItemIndicator',
1494
+ host: {
1495
+ 'aria-hidden': 'true',
1496
+ '[hidden]': '!itemContext.isSelected()'
1497
+ }
1498
+ }]
1499
+ }] });
1500
+
1501
+ /**
1502
+ * An accessible label for the combobox. Registers its id so the input (and trigger) reference it via
1503
+ * `aria-labelledby`.
1504
+ *
1505
+ * @group Components
1506
+ */
1507
+ class RdxComboboxLabel {
1508
+ constructor() {
1509
+ this.rootContext = injectComboboxRootContext();
1510
+ this.id = injectId('rdx-combobox-label-');
1511
+ this.rootContext.setLabelId(this.id);
1512
+ inject(DestroyRef).onDestroy(() => this.rootContext.setLabelId(undefined));
1513
+ }
1514
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxLabel, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1515
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxComboboxLabel, isStandalone: true, selector: "[rdxComboboxLabel]", host: { properties: { "attr.id": "id" } }, exportAs: ["rdxComboboxLabel"], ngImport: i0 }); }
1516
+ }
1517
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxLabel, decorators: [{
1518
+ type: Directive,
1519
+ args: [{
1520
+ selector: '[rdxComboboxLabel]',
1521
+ exportAs: 'rdxComboboxLabel',
1522
+ host: {
1523
+ '[attr.id]': 'id'
1524
+ }
1525
+ }]
1526
+ }], ctorParameters: () => [] });
1527
+
1528
+ /**
1529
+ * The listbox container for options. Carries the id referenced by the input's `aria-controls`.
1530
+ *
1531
+ * @group Components
1532
+ */
1533
+ class RdxComboboxList {
1534
+ constructor() {
1535
+ this.rootContext = injectComboboxRootContext();
1536
+ }
1537
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxList, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1538
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxComboboxList, isStandalone: true, selector: "[rdxComboboxList]", host: { attributes: { "role": "listbox" }, properties: { "attr.id": "rootContext.listId", "attr.aria-multiselectable": "rootContext.multiple() ? \"true\" : undefined" } }, exportAs: ["rdxComboboxList"], ngImport: i0 }); }
1539
+ }
1540
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxList, decorators: [{
1541
+ type: Directive,
1542
+ args: [{
1543
+ selector: '[rdxComboboxList]',
1544
+ exportAs: 'rdxComboboxList',
1545
+ host: {
1546
+ role: 'listbox',
1547
+ '[attr.id]': 'rootContext.listId',
1548
+ '[attr.aria-multiselectable]': 'rootContext.multiple() ? "true" : undefined'
1549
+ }
1550
+ }]
1551
+ }] });
1552
+
1553
+ /**
1554
+ * The popup surface. Composes the popper content (for `data-side` / `data-align`) and a dismissable
1555
+ * layer for outside-dismiss. It does **not** trap focus — focus stays in the input throughout.
1556
+ *
1557
+ * @group Components
1558
+ */
1559
+ class RdxComboboxPopup {
1560
+ constructor() {
1561
+ this.rootContext = injectComboboxRootContext();
1562
+ this.dismissableLayer = inject(RdxDismissableLayer);
1563
+ this.popper = injectPopperContentWrapperContext();
1564
+ this.element = inject(ElementRef).nativeElement;
1565
+ // The popup mounts only while open, so locking on `modal` locks scroll for as long as a modal
1566
+ // popup is open and releases it on close.
1567
+ useScrollLock(this.rootContext.modal);
1568
+ // The popup's animation determines when the open/close transition (onOpenChangeComplete) is done.
1569
+ const unregister = this.rootContext.registerTransitionElement(this.element);
1570
+ inject(DestroyRef).onDestroy(unregister);
1571
+ // The input keeps focus while the popup is open; it is registered as a layer branch, so
1572
+ // focus/pointer interactions on it don't count as "outside" and won't self-dismiss. Escape
1573
+ // is handled by the input (which calls preventDefault), so the layer won't dismiss for it.
1574
+ this.dismissableLayer.dismiss.subscribe(() => this.rootContext.closePopup(true));
1575
+ // For the "input inside the popup" pattern, move focus to the input once the popup is
1576
+ // positioned. Focusing earlier fails in the browser: the portal `appendChild` blurs the
1577
+ // input and an unplaced popup isn't yet visible/focusable.
1578
+ effect(() => {
1579
+ if (!this.popper.isPositioned() || !this.rootContext.open()) {
1580
+ return;
1581
+ }
1582
+ const input = this.rootContext.inputElement();
1583
+ if (input && input.closest('[rdxComboboxPopup]')) {
1584
+ untracked(() => {
1585
+ input.focus();
1586
+ input.select();
1587
+ });
1588
+ }
1589
+ });
1590
+ }
1591
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxPopup, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1592
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxComboboxPopup, isStandalone: true, selector: "[rdxComboboxPopup]", host: { properties: { "attr.data-state": "rootContext.open() ? \"open\" : \"closed\"", "attr.data-open": "rootContext.open() ? \"\" : undefined", "attr.data-closed": "rootContext.open() ? undefined : \"\"", "attr.data-starting-style": "rootContext.transitionStatus() === \"starting\" ? \"\" : undefined", "attr.data-ending-style": "rootContext.transitionStatus() === \"ending\" ? \"\" : undefined" } }, providers: [
1593
+ // In modal mode, make content outside the popup inert (Base UI's `modal`).
1594
+ provideRdxDismissableLayerConfig(() => ({ disableOutsidePointerEvents: injectComboboxRootContext().modal }))
1595
+ ], exportAs: ["rdxComboboxPopup"], hostDirectives: [{ directive: i1.RdxPopperContent }, { directive: i1$1.RdxDismissableLayer }], ngImport: i0 }); }
1596
+ }
1597
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxPopup, decorators: [{
1598
+ type: Directive,
1599
+ args: [{
1600
+ selector: '[rdxComboboxPopup]',
1601
+ exportAs: 'rdxComboboxPopup',
1602
+ hostDirectives: [RdxPopperContent, RdxDismissableLayer],
1603
+ providers: [
1604
+ // In modal mode, make content outside the popup inert (Base UI's `modal`).
1605
+ provideRdxDismissableLayerConfig(() => ({ disableOutsidePointerEvents: injectComboboxRootContext().modal }))
1606
+ ],
1607
+ host: {
1608
+ '[attr.data-state]': 'rootContext.open() ? "open" : "closed"',
1609
+ '[attr.data-open]': 'rootContext.open() ? "" : undefined',
1610
+ '[attr.data-closed]': 'rootContext.open() ? undefined : ""',
1611
+ '[attr.data-starting-style]': 'rootContext.transitionStatus() === "starting" ? "" : undefined',
1612
+ '[attr.data-ending-style]': 'rootContext.transitionStatus() === "ending" ? "" : undefined'
1613
+ }
1614
+ }]
1615
+ }], ctorParameters: () => [] });
1616
+
1617
+ /**
1618
+ * Teleports the popup into a container (default `document.body`).
1619
+ *
1620
+ * @group Components
1621
+ */
1622
+ class RdxComboboxPortal {
1623
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxPortal, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1624
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxComboboxPortal, isStandalone: true, selector: "[rdxComboboxPortal]", exportAs: ["rdxComboboxPortal"], hostDirectives: [{ directive: i1$2.RdxPortal, inputs: ["container", "container"] }], ngImport: i0 }); }
1625
+ }
1626
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxPortal, decorators: [{
1627
+ type: Directive,
1628
+ args: [{
1629
+ selector: '[rdxComboboxPortal]',
1630
+ exportAs: 'rdxComboboxPortal',
1631
+ hostDirectives: [{ directive: RdxPortal, inputs: ['container'] }]
1632
+ }]
1633
+ }] });
1634
+
1635
+ /**
1636
+ * Wraps the popup template in {@link RdxPresenceDirective} so it mounts/unmounts with the open state
1637
+ * and can run enter/leave animations.
1638
+ *
1639
+ * @group Components
1640
+ */
1641
+ class RdxComboboxPortalPresence {
1642
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxPortalPresence, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1643
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxComboboxPortalPresence, isStandalone: true, selector: "ng-template[rdxComboboxPortalPresence]", providers: [
1644
+ provideRdxPresenceContext(() => {
1645
+ const context = injectComboboxRootContext();
1646
+ return { present: context.open };
1647
+ })
1648
+ ], hostDirectives: [{ directive: i1$3.RdxPresenceDirective }], ngImport: i0 }); }
1649
+ }
1650
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxPortalPresence, decorators: [{
1651
+ type: Directive,
1652
+ args: [{
1653
+ selector: 'ng-template[rdxComboboxPortalPresence]',
1654
+ hostDirectives: [RdxPresenceDirective],
1655
+ providers: [
1656
+ provideRdxPresenceContext(() => {
1657
+ const context = injectComboboxRootContext();
1658
+ return { present: context.open };
1659
+ })
1660
+ ]
1661
+ }]
1662
+ }] });
1663
+
1664
+ /**
1665
+ * Positions the popup relative to the input anchor using the popper engine. Re-exposes the popper
1666
+ * positioning inputs.
1667
+ *
1668
+ * @group Components
1669
+ */
1670
+ class RdxComboboxPositioner {
1671
+ constructor() {
1672
+ this.side = input('bottom', ...(ngDevMode ? [{ debugName: "side" }] : /* istanbul ignore next */ []));
1673
+ this.sideOffset = input(4, { ...(ngDevMode ? { debugName: "sideOffset" } : /* istanbul ignore next */ {}), transform: numberAttribute });
1674
+ this.align = input('start', ...(ngDevMode ? [{ debugName: "align" }] : /* istanbul ignore next */ []));
1675
+ this.alignOffset = input(0, { ...(ngDevMode ? { debugName: "alignOffset" } : /* istanbul ignore next */ {}), transform: numberAttribute });
1676
+ this.arrowPadding = input(0, { ...(ngDevMode ? { debugName: "arrowPadding" } : /* istanbul ignore next */ {}), transform: numberAttribute });
1677
+ this.avoidCollisions = input(true, { ...(ngDevMode ? { debugName: "avoidCollisions" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1678
+ this.collisionBoundary = input(...(ngDevMode ? [undefined, { debugName: "collisionBoundary" }] : /* istanbul ignore next */ []));
1679
+ this.collisionPadding = input(0, ...(ngDevMode ? [{ debugName: "collisionPadding" }] : /* istanbul ignore next */ []));
1680
+ this.sticky = input('partial', ...(ngDevMode ? [{ debugName: "sticky" }] : /* istanbul ignore next */ []));
1681
+ this.hideWhenDetached = input(false, { ...(ngDevMode ? { debugName: "hideWhenDetached" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1682
+ this.updatePositionStrategy = input('optimized', ...(ngDevMode ? [{ debugName: "updatePositionStrategy" }] : /* istanbul ignore next */ []));
1683
+ }
1684
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxPositioner, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1685
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxComboboxPositioner, isStandalone: true, selector: "[rdxComboboxPositioner]", inputs: { side: { classPropertyName: "side", publicName: "side", isSignal: true, isRequired: false, transformFunction: null }, sideOffset: { classPropertyName: "sideOffset", publicName: "sideOffset", isSignal: true, isRequired: false, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, alignOffset: { classPropertyName: "alignOffset", publicName: "alignOffset", isSignal: true, isRequired: false, transformFunction: null }, arrowPadding: { classPropertyName: "arrowPadding", publicName: "arrowPadding", isSignal: true, isRequired: false, transformFunction: null }, avoidCollisions: { classPropertyName: "avoidCollisions", publicName: "avoidCollisions", isSignal: true, isRequired: false, transformFunction: null }, collisionBoundary: { classPropertyName: "collisionBoundary", publicName: "collisionBoundary", isSignal: true, isRequired: false, transformFunction: null }, collisionPadding: { classPropertyName: "collisionPadding", publicName: "collisionPadding", isSignal: true, isRequired: false, transformFunction: null }, sticky: { classPropertyName: "sticky", publicName: "sticky", isSignal: true, isRequired: false, transformFunction: null }, hideWhenDetached: { classPropertyName: "hideWhenDetached", publicName: "hideWhenDetached", isSignal: true, isRequired: false, transformFunction: null }, updatePositionStrategy: { classPropertyName: "updatePositionStrategy", publicName: "updatePositionStrategy", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "style": "{\n 'boxSizing': 'border-box',\n '--radix-combobox-content-transform-origin': 'var(--radix-popper-transform-origin)',\n '--radix-combobox-content-available-width': 'var(--radix-popper-available-width)',\n '--radix-combobox-content-available-height': 'var(--radix-popper-available-height)',\n '--radix-combobox-trigger-width': 'var(--radix-popper-anchor-width)',\n '--radix-combobox-trigger-height': 'var(--radix-popper-anchor-height)'\n }" } }, exportAs: ["rdxComboboxPositioner"], hostDirectives: [{ directive: i1.RdxPopperContentWrapper, inputs: ["side", "side", "sideOffset", "sideOffset", "align", "align", "alignOffset", "alignOffset", "arrowPadding", "arrowPadding", "avoidCollisions", "avoidCollisions", "collisionBoundary", "collisionBoundary", "collisionPadding", "collisionPadding", "sticky", "sticky", "hideWhenDetached", "hideWhenDetached", "updatePositionStrategy", "updatePositionStrategy"] }], ngImport: i0 }); }
1686
+ }
1687
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxPositioner, decorators: [{
1688
+ type: Directive,
1689
+ args: [{
1690
+ selector: '[rdxComboboxPositioner]',
1691
+ exportAs: 'rdxComboboxPositioner',
1692
+ hostDirectives: [
1693
+ {
1694
+ directive: RdxPopperContentWrapper,
1695
+ inputs: [
1696
+ 'side',
1697
+ 'sideOffset',
1698
+ 'align',
1699
+ 'alignOffset',
1700
+ 'arrowPadding',
1701
+ 'avoidCollisions',
1702
+ 'collisionBoundary',
1703
+ 'collisionPadding',
1704
+ 'sticky',
1705
+ 'hideWhenDetached',
1706
+ 'updatePositionStrategy'
1707
+ ]
1708
+ }
1709
+ ],
1710
+ host: {
1711
+ '[style]': `{
1712
+ 'boxSizing': 'border-box',
1713
+ '--radix-combobox-content-transform-origin': 'var(--radix-popper-transform-origin)',
1714
+ '--radix-combobox-content-available-width': 'var(--radix-popper-available-width)',
1715
+ '--radix-combobox-content-available-height': 'var(--radix-popper-available-height)',
1716
+ '--radix-combobox-trigger-width': 'var(--radix-popper-anchor-width)',
1717
+ '--radix-combobox-trigger-height': 'var(--radix-popper-anchor-height)'
1718
+ }`
1719
+ }
1720
+ }]
1721
+ }], propDecorators: { side: [{ type: i0.Input, args: [{ isSignal: true, alias: "side", required: false }] }], sideOffset: [{ type: i0.Input, args: [{ isSignal: true, alias: "sideOffset", required: false }] }], align: [{ type: i0.Input, args: [{ isSignal: true, alias: "align", required: false }] }], alignOffset: [{ type: i0.Input, args: [{ isSignal: true, alias: "alignOffset", required: false }] }], arrowPadding: [{ type: i0.Input, args: [{ isSignal: true, alias: "arrowPadding", required: false }] }], avoidCollisions: [{ type: i0.Input, args: [{ isSignal: true, alias: "avoidCollisions", required: false }] }], collisionBoundary: [{ type: i0.Input, args: [{ isSignal: true, alias: "collisionBoundary", required: false }] }], collisionPadding: [{ type: i0.Input, args: [{ isSignal: true, alias: "collisionPadding", required: false }] }], sticky: [{ type: i0.Input, args: [{ isSignal: true, alias: "sticky", required: false }] }], hideWhenDetached: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideWhenDetached", required: false }] }], updatePositionStrategy: [{ type: i0.Input, args: [{ isSignal: true, alias: "updatePositionStrategy", required: false }] }] } });
1722
+
1723
+ /**
1724
+ * A polite live region for async status (loading, result counts) announced without moving focus.
1725
+ *
1726
+ * @group Components
1727
+ */
1728
+ class RdxComboboxStatus {
1729
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxStatus, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1730
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxComboboxStatus, isStandalone: true, selector: "[rdxComboboxStatus]", host: { attributes: { "role": "status", "aria-live": "polite" } }, exportAs: ["rdxComboboxStatus"], ngImport: i0 }); }
1731
+ }
1732
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxStatus, decorators: [{
1733
+ type: Directive,
1734
+ args: [{
1735
+ selector: '[rdxComboboxStatus]',
1736
+ exportAs: 'rdxComboboxStatus',
1737
+ host: {
1738
+ role: 'status',
1739
+ 'aria-live': 'polite'
1740
+ }
1741
+ }]
1742
+ }] });
1743
+
1744
+ /**
1745
+ * Toggles the combobox popup. Carries `tabindex="-1"` so it never steals focus from the input.
1746
+ *
1747
+ * @group Components
1748
+ */
1749
+ class RdxComboboxTrigger {
1750
+ constructor() {
1751
+ this.rootContext = injectComboboxRootContext();
1752
+ this.element = inject(ElementRef).nativeElement;
1753
+ this.rootContext.registerTrigger(this.element);
1754
+ inject(DestroyRef).onDestroy(() => this.rootContext.registerTrigger(null));
1755
+ }
1756
+ onClick() {
1757
+ if (this.rootContext.open()) {
1758
+ this.rootContext.closePopup(true);
1759
+ }
1760
+ else {
1761
+ this.rootContext.focusInput();
1762
+ this.rootContext.openForBrowse();
1763
+ }
1764
+ }
1765
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxTrigger, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1766
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxComboboxTrigger, isStandalone: true, selector: "button[rdxComboboxTrigger]", host: { attributes: { "type": "button", "tabindex": "-1", "aria-label": "Open" }, listeners: { "click": "onClick()" }, properties: { "attr.aria-expanded": "rootContext.open()", "attr.aria-controls": "rootContext.listId", "attr.aria-labelledby": "rootContext.labelId()", "attr.disabled": "rootContext.disabledState() ? \"\" : undefined", "attr.data-popup-open": "rootContext.open() ? \"\" : undefined", "attr.data-disabled": "rootContext.disabledState() ? \"\" : undefined" } }, exportAs: ["rdxComboboxTrigger"], hostDirectives: [{ directive: i1$1.RdxDismissableLayerBranch }], ngImport: i0 }); }
1767
+ }
1768
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxTrigger, decorators: [{
1769
+ type: Directive,
1770
+ args: [{
1771
+ selector: 'button[rdxComboboxTrigger]',
1772
+ exportAs: 'rdxComboboxTrigger',
1773
+ hostDirectives: [RdxDismissableLayerBranch],
1774
+ host: {
1775
+ type: 'button',
1776
+ tabindex: '-1',
1777
+ 'aria-label': 'Open',
1778
+ '[attr.aria-expanded]': 'rootContext.open()',
1779
+ '[attr.aria-controls]': 'rootContext.listId',
1780
+ '[attr.aria-labelledby]': 'rootContext.labelId()',
1781
+ '[attr.disabled]': 'rootContext.disabledState() ? "" : undefined',
1782
+ '[attr.data-popup-open]': 'rootContext.open() ? "" : undefined',
1783
+ '[attr.data-disabled]': 'rootContext.disabledState() ? "" : undefined',
1784
+ '(click)': 'onClick()'
1785
+ }
1786
+ }]
1787
+ }], ctorParameters: () => [] });
1788
+
1789
+ /**
1790
+ * Renders the current selection's label(s) — typically inside a {@link RdxComboboxTrigger} for a
1791
+ * select-like control. Read `slotText()` in the template, or compose your own from `selectedLabels()`.
1792
+ * Exposes `data-placeholder` while nothing is selected.
1793
+ *
1794
+ * @example
1795
+ * <span #value="rdxComboboxValue" rdxComboboxValue placeholder="Select…">{{ value.slotText() }}</span>
1796
+ *
1797
+ * @group Components
1798
+ */
1799
+ class RdxComboboxValue {
1800
+ constructor() {
1801
+ this.rootContext = injectComboboxRootContext();
1802
+ /** Text shown when there is no selection. */
1803
+ this.placeholder = input(...(ngDevMode ? [undefined, { debugName: "placeholder" }] : /* istanbul ignore next */ []));
1804
+ /** The label(s) of the current selection, resolved via the root's `itemToStringLabel`/items. */
1805
+ this.selectedLabels = computed(() => {
1806
+ const value = this.rootContext.value();
1807
+ if (Array.isArray(value)) {
1808
+ return value.map((v) => this.rootContext.labelFor(v)).filter(Boolean);
1809
+ }
1810
+ return isNullish(value) ? [] : [this.rootContext.labelFor(value)].filter(Boolean);
1811
+ }, ...(ngDevMode ? [{ debugName: "selectedLabels" }] : /* istanbul ignore next */ []));
1812
+ this.isEmpty = computed(() => this.selectedLabels().length === 0, ...(ngDevMode ? [{ debugName: "isEmpty" }] : /* istanbul ignore next */ []));
1813
+ /** The selection joined for display, or the `placeholder` when empty. */
1814
+ this.slotText = computed(() => {
1815
+ const labels = this.selectedLabels();
1816
+ return labels.length ? labels.join(', ') : (this.placeholder() ?? '');
1817
+ }, ...(ngDevMode ? [{ debugName: "slotText" }] : /* istanbul ignore next */ []));
1818
+ }
1819
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxValue, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1820
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxComboboxValue, isStandalone: true, selector: "[rdxComboboxValue]", inputs: { placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-placeholder": "isEmpty() ? \"\" : undefined" } }, exportAs: ["rdxComboboxValue"], ngImport: i0 }); }
1821
+ }
1822
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxValue, decorators: [{
1823
+ type: Directive,
1824
+ args: [{
1825
+ selector: '[rdxComboboxValue]',
1826
+ exportAs: 'rdxComboboxValue',
1827
+ host: {
1828
+ '[attr.data-placeholder]': 'isEmpty() ? "" : undefined'
1829
+ }
1830
+ }]
1831
+ }], propDecorators: { placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }] } });
1832
+
1833
+ const _importsCombobox = [
1834
+ RdxComboboxRoot,
1835
+ RdxComboboxAnchor,
1836
+ RdxComboboxLabel,
1837
+ RdxComboboxInput,
1838
+ RdxComboboxValue,
1839
+ RdxComboboxTrigger,
1840
+ RdxComboboxIcon,
1841
+ RdxComboboxClear,
1842
+ RdxComboboxPortal,
1843
+ RdxComboboxPortalPresence,
1844
+ RdxComboboxBackdrop,
1845
+ RdxComboboxPositioner,
1846
+ RdxComboboxPopup,
1847
+ RdxComboboxArrow,
1848
+ RdxComboboxList,
1849
+ RdxComboboxItem,
1850
+ RdxComboboxItemIndicator,
1851
+ RdxComboboxGroup,
1852
+ RdxComboboxGroupLabel,
1853
+ RdxComboboxEmpty,
1854
+ RdxComboboxStatus,
1855
+ RdxComboboxChips,
1856
+ RdxComboboxChip,
1857
+ RdxComboboxChipRemove
1858
+ ];
1859
+ class RdxComboboxModule {
1860
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
1861
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxModule, imports: [RdxComboboxRoot,
1862
+ RdxComboboxAnchor,
1863
+ RdxComboboxLabel,
1864
+ RdxComboboxInput,
1865
+ RdxComboboxValue,
1866
+ RdxComboboxTrigger,
1867
+ RdxComboboxIcon,
1868
+ RdxComboboxClear,
1869
+ RdxComboboxPortal,
1870
+ RdxComboboxPortalPresence,
1871
+ RdxComboboxBackdrop,
1872
+ RdxComboboxPositioner,
1873
+ RdxComboboxPopup,
1874
+ RdxComboboxArrow,
1875
+ RdxComboboxList,
1876
+ RdxComboboxItem,
1877
+ RdxComboboxItemIndicator,
1878
+ RdxComboboxGroup,
1879
+ RdxComboboxGroupLabel,
1880
+ RdxComboboxEmpty,
1881
+ RdxComboboxStatus,
1882
+ RdxComboboxChips,
1883
+ RdxComboboxChip,
1884
+ RdxComboboxChipRemove], exports: [RdxComboboxRoot,
1885
+ RdxComboboxAnchor,
1886
+ RdxComboboxLabel,
1887
+ RdxComboboxInput,
1888
+ RdxComboboxValue,
1889
+ RdxComboboxTrigger,
1890
+ RdxComboboxIcon,
1891
+ RdxComboboxClear,
1892
+ RdxComboboxPortal,
1893
+ RdxComboboxPortalPresence,
1894
+ RdxComboboxBackdrop,
1895
+ RdxComboboxPositioner,
1896
+ RdxComboboxPopup,
1897
+ RdxComboboxArrow,
1898
+ RdxComboboxList,
1899
+ RdxComboboxItem,
1900
+ RdxComboboxItemIndicator,
1901
+ RdxComboboxGroup,
1902
+ RdxComboboxGroupLabel,
1903
+ RdxComboboxEmpty,
1904
+ RdxComboboxStatus,
1905
+ RdxComboboxChips,
1906
+ RdxComboboxChip,
1907
+ RdxComboboxChipRemove] }); }
1908
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxModule }); }
1909
+ }
1910
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxComboboxModule, decorators: [{
1911
+ type: NgModule,
1912
+ args: [{
1913
+ imports: [..._importsCombobox],
1914
+ exports: [..._importsCombobox]
1915
+ }]
1916
+ }] });
1917
+
1918
+ /**
1919
+ * Generated bundle index. Do not edit.
1920
+ */
1921
+
1922
+ export { RdxComboboxAnchor, RdxComboboxArrow, RdxComboboxBackdrop, RdxComboboxChip, RdxComboboxChipRemove, RdxComboboxChips, RdxComboboxClear, RdxComboboxEmpty, RdxComboboxGroup, RdxComboboxGroupLabel, RdxComboboxIcon, RdxComboboxInput, RdxComboboxItem, RdxComboboxItemIndicator, RdxComboboxLabel, RdxComboboxList, RdxComboboxModule, RdxComboboxPopup, RdxComboboxPortal, RdxComboboxPortalPresence, RdxComboboxPositioner, RdxComboboxRoot, RdxComboboxStatus, RdxComboboxTrigger, RdxComboboxValue, _importsCombobox, injectComboboxChipContext, injectComboboxGroupContext, injectComboboxItemContext, injectComboboxRootContext, provideComboboxChipContext, provideComboboxGroupContext, provideComboboxItemContext, provideComboboxRootContext };
1923
+ //# sourceMappingURL=radix-ng-primitives-combobox.mjs.map