@radix-ng/primitives 1.0.5 → 1.0.7

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 (138) hide show
  1. package/aspect-ratio/README.md +1 -1
  2. package/fesm2022/radix-ng-primitives-accordion.mjs +40 -27
  3. package/fesm2022/radix-ng-primitives-accordion.mjs.map +1 -1
  4. package/fesm2022/radix-ng-primitives-alert-dialog.mjs +34 -34
  5. package/fesm2022/radix-ng-primitives-arrow.mjs +3 -3
  6. package/fesm2022/radix-ng-primitives-aspect-ratio.mjs +11 -35
  7. package/fesm2022/radix-ng-primitives-aspect-ratio.mjs.map +1 -1
  8. package/fesm2022/radix-ng-primitives-autocomplete.mjs +175 -125
  9. package/fesm2022/radix-ng-primitives-autocomplete.mjs.map +1 -1
  10. package/fesm2022/radix-ng-primitives-avatar.mjs +92 -35
  11. package/fesm2022/radix-ng-primitives-avatar.mjs.map +1 -1
  12. package/fesm2022/radix-ng-primitives-button.mjs +11 -7
  13. package/fesm2022/radix-ng-primitives-button.mjs.map +1 -1
  14. package/fesm2022/radix-ng-primitives-calendar.mjs +140 -90
  15. package/fesm2022/radix-ng-primitives-calendar.mjs.map +1 -1
  16. package/fesm2022/radix-ng-primitives-checkbox.mjs +63 -46
  17. package/fesm2022/radix-ng-primitives-checkbox.mjs.map +1 -1
  18. package/fesm2022/radix-ng-primitives-collapsible.mjs +37 -25
  19. package/fesm2022/radix-ng-primitives-collapsible.mjs.map +1 -1
  20. package/fesm2022/radix-ng-primitives-combobox.mjs +278 -177
  21. package/fesm2022/radix-ng-primitives-combobox.mjs.map +1 -1
  22. package/fesm2022/radix-ng-primitives-composite.mjs +50 -33
  23. package/fesm2022/radix-ng-primitives-composite.mjs.map +1 -1
  24. package/fesm2022/radix-ng-primitives-config.mjs +7 -5
  25. package/fesm2022/radix-ng-primitives-config.mjs.map +1 -1
  26. package/fesm2022/radix-ng-primitives-context-menu.mjs +48 -22
  27. package/fesm2022/radix-ng-primitives-context-menu.mjs.map +1 -1
  28. package/fesm2022/radix-ng-primitives-core.mjs +44 -28
  29. package/fesm2022/radix-ng-primitives-core.mjs.map +1 -1
  30. package/fesm2022/radix-ng-primitives-cropper.mjs +73 -45
  31. package/fesm2022/radix-ng-primitives-cropper.mjs.map +1 -1
  32. package/fesm2022/radix-ng-primitives-date-field.mjs +70 -40
  33. package/fesm2022/radix-ng-primitives-date-field.mjs.map +1 -1
  34. package/fesm2022/radix-ng-primitives-dialog.mjs +93 -67
  35. package/fesm2022/radix-ng-primitives-dialog.mjs.map +1 -1
  36. package/fesm2022/radix-ng-primitives-direction-provider.mjs +5 -4
  37. package/fesm2022/radix-ng-primitives-direction-provider.mjs.map +1 -1
  38. package/fesm2022/radix-ng-primitives-dismissable-layer.mjs +20 -16
  39. package/fesm2022/radix-ng-primitives-dismissable-layer.mjs.map +1 -1
  40. package/fesm2022/radix-ng-primitives-drawer.mjs +107 -81
  41. package/fesm2022/radix-ng-primitives-drawer.mjs.map +1 -1
  42. package/fesm2022/radix-ng-primitives-editable.mjs +53 -39
  43. package/fesm2022/radix-ng-primitives-editable.mjs.map +1 -1
  44. package/fesm2022/radix-ng-primitives-field.mjs +76 -50
  45. package/fesm2022/radix-ng-primitives-field.mjs.map +1 -1
  46. package/fesm2022/radix-ng-primitives-fieldset.mjs +8 -7
  47. package/fesm2022/radix-ng-primitives-fieldset.mjs.map +1 -1
  48. package/fesm2022/radix-ng-primitives-floating-focus-manager.mjs +77 -40
  49. package/fesm2022/radix-ng-primitives-floating-focus-manager.mjs.map +1 -1
  50. package/fesm2022/radix-ng-primitives-focus-guards.mjs +3 -3
  51. package/fesm2022/radix-ng-primitives-focus-scope.mjs +7 -5
  52. package/fesm2022/radix-ng-primitives-focus-scope.mjs.map +1 -1
  53. package/fesm2022/radix-ng-primitives-form.mjs +26 -17
  54. package/fesm2022/radix-ng-primitives-form.mjs.map +1 -1
  55. package/fesm2022/radix-ng-primitives-input.mjs +41 -22
  56. package/fesm2022/radix-ng-primitives-input.mjs.map +1 -1
  57. package/fesm2022/radix-ng-primitives-label.mjs +11 -8
  58. package/fesm2022/radix-ng-primitives-label.mjs.map +1 -1
  59. package/fesm2022/radix-ng-primitives-menu.mjs +274 -154
  60. package/fesm2022/radix-ng-primitives-menu.mjs.map +1 -1
  61. package/fesm2022/radix-ng-primitives-menubar.mjs +35 -17
  62. package/fesm2022/radix-ng-primitives-menubar.mjs.map +1 -1
  63. package/fesm2022/radix-ng-primitives-meter.mjs +41 -30
  64. package/fesm2022/radix-ng-primitives-meter.mjs.map +1 -1
  65. package/fesm2022/radix-ng-primitives-navigation-menu.mjs +220 -183
  66. package/fesm2022/radix-ng-primitives-navigation-menu.mjs.map +1 -1
  67. package/fesm2022/radix-ng-primitives-number-field.mjs +93 -61
  68. package/fesm2022/radix-ng-primitives-number-field.mjs.map +1 -1
  69. package/fesm2022/radix-ng-primitives-pagination.mjs +48 -38
  70. package/fesm2022/radix-ng-primitives-pagination.mjs.map +1 -1
  71. package/fesm2022/radix-ng-primitives-popover.mjs +150 -107
  72. package/fesm2022/radix-ng-primitives-popover.mjs.map +1 -1
  73. package/fesm2022/radix-ng-primitives-popper.mjs +79 -47
  74. package/fesm2022/radix-ng-primitives-popper.mjs.map +1 -1
  75. package/fesm2022/radix-ng-primitives-portal.mjs +20 -13
  76. package/fesm2022/radix-ng-primitives-portal.mjs.map +1 -1
  77. package/fesm2022/radix-ng-primitives-presence.mjs +3 -3
  78. package/fesm2022/radix-ng-primitives-preview-card.mjs +119 -74
  79. package/fesm2022/radix-ng-primitives-preview-card.mjs.map +1 -1
  80. package/fesm2022/radix-ng-primitives-progress.mjs +73 -46
  81. package/fesm2022/radix-ng-primitives-progress.mjs.map +1 -1
  82. package/fesm2022/radix-ng-primitives-radio.mjs +58 -40
  83. package/fesm2022/radix-ng-primitives-radio.mjs.map +1 -1
  84. package/fesm2022/radix-ng-primitives-scroll-area.mjs +57 -41
  85. package/fesm2022/radix-ng-primitives-scroll-area.mjs.map +1 -1
  86. package/fesm2022/radix-ng-primitives-select.mjs +595 -102
  87. package/fesm2022/radix-ng-primitives-select.mjs.map +1 -1
  88. package/fesm2022/radix-ng-primitives-separator.mjs +11 -15
  89. package/fesm2022/radix-ng-primitives-separator.mjs.map +1 -1
  90. package/fesm2022/radix-ng-primitives-slider.mjs +101 -64
  91. package/fesm2022/radix-ng-primitives-slider.mjs.map +1 -1
  92. package/fesm2022/radix-ng-primitives-stepper.mjs +53 -39
  93. package/fesm2022/radix-ng-primitives-stepper.mjs.map +1 -1
  94. package/fesm2022/radix-ng-primitives-switch.mjs +25 -20
  95. package/fesm2022/radix-ng-primitives-switch.mjs.map +1 -1
  96. package/fesm2022/radix-ng-primitives-tabs.mjs +76 -51
  97. package/fesm2022/radix-ng-primitives-tabs.mjs.map +1 -1
  98. package/fesm2022/radix-ng-primitives-time-field.mjs +72 -41
  99. package/fesm2022/radix-ng-primitives-time-field.mjs.map +1 -1
  100. package/fesm2022/radix-ng-primitives-toast.mjs +75 -56
  101. package/fesm2022/radix-ng-primitives-toast.mjs.map +1 -1
  102. package/fesm2022/radix-ng-primitives-toggle-group.mjs +39 -23
  103. package/fesm2022/radix-ng-primitives-toggle-group.mjs.map +1 -1
  104. package/fesm2022/radix-ng-primitives-toggle.mjs +36 -36
  105. package/fesm2022/radix-ng-primitives-toggle.mjs.map +1 -1
  106. package/fesm2022/radix-ng-primitives-toolbar.mjs +52 -31
  107. package/fesm2022/radix-ng-primitives-toolbar.mjs.map +1 -1
  108. package/fesm2022/radix-ng-primitives-tooltip.mjs +118 -63
  109. package/fesm2022/radix-ng-primitives-tooltip.mjs.map +1 -1
  110. package/fesm2022/radix-ng-primitives-visually-hidden.mjs +31 -20
  111. package/fesm2022/radix-ng-primitives-visually-hidden.mjs.map +1 -1
  112. package/package.json +2 -2
  113. package/types/radix-ng-primitives-accordion.d.ts +1 -0
  114. package/types/radix-ng-primitives-aspect-ratio.d.ts +10 -23
  115. package/types/radix-ng-primitives-autocomplete.d.ts +3 -1
  116. package/types/radix-ng-primitives-avatar.d.ts +51 -25
  117. package/types/radix-ng-primitives-checkbox.d.ts +26 -98
  118. package/types/radix-ng-primitives-combobox.d.ts +28 -9
  119. package/types/radix-ng-primitives-context-menu.d.ts +2 -0
  120. package/types/radix-ng-primitives-dialog.d.ts +6 -3
  121. package/types/radix-ng-primitives-field.d.ts +33 -207
  122. package/types/radix-ng-primitives-floating-focus-manager.d.ts +11 -0
  123. package/types/radix-ng-primitives-form.d.ts +24 -33
  124. package/types/radix-ng-primitives-label.d.ts +3 -2
  125. package/types/radix-ng-primitives-menu.d.ts +47 -15
  126. package/types/radix-ng-primitives-menubar.d.ts +6 -1
  127. package/types/radix-ng-primitives-navigation-menu.d.ts +8 -6
  128. package/types/radix-ng-primitives-number-field.d.ts +1 -1
  129. package/types/radix-ng-primitives-popover.d.ts +12 -4
  130. package/types/radix-ng-primitives-preview-card.d.ts +18 -5
  131. package/types/radix-ng-primitives-progress.d.ts +59 -121
  132. package/types/radix-ng-primitives-radio.d.ts +0 -1
  133. package/types/radix-ng-primitives-select.d.ts +186 -391
  134. package/types/radix-ng-primitives-separator.d.ts +0 -7
  135. package/types/radix-ng-primitives-toggle-group.d.ts +7 -3
  136. package/types/radix-ng-primitives-toggle.d.ts +8 -8
  137. package/types/radix-ng-primitives-toolbar.d.ts +2 -0
  138. package/types/radix-ng-primitives-tooltip.d.ts +17 -3
@@ -1,7 +1,7 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, model, signal, computed, ElementRef, input, booleanAttribute, output, untracked, effect, Directive, InjectionToken, Injector, DestroyRef, afterNextRender, linkedSignal, isDevMode, forwardRef } from '@angular/core';
2
+ import { inject, model, signal, computed, ElementRef, input, booleanAttribute, output, effect, untracked, Directive, InjectionToken, Injector, DestroyRef, afterNextRender, linkedSignal, contentChild, forwardRef, isDevMode } from '@angular/core';
3
3
  import * as i3 from '@radix-ng/primitives/core';
4
- import { isEqual, getActiveElement, createContext, injectId, createFloatingRootContext, useTransitionStatus, isNullish, createCancelableChangeEventDetails, provideFloatingTree, provideFloatingRootContext, RDX_FLOATING_ROOT_CONTEXT, RDX_FLOATING_REGISTRATION, useListHighlight, useAnchoredScrollLock, RdxFloatingNodeRegistration, handleAndDispatchCustomEvent, rdxDevError, setupInternalBackdrop } from '@radix-ng/primitives/core';
4
+ import { isEqual, getActiveElement, createContext, injectId, createFloatingRootContext, useTransitionStatus, isNullish, createCancelableChangeEventDetails, provideFloatingTree, provideFloatingRootContext, RDX_FLOATING_ROOT_CONTEXT, RDX_FLOATING_REGISTRATION, useListHighlight, useAnchoredScrollLock, RdxFloatingNodeRegistration, handleAndDispatchCustomEvent, resizeEffect, clamp, rdxDevError, setupInternalBackdrop } from '@radix-ng/primitives/core';
5
5
  import { injectDirection } from '@radix-ng/primitives/direction-provider';
6
6
  import * as i2 from '@radix-ng/primitives/floating-focus-manager';
7
7
  import { getInteractionTypeFromEvent, provideFloatingFocusManagerConfig, RdxFloatingFocusManager, createRdxTriggerInteraction } from '@radix-ng/primitives/floating-focus-manager';
@@ -70,6 +70,8 @@ const context$2 = () => {
70
70
  openInteractionType: context.openInteractionType,
71
71
  closeInteractionType: context.closeInteractionType,
72
72
  disabled: context.disabled,
73
+ readOnly: context.readOnly,
74
+ required: context.required,
73
75
  modal: context.modal,
74
76
  isEmptyModelValue: context.isEmptyModelValue,
75
77
  transitionStatus: context.transitionStatus,
@@ -102,31 +104,48 @@ const [injectSelectRootContext, provideSelectRootContext] = createContext('RdxSe
102
104
  class RdxSelectRoot {
103
105
  constructor() {
104
106
  this.contentId = injectId('rdx-select-content-');
105
- this.open = model(false, ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
107
+ this.open = model(false, /* @ts-ignore */
108
+ ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
106
109
  /** Whether the current open was initiated by **touch** (ADR 0016 §3 — gates the anchored scroll lock). */
107
- this.openedByTouch = signal(false, ...(ngDevMode ? [{ debugName: "openedByTouch" }] : /* istanbul ignore next */ []));
110
+ this.openedByTouch = signal(false, /* @ts-ignore */
111
+ ...(ngDevMode ? [{ debugName: "openedByTouch" }] : /* istanbul ignore next */ []));
108
112
  /** How the select was opened. Base UI names this state `openMethod`. */
109
- this.openInteractionType = signal(null, ...(ngDevMode ? [{ debugName: "openInteractionType" }] : /* istanbul ignore next */ []));
113
+ this.openInteractionType = signal(null, /* @ts-ignore */
114
+ ...(ngDevMode ? [{ debugName: "openInteractionType" }] : /* istanbul ignore next */ []));
110
115
  /** How the select was closed. */
111
- this.closeInteractionType = signal(null, ...(ngDevMode ? [{ debugName: "closeInteractionType" }] : /* istanbul ignore next */ []));
116
+ this.closeInteractionType = signal(null, /* @ts-ignore */
117
+ ...(ngDevMode ? [{ debugName: "closeInteractionType" }] : /* istanbul ignore next */ []));
112
118
  /** Public Base UI-aligned alias for the open interaction type. */
113
- this.openMethod = computed(() => this.openInteractionType(), ...(ngDevMode ? [{ debugName: "openMethod" }] : /* istanbul ignore next */ []));
119
+ this.openMethod = computed(() => this.openInteractionType(), /* @ts-ignore */
120
+ ...(ngDevMode ? [{ debugName: "openMethod" }] : /* istanbul ignore next */ []));
114
121
  /** Per-popup floating root context (ADR 0015) — `open` / `triggers` / reference for the dismissal engine. */
115
122
  this.floatingContext = createFloatingRootContext({
116
123
  ownerDocument: inject(ElementRef).nativeElement.ownerDocument,
117
124
  open: () => this.open()
118
125
  });
119
- this.value = model(...(ngDevMode ? [undefined, { debugName: "value" }] : /* istanbul ignore next */ []));
126
+ this.value = model(/* @ts-ignore */
127
+ ...(ngDevMode ? [undefined, { debugName: "value" }] : /* istanbul ignore next */ []));
128
+ /** Initial value for the uncontrolled case. Applied once on init; `value`/`[(value)]` take over after. */
129
+ this.defaultValue = input(/* @ts-ignore */
130
+ ...(ngDevMode ? [undefined, { debugName: "defaultValue" }] : /* istanbul ignore next */ []));
131
+ /** Whether the select is initially open (uncontrolled). Applied once on init. */
132
+ this.defaultOpen = input(false, { ...(ngDevMode ? { debugName: "defaultOpen" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
120
133
  this.multiple = input(false, { ...(ngDevMode ? { debugName: "multiple" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
121
134
  this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
135
+ /** When `true`, the value cannot be changed by the user (the popup can still be opened to view it). */
136
+ this.readOnly = input(false, { ...(ngDevMode ? { debugName: "readOnly" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
137
+ /** Marks the control as required — reflected on the trigger as `aria-required` / `data-required`. */
138
+ this.required = input(false, { ...(ngDevMode ? { debugName: "required" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
122
139
  /** Whether the popup is modal: locks page scroll and makes outside content inert while open. */
123
140
  this.modal = input(true, { ...(ngDevMode ? { debugName: "modal" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
124
141
  this.dirInput = input(undefined, { ...(ngDevMode ? { debugName: "dirInput" } : /* istanbul ignore next */ {}), alias: 'dir' });
125
142
  this.dir = injectDirection(this.dirInput);
126
143
  /** How item values are compared for equality — a function `(a, b) => boolean` or an object key. */
127
- this.isItemEqualToValue = input(...(ngDevMode ? [undefined, { debugName: "isItemEqualToValue" }] : /* istanbul ignore next */ []));
144
+ this.isItemEqualToValue = input(/* @ts-ignore */
145
+ ...(ngDevMode ? [undefined, { debugName: "isItemEqualToValue" }] : /* istanbul ignore next */ []));
128
146
  /** Converts a value to its display label (used by `RdxSelectValue`). */
129
- this.itemToStringLabel = input(...(ngDevMode ? [undefined, { debugName: "itemToStringLabel" }] : /* istanbul ignore next */ []));
147
+ this.itemToStringLabel = input(/* @ts-ignore */
148
+ ...(ngDevMode ? [undefined, { debugName: "itemToStringLabel" }] : /* istanbul ignore next */ []));
130
149
  /** Emits before an open-state change is committed; call `eventDetails.cancel()` to veto it. */
131
150
  this.onOpenChange = output();
132
151
  /** Emits before a value change is committed; call `eventDetails.cancel()` to veto it. */
@@ -144,8 +163,12 @@ class RdxSelectRoot {
144
163
  return value.length === 0;
145
164
  }
146
165
  return isNullish(value);
147
- }, ...(ngDevMode ? [{ debugName: "isEmptyModelValue" }] : /* istanbul ignore next */ []));
148
- this.optionsSet = signal(new Set(), ...(ngDevMode ? [{ debugName: "optionsSet" }] : /* istanbul ignore next */ []));
166
+ }, /* @ts-ignore */
167
+ ...(ngDevMode ? [{ debugName: "isEmptyModelValue" }] : /* istanbul ignore next */ []));
168
+ this.hasAppliedDefaultValue = false;
169
+ this.hasAppliedDefaultOpen = false;
170
+ this.optionsSet = signal(new Set(), /* @ts-ignore */
171
+ ...(ngDevMode ? [{ debugName: "optionsSet" }] : /* istanbul ignore next */ []));
149
172
  // The native `select` only associates the correct default value if the corresponding
150
173
  // `option` is rendered as a child **at the same time** as itself.
151
174
  // Because it might take a few renders for our items to gather the information to build
@@ -155,13 +178,33 @@ class RdxSelectRoot {
155
178
  return Array.from(this.optionsSet())
156
179
  .map((option) => option.value)
157
180
  .join(';');
158
- }, ...(ngDevMode ? [{ debugName: "nativeSelectKey" }] : /* istanbul ignore next */ []));
159
- this.triggerElement = signal(null, ...(ngDevMode ? [{ debugName: "triggerElement" }] : /* istanbul ignore next */ []));
160
- this.valueElement = signal(null, ...(ngDevMode ? [{ debugName: "valueElement" }] : /* istanbul ignore next */ []));
181
+ }, /* @ts-ignore */
182
+ ...(ngDevMode ? [{ debugName: "nativeSelectKey" }] : /* istanbul ignore next */ []));
183
+ this.triggerElement = signal(null, /* @ts-ignore */
184
+ ...(ngDevMode ? [{ debugName: "triggerElement" }] : /* istanbul ignore next */ []));
185
+ this.valueElement = signal(null, /* @ts-ignore */
186
+ ...(ngDevMode ? [{ debugName: "valueElement" }] : /* istanbul ignore next */ []));
161
187
  this.triggerPointerDownPosRef = signal({
162
188
  x: 0,
163
189
  y: 0
164
- }, ...(ngDevMode ? [{ debugName: "triggerPointerDownPosRef" }] : /* istanbul ignore next */ []));
190
+ }, /* @ts-ignore */
191
+ ...(ngDevMode ? [{ debugName: "triggerPointerDownPosRef" }] : /* istanbul ignore next */ []));
192
+ // Seed the uncontrolled value/open exactly once, then hand off to the model (so a later user
193
+ // change is not snapped back by the still-set default input).
194
+ effect(() => {
195
+ const defaultValue = this.defaultValue();
196
+ if (!this.hasAppliedDefaultValue && defaultValue !== undefined) {
197
+ this.hasAppliedDefaultValue = true;
198
+ this.value.set(defaultValue);
199
+ }
200
+ });
201
+ effect(() => {
202
+ const defaultOpen = this.defaultOpen();
203
+ if (!this.hasAppliedDefaultOpen && defaultOpen) {
204
+ this.hasAppliedDefaultOpen = true;
205
+ this.open.set(defaultOpen);
206
+ }
207
+ });
165
208
  let previousOpen = untracked(this.open);
166
209
  effect(() => {
167
210
  const open = this.open();
@@ -192,10 +235,16 @@ class RdxSelectRoot {
192
235
  return Array.from(this.optionsSet()).find((option) => valueComparator(value, option.value, this.isItemEqualToValue()));
193
236
  }
194
237
  setValue(value, reason = 'none', event = new Event('select.value-change')) {
238
+ if (this.readOnly()) {
239
+ return false;
240
+ }
195
241
  const nextValue = this.multiple()
196
242
  ? (() => {
197
243
  const current = this.value();
198
244
  const array = Array.isArray(current) ? [...current] : [];
245
+ if (value === undefined) {
246
+ return array;
247
+ }
199
248
  const index = array.findIndex((i) => compare(i, value, this.isItemEqualToValue()));
200
249
  index === -1 ? array.push(value) : array.splice(index, 1);
201
250
  return [...array];
@@ -206,12 +255,7 @@ class RdxSelectRoot {
206
255
  if (eventDetails.isCanceled()) {
207
256
  return false;
208
257
  }
209
- if (this.multiple()) {
210
- this.value.set(nextValue);
211
- }
212
- else {
213
- this.value.set(nextValue);
214
- }
258
+ this.value.set(nextValue);
215
259
  return true;
216
260
  }
217
261
  setOpen(open, reason = 'none', event) {
@@ -232,15 +276,15 @@ class RdxSelectRoot {
232
276
  }
233
277
  return true;
234
278
  }
235
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectRoot, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
236
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxSelectRoot, isStandalone: true, selector: "[rdxSelectRoot]", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, modal: { classPropertyName: "modal", publicName: "modal", isSignal: true, isRequired: false, transformFunction: null }, dirInput: { classPropertyName: "dirInput", publicName: "dir", isSignal: true, isRequired: false, transformFunction: null }, isItemEqualToValue: { classPropertyName: "isItemEqualToValue", publicName: "isItemEqualToValue", isSignal: true, isRequired: false, transformFunction: null }, itemToStringLabel: { classPropertyName: "itemToStringLabel", publicName: "itemToStringLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange", value: "valueChange", onOpenChange: "onOpenChange", onValueChange: "onValueChange", onOpenChangeComplete: "onOpenChangeComplete" }, providers: [
279
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectRoot, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
280
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: RdxSelectRoot, isStandalone: true, selector: "[rdxSelectRoot]", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, defaultValue: { classPropertyName: "defaultValue", publicName: "defaultValue", isSignal: true, isRequired: false, transformFunction: null }, defaultOpen: { classPropertyName: "defaultOpen", publicName: "defaultOpen", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", 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 }, modal: { classPropertyName: "modal", publicName: "modal", isSignal: true, isRequired: false, transformFunction: null }, dirInput: { classPropertyName: "dirInput", publicName: "dir", isSignal: true, isRequired: false, transformFunction: null }, isItemEqualToValue: { classPropertyName: "isItemEqualToValue", publicName: "isItemEqualToValue", isSignal: true, isRequired: false, transformFunction: null }, itemToStringLabel: { classPropertyName: "itemToStringLabel", publicName: "itemToStringLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange", value: "valueChange", onOpenChange: "onOpenChange", onValueChange: "onValueChange", onOpenChangeComplete: "onOpenChangeComplete" }, providers: [
237
281
  provideSelectRootContext(context$2),
238
282
  // New floating foundation (ADR 0015/0017) — the dismissal capability reads this shared context.
239
283
  provideFloatingTree(),
240
284
  provideFloatingRootContext(() => inject(RdxSelectRoot).floatingContext)
241
285
  ], exportAs: ["rdxSelectRoot"], hostDirectives: [{ directive: i1.RdxPopper }], ngImport: i0 }); }
242
286
  }
243
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectRoot, decorators: [{
287
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectRoot, decorators: [{
244
288
  type: Directive,
245
289
  args: [{
246
290
  selector: '[rdxSelectRoot]',
@@ -253,7 +297,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
253
297
  ],
254
298
  hostDirectives: [RdxPopper]
255
299
  }]
256
- }], ctorParameters: () => [], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], modal: [{ type: i0.Input, args: [{ isSignal: true, alias: "modal", required: false }] }], dirInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "dir", required: false }] }], isItemEqualToValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "isItemEqualToValue", required: false }] }], itemToStringLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemToStringLabel", required: false }] }], onOpenChange: [{ type: i0.Output, args: ["onOpenChange"] }], onValueChange: [{ type: i0.Output, args: ["onValueChange"] }], onOpenChangeComplete: [{ type: i0.Output, args: ["onOpenChangeComplete"] }] } });
300
+ }], ctorParameters: () => [], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], 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 }] }], defaultOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultOpen", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", 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 }] }], modal: [{ type: i0.Input, args: [{ isSignal: true, alias: "modal", required: false }] }], dirInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "dir", required: false }] }], isItemEqualToValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "isItemEqualToValue", required: false }] }], itemToStringLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemToStringLabel", required: false }] }], onOpenChange: [{ type: i0.Output, args: ["onOpenChange"] }], onValueChange: [{ type: i0.Output, args: ["onValueChange"] }], onOpenChangeComplete: [{ type: i0.Output, args: ["onOpenChangeComplete"] }] } });
257
301
 
258
302
  /**
259
303
  * An overlay rendered beneath the popup in `modal` mode. Place it inside the portal/presence; style
@@ -265,19 +309,20 @@ class RdxSelectBackdrop {
265
309
  constructor() {
266
310
  this.rootContext = injectSelectRootContext();
267
311
  }
268
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectBackdrop, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
269
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxSelectBackdrop, isStandalone: true, selector: "[rdxSelectBackdrop]", 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: ["rdxSelectBackdrop"], ngImport: i0 }); }
312
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectBackdrop, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
313
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: RdxSelectBackdrop, isStandalone: true, selector: "[rdxSelectBackdrop]", host: { attributes: { "aria-hidden": "true" }, properties: { "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" } }, exportAs: ["rdxSelectBackdrop"], ngImport: i0 }); }
270
314
  }
271
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectBackdrop, decorators: [{
315
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectBackdrop, decorators: [{
272
316
  type: Directive,
273
317
  args: [{
274
318
  selector: '[rdxSelectBackdrop]',
275
319
  exportAs: 'rdxSelectBackdrop',
276
320
  host: {
277
321
  'aria-hidden': 'true',
278
- '[attr.data-state]': 'rootContext.open() ? "open" : "closed"',
279
322
  '[attr.data-open]': 'rootContext.open() ? "" : undefined',
280
- '[attr.data-closed]': 'rootContext.open() ? undefined : ""'
323
+ '[attr.data-closed]': 'rootContext.open() ? undefined : ""',
324
+ '[attr.data-starting-style]': 'rootContext.transitionStatus() === "starting" ? "" : undefined',
325
+ '[attr.data-ending-style]': 'rootContext.transitionStatus() === "ending" ? "" : undefined'
281
326
  }
282
327
  }]
283
328
  }] });
@@ -286,10 +331,10 @@ class RdxSelectGroup {
286
331
  constructor() {
287
332
  this.id = injectId('rdx-select-group-');
288
333
  }
289
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectGroup, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
290
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxSelectGroup, isStandalone: true, selector: "[rdxSelectGroup]", host: { attributes: { "role": "group" }, properties: { "attr.aria-labelledby": "id" } }, exportAs: ["rdxSelectGroup"], ngImport: i0 }); }
334
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectGroup, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
335
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: RdxSelectGroup, isStandalone: true, selector: "[rdxSelectGroup]", host: { attributes: { "role": "group" }, properties: { "attr.aria-labelledby": "id" } }, exportAs: ["rdxSelectGroup"], ngImport: i0 }); }
291
336
  }
292
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectGroup, decorators: [{
337
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectGroup, decorators: [{
293
338
  type: Directive,
294
339
  args: [{
295
340
  selector: '[rdxSelectGroup]',
@@ -302,10 +347,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
302
347
  }] });
303
348
 
304
349
  class RdxSelectGroupLabel {
305
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectGroupLabel, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
306
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxSelectGroupLabel, isStandalone: true, selector: "[rdxSelectGroupLabel]", ngImport: i0 }); }
350
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectGroupLabel, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
351
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: RdxSelectGroupLabel, isStandalone: true, selector: "[rdxSelectGroupLabel]", ngImport: i0 }); }
307
352
  }
308
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectGroupLabel, decorators: [{
353
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectGroupLabel, decorators: [{
309
354
  type: Directive,
310
355
  args: [{
311
356
  selector: '[rdxSelectGroupLabel]',
@@ -319,10 +364,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
319
364
  * @group Components
320
365
  */
321
366
  class RdxSelectIcon {
322
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectIcon, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
323
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxSelectIcon, isStandalone: true, selector: "[rdxSelectIcon]", host: { attributes: { "aria-hidden": "true" } }, exportAs: ["rdxSelectIcon"], ngImport: i0 }); }
367
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectIcon, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
368
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: RdxSelectIcon, isStandalone: true, selector: "[rdxSelectIcon]", host: { attributes: { "aria-hidden": "true" } }, exportAs: ["rdxSelectIcon"], ngImport: i0 }); }
324
369
  }
325
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectIcon, decorators: [{
370
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectIcon, decorators: [{
326
371
  type: Directive,
327
372
  args: [{
328
373
  selector: '[rdxSelectIcon]',
@@ -333,6 +378,42 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
333
378
  }]
334
379
  }] });
335
380
 
381
+ /**
382
+ * Wraps an array around itself at a given start index
383
+ * Example: `wrapArray(['a', 'b', 'c', 'd'], 2) === ['c', 'd', 'a', 'b']`
384
+ */
385
+ function wrapArray(array, startIndex) {
386
+ return array.map((_, index) => array[(startIndex + index) % array.length]);
387
+ }
388
+ /**
389
+ * This is the "meat" of the typeahead matching logic. It takes in all the values,
390
+ * the search and the current match, and returns the next match (or `undefined`).
391
+ *
392
+ * We normalize the search because if a user has repeatedly pressed a character,
393
+ * we want the exact same behavior as if we only had that one character
394
+ * (ie. cycle through options starting with that character)
395
+ *
396
+ * We also reorder the values by wrapping the array around the current match.
397
+ * This is so we always look forward from the current match, and picking the first
398
+ * match will always be the correct one.
399
+ *
400
+ * Finally, if the normalized search is exactly one character, we exclude the
401
+ * current match from the values because otherwise it would be the first to match always
402
+ * and focus would never move. This is as opposed to the regular case, where we
403
+ * don't want focus to move if the current match still matches.
404
+ */
405
+ function getNextMatch(values, search, currentMatch) {
406
+ const isRepeated = search.length > 1 && Array.from(search).every((char) => char === search[0]);
407
+ const normalizedSearch = isRepeated ? search[0] : search;
408
+ const currentMatchIndex = currentMatch ? values.indexOf(currentMatch) : -1;
409
+ let wrappedValues = wrapArray(values, Math.max(currentMatchIndex, 0));
410
+ const excludeCurrentMatch = normalizedSearch.length === 1;
411
+ if (excludeCurrentMatch)
412
+ wrappedValues = wrappedValues.filter((v) => v !== currentMatch);
413
+ const nextMatch = wrappedValues.find((value) => value.toLowerCase().startsWith(normalizedSearch.toLowerCase()));
414
+ return nextMatch !== currentMatch ? nextMatch : undefined;
415
+ }
416
+
336
417
  const context$1 = () => {
337
418
  const context = inject(RdxSelectPopup);
338
419
  return {
@@ -405,7 +486,8 @@ class RdxSelectPopup {
405
486
  * **ancestor** — can read them without injecting the composite list (which the popup provides as
406
487
  * a descendant, so an upward `inject` would not find it).
407
488
  */
408
- this.items = computed(() => this.compositeList.items(), ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
489
+ this.items = computed(() => this.compositeList.items(), /* @ts-ignore */
490
+ ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
409
491
  /**
410
492
  * Highlight-model navigation over the collected items (DOM order). `loop` is disabled so arrow
411
493
  * navigation stops at the first / last item instead of wrapping around — matching native
@@ -418,15 +500,23 @@ class RdxSelectPopup {
418
500
  loop: signal(false),
419
501
  injector: this.injector
420
502
  });
421
- this.selectedItem = signal(undefined, ...(ngDevMode ? [{ debugName: "selectedItem" }] : /* istanbul ignore next */ []));
422
- this.selectedItemText = signal(undefined, ...(ngDevMode ? [{ debugName: "selectedItemText" }] : /* istanbul ignore next */ []));
423
- this.firstValidItemFoundRef = signal(false, ...(ngDevMode ? [{ debugName: "firstValidItemFoundRef" }] : /* istanbul ignore next */ []));
424
- this.viewport = signal(undefined, ...(ngDevMode ? [{ debugName: "viewport" }] : /* istanbul ignore next */ []));
425
- this.isPositioned = signal(false, ...(ngDevMode ? [{ debugName: "isPositioned" }] : /* istanbul ignore next */ []));
503
+ this.selectedItem = signal(undefined, /* @ts-ignore */
504
+ ...(ngDevMode ? [{ debugName: "selectedItem" }] : /* istanbul ignore next */ []));
505
+ this.selectedItemText = signal(undefined, /* @ts-ignore */
506
+ ...(ngDevMode ? [{ debugName: "selectedItemText" }] : /* istanbul ignore next */ []));
507
+ this.firstValidItemFoundRef = signal(false, /* @ts-ignore */
508
+ ...(ngDevMode ? [{ debugName: "firstValidItemFoundRef" }] : /* istanbul ignore next */ []));
509
+ this.viewport = signal(undefined, /* @ts-ignore */
510
+ ...(ngDevMode ? [{ debugName: "viewport" }] : /* istanbul ignore next */ []));
511
+ this.isPositioned = signal(false, /* @ts-ignore */
512
+ ...(ngDevMode ? [{ debugName: "isPositioned" }] : /* istanbul ignore next */ []));
426
513
  // Tracks whether the last interaction was the keyboard, so the highlight doesn't jump to an item
427
514
  // the cursor happens to rest on when arrow-key navigation scrolls the list.
428
515
  this.keyboardActive = false;
429
516
  this.hasHighlightedOpen = false;
517
+ // Typeahead buffer (APG listbox): printable characters accumulate for 1s and jump the highlight to
518
+ // the next item whose text matches.
519
+ this.typeaheadSearch = '';
430
520
  /**
431
521
  * Event handler called when the escape key is down.
432
522
  * Can be prevented.
@@ -437,7 +527,8 @@ class RdxSelectPopup {
437
527
  * Can be prevented.
438
528
  */
439
529
  this.pointerDownOutside = output();
440
- this.content = signal(null, ...(ngDevMode ? [{ debugName: "content" }] : /* istanbul ignore next */ []));
530
+ this.content = signal(null, /* @ts-ignore */
531
+ ...(ngDevMode ? [{ debugName: "content" }] : /* istanbul ignore next */ []));
441
532
  /**
442
533
  * The positioner — now an **ancestor** element — provides {@link RDX_SELECT_POSITIONER_TOKEN}
443
534
  * (Popper or item-aligned). We react to its `placed` to highlight and scroll the selected item
@@ -459,6 +550,9 @@ class RdxSelectPopup {
459
550
  effect(() => {
460
551
  if (!this.rootContext.open()) {
461
552
  this.hasHighlightedOpen = false;
553
+ // Reset the "first valid item" seed so the next open re-resolves it (otherwise the
554
+ // selected/first-item highlight seed is stale on reopen).
555
+ this.firstValidItemFoundRef.set(false);
462
556
  return;
463
557
  }
464
558
  if (!this.isPositioned() || this.hasHighlightedOpen) {
@@ -482,7 +576,8 @@ class RdxSelectPopup {
482
576
  // `modal === false`. The gate keys on `open` (not mounted) so it releases at close-start. A
483
577
  // **touch** open never uses item-aligned mode (the positioner falls back), so the lock there is
484
578
  // driven by `modal` alone and the anchored helper only engages when the popup is viewport-width (§3).
485
- const itemAlignedActive = computed(() => this.positioner?.alignItemWithTriggerActive?.() ?? false, ...(ngDevMode ? [{ debugName: "itemAlignedActive" }] : /* istanbul ignore next */ []));
579
+ const itemAlignedActive = computed(() => this.positioner?.alignItemWithTriggerActive?.() ?? false, /* @ts-ignore */
580
+ ...(ngDevMode ? [{ debugName: "itemAlignedActive" }] : /* istanbul ignore next */ []));
486
581
  useAnchoredScrollLock(computed(() => (itemAlignedActive() || this.rootContext.modal()) && this.rootContext.open()), {
487
582
  touchOpen: () => this.rootContext.openedByTouch(),
488
583
  element: () => this.currentElement.nativeElement
@@ -490,6 +585,7 @@ class RdxSelectPopup {
490
585
  // The popup's animation determines when the open/close transition (onOpenChangeComplete) is done.
491
586
  const unregisterTransition = this.rootContext.registerTransitionElement(this.currentElement.nativeElement);
492
587
  inject(DestroyRef).onDestroy(unregisterTransition);
588
+ inject(DestroyRef).onDestroy(() => clearTimeout(this.typeaheadTimer));
493
589
  // The popup (listbox) is this layer's floating element — the inside surface for containment.
494
590
  this.floatingContext.setFloatingElement(this.currentElement.nativeElement);
495
591
  // Dismissal (ADR 0015): Escape or an outside press closes the select. Focus-out does NOT close it
@@ -630,10 +726,36 @@ class RdxSelectPopup {
630
726
  this.highlight.last();
631
727
  break;
632
728
  }
729
+ return;
730
+ }
731
+ // Typeahead: a single printable character jumps the highlight to the next matching item.
732
+ if (keyEvent.key.length === 1 && !keyEvent.ctrlKey && !keyEvent.altKey && !keyEvent.metaKey) {
733
+ this.handleTypeahead(keyEvent.key);
633
734
  }
634
735
  }
635
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectPopup, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
636
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxSelectPopup, isStandalone: true, selector: "[rdxSelectPopup]", outputs: { escapeKeyDown: "escapeKeyDown", pointerDownOutside: "pointerDownOutside" }, host: { attributes: { "role": "listbox", "tabindex": "-1" }, listeners: { "keydown": "handleKeyDown($event)" }, properties: { "id": "rootContext.contentId", "attr.aria-activedescendant": "highlight.activeId()", "attr.aria-multiselectable": "rootContext.multiple() ? \"true\" : undefined", "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", "dir": "rootContext.dir()", "style": "{\n display: 'flex',\n flexDirection: 'column',\n outline: 'none'\n }" } }, providers: [
736
+ /** Accumulates printable keys for 1s and moves the highlight to the next item whose text matches. */
737
+ handleTypeahead(key) {
738
+ this.typeaheadSearch += key;
739
+ clearTimeout(this.typeaheadTimer);
740
+ this.typeaheadTimer = setTimeout(() => (this.typeaheadSearch = ''), 1000);
741
+ const navigableItems = this.items().filter((item) => !item.metadata()?.disabled);
742
+ const values = navigableItems.map((item) => this.itemText(item));
743
+ const current = this.highlight.highlightedItem();
744
+ const nextMatch = getNextMatch(values, this.typeaheadSearch, current ? this.itemText(current) : undefined);
745
+ if (nextMatch === undefined) {
746
+ return;
747
+ }
748
+ const match = navigableItems[values.indexOf(nextMatch)];
749
+ if (match) {
750
+ this.keyboardActive = true;
751
+ this.highlight.set(match);
752
+ }
753
+ }
754
+ itemText(item) {
755
+ return (item.metadata()?.textValue || item.element.textContent || '').trim();
756
+ }
757
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectPopup, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
758
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: RdxSelectPopup, isStandalone: true, selector: "[rdxSelectPopup]", outputs: { escapeKeyDown: "escapeKeyDown", pointerDownOutside: "pointerDownOutside" }, host: { attributes: { "role": "listbox", "tabindex": "-1" }, listeners: { "keydown": "handleKeyDown($event)" }, properties: { "id": "rootContext.contentId", "attr.aria-activedescendant": "highlight.activeId()", "attr.aria-multiselectable": "rootContext.multiple() ? \"true\" : undefined", "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", "dir": "rootContext.dir()", "style": "{\n display: 'flex',\n flexDirection: 'column',\n outline: 'none'\n }" } }, providers: [
637
759
  provideSelectPopupContext(context$1),
638
760
  provideFloatingFocusManagerConfig(() => {
639
761
  const rootContext = injectSelectRootContext();
@@ -651,7 +773,7 @@ class RdxSelectPopup {
651
773
  })
652
774
  ], hostDirectives: [{ directive: i1.RdxPopperContent }, { directive: i2.RdxFloatingFocusManager, inputs: ["returnFocus", "finalFocus"] }, { directive: i3.RdxFloatingNodeRegistration }, { directive: i4.RdxCompositeList }], ngImport: i0 }); }
653
775
  }
654
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectPopup, decorators: [{
776
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectPopup, decorators: [{
655
777
  type: Directive,
656
778
  args: [{
657
779
  selector: '[rdxSelectPopup]',
@@ -684,7 +806,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
684
806
  '[id]': 'rootContext.contentId',
685
807
  '[attr.aria-activedescendant]': 'highlight.activeId()',
686
808
  '[attr.aria-multiselectable]': 'rootContext.multiple() ? "true" : undefined',
687
- '[attr.data-state]': 'rootContext.open() ? "open" : "closed"',
688
809
  '[attr.data-open]': 'rootContext.open() ? "" : undefined',
689
810
  '[attr.data-closed]': 'rootContext.open() ? undefined : ""',
690
811
  '[attr.data-starting-style]': 'rootContext.transitionStatus() === "starting" ? "" : undefined',
@@ -719,13 +840,18 @@ class RdxSelectItem {
719
840
  this.contentContext = injectSelectPopupContext();
720
841
  this.listItem = inject(RdxCompositeListItem, { self: true });
721
842
  this.currentElement = inject(ElementRef);
722
- this.value = input(...(ngDevMode ? [undefined, { debugName: "value" }] : /* istanbul ignore next */ []));
723
- this.textValue = input('', ...(ngDevMode ? [{ debugName: "textValue" }] : /* istanbul ignore next */ []));
843
+ this.value = input(/* @ts-ignore */
844
+ ...(ngDevMode ? [undefined, { debugName: "value" }] : /* istanbul ignore next */ []));
845
+ this.textValue = input('', /* @ts-ignore */
846
+ ...(ngDevMode ? [{ debugName: "textValue" }] : /* istanbul ignore next */ []));
724
847
  this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
725
- this.textValue$ = linkedSignal(this.textValue, ...(ngDevMode ? [{ debugName: "textValue$" }] : /* istanbul ignore next */ []));
726
- this.isSelected = computed(() => valueComparator(this.rootContext.value(), this.value(), this.rootContext.isItemEqualToValue()), ...(ngDevMode ? [{ debugName: "isSelected" }] : /* istanbul ignore next */ []));
848
+ this.textValue$ = linkedSignal(this.textValue, /* @ts-ignore */
849
+ ...(ngDevMode ? [{ debugName: "textValue$" }] : /* istanbul ignore next */ []));
850
+ this.isSelected = computed(() => valueComparator(this.rootContext.value(), this.value(), this.rootContext.isItemEqualToValue()), /* @ts-ignore */
851
+ ...(ngDevMode ? [{ debugName: "isSelected" }] : /* istanbul ignore next */ []));
727
852
  /** Highlighted via the highlight model (keyboard / hover), not DOM focus. */
728
- this.isHighlighted = computed(() => this.contentContext.isHighlighted(this.currentElement.nativeElement), ...(ngDevMode ? [{ debugName: "isHighlighted" }] : /* istanbul ignore next */ []));
853
+ this.isHighlighted = computed(() => this.contentContext.isHighlighted(this.currentElement.nativeElement), /* @ts-ignore */
854
+ ...(ngDevMode ? [{ debugName: "isHighlighted" }] : /* istanbul ignore next */ []));
729
855
  /** Item id, referenced by the popup's `aria-activedescendant`. */
730
856
  this.id = injectId('rdx-select-item-');
731
857
  this.textId = injectId('rdx-select-item-text-');
@@ -780,10 +906,10 @@ class RdxSelectItem {
780
906
  if (SELECTION_KEYS.includes(keyEvent.key))
781
907
  this.onPointerUp(keyEvent);
782
908
  }
783
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectItem, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
784
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxSelectItem, isStandalone: true, selector: "[rdxSelectItem]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, textValue: { classPropertyName: "textValue", publicName: "textValue", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "option" }, listeners: { "pointerup": "onPointerUp($event)", "pointerleave": "onPointerLeave($event)", "pointermove": "onPointerMove($event)" }, properties: { "attr.id": "id", "attr.aria-selected": "isSelected()", "attr.aria-disabled": "disabled() ? \"true\" : undefined", "attr.data-state": "isSelected() ? \"checked\" : \"unchecked\"", "attr.data-selected": "isSelected() ? \"\" : undefined", "attr.data-highlighted": "isHighlighted() ? \"\" : undefined", "attr.data-disabled": "disabled() ? \"\" : undefined" } }, providers: [provideSelectItemContext(context)], exportAs: ["rdxSelectItem"], hostDirectives: [{ directive: i4.RdxCompositeListItem }], ngImport: i0 }); }
909
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectItem, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
910
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: RdxSelectItem, isStandalone: true, selector: "[rdxSelectItem]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, textValue: { classPropertyName: "textValue", publicName: "textValue", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "option" }, listeners: { "pointerup": "onPointerUp($event)", "pointerleave": "onPointerLeave($event)", "pointermove": "onPointerMove($event)" }, properties: { "attr.id": "id", "attr.aria-selected": "isSelected()", "attr.aria-disabled": "disabled() ? \"true\" : undefined", "attr.data-selected": "isSelected() ? \"\" : undefined", "attr.data-highlighted": "isHighlighted() ? \"\" : undefined", "attr.data-disabled": "disabled() ? \"\" : undefined" } }, providers: [provideSelectItemContext(context)], exportAs: ["rdxSelectItem"], hostDirectives: [{ directive: i4.RdxCompositeListItem }], ngImport: i0 }); }
785
911
  }
786
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectItem, decorators: [{
912
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectItem, decorators: [{
787
913
  type: Directive,
788
914
  args: [{
789
915
  selector: '[rdxSelectItem]',
@@ -795,7 +921,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
795
921
  '[attr.id]': 'id',
796
922
  '[attr.aria-selected]': 'isSelected()',
797
923
  '[attr.aria-disabled]': 'disabled() ? "true" : undefined',
798
- '[attr.data-state]': 'isSelected() ? "checked" : "unchecked"',
799
924
  '[attr.data-selected]': 'isSelected() ? "" : undefined',
800
925
  '[attr.data-highlighted]': 'isHighlighted() ? "" : undefined',
801
926
  '[attr.data-disabled]': 'disabled() ? "" : undefined',
@@ -806,14 +931,233 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
806
931
  }]
807
932
  }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], textValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "textValue", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
808
933
 
934
+ class RdxSelectItemAlignedPositionContent {
935
+ constructor() {
936
+ this.currentElementRef = inject(ElementRef);
937
+ }
938
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectItemAlignedPositionContent, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
939
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: RdxSelectItemAlignedPositionContent, isStandalone: true, selector: "[rdxSelectItemAlignedPositionContent]", host: { properties: { "style": "{\n boxSizing: 'border-box',\n display: 'flex',\n flexDirection: 'column'\n }" } }, ngImport: i0 }); }
940
+ }
941
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectItemAlignedPositionContent, decorators: [{
942
+ type: Directive,
943
+ args: [{
944
+ selector: '[rdxSelectItemAlignedPositionContent]',
945
+ host: {
946
+ '[style]': `{
947
+ boxSizing: 'border-box',
948
+ display: 'flex',
949
+ flexDirection: 'column'
950
+ }`
951
+ }
952
+ }]
953
+ }] });
954
+
955
+ class RdxSelectItemAlignedPosition {
956
+ constructor() {
957
+ this.currentElement = inject(ElementRef);
958
+ this.rootContext = injectSelectRootContext();
959
+ /**
960
+ * The popup is now a **descendant** (item-aligned-position is the outer positioner), so read the
961
+ * viewport / selected-item measurements off the popup instance instead of injecting its context.
962
+ */
963
+ this.popup = contentChild.required(RdxSelectPopup);
964
+ this.placed = output();
965
+ /**
966
+ * Whether item-aligned positioning is active (Base UI `alignItemWithTriggerActive`): open **and not**
967
+ * touch-opened. A touch open falls back to a plain anchored dropdown ({@link positionBelowTrigger}).
968
+ * Read by the popup's scroll-lock policy — an active item-aligned popup locks even when `modal=false`.
969
+ */
970
+ this.alignItemWithTriggerActive = computed(() => this.rootContext.open() && !this.rootContext.openedByTouch(), /* @ts-ignore */
971
+ ...(ngDevMode ? [{ debugName: "alignItemWithTriggerActive" }] : /* istanbul ignore next */ []));
972
+ this.contentZIndex = signal('', /* @ts-ignore */
973
+ ...(ngDevMode ? [{ debugName: "contentZIndex" }] : /* istanbul ignore next */ []));
974
+ this.shouldExpandOnScrollRef = signal(false, /* @ts-ignore */
975
+ ...(ngDevMode ? [{ debugName: "shouldExpandOnScrollRef" }] : /* istanbul ignore next */ []));
976
+ this.contentWrapperElement = signal(this.currentElement.nativeElement, /* @ts-ignore */
977
+ ...(ngDevMode ? [{ debugName: "contentWrapperElement" }] : /* istanbul ignore next */ []));
978
+ this.contentElement = contentChild.required(RdxSelectItemAlignedPositionContent);
979
+ afterNextRender(() => {
980
+ this.position();
981
+ if (this.contentElement().currentElementRef.nativeElement) {
982
+ this.contentZIndex.set(window.getComputedStyle(this.contentElement().currentElementRef.nativeElement).zIndex);
983
+ }
984
+ });
985
+ const injector = inject(Injector);
986
+ // Resize and position when trigger element changes
987
+ resizeEffect({
988
+ injector,
989
+ element: this.rootContext.triggerElement,
990
+ onResize: () => this.position()
991
+ });
992
+ }
993
+ position() {
994
+ // Base UI parity: a touch-opened select does NOT align the item with the trigger (the macOS-style
995
+ // overlay is awkward on mobile). Fall back to a plain anchored dropdown below the trigger.
996
+ if (!this.alignItemWithTriggerActive()) {
997
+ this.positionBelowTrigger();
998
+ return;
999
+ }
1000
+ if (this.rootContext.triggerElement() &&
1001
+ this.rootContext.valueElement() &&
1002
+ this.contentWrapperElement() &&
1003
+ this.contentElement().currentElementRef.nativeElement &&
1004
+ this.popup().viewport() &&
1005
+ this.popup().selectedItem() &&
1006
+ this.popup().selectedItemText()) {
1007
+ const triggerRect = this.rootContext.triggerElement().getBoundingClientRect();
1008
+ // -----------------------------------------------------------------------------------------
1009
+ // Horizontal positioning
1010
+ // -----------------------------------------------------------------------------------------
1011
+ const contentRect = this.currentElement.nativeElement.getBoundingClientRect();
1012
+ const valueNodeRect = this.rootContext.valueElement().getBoundingClientRect();
1013
+ const itemTextRect = this.popup().selectedItemText().getBoundingClientRect();
1014
+ if (this.rootContext.dir() !== 'rtl') {
1015
+ const itemTextOffset = itemTextRect.left - contentRect.left;
1016
+ const left = valueNodeRect.left - itemTextOffset;
1017
+ const leftDelta = triggerRect.left - left;
1018
+ const minContentWidth = triggerRect.width + leftDelta;
1019
+ const contentWidth = Math.max(minContentWidth, contentRect.width);
1020
+ const rightEdge = window.innerWidth - CONTENT_MARGIN;
1021
+ const clampedLeft = clamp(left, CONTENT_MARGIN, Math.max(CONTENT_MARGIN, rightEdge - contentWidth));
1022
+ this.contentWrapperElement().style.minWidth = `${minContentWidth}px`;
1023
+ this.contentWrapperElement().style.left = `${clampedLeft}px`;
1024
+ }
1025
+ else {
1026
+ const itemTextOffset = contentRect.right - itemTextRect.right;
1027
+ const right = window.innerWidth - valueNodeRect.right - itemTextOffset;
1028
+ const rightDelta = window.innerWidth - triggerRect.right - right;
1029
+ const minContentWidth = triggerRect.width + rightDelta;
1030
+ const contentWidth = Math.max(minContentWidth, contentRect.width);
1031
+ const leftEdge = window.innerWidth - CONTENT_MARGIN;
1032
+ const clampedRight = clamp(right, CONTENT_MARGIN, Math.max(CONTENT_MARGIN, leftEdge - contentWidth));
1033
+ this.contentWrapperElement().style.minWidth = `${minContentWidth}px`;
1034
+ this.contentWrapperElement().style.right = `${clampedRight}px`;
1035
+ }
1036
+ // -----------------------------------------------------------------------------------------
1037
+ // Vertical positioning
1038
+ // -----------------------------------------------------------------------------------------
1039
+ const items = this.popup()
1040
+ .items()
1041
+ .map((i) => i.element);
1042
+ const availableHeight = window.innerHeight - CONTENT_MARGIN * 2;
1043
+ const itemsHeight = this.popup().viewport().scrollHeight;
1044
+ const contentStyles = window.getComputedStyle(this.currentElement.nativeElement);
1045
+ const contentBorderTopWidth = Number.parseInt(contentStyles.borderTopWidth, 10);
1046
+ const contentPaddingTop = Number.parseInt(contentStyles.paddingTop, 10);
1047
+ const contentBorderBottomWidth = Number.parseInt(contentStyles.borderBottomWidth, 10);
1048
+ const contentPaddingBottom = Number.parseInt(contentStyles.paddingBottom, 10);
1049
+ const fullContentHeight = contentBorderTopWidth +
1050
+ contentPaddingTop +
1051
+ itemsHeight +
1052
+ contentPaddingBottom +
1053
+ contentBorderBottomWidth;
1054
+ const minContentHeight = Math.min(this.popup().selectedItem().offsetHeight * 5, fullContentHeight);
1055
+ const viewportStyles = window.getComputedStyle(this.popup().viewport());
1056
+ const viewportPaddingTop = Number.parseInt(viewportStyles.paddingTop, 10);
1057
+ const viewportPaddingBottom = Number.parseInt(viewportStyles.paddingBottom, 10);
1058
+ const topEdgeToTriggerMiddle = triggerRect.top + triggerRect.height / 2 - CONTENT_MARGIN;
1059
+ const triggerMiddleToBottomEdge = availableHeight - topEdgeToTriggerMiddle;
1060
+ const selectedItemHalfHeight = this.popup().selectedItem().offsetHeight / 2;
1061
+ const itemOffsetMiddle = this.popup().selectedItem().offsetTop + selectedItemHalfHeight;
1062
+ const contentTopToItemMiddle = contentBorderTopWidth + contentPaddingTop + itemOffsetMiddle;
1063
+ const itemMiddleToContentBottom = fullContentHeight - contentTopToItemMiddle;
1064
+ const willAlignWithoutTopOverflow = contentTopToItemMiddle <= topEdgeToTriggerMiddle;
1065
+ if (willAlignWithoutTopOverflow) {
1066
+ const isLastItem = this.popup().selectedItem() === items[items.length - 1];
1067
+ this.contentWrapperElement().style.bottom = `${0}px`;
1068
+ const viewportOffsetBottom = this.currentElement.nativeElement.clientHeight -
1069
+ this.popup().viewport().offsetTop -
1070
+ this.popup().viewport().offsetHeight;
1071
+ const clampedTriggerMiddleToBottomEdge = Math.max(triggerMiddleToBottomEdge, selectedItemHalfHeight +
1072
+ // viewport might have padding bottom, include it to avoid a scrollable viewport
1073
+ (isLastItem ? viewportPaddingBottom : 0) +
1074
+ viewportOffsetBottom +
1075
+ contentBorderBottomWidth);
1076
+ const height = contentTopToItemMiddle + clampedTriggerMiddleToBottomEdge;
1077
+ this.contentWrapperElement().style.height = `${height}px`;
1078
+ }
1079
+ else {
1080
+ const isFirstItem = this.popup().selectedItem() === items[0];
1081
+ this.contentWrapperElement().style.top = `${0}px`;
1082
+ const clampedTopEdgeToTriggerMiddle = Math.max(topEdgeToTriggerMiddle, contentBorderTopWidth +
1083
+ this.popup().viewport().offsetTop +
1084
+ // viewport might have padding top, include it to avoid a scrollable viewport
1085
+ (isFirstItem ? viewportPaddingTop : 0) +
1086
+ selectedItemHalfHeight);
1087
+ const height = clampedTopEdgeToTriggerMiddle + itemMiddleToContentBottom;
1088
+ this.contentWrapperElement().style.height = `${height}px`;
1089
+ this.popup().viewport().scrollTop =
1090
+ contentTopToItemMiddle - topEdgeToTriggerMiddle + this.popup().viewport().offsetTop;
1091
+ }
1092
+ this.contentWrapperElement().style.margin = `${CONTENT_MARGIN}px 0`;
1093
+ this.contentWrapperElement().style.minHeight = `${minContentHeight}px`;
1094
+ this.contentWrapperElement().style.maxHeight = `${availableHeight}px`;
1095
+ // -----------------------------------------------------------------------------------------
1096
+ this.placed.emit();
1097
+ // we don't want the initial scroll position adjustment to trigger "expand on scroll"
1098
+ // so we explicitly turn it on only after they've registered.
1099
+ requestAnimationFrame(() => this.shouldExpandOnScrollRef.set(true));
1100
+ }
1101
+ }
1102
+ /**
1103
+ * Touch fallback (Base UI disables item-alignment on touch): position the popup as a plain anchored
1104
+ * dropdown just below the trigger, clamped to the viewport, with no item alignment or pre-scroll.
1105
+ */
1106
+ positionBelowTrigger() {
1107
+ const trigger = this.rootContext.triggerElement();
1108
+ const wrapper = this.contentWrapperElement();
1109
+ if (!trigger || !wrapper) {
1110
+ return;
1111
+ }
1112
+ const triggerRect = trigger.getBoundingClientRect();
1113
+ const availableHeight = window.innerHeight - CONTENT_MARGIN * 2;
1114
+ const maxLeft = Math.max(CONTENT_MARGIN, window.innerWidth - CONTENT_MARGIN - triggerRect.width);
1115
+ wrapper.style.left = `${clamp(triggerRect.left, CONTENT_MARGIN, maxLeft)}px`;
1116
+ wrapper.style.top = `${triggerRect.bottom}px`;
1117
+ wrapper.style.bottom = '';
1118
+ wrapper.style.height = '';
1119
+ wrapper.style.margin = `${CONTENT_MARGIN}px 0`;
1120
+ wrapper.style.minWidth = `${triggerRect.width}px`;
1121
+ wrapper.style.maxHeight = `${availableHeight}px`;
1122
+ this.placed.emit();
1123
+ }
1124
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectItemAlignedPosition, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1125
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "22.0.2", type: RdxSelectItemAlignedPosition, isStandalone: true, selector: "[rdxSelectItemAlignedPosition]", outputs: { placed: "placed" }, host: { properties: { "style": "{\n display: 'flex',\n flexDirection: 'column',\n position: 'fixed',\n zIndex: contentZIndex(),\n }" } }, providers: [
1126
+ {
1127
+ provide: RDX_SELECT_POSITIONER_TOKEN,
1128
+ useExisting: forwardRef(() => RdxSelectItemAlignedPosition)
1129
+ }
1130
+ ], queries: [{ propertyName: "popup", first: true, predicate: RdxSelectPopup, descendants: true, isSignal: true }, { propertyName: "contentElement", first: true, predicate: RdxSelectItemAlignedPositionContent, descendants: true, isSignal: true }], ngImport: i0 }); }
1131
+ }
1132
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectItemAlignedPosition, decorators: [{
1133
+ type: Directive,
1134
+ args: [{
1135
+ selector: '[rdxSelectItemAlignedPosition]',
1136
+ providers: [
1137
+ {
1138
+ provide: RDX_SELECT_POSITIONER_TOKEN,
1139
+ useExisting: forwardRef(() => RdxSelectItemAlignedPosition)
1140
+ }
1141
+ ],
1142
+ host: {
1143
+ '[style]': `{
1144
+ display: 'flex',
1145
+ flexDirection: 'column',
1146
+ position: 'fixed',
1147
+ zIndex: contentZIndex(),
1148
+ }`
1149
+ }
1150
+ }]
1151
+ }], ctorParameters: () => [], propDecorators: { popup: [{ type: i0.ContentChild, args: [i0.forwardRef(() => RdxSelectPopup), { isSignal: true }] }], placed: [{ type: i0.Output, args: ["placed"] }], contentElement: [{ type: i0.ContentChild, args: [i0.forwardRef(() => RdxSelectItemAlignedPositionContent), { isSignal: true }] }] } });
1152
+
809
1153
  class RdxSelectItemIndicator {
810
1154
  constructor() {
811
1155
  this.itemContext = injectSelectItemContext();
812
1156
  }
813
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectItemIndicator, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
814
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxSelectItemIndicator, isStandalone: true, selector: "[rdxSelectItemIndicator]", host: { properties: { "attr.aria-hidden": "true", "hidden": "!itemContext.isSelected()" } }, ngImport: i0 }); }
1157
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectItemIndicator, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1158
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: RdxSelectItemIndicator, isStandalone: true, selector: "[rdxSelectItemIndicator]", host: { properties: { "attr.aria-hidden": "true", "hidden": "!itemContext.isSelected()" } }, ngImport: i0 }); }
815
1159
  }
816
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectItemIndicator, decorators: [{
1160
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectItemIndicator, decorators: [{
817
1161
  type: Directive,
818
1162
  args: [{
819
1163
  selector: '[rdxSelectItemIndicator]',
@@ -836,7 +1180,8 @@ class RdxSelectItemText {
836
1180
  disabled: this.itemContext.disabled(),
837
1181
  textContent: this.elementRef.nativeElement.textContent ?? this.itemContext.value()?.toString() ?? ''
838
1182
  };
839
- }, ...(ngDevMode ? [{ debugName: "optionProps" }] : /* istanbul ignore next */ []));
1183
+ }, /* @ts-ignore */
1184
+ ...(ngDevMode ? [{ debugName: "optionProps" }] : /* istanbul ignore next */ []));
840
1185
  afterNextRender(() => {
841
1186
  this.itemContext.onItemTextChange(this.elementRef.nativeElement);
842
1187
  this.contentContext.itemTextRefCallback(this.elementRef.nativeElement, this.itemContext.value(), this.itemContext.disabled());
@@ -847,10 +1192,10 @@ class RdxSelectItemText {
847
1192
  this.rootContext.onOptionRemove(this.optionProps);
848
1193
  });
849
1194
  }
850
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectItemText, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
851
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxSelectItemText, isStandalone: true, selector: "[rdxSelectItemText]", host: { properties: { "id": "itemContext.textId" } }, ngImport: i0 }); }
1195
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectItemText, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1196
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: RdxSelectItemText, isStandalone: true, selector: "[rdxSelectItemText]", host: { properties: { "id": "itemContext.textId" } }, ngImport: i0 }); }
852
1197
  }
853
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectItemText, decorators: [{
1198
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectItemText, decorators: [{
854
1199
  type: Directive,
855
1200
  args: [{
856
1201
  selector: '[rdxSelectItemText]',
@@ -864,7 +1209,8 @@ class RdxSelectList {
864
1209
  constructor() {
865
1210
  this.contentContext = injectSelectPopupContext();
866
1211
  this.elementRef = inject((ElementRef));
867
- this.prevScrollTopRef = signal(0, ...(ngDevMode ? [{ debugName: "prevScrollTopRef" }] : /* istanbul ignore next */ []));
1212
+ this.prevScrollTopRef = signal(0, /* @ts-ignore */
1213
+ ...(ngDevMode ? [{ debugName: "prevScrollTopRef" }] : /* istanbul ignore next */ []));
868
1214
  afterNextRender(() => {
869
1215
  this.contentContext?.onViewportChange(this.elementRef.nativeElement);
870
1216
  });
@@ -873,10 +1219,10 @@ class RdxSelectList {
873
1219
  const viewport = event.currentTarget;
874
1220
  this.prevScrollTopRef.set(viewport.scrollTop);
875
1221
  }
876
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectList, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
877
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxSelectList, isStandalone: true, selector: "[rdxSelectList]", host: { attributes: { "role": "presentation" }, listeners: { "scroll": "handleScroll($event)" }, properties: { "attr.data-rdx-select-list": "\"\"", "style": "{\n position: 'relative',\n flex: 1,\n overflow: 'hidden auto',\n scrollbarWidth: 'none'\n }" } }, ngImport: i0 }); }
1222
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectList, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1223
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: RdxSelectList, isStandalone: true, selector: "[rdxSelectList]", host: { attributes: { "role": "presentation" }, listeners: { "scroll": "handleScroll($event)" }, properties: { "attr.data-rdx-select-list": "\"\"", "style": "{\n position: 'relative',\n flex: 1,\n overflow: 'hidden auto',\n scrollbarWidth: 'none'\n }" } }, ngImport: i0 }); }
878
1224
  }
879
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectList, decorators: [{
1225
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectList, decorators: [{
880
1226
  type: Directive,
881
1227
  args: [{
882
1228
  selector: '[rdxSelectList]',
@@ -904,10 +1250,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
904
1250
  * `document.body` while the select is closed.
905
1251
  */
906
1252
  class RdxSelectPortal {
907
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectPortal, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
908
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxSelectPortal, isStandalone: true, selector: "ng-template[rdxSelectPortal]", providers: [provideRdxPresenceContext(() => ({ present: injectSelectRootContext().open }))], exportAs: ["rdxSelectPortal"], hostDirectives: [{ directive: i1$1.RdxPortalPresence, inputs: ["container", "container"] }], ngImport: i0 }); }
1253
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectPortal, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1254
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: RdxSelectPortal, isStandalone: true, selector: "ng-template[rdxSelectPortal]", providers: [provideRdxPresenceContext(() => ({ present: injectSelectRootContext().open }))], exportAs: ["rdxSelectPortal"], hostDirectives: [{ directive: i1$1.RdxPortalPresence, inputs: ["container", "container"] }], ngImport: i0 }); }
909
1255
  }
910
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectPortal, decorators: [{
1256
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectPortal, decorators: [{
911
1257
  type: Directive,
912
1258
  args: [{
913
1259
  selector: 'ng-template[rdxSelectPortal]',
@@ -929,10 +1275,10 @@ class RdxSelectPortalMisuseGuard {
929
1275
  'rdxSelectPortalPresence has been removed.', 'components/select');
930
1276
  }
931
1277
  }
932
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectPortalMisuseGuard, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
933
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxSelectPortalMisuseGuard, isStandalone: true, selector: "[rdxSelectPortal]:not(ng-template)", ngImport: i0 }); }
1278
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectPortalMisuseGuard, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1279
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: RdxSelectPortalMisuseGuard, isStandalone: true, selector: "[rdxSelectPortal]:not(ng-template)", ngImport: i0 }); }
934
1280
  }
935
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectPortalMisuseGuard, decorators: [{
1281
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectPortalMisuseGuard, decorators: [{
936
1282
  type: Directive,
937
1283
  args: [{
938
1284
  selector: '[rdxSelectPortal]:not(ng-template)'
@@ -963,14 +1309,14 @@ class RdxSelectPositioner extends RdxPopperContentWrapper {
963
1309
  cutout: () => rootContext.triggerElement()
964
1310
  }));
965
1311
  }
966
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectPositioner, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
967
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxSelectPositioner, isStandalone: true, selector: "[rdxSelectPositioner]", host: { properties: { "style": "positionerStyle" } }, providers: [
1312
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectPositioner, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1313
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: RdxSelectPositioner, isStandalone: true, selector: "[rdxSelectPositioner]", host: { properties: { "style": "positionerStyle" } }, providers: [
968
1314
  ...provideRdxPopperContentWrapper(RdxSelectPositioner),
969
1315
  provideRdxPopperContentConfig({ align: 'start', updatePositionStrategy: 'always' }),
970
1316
  { provide: RDX_SELECT_POSITIONER_TOKEN, useExisting: forwardRef(() => RdxSelectPositioner) }
971
1317
  ], usesInheritance: true, ngImport: i0 }); }
972
1318
  }
973
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectPositioner, decorators: [{
1319
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectPositioner, decorators: [{
974
1320
  type: Directive,
975
1321
  args: [{
976
1322
  selector: '[rdxSelectPositioner]',
@@ -987,16 +1333,146 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
987
1333
  }]
988
1334
  }], ctorParameters: () => [] });
989
1335
 
1336
+ class RdxSelectScrollButtonBase {
1337
+ constructor() {
1338
+ this.contentContext = injectSelectPopupContext();
1339
+ this.autoScrollTimerRef = signal(null, /* @ts-ignore */
1340
+ ...(ngDevMode ? [{ debugName: "autoScrollTimerRef" }] : /* istanbul ignore next */ []));
1341
+ this.autoScroll = output();
1342
+ effect(() => {
1343
+ this.contentContext.highlightedItem()?.element.scrollIntoView({ block: 'nearest' });
1344
+ });
1345
+ inject(DestroyRef).onDestroy(() => {
1346
+ this.clearAutoScrollTimer();
1347
+ });
1348
+ }
1349
+ clearAutoScrollTimer() {
1350
+ if (this.autoScrollTimerRef() !== null) {
1351
+ window.clearInterval(this.autoScrollTimerRef());
1352
+ this.autoScrollTimerRef.set(null);
1353
+ }
1354
+ }
1355
+ handlePointerDown() {
1356
+ if (this.autoScrollTimerRef() === null) {
1357
+ this.autoScrollTimerRef.set(window.setInterval(() => {
1358
+ this.autoScroll.emit();
1359
+ }, 50));
1360
+ }
1361
+ }
1362
+ handlePointerMove() {
1363
+ this.contentContext.onItemLeave?.();
1364
+ if (this.autoScrollTimerRef() === null) {
1365
+ this.autoScrollTimerRef.set(window.setInterval(() => {
1366
+ this.autoScroll.emit();
1367
+ }, 50));
1368
+ }
1369
+ }
1370
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectScrollButtonBase, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1371
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: RdxSelectScrollButtonBase, isStandalone: true, selector: "[rdxSelectScrollButtonBase]", outputs: { autoScroll: "autoScroll" }, host: { listeners: { "pointerdown": "handlePointerDown()", "pointermove": "handlePointerMove()", "pointerleave": "clearAutoScrollTimer()" }, properties: { "attr.aria-hidden": "true", "style": "{\n flexShrink: 0\n }" } }, ngImport: i0 }); }
1372
+ }
1373
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectScrollButtonBase, decorators: [{
1374
+ type: Directive,
1375
+ args: [{
1376
+ selector: '[rdxSelectScrollButtonBase]',
1377
+ host: {
1378
+ '[attr.aria-hidden]': 'true',
1379
+ '(pointerdown)': 'handlePointerDown()',
1380
+ '(pointermove)': 'handlePointerMove()',
1381
+ '(pointerleave)': 'clearAutoScrollTimer()',
1382
+ '[style]': `{
1383
+ flexShrink: 0
1384
+ }`
1385
+ }
1386
+ }]
1387
+ }], ctorParameters: () => [], propDecorators: { autoScroll: [{ type: i0.Output, args: ["autoScroll"] }] } });
1388
+
1389
+ class RdxSelectScrollDownButton {
1390
+ constructor() {
1391
+ this.contentContext = injectSelectPopupContext();
1392
+ this.canScrollDown = signal(false, /* @ts-ignore */
1393
+ ...(ngDevMode ? [{ debugName: "canScrollDown" }] : /* istanbul ignore next */ []));
1394
+ inject(RdxSelectScrollButtonBase).autoScroll.subscribe(() => {
1395
+ const { viewport, selectedItem } = this.contentContext;
1396
+ if (viewport() && selectedItem()) {
1397
+ viewport().scrollTop = viewport().scrollTop + selectedItem().offsetHeight;
1398
+ }
1399
+ });
1400
+ effect((cleanup) => {
1401
+ if (this.contentContext.viewport() && this.contentContext.isPositioned()) {
1402
+ const viewport = this.contentContext.viewport();
1403
+ const handleScroll = () => {
1404
+ const maxScroll = viewport.scrollHeight - viewport.clientHeight;
1405
+ // we use Math.ceil here because if the UI is zoomed-in
1406
+ // `scrollTop` is not always reported as an integer
1407
+ this.canScrollDown.set(Math.ceil(viewport.scrollTop) < maxScroll);
1408
+ };
1409
+ handleScroll();
1410
+ viewport.addEventListener('scroll', handleScroll);
1411
+ cleanup(() => viewport.removeEventListener('scroll', handleScroll));
1412
+ }
1413
+ });
1414
+ }
1415
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectScrollDownButton, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1416
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: RdxSelectScrollDownButton, isStandalone: true, selector: "[rdxSelectScrollDownButton]", host: { properties: { "hidden": "!canScrollDown()" } }, hostDirectives: [{ directive: RdxSelectScrollButtonBase }], ngImport: i0 }); }
1417
+ }
1418
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectScrollDownButton, decorators: [{
1419
+ type: Directive,
1420
+ args: [{
1421
+ selector: '[rdxSelectScrollDownButton]',
1422
+ hostDirectives: [RdxSelectScrollButtonBase],
1423
+ host: {
1424
+ '[hidden]': '!canScrollDown()'
1425
+ }
1426
+ }]
1427
+ }], ctorParameters: () => [] });
1428
+
1429
+ class RdxSelectScrollUpButton {
1430
+ constructor() {
1431
+ this.contentContext = injectSelectPopupContext();
1432
+ this.canScrollUp = signal(false, /* @ts-ignore */
1433
+ ...(ngDevMode ? [{ debugName: "canScrollUp" }] : /* istanbul ignore next */ []));
1434
+ inject(RdxSelectScrollButtonBase).autoScroll.subscribe(() => {
1435
+ const { viewport, selectedItem } = this.contentContext;
1436
+ if (viewport() && selectedItem()) {
1437
+ viewport().scrollTop = viewport().scrollTop - selectedItem().offsetHeight;
1438
+ }
1439
+ });
1440
+ effect((cleanup) => {
1441
+ if (this.contentContext.viewport() && this.contentContext.isPositioned()) {
1442
+ const viewport = this.contentContext.viewport();
1443
+ const handleScroll = () => {
1444
+ this.canScrollUp.set(viewport.scrollTop > 0);
1445
+ };
1446
+ handleScroll();
1447
+ viewport.addEventListener('scroll', handleScroll);
1448
+ cleanup(() => viewport.removeEventListener('scroll', handleScroll));
1449
+ }
1450
+ });
1451
+ }
1452
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectScrollUpButton, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1453
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: RdxSelectScrollUpButton, isStandalone: true, selector: "[rdxSelectScrollUpButton]", host: { properties: { "hidden": "!canScrollUp()" } }, hostDirectives: [{ directive: RdxSelectScrollButtonBase }], ngImport: i0 }); }
1454
+ }
1455
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectScrollUpButton, decorators: [{
1456
+ type: Directive,
1457
+ args: [{
1458
+ selector: '[rdxSelectScrollUpButton]',
1459
+ hostDirectives: [RdxSelectScrollButtonBase],
1460
+ host: {
1461
+ '[hidden]': '!canScrollUp()'
1462
+ }
1463
+ }]
1464
+ }], ctorParameters: () => [] });
1465
+
990
1466
  /**
991
1467
  * A visual divider between groups of items.
992
1468
  *
993
1469
  * @group Components
994
1470
  */
995
1471
  class RdxSelectSeparator {
996
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectSeparator, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
997
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxSelectSeparator, isStandalone: true, selector: "[rdxSelectSeparator]", host: { attributes: { "role": "separator", "aria-hidden": "true" } }, exportAs: ["rdxSelectSeparator"], ngImport: i0 }); }
1472
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectSeparator, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1473
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: RdxSelectSeparator, isStandalone: true, selector: "[rdxSelectSeparator]", host: { attributes: { "role": "separator", "aria-hidden": "true" } }, exportAs: ["rdxSelectSeparator"], ngImport: i0 }); }
998
1474
  }
999
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectSeparator, decorators: [{
1475
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectSeparator, decorators: [{
1000
1476
  type: Directive,
1001
1477
  args: [{
1002
1478
  selector: '[rdxSelectSeparator]',
@@ -1015,9 +1491,11 @@ class RdxSelectTrigger {
1015
1491
  this.elementRef = inject(ElementRef);
1016
1492
  this.fieldRootContext = injectFieldRootContext(true);
1017
1493
  /** The trigger id; Field labels and descriptions reference it for accessible relationships. */
1018
- this.id = input(injectId('rdx-select-trigger-'), ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
1494
+ this.id = input(injectId('rdx-select-trigger-'), /* @ts-ignore */
1495
+ ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
1019
1496
  this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1020
- this.isDisabled = computed(() => this.rootContext.disabled() || this.disabled() || Boolean(this.fieldRootContext?.disabledState()), ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
1497
+ this.isDisabled = computed(() => this.rootContext.disabled() || this.disabled() || Boolean(this.fieldRootContext?.disabledState()), /* @ts-ignore */
1498
+ ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
1021
1499
  this.triggerInteraction = createRdxTriggerInteraction({
1022
1500
  trigger: () => this.elementRef.nativeElement,
1023
1501
  activeTrigger: () => this.rootContext.triggerElement(),
@@ -1025,10 +1503,16 @@ class RdxSelectTrigger {
1025
1503
  disabled: () => this.isDisabled(),
1026
1504
  contentId: () => this.rootContext.contentId
1027
1505
  });
1028
- this.invalidState = computed(() => Boolean(this.fieldRootContext?.invalidState()), ...(ngDevMode ? [{ debugName: "invalidState" }] : /* istanbul ignore next */ []));
1029
- this.requiredState = computed(() => Boolean(this.fieldRootContext?.requiredState()), ...(ngDevMode ? [{ debugName: "requiredState" }] : /* istanbul ignore next */ []));
1030
- this.filledState = computed(() => !this.rootContext.isEmptyModelValue() || Boolean(this.fieldRootContext?.filledState()), ...(ngDevMode ? [{ debugName: "filledState" }] : /* istanbul ignore next */ []));
1031
- this.focusedState = computed(() => Boolean(this.fieldRootContext?.focusedState()), ...(ngDevMode ? [{ debugName: "focusedState" }] : /* istanbul ignore next */ []));
1506
+ this.invalidState = computed(() => Boolean(this.fieldRootContext?.invalidState()), /* @ts-ignore */
1507
+ ...(ngDevMode ? [{ debugName: "invalidState" }] : /* istanbul ignore next */ []));
1508
+ this.requiredState = computed(() => this.rootContext.required() || Boolean(this.fieldRootContext?.requiredState()), /* @ts-ignore */
1509
+ ...(ngDevMode ? [{ debugName: "requiredState" }] : /* istanbul ignore next */ []));
1510
+ this.readOnlyState = computed(() => this.rootContext.readOnly(), /* @ts-ignore */
1511
+ ...(ngDevMode ? [{ debugName: "readOnlyState" }] : /* istanbul ignore next */ []));
1512
+ this.filledState = computed(() => !this.rootContext.isEmptyModelValue() || Boolean(this.fieldRootContext?.filledState()), /* @ts-ignore */
1513
+ ...(ngDevMode ? [{ debugName: "filledState" }] : /* istanbul ignore next */ []));
1514
+ this.focusedState = computed(() => Boolean(this.fieldRootContext?.focusedState()), /* @ts-ignore */
1515
+ ...(ngDevMode ? [{ debugName: "focusedState" }] : /* istanbul ignore next */ []));
1032
1516
  this.describedBy = computed(() => {
1033
1517
  if (!this.fieldRootContext) {
1034
1518
  return undefined;
@@ -1038,7 +1522,8 @@ class RdxSelectTrigger {
1038
1522
  ...(this.fieldRootContext.invalidState() ? this.fieldRootContext.errorIds() : [])
1039
1523
  ];
1040
1524
  return ids.length ? ids.join(' ') : undefined;
1041
- }, ...(ngDevMode ? [{ debugName: "describedBy" }] : /* istanbul ignore next */ []));
1525
+ }, /* @ts-ignore */
1526
+ ...(ngDevMode ? [{ debugName: "describedBy" }] : /* istanbul ignore next */ []));
1042
1527
  this.dataAttr = attr;
1043
1528
  afterNextRender(() => {
1044
1529
  this.rootContext.onTriggerChange(this.elementRef);
@@ -1113,10 +1598,10 @@ class RdxSelectTrigger {
1113
1598
  this.fieldRootContext?.setFocused(false);
1114
1599
  this.fieldRootContext?.setTouched(true);
1115
1600
  }
1116
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectTrigger, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1117
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxSelectTrigger, isStandalone: true, selector: "button[rdxSelectTrigger]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "combobox", "type": "button" }, listeners: { "click": "onClickHandler($event)", "pointerdown": "onPointerDown($event)", "pointerup": "onPointerUp($event)", "keydown": "onKeydown($event)", "focus": "onFocus()", "blur": "onBlur()" }, properties: { "attr.id": "id()", "attr.aria-describedby": "describedBy()", "attr.aria-controls": "triggerInteraction.ariaControls()", "attr.aria-invalid": "invalidState() ? \"true\" : undefined", "attr.aria-required": "requiredState() ? \"true\" : undefined", "attr.disabled": "isDisabled() ? \"\" : undefined", "dir": "rootContext.dir()", "attr.data-state": "triggerInteraction.dataState()", "attr.data-popup-open": "triggerInteraction.dataPopupOpen()", "attr.data-placeholder": "dataAttr(rootContext.isEmptyModelValue())", "attr.data-disabled": "dataAttr(isDisabled())", "attr.data-invalid": "dataAttr(invalidState())", "attr.data-valid": "dataAttr(!invalidState())", "attr.data-required": "dataAttr(requiredState())", "attr.data-filled": "dataAttr(filledState())", "attr.data-focused": "dataAttr(focusedState())" } }, hostDirectives: [{ directive: i1.RdxPopperAnchor }], ngImport: i0 }); }
1601
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectTrigger, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1602
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: RdxSelectTrigger, isStandalone: true, selector: "button[rdxSelectTrigger]", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "combobox", "type": "button" }, listeners: { "click": "onClickHandler($event)", "pointerdown": "onPointerDown($event)", "pointerup": "onPointerUp($event)", "keydown": "onKeydown($event)", "focus": "onFocus()", "blur": "onBlur()" }, properties: { "attr.id": "id()", "attr.aria-describedby": "describedBy()", "attr.aria-controls": "triggerInteraction.ariaControls()", "attr.aria-invalid": "invalidState() ? \"true\" : undefined", "attr.aria-required": "requiredState() ? \"true\" : undefined", "attr.aria-readonly": "readOnlyState() ? \"true\" : undefined", "attr.disabled": "isDisabled() ? \"\" : undefined", "dir": "rootContext.dir()", "attr.data-popup-open": "triggerInteraction.dataPopupOpen()", "attr.data-placeholder": "dataAttr(rootContext.isEmptyModelValue())", "attr.data-disabled": "dataAttr(isDisabled())", "attr.data-invalid": "dataAttr(invalidState())", "attr.data-valid": "dataAttr(!invalidState())", "attr.data-required": "dataAttr(requiredState())", "attr.data-readonly": "dataAttr(readOnlyState())", "attr.data-filled": "dataAttr(filledState())", "attr.data-focused": "dataAttr(focusedState())" } }, hostDirectives: [{ directive: i1.RdxPopperAnchor }], ngImport: i0 }); }
1118
1603
  }
1119
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectTrigger, decorators: [{
1604
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectTrigger, decorators: [{
1120
1605
  type: Directive,
1121
1606
  args: [{
1122
1607
  selector: 'button[rdxSelectTrigger]',
@@ -1129,15 +1614,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
1129
1614
  '[attr.aria-controls]': 'triggerInteraction.ariaControls()',
1130
1615
  '[attr.aria-invalid]': 'invalidState() ? "true" : undefined',
1131
1616
  '[attr.aria-required]': 'requiredState() ? "true" : undefined',
1617
+ '[attr.aria-readonly]': 'readOnlyState() ? "true" : undefined',
1132
1618
  '[attr.disabled]': 'isDisabled() ? "" : undefined',
1133
1619
  '[dir]': 'rootContext.dir()',
1134
- '[attr.data-state]': 'triggerInteraction.dataState()',
1135
1620
  '[attr.data-popup-open]': 'triggerInteraction.dataPopupOpen()',
1136
1621
  '[attr.data-placeholder]': 'dataAttr(rootContext.isEmptyModelValue())',
1137
1622
  '[attr.data-disabled]': 'dataAttr(isDisabled())',
1138
1623
  '[attr.data-invalid]': 'dataAttr(invalidState())',
1139
1624
  '[attr.data-valid]': 'dataAttr(!invalidState())',
1140
1625
  '[attr.data-required]': 'dataAttr(requiredState())',
1626
+ '[attr.data-readonly]': 'dataAttr(readOnlyState())',
1141
1627
  '[attr.data-filled]': 'dataAttr(filledState())',
1142
1628
  '[attr.data-focused]': 'dataAttr(focusedState())',
1143
1629
  '(click)': 'onClickHandler($event)',
@@ -1154,10 +1640,12 @@ class RdxSelectValue {
1154
1640
  constructor() {
1155
1641
  this.rootContext = injectSelectRootContext();
1156
1642
  this.elementRef = inject(ElementRef);
1157
- this.placeholder = input(...(ngDevMode ? [undefined, { debugName: "placeholder" }] : /* istanbul ignore next */ []));
1643
+ this.placeholder = input(/* @ts-ignore */
1644
+ ...(ngDevMode ? [undefined, { debugName: "placeholder" }] : /* istanbul ignore next */ []));
1158
1645
  this.slotText = computed(() => {
1159
1646
  return this.selectedLabel().length ? this.selectedLabel().join(', ') : this.placeholder();
1160
- }, ...(ngDevMode ? [{ debugName: "slotText" }] : /* istanbul ignore next */ []));
1647
+ }, /* @ts-ignore */
1648
+ ...(ngDevMode ? [{ debugName: "slotText" }] : /* istanbul ignore next */ []));
1161
1649
  this.selectedLabel = computed(() => {
1162
1650
  const options = Array.from(this.rootContext.optionsSet());
1163
1651
  const customLabel = this.rootContext.itemToStringLabel();
@@ -1171,15 +1659,16 @@ class RdxSelectValue {
1171
1659
  const value = this.rootContext.value();
1172
1660
  const list = Array.isArray(value) ? value.map((v) => labelFor(v)) : [labelFor(value)];
1173
1661
  return list.filter(Boolean);
1174
- }, ...(ngDevMode ? [{ debugName: "selectedLabel" }] : /* istanbul ignore next */ []));
1662
+ }, /* @ts-ignore */
1663
+ ...(ngDevMode ? [{ debugName: "selectedLabel" }] : /* istanbul ignore next */ []));
1175
1664
  afterNextRender(() => {
1176
1665
  this.rootContext.valueElement.set(this.elementRef.nativeElement);
1177
1666
  });
1178
1667
  }
1179
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectValue, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1180
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxSelectValue, isStandalone: true, selector: "span[rdxSelectValue]", inputs: { placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "style.pointer-events": "\"none\"" } }, exportAs: ["rdxSelectedValue"], ngImport: i0 }); }
1668
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectValue, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1669
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: RdxSelectValue, isStandalone: true, selector: "span[rdxSelectValue]", inputs: { placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "style.pointer-events": "\"none\"" } }, exportAs: ["rdxSelectedValue"], ngImport: i0 }); }
1181
1670
  }
1182
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectValue, decorators: [{
1671
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: RdxSelectValue, decorators: [{
1183
1672
  type: Directive,
1184
1673
  args: [{
1185
1674
  selector: 'span[rdxSelectValue]',
@@ -1204,6 +1693,10 @@ const _importsSelect = [
1204
1693
  RdxSelectGroupLabel,
1205
1694
  RdxSelectGroup,
1206
1695
  RdxSelectPositioner,
1696
+ RdxSelectItemAlignedPosition,
1697
+ RdxSelectItemAlignedPositionContent,
1698
+ RdxSelectScrollUpButton,
1699
+ RdxSelectScrollDownButton,
1207
1700
  RdxSelectIcon,
1208
1701
  RdxSelectSeparator,
1209
1702
  RdxSelectBackdrop
@@ -1213,5 +1706,5 @@ const _importsSelect = [
1213
1706
  * Generated bundle index. Do not edit.
1214
1707
  */
1215
1708
 
1216
- export { CONTENT_MARGIN, OPEN_KEYS, RDX_SELECT_POSITIONER_TOKEN, RdxSelectBackdrop, RdxSelectGroup, RdxSelectGroupLabel, RdxSelectIcon, RdxSelectItem, RdxSelectItemIndicator, RdxSelectItemText, RdxSelectList, RdxSelectPopup, RdxSelectPortal, RdxSelectPortalMisuseGuard, RdxSelectPositioner, RdxSelectRoot, RdxSelectSeparator, RdxSelectTrigger, RdxSelectValue, SELECTION_KEYS, _importsSelect, compare, focusFirst, injectSelectItemContext, injectSelectPopupContext, injectSelectRootContext, provideSelectItemContext, provideSelectPopupContext, provideSelectRootContext, shouldShowPlaceholder, valueComparator };
1709
+ export { CONTENT_MARGIN, OPEN_KEYS, RDX_SELECT_POSITIONER_TOKEN, RdxSelectBackdrop, RdxSelectGroup, RdxSelectGroupLabel, RdxSelectIcon, RdxSelectItem, RdxSelectItemAlignedPosition, RdxSelectItemAlignedPositionContent, RdxSelectItemIndicator, RdxSelectItemText, RdxSelectList, RdxSelectPopup, RdxSelectPortal, RdxSelectPortalMisuseGuard, RdxSelectPositioner, RdxSelectRoot, RdxSelectScrollButtonBase, RdxSelectScrollDownButton, RdxSelectScrollUpButton, RdxSelectSeparator, RdxSelectTrigger, RdxSelectValue, SELECTION_KEYS, _importsSelect, compare, focusFirst, injectSelectItemContext, injectSelectPopupContext, injectSelectRootContext, provideSelectItemContext, provideSelectPopupContext, provideSelectRootContext, shouldShowPlaceholder, valueComparator };
1217
1710
  //# sourceMappingURL=radix-ng-primitives-select.mjs.map