@radix-ng/primitives 1.0.0-beta.3 → 1.0.0-beta.4

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 (97) hide show
  1. package/README.md +1 -1
  2. package/fesm2022/radix-ng-primitives-accordion.mjs +5 -3
  3. package/fesm2022/radix-ng-primitives-accordion.mjs.map +1 -1
  4. package/fesm2022/radix-ng-primitives-alert-dialog.mjs +3 -2
  5. package/fesm2022/radix-ng-primitives-alert-dialog.mjs.map +1 -1
  6. package/fesm2022/radix-ng-primitives-autocomplete.mjs +617 -659
  7. package/fesm2022/radix-ng-primitives-autocomplete.mjs.map +1 -1
  8. package/fesm2022/radix-ng-primitives-calendar.mjs +5 -3
  9. package/fesm2022/radix-ng-primitives-calendar.mjs.map +1 -1
  10. package/fesm2022/radix-ng-primitives-combobox.mjs +1305 -572
  11. package/fesm2022/radix-ng-primitives-combobox.mjs.map +1 -1
  12. package/fesm2022/radix-ng-primitives-config.mjs +13 -4
  13. package/fesm2022/radix-ng-primitives-config.mjs.map +1 -1
  14. package/fesm2022/radix-ng-primitives-context-menu.mjs +51 -10
  15. package/fesm2022/radix-ng-primitives-context-menu.mjs.map +1 -1
  16. package/fesm2022/radix-ng-primitives-core.mjs +1345 -64
  17. package/fesm2022/radix-ng-primitives-core.mjs.map +1 -1
  18. package/fesm2022/radix-ng-primitives-date-field.mjs +5 -3
  19. package/fesm2022/radix-ng-primitives-date-field.mjs.map +1 -1
  20. package/fesm2022/radix-ng-primitives-dialog.mjs +240 -112
  21. package/fesm2022/radix-ng-primitives-dialog.mjs.map +1 -1
  22. package/fesm2022/radix-ng-primitives-direction-provider.mjs +70 -0
  23. package/fesm2022/radix-ng-primitives-direction-provider.mjs.map +1 -0
  24. package/fesm2022/radix-ng-primitives-dismissable-layer.mjs +519 -184
  25. package/fesm2022/radix-ng-primitives-dismissable-layer.mjs.map +1 -1
  26. package/fesm2022/radix-ng-primitives-drawer.mjs +3 -3
  27. package/fesm2022/radix-ng-primitives-drawer.mjs.map +1 -1
  28. package/fesm2022/radix-ng-primitives-field.mjs +3 -2
  29. package/fesm2022/radix-ng-primitives-field.mjs.map +1 -1
  30. package/fesm2022/radix-ng-primitives-floating-focus-manager.mjs +517 -0
  31. package/fesm2022/radix-ng-primitives-floating-focus-manager.mjs.map +1 -0
  32. package/fesm2022/radix-ng-primitives-focus-scope.mjs +296 -70
  33. package/fesm2022/radix-ng-primitives-focus-scope.mjs.map +1 -1
  34. package/fesm2022/radix-ng-primitives-menu.mjs +861 -286
  35. package/fesm2022/radix-ng-primitives-menu.mjs.map +1 -1
  36. package/fesm2022/radix-ng-primitives-menubar.mjs +32 -4
  37. package/fesm2022/radix-ng-primitives-menubar.mjs.map +1 -1
  38. package/fesm2022/radix-ng-primitives-navigation-menu.mjs +144 -159
  39. package/fesm2022/radix-ng-primitives-navigation-menu.mjs.map +1 -1
  40. package/fesm2022/radix-ng-primitives-popover.mjs +220 -205
  41. package/fesm2022/radix-ng-primitives-popover.mjs.map +1 -1
  42. package/fesm2022/radix-ng-primitives-popper.mjs +94 -51
  43. package/fesm2022/radix-ng-primitives-popper.mjs.map +1 -1
  44. package/fesm2022/radix-ng-primitives-presence.mjs +1 -1
  45. package/fesm2022/radix-ng-primitives-presence.mjs.map +1 -1
  46. package/fesm2022/radix-ng-primitives-preview-card.mjs +141 -173
  47. package/fesm2022/radix-ng-primitives-preview-card.mjs.map +1 -1
  48. package/fesm2022/radix-ng-primitives-roving-focus.mjs +4 -2
  49. package/fesm2022/radix-ng-primitives-roving-focus.mjs.map +1 -1
  50. package/fesm2022/radix-ng-primitives-scroll-area.mjs +5 -4
  51. package/fesm2022/radix-ng-primitives-scroll-area.mjs.map +1 -1
  52. package/fesm2022/radix-ng-primitives-select.mjs +211 -156
  53. package/fesm2022/radix-ng-primitives-select.mjs.map +1 -1
  54. package/fesm2022/radix-ng-primitives-slider.mjs +5 -3
  55. package/fesm2022/radix-ng-primitives-slider.mjs.map +1 -1
  56. package/fesm2022/radix-ng-primitives-stepper.mjs +5 -3
  57. package/fesm2022/radix-ng-primitives-stepper.mjs.map +1 -1
  58. package/fesm2022/radix-ng-primitives-time-field.mjs +5 -3
  59. package/fesm2022/radix-ng-primitives-time-field.mjs.map +1 -1
  60. package/fesm2022/radix-ng-primitives-toast.mjs +15 -36
  61. package/fesm2022/radix-ng-primitives-toast.mjs.map +1 -1
  62. package/fesm2022/radix-ng-primitives-toggle-group.mjs +5 -3
  63. package/fesm2022/radix-ng-primitives-toggle-group.mjs.map +1 -1
  64. package/fesm2022/radix-ng-primitives-toolbar.mjs +5 -3
  65. package/fesm2022/radix-ng-primitives-toolbar.mjs.map +1 -1
  66. package/fesm2022/radix-ng-primitives-tooltip.mjs +73 -110
  67. package/fesm2022/radix-ng-primitives-tooltip.mjs.map +1 -1
  68. package/package.json +10 -1
  69. package/types/radix-ng-primitives-accordion.d.ts +4 -3
  70. package/types/radix-ng-primitives-autocomplete.d.ts +217 -152
  71. package/types/radix-ng-primitives-calendar.d.ts +5 -3
  72. package/types/radix-ng-primitives-combobox.d.ts +672 -283
  73. package/types/radix-ng-primitives-config.d.ts +1 -1
  74. package/types/radix-ng-primitives-context-menu.d.ts +15 -5
  75. package/types/radix-ng-primitives-core.d.ts +762 -14
  76. package/types/radix-ng-primitives-date-field.d.ts +3 -2
  77. package/types/radix-ng-primitives-dialog.d.ts +77 -32
  78. package/types/radix-ng-primitives-direction-provider.d.ts +41 -0
  79. package/types/radix-ng-primitives-dismissable-layer.d.ts +147 -99
  80. package/types/radix-ng-primitives-field.d.ts +1 -0
  81. package/types/radix-ng-primitives-floating-focus-manager.d.ts +175 -0
  82. package/types/radix-ng-primitives-focus-scope.d.ts +132 -1
  83. package/types/radix-ng-primitives-menu.d.ts +186 -103
  84. package/types/radix-ng-primitives-navigation-menu.d.ts +37 -75
  85. package/types/radix-ng-primitives-popover.d.ts +59 -92
  86. package/types/radix-ng-primitives-popper.d.ts +39 -9
  87. package/types/radix-ng-primitives-preview-card.d.ts +39 -72
  88. package/types/radix-ng-primitives-roving-focus.d.ts +7 -6
  89. package/types/radix-ng-primitives-scroll-area.d.ts +2 -2
  90. package/types/radix-ng-primitives-select.d.ts +145 -108
  91. package/types/radix-ng-primitives-slider.d.ts +5 -4
  92. package/types/radix-ng-primitives-stepper.d.ts +4 -3
  93. package/types/radix-ng-primitives-time-field.d.ts +3 -2
  94. package/types/radix-ng-primitives-toast.d.ts +7 -7
  95. package/types/radix-ng-primitives-toggle-group.d.ts +5 -4
  96. package/types/radix-ng-primitives-toolbar.d.ts +3 -2
  97. package/types/radix-ng-primitives-tooltip.d.ts +24 -67
@@ -1,13 +1,14 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, model, input, booleanAttribute, output, computed, signal, untracked, effect, Directive, InjectionToken, ElementRef, Injector, DestroyRef, afterNextRender, linkedSignal, isDevMode, numberAttribute, forwardRef } from '@angular/core';
3
- import { isEqual, getActiveElement, createContext, useTransitionStatus, isNullish, injectId, useListHighlight, useScrollLock, handleAndDispatchCustomEvent } from '@radix-ng/primitives/core';
2
+ import { inject, model, signal, computed, ElementRef, input, booleanAttribute, output, untracked, effect, Directive, InjectionToken, Injector, DestroyRef, afterNextRender, linkedSignal, isDevMode, forwardRef } from '@angular/core';
3
+ import * as i3 from '@radix-ng/primitives/core';
4
+ import { isEqual, getActiveElement, createContext, createFloatingRootContext, useTransitionStatus, isNullish, provideFloatingTree, provideFloatingRootContext, injectId, RDX_FLOATING_ROOT_CONTEXT, RDX_FLOATING_REGISTRATION, useListHighlight, useAnchoredScrollLock, RdxFloatingNodeRegistration, handleAndDispatchCustomEvent, rdxDevError, setupInternalBackdrop } from '@radix-ng/primitives/core';
5
+ import { injectDirection } from '@radix-ng/primitives/direction-provider';
6
+ import { getInteractionTypeFromEvent } from '@radix-ng/primitives/floating-focus-manager';
4
7
  import * as i1 from '@radix-ng/primitives/popper';
5
- import { RdxPopper, RdxPopperContent, RdxPopperContentWrapper, RdxPopperAnchor } from '@radix-ng/primitives/popper';
8
+ import { RdxPopper, RdxPopperContent, RdxPopperContentWrapper, legacyPopperVars, provideRdxPopperContentWrapper, provideRdxPopperContentConfig, RdxPopperAnchor } from '@radix-ng/primitives/popper';
6
9
  import * as i4 from '@radix-ng/primitives/collection';
7
10
  import { RdxCollectionProvider, RdxCollectionItem } from '@radix-ng/primitives/collection';
8
- import { outputFromObservable, outputToObservable } from '@angular/core/rxjs-interop';
9
- import * as i3 from '@radix-ng/primitives/dismissable-layer';
10
- import { RdxDismissableLayer, provideRdxDismissableLayerConfig } from '@radix-ng/primitives/dismissable-layer';
11
+ import { RdxDismiss } from '@radix-ng/primitives/dismissable-layer';
11
12
  import * as i2 from '@radix-ng/primitives/focus-scope';
12
13
  import { RdxFocusScope } from '@radix-ng/primitives/focus-scope';
13
14
  import * as i1$1 from '@radix-ng/primitives/portal';
@@ -52,6 +53,17 @@ function focusFirst(candidates) {
52
53
  }
53
54
  }
54
55
 
56
+ function createChangeEventDetails(reason, event) {
57
+ let canceled = false;
58
+ return {
59
+ reason,
60
+ event,
61
+ cancel: () => {
62
+ canceled = true;
63
+ },
64
+ isCanceled: () => canceled
65
+ };
66
+ }
55
67
  const context$2 = () => {
56
68
  const context = inject(RdxSelectRoot);
57
69
  return {
@@ -65,6 +77,10 @@ const context$2 = () => {
65
77
  isItemEqualToValue: context.isItemEqualToValue,
66
78
  itemToStringLabel: context.itemToStringLabel,
67
79
  open: context.open,
80
+ openedByTouch: context.openedByTouch,
81
+ openMethod: context.openMethod,
82
+ openInteractionType: context.openInteractionType,
83
+ closeInteractionType: context.closeInteractionType,
68
84
  disabled: context.disabled,
69
85
  modal: context.modal,
70
86
  isEmptyModelValue: context.isEmptyModelValue,
@@ -84,32 +100,48 @@ const context$2 = () => {
84
100
  context.optionsSet().delete(existingOption);
85
101
  }
86
102
  },
87
- onValueChange: context.handleValueChange,
103
+ onValueChange: (value, reason, event) => context.setValue(value, reason, event),
88
104
  onTriggerChange: (node) => {
89
105
  context.triggerElement.set(node.nativeElement);
90
106
  },
91
107
  onValueElementChange: (node) => {
92
108
  context.valueElement.set(node);
93
109
  },
94
- onOpenChange: (value) => {
95
- context.open.set(value);
96
- }
110
+ onOpenChange: (value, reason, event) => context.setOpen(value, reason, event)
97
111
  };
98
112
  };
99
113
  const [injectSelectRootContext, provideSelectRootContext] = createContext('RdxSelectRootContext', 'components/select');
100
114
  class RdxSelectRoot {
101
115
  constructor() {
102
116
  this.open = model(false, ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
117
+ /** Whether the current open was initiated by **touch** (ADR 0016 §3 — gates the anchored scroll lock). */
118
+ this.openedByTouch = signal(false, ...(ngDevMode ? [{ debugName: "openedByTouch" }] : /* istanbul ignore next */ []));
119
+ /** How the select was opened. Base UI names this state `openMethod`. */
120
+ this.openInteractionType = signal(null, ...(ngDevMode ? [{ debugName: "openInteractionType" }] : /* istanbul ignore next */ []));
121
+ /** How the select was closed. */
122
+ this.closeInteractionType = signal(null, ...(ngDevMode ? [{ debugName: "closeInteractionType" }] : /* istanbul ignore next */ []));
123
+ /** Public Base UI-aligned alias for the open interaction type. */
124
+ this.openMethod = computed(() => this.openInteractionType(), ...(ngDevMode ? [{ debugName: "openMethod" }] : /* istanbul ignore next */ []));
125
+ /** Per-popup floating root context (ADR 0015) — `open` / `triggers` / reference for the dismissal engine. */
126
+ this.floatingContext = createFloatingRootContext({
127
+ ownerDocument: inject(ElementRef).nativeElement.ownerDocument,
128
+ open: () => this.open()
129
+ });
103
130
  this.value = model(...(ngDevMode ? [undefined, { debugName: "value" }] : /* istanbul ignore next */ []));
104
131
  this.multiple = input(false, { ...(ngDevMode ? { debugName: "multiple" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
105
132
  this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
106
133
  /** Whether the popup is modal: locks page scroll and makes outside content inert while open. */
107
134
  this.modal = input(true, { ...(ngDevMode ? { debugName: "modal" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
108
- this.dir = input('ltr', ...(ngDevMode ? [{ debugName: "dir" }] : /* istanbul ignore next */ []));
135
+ this.dirInput = input(undefined, { ...(ngDevMode ? { debugName: "dirInput" } : /* istanbul ignore next */ {}), alias: 'dir' });
136
+ this.dir = injectDirection(this.dirInput);
109
137
  /** How item values are compared for equality — a function `(a, b) => boolean` or an object key. */
110
138
  this.isItemEqualToValue = input(...(ngDevMode ? [undefined, { debugName: "isItemEqualToValue" }] : /* istanbul ignore next */ []));
111
139
  /** Converts a value to its display label (used by `RdxSelectValue`). */
112
140
  this.itemToStringLabel = input(...(ngDevMode ? [undefined, { debugName: "itemToStringLabel" }] : /* istanbul ignore next */ []));
141
+ /** Emits before an open-state change is committed; call `eventDetails.cancel()` to veto it. */
142
+ this.onOpenChange = output();
143
+ /** Emits before a value change is committed; call `eventDetails.cancel()` to veto it. */
144
+ this.onValueChange = output();
113
145
  /** Emits after the open/close transition (including any exit animation) finishes. */
114
146
  this.onOpenChangeComplete = output();
115
147
  this.transition = useTransitionStatus((open) => this.onOpenChangeComplete.emit(open));
@@ -150,34 +182,89 @@ class RdxSelectRoot {
150
182
  previousOpen = open;
151
183
  untracked(() => this.transition.start(open));
152
184
  });
185
+ // A fresh open starts non-touch; the trigger flips it on a touch open. Reset whenever it closes.
186
+ effect(() => {
187
+ if (!this.open()) {
188
+ untracked(() => this.openedByTouch.set(false));
189
+ }
190
+ });
191
+ // Bridge the trigger into the floating context: the dismissal capability treats a press on the
192
+ // trigger (the reference) as "inside", and uses it for positioning containment (ADR 0015).
193
+ effect((onCleanup) => {
194
+ const trigger = this.triggerElement();
195
+ this.floatingContext.setReferenceElement(trigger);
196
+ if (trigger) {
197
+ this.floatingContext.triggers.add(trigger);
198
+ onCleanup(() => this.floatingContext.triggers.delete(trigger));
199
+ }
200
+ });
153
201
  }
154
202
  getOption(value) {
155
203
  return Array.from(this.optionsSet()).find((option) => valueComparator(value, option.value, this.isItemEqualToValue()));
156
204
  }
157
- handleValueChange(value) {
205
+ setValue(value, reason = 'none', event = new Event('select.value-change')) {
206
+ const nextValue = this.multiple()
207
+ ? (() => {
208
+ const current = this.value();
209
+ const array = Array.isArray(current) ? [...current] : [];
210
+ const index = array.findIndex((i) => compare(i, value, this.isItemEqualToValue()));
211
+ index === -1 ? array.push(value) : array.splice(index, 1);
212
+ return [...array];
213
+ })()
214
+ : value;
215
+ const eventDetails = createChangeEventDetails(reason, event);
216
+ this.onValueChange.emit({ value: nextValue, eventDetails });
217
+ if (eventDetails.isCanceled()) {
218
+ return false;
219
+ }
158
220
  if (this.multiple()) {
159
- const current = this.value();
160
- const array = Array.isArray(current) ? [...current] : [];
161
- const index = array.findIndex((i) => compare(i, value, this.isItemEqualToValue()));
162
- index === -1 ? array.push(value) : array.splice(index, 1);
163
- this.value.set([...array]);
221
+ this.value.set(nextValue);
164
222
  }
165
223
  else {
166
- this.value.set(value);
224
+ this.value.set(nextValue);
167
225
  }
226
+ return true;
227
+ }
228
+ setOpen(open, reason = 'none', event) {
229
+ const resolvedEvent = event ?? new Event('select.open-change');
230
+ const interactionType = getInteractionTypeFromEvent(event);
231
+ const eventDetails = createChangeEventDetails(reason, resolvedEvent);
232
+ this.onOpenChange.emit({ open, eventDetails });
233
+ if (eventDetails.isCanceled()) {
234
+ return false;
235
+ }
236
+ this.open.set(open);
237
+ if (open) {
238
+ this.openedByTouch.set(event?.pointerType === 'touch');
239
+ this.openInteractionType.set(interactionType);
240
+ }
241
+ else {
242
+ this.closeInteractionType.set(interactionType);
243
+ }
244
+ return true;
168
245
  }
169
246
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectRoot, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
170
- 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 }, dir: { classPropertyName: "dir", 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", onOpenChangeComplete: "onOpenChangeComplete" }, providers: [provideSelectRootContext(context$2)], exportAs: ["rdxSelectRoot"], hostDirectives: [{ directive: i1.RdxPopper }], ngImport: i0 }); }
247
+ 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: [
248
+ provideSelectRootContext(context$2),
249
+ // New floating foundation (ADR 0015/0017) — the dismissal capability reads this shared context.
250
+ provideFloatingTree(),
251
+ provideFloatingRootContext(() => inject(RdxSelectRoot).floatingContext)
252
+ ], exportAs: ["rdxSelectRoot"], hostDirectives: [{ directive: i1.RdxPopper }], ngImport: i0 }); }
171
253
  }
172
254
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectRoot, decorators: [{
173
255
  type: Directive,
174
256
  args: [{
175
257
  selector: '[rdxSelectRoot]',
176
258
  exportAs: 'rdxSelectRoot',
177
- providers: [provideSelectRootContext(context$2)],
259
+ providers: [
260
+ provideSelectRootContext(context$2),
261
+ // New floating foundation (ADR 0015/0017) — the dismissal capability reads this shared context.
262
+ provideFloatingTree(),
263
+ provideFloatingRootContext(() => inject(RdxSelectRoot).floatingContext)
264
+ ],
178
265
  hostDirectives: [RdxPopper]
179
266
  }]
180
- }], 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 }] }], dir: [{ 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 }] }], onOpenChangeComplete: [{ type: i0.Output, args: ["onOpenChangeComplete"] }] } });
267
+ }], 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"] }] } });
181
268
 
182
269
  /**
183
270
  * An overlay rendered beneath the popup in `modal` mode. Place it inside the portal/presence; style
@@ -312,7 +399,8 @@ const RDX_SELECT_POSITIONER_TOKEN = new InjectionToken('RDX_SELECT_POSITIONER_TO
312
399
  */
313
400
  class RdxSelectPopup {
314
401
  constructor() {
315
- this.dismissableLayer = inject(RdxDismissableLayer);
402
+ this.floatingContext = inject(RDX_FLOATING_ROOT_CONTEXT);
403
+ this.registration = inject(RDX_FLOATING_REGISTRATION, { optional: true });
316
404
  this.currentElement = inject(ElementRef);
317
405
  this.collection = inject(RdxCollectionProvider);
318
406
  this.injector = inject(Injector);
@@ -347,12 +435,12 @@ class RdxSelectPopup {
347
435
  * Event handler called when the escape key is down.
348
436
  * Can be prevented.
349
437
  */
350
- this.escapeKeyDown = outputFromObservable(outputToObservable(this.dismissableLayer.escapeKeyDown));
438
+ this.escapeKeyDown = output();
351
439
  /**
352
- * Event handler called when a `pointerdown` event happens outside of the `DismissableLayer`.
440
+ * Event handler called when a `pointerdown` event happens outside of the popup.
353
441
  * Can be prevented.
354
442
  */
355
- this.pointerDownOutside = outputFromObservable(outputToObservable(this.dismissableLayer.pointerDownOutside));
443
+ this.pointerDownOutside = output();
356
444
  this.content = signal(null, ...(ngDevMode ? [{ debugName: "content" }] : /* istanbul ignore next */ []));
357
445
  /**
358
446
  * The positioner — now an **ancestor** element — provides {@link RDX_SELECT_POSITIONER_TOKEN}
@@ -383,13 +471,33 @@ class RdxSelectPopup {
383
471
  item.element.scrollIntoView?.({ block: 'nearest' });
384
472
  }
385
473
  });
386
- // Lock page scroll while a modal popup is open (content mounts only while open).
387
- useScrollLock(this.rootContext.modal);
474
+ // Activation policy (ADR 0016 §2 + §3). Lock page scroll while the popup is OPEN and either modal
475
+ // **or** item-aligned — Base UI `(alignItemWithTriggerActive || modal) && open` (AC #3): an
476
+ // item-aligned select overlays the trigger, so the page must not scroll behind it even when
477
+ // `modal === false`. The gate keys on `open` (not mounted) so it releases at close-start. A
478
+ // **touch** open never uses item-aligned mode (the positioner falls back), so the lock there is
479
+ // driven by `modal` alone and the anchored helper only engages when the popup is viewport-width (§3).
480
+ const itemAlignedActive = computed(() => this.positioner?.alignItemWithTriggerActive?.() ?? false, ...(ngDevMode ? [{ debugName: "itemAlignedActive" }] : /* istanbul ignore next */ []));
481
+ useAnchoredScrollLock(computed(() => (itemAlignedActive() || this.rootContext.modal()) && this.rootContext.open()), {
482
+ touchOpen: () => this.rootContext.openedByTouch(),
483
+ element: () => this.currentElement.nativeElement
484
+ });
388
485
  // The popup's animation determines when the open/close transition (onOpenChangeComplete) is done.
389
486
  const unregisterTransition = this.rootContext.registerTransitionElement(this.currentElement.nativeElement);
390
487
  inject(DestroyRef).onDestroy(unregisterTransition);
391
- this.dismissableLayer.focusOutside.subscribe((e) => e.preventDefault());
392
- this.dismissableLayer.dismiss.subscribe(() => this.rootContext.onOpenChange(false));
488
+ // The popup (listbox) is this layer's floating element — the inside surface for containment.
489
+ this.floatingContext.setFloatingElement(this.currentElement.nativeElement);
490
+ // Dismissal (ADR 0015): Escape or an outside press closes the select. Focus-out does NOT close it
491
+ // — the listbox holds focus while open (items are navigated virtually), so a focus-out is not a
492
+ // dismissal (the legacy preventDefaulted it too).
493
+ new RdxDismiss(this.floatingContext, () => this.registration?.node() ?? null, {
494
+ escapeKey: () => true,
495
+ outsidePress: () => true,
496
+ focusOutside: () => false,
497
+ onEscapeKeyDown: (event) => this.escapeKeyDown.emit(event),
498
+ onPointerDownOutside: (event) => this.pointerDownOutside.emit(event),
499
+ onDismiss: (reason, event) => this.rootContext.onOpenChange(false, reason === 'escape-key' ? 'escape-key' : reason === 'focus-outside' ? 'focus-out' : 'outside-press', event)
500
+ });
393
501
  const focusScope = inject(RdxFocusScope);
394
502
  afterNextRender(() => {
395
503
  focusScope.unmountAutoFocus.subscribe((event) => {
@@ -427,8 +535,9 @@ class RdxSelectPopup {
427
535
  }
428
536
  else {
429
537
  // otherwise, if the event was outside the content, close.
430
- if (!this.content()?.contains(event.target))
431
- this.rootContext.onOpenChange(false);
538
+ if (!this.content()?.contains(event.target)) {
539
+ this.rootContext.onOpenChange(false, 'outside-press', event);
540
+ }
432
541
  }
433
542
  document.removeEventListener('pointermove', handlePointerMove);
434
543
  this.rootContext.triggerPointerDownPosRef.set(null);
@@ -447,6 +556,21 @@ class RdxSelectPopup {
447
556
  });
448
557
  });
449
558
  });
559
+ effect((onCleanup) => {
560
+ if (!itemAlignedActive() || !this.rootContext.open()) {
561
+ return;
562
+ }
563
+ const popup = this.content();
564
+ const view = popup?.ownerDocument.defaultView;
565
+ if (!view) {
566
+ return;
567
+ }
568
+ const handleResize = (event) => {
569
+ this.rootContext.onOpenChange(false, 'window-resize', event);
570
+ };
571
+ view.addEventListener('resize', handleResize);
572
+ onCleanup(() => view.removeEventListener('resize', handleResize));
573
+ });
450
574
  }
451
575
  /** Highlights the selected item (or the first enabled one) when the popup opens. */
452
576
  highlightSelectedItem() {
@@ -481,9 +605,10 @@ class RdxSelectPopup {
481
605
  event.preventDefault();
482
606
  const item = this.highlight.highlightedItem();
483
607
  if (item && !item.disabled()) {
484
- this.rootContext.onValueChange(item.value());
485
- if (!this.rootContext.multiple())
486
- this.rootContext.onOpenChange(false);
608
+ this.rootContext.onValueChange(item.value(), 'item-press', event);
609
+ if (!this.rootContext.multiple()) {
610
+ this.rootContext.onOpenChange(false, 'item-press', event);
611
+ }
487
612
  }
488
613
  return;
489
614
  }
@@ -507,28 +632,14 @@ class RdxSelectPopup {
507
632
  }
508
633
  }
509
634
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectPopup, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
510
- 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: [
511
- provideSelectPopupContext(context$1),
512
- provideRdxDismissableLayerConfig(() => {
513
- return {
514
- disableOutsidePointerEvents: injectSelectRootContext().modal
515
- };
516
- })
517
- ], hostDirectives: [{ directive: i1.RdxPopperContent }, { directive: i2.RdxFocusScope }, { directive: i3.RdxDismissableLayer }, { directive: i4.RdxCollectionProvider }], ngImport: i0 }); }
635
+ 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: [provideSelectPopupContext(context$1)], hostDirectives: [{ directive: i1.RdxPopperContent }, { directive: i2.RdxFocusScope }, { directive: i3.RdxFloatingNodeRegistration }, { directive: i4.RdxCollectionProvider }], ngImport: i0 }); }
518
636
  }
519
637
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectPopup, decorators: [{
520
638
  type: Directive,
521
639
  args: [{
522
640
  selector: '[rdxSelectPopup]',
523
- hostDirectives: [RdxPopperContent, RdxFocusScope, RdxDismissableLayer, RdxCollectionProvider],
524
- providers: [
525
- provideSelectPopupContext(context$1),
526
- provideRdxDismissableLayerConfig(() => {
527
- return {
528
- disableOutsidePointerEvents: injectSelectRootContext().modal
529
- };
530
- })
531
- ],
641
+ hostDirectives: [RdxPopperContent, RdxFocusScope, RdxFloatingNodeRegistration, RdxCollectionProvider],
642
+ providers: [provideSelectPopupContext(context$1)],
532
643
  host: {
533
644
  role: 'listbox',
534
645
  tabindex: '-1',
@@ -594,9 +705,9 @@ class RdxSelectItem {
594
705
  handleAndDispatchCustomEvent(this.SELECT_SELECT, async (event) => {
595
706
  if (event.defaultPrevented)
596
707
  return;
597
- this.rootContext.onValueChange(this.value());
708
+ this.rootContext.onValueChange(this.value(), 'item-press', event.detail.originalEvent);
598
709
  if (!this.rootContext.multiple()) {
599
- this.rootContext.onOpenChange(false);
710
+ this.rootContext.onOpenChange(false, 'item-press', event.detail.originalEvent);
600
711
  }
601
712
  }, eventDetail);
602
713
  }
@@ -773,9 +884,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
773
884
  class RdxSelectPortalMisuseGuard {
774
885
  constructor() {
775
886
  if (isDevMode()) {
776
- throw new Error('[rdxSelectPortal] is now a structural directive. ' +
887
+ rdxDevError('select/portal-on-element', '`rdxSelectPortal` is now a structural directive. ' +
777
888
  'Use `*rdxSelectPortal` on the popup element or `<ng-template rdxSelectPortal>`. ' +
778
- 'rdxSelectPortalPresence has been removed. See https://radix-ng.com/components/select.md');
889
+ 'rdxSelectPortalPresence has been removed.', 'components/select');
779
890
  }
780
891
  }
781
892
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectPortalMisuseGuard, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
@@ -788,112 +899,53 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
788
899
  }]
789
900
  }], ctorParameters: () => [] });
790
901
 
791
- class RdxSelectPositioner {
902
+ /**
903
+ * Positions the select popup against the trigger using the popper engine.
904
+ *
905
+ * A "thin" positioner (ADR 0012): it inherits the full popper positioning surface — the inputs
906
+ * (`side`, `sideOffset`, `align`, …), the `placed` output, and the host bindings — from
907
+ * {@link RdxPopperContentWrapper}, and declares select's Base UI-aligned defaults via the config
908
+ * provider. It also satisfies {@link RdxPositionerImpl} (via the inherited `placed`) so the popup can
909
+ * resolve it through {@link RDX_SELECT_POSITIONER_TOKEN}, the same as the item-aligned positioner.
910
+ */
911
+ class RdxSelectPositioner extends RdxPopperContentWrapper {
792
912
  constructor() {
793
- /**
794
- * The preferred side of the anchor to render against when open.
795
- * Will be reversed when collisions occur and avoidCollisions is enabled.
796
- */
797
- this.side = input('bottom', ...(ngDevMode ? [{ debugName: "side" }] : /* istanbul ignore next */ []));
798
- /**
799
- * The distance in pixels from the anchor.
800
- */
801
- this.sideOffset = input(0, { ...(ngDevMode ? { debugName: "sideOffset" } : /* istanbul ignore next */ {}), transform: numberAttribute });
802
- /**
803
- * The preferred alignment against the anchor. May change when collisions occur.
804
- */
805
- this.align = input('start', ...(ngDevMode ? [{ debugName: "align" }] : /* istanbul ignore next */ []));
806
- /**
807
- * An offset in pixels from the `start` or `end` alignment options.
808
- */
809
- this.alignOffset = input(0, { ...(ngDevMode ? { debugName: "alignOffset" } : /* istanbul ignore next */ {}), transform: numberAttribute });
810
- /**
811
- * The padding between the arrow and the edges of the content.
812
- * If your content has border-radius, this will prevent it from overflowing the corners.
813
- */
814
- this.arrowPadding = input(0, { ...(ngDevMode ? { debugName: "arrowPadding" } : /* istanbul ignore next */ {}), transform: numberAttribute });
815
- /**
816
- * When `true`, overrides the `side` and `align` preferences to prevent collisions with boundary edges.
817
- */
818
- this.avoidCollisions = input(true, { ...(ngDevMode ? { debugName: "avoidCollisions" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
819
- /**
820
- * The element used as the collision boundary.
821
- * By default this is the viewport, though you can provide additional element(s) to be included in this check.
822
- */
823
- this.collisionBoundary = input(...(ngDevMode ? [undefined, { debugName: "collisionBoundary" }] : /* istanbul ignore next */ []));
824
- /**
825
- * The distance in pixels from the boundary edges where collision detection should occur.
826
- * Accepts a number (same for all sides), or a partial padding object, for example: `{ top: 20, left: 20 }`.
827
- */
828
- this.collisionPadding = input(0, ...(ngDevMode ? [{ debugName: "collisionPadding" }] : /* istanbul ignore next */ []));
829
- /**
830
- * The sticky behavior on the `align` axis.
831
- * - `partial` will keep the content in the boundary as long as the trigger is at least partially in the boundary
832
- * - `always` will keep the content in the boundary regardless.
833
- */
834
- this.sticky = input('partial', ...(ngDevMode ? [{ debugName: "sticky" }] : /* istanbul ignore next */ []));
835
- /**
836
- * Whether to hide the content when the trigger becomes fully occluded.
837
- */
838
- this.hideWhenDetached = input(false, { ...(ngDevMode ? { debugName: "hideWhenDetached" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
839
- /**
840
- * Whether to update the position of the floating element on every animation frame if required.
841
- */
842
- this.updatePositionStrategy = input('always', ...(ngDevMode ? [{ debugName: "updatePositionStrategy" }] : /* istanbul ignore next */ []));
843
- /**
844
- * Emits when the element is placed.
845
- */
846
- this.placed = outputFromObservable(outputToObservable(inject(RdxPopperContentWrapper).placed));
913
+ super();
914
+ this.positionerStyle = { boxSizing: 'border-box', ...legacyPopperVars('select') };
915
+ const rootContext = injectSelectRootContext();
916
+ const injector = inject(Injector);
917
+ const host = inject(ElementRef).nativeElement;
918
+ // Modal select isolates the background with an internal backdrop (finding #1 / Base UI) instead of
919
+ // a global pointer lock; the trigger stays clickable through a cutout (toggle-close).
920
+ afterNextRender(() => setupInternalBackdrop(host, injector, {
921
+ isOpen: () => rootContext.open(),
922
+ shouldRender: () => rootContext.modal(),
923
+ cutout: () => rootContext.triggerElement()
924
+ }));
847
925
  }
848
926
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectPositioner, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
849
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxSelectPositioner, isStandalone: true, selector: "[rdxSelectPositioner]", inputs: { side: { classPropertyName: "side", publicName: "side", isSignal: true, isRequired: false, transformFunction: null }, sideOffset: { classPropertyName: "sideOffset", publicName: "sideOffset", isSignal: true, isRequired: false, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, alignOffset: { classPropertyName: "alignOffset", publicName: "alignOffset", isSignal: true, isRequired: false, transformFunction: null }, arrowPadding: { classPropertyName: "arrowPadding", publicName: "arrowPadding", isSignal: true, isRequired: false, transformFunction: null }, avoidCollisions: { classPropertyName: "avoidCollisions", publicName: "avoidCollisions", isSignal: true, isRequired: false, transformFunction: null }, collisionBoundary: { classPropertyName: "collisionBoundary", publicName: "collisionBoundary", isSignal: true, isRequired: false, transformFunction: null }, collisionPadding: { classPropertyName: "collisionPadding", publicName: "collisionPadding", isSignal: true, isRequired: false, transformFunction: null }, sticky: { classPropertyName: "sticky", publicName: "sticky", isSignal: true, isRequired: false, transformFunction: null }, hideWhenDetached: { classPropertyName: "hideWhenDetached", publicName: "hideWhenDetached", isSignal: true, isRequired: false, transformFunction: null }, updatePositionStrategy: { classPropertyName: "updatePositionStrategy", publicName: "updatePositionStrategy", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { placed: "placed" }, host: { properties: { "style": "{\n 'boxSizing': 'border-box',\n '--radix-select-content-transform-origin': 'var(--radix-popper-transform-origin)',\n '--radix-select-content-available-width': 'var(--radix-popper-available-width)',\n '--radix-select-content-available-height': 'var(--radix-popper-available-height)',\n '--radix-select-trigger-width': 'var(--radix-popper-anchor-width)',\n '--radix-select-trigger-height': 'var(--radix-popper-anchor-height)',\n }" } }, providers: [
850
- {
851
- provide: RDX_SELECT_POSITIONER_TOKEN,
852
- useExisting: forwardRef(() => RdxSelectPositioner)
853
- }
854
- ], hostDirectives: [{ directive: i1.RdxPopperContentWrapper, inputs: ["side", "side", "sideOffset", "sideOffset", "align", "align", "alignOffset", "alignOffset", "arrowPadding", "arrowPadding", "avoidCollisions", "avoidCollisions", "collisionBoundary", "collisionBoundary", "collisionPadding", "collisionPadding", "sticky", "sticky", "hideWhenDetached", "hideWhenDetached", "updatePositionStrategy", "updatePositionStrategy"] }], ngImport: i0 }); }
927
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxSelectPositioner, isStandalone: true, selector: "[rdxSelectPositioner]", host: { properties: { "style": "positionerStyle" } }, providers: [
928
+ ...provideRdxPopperContentWrapper(RdxSelectPositioner),
929
+ provideRdxPopperContentConfig({ align: 'start', updatePositionStrategy: 'always' }),
930
+ { provide: RDX_SELECT_POSITIONER_TOKEN, useExisting: forwardRef(() => RdxSelectPositioner) }
931
+ ], usesInheritance: true, ngImport: i0 }); }
855
932
  }
856
933
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxSelectPositioner, decorators: [{
857
934
  type: Directive,
858
935
  args: [{
859
936
  selector: '[rdxSelectPositioner]',
860
937
  providers: [
861
- {
862
- provide: RDX_SELECT_POSITIONER_TOKEN,
863
- useExisting: forwardRef(() => RdxSelectPositioner)
864
- }
865
- ],
866
- hostDirectives: [
867
- {
868
- directive: RdxPopperContentWrapper,
869
- inputs: [
870
- 'side',
871
- 'sideOffset',
872
- 'align',
873
- 'alignOffset',
874
- 'arrowPadding',
875
- 'avoidCollisions',
876
- 'collisionBoundary',
877
- 'collisionPadding',
878
- 'sticky',
879
- 'hideWhenDetached',
880
- 'updatePositionStrategy'
881
- ]
882
- }
938
+ ...provideRdxPopperContentWrapper(RdxSelectPositioner),
939
+ provideRdxPopperContentConfig({ align: 'start', updatePositionStrategy: 'always' }),
940
+ { provide: RDX_SELECT_POSITIONER_TOKEN, useExisting: forwardRef(() => RdxSelectPositioner) }
883
941
  ],
884
942
  host: {
885
- // re-namespace exposed content custom properties
886
- '[style]': `{
887
- 'boxSizing': 'border-box',
888
- '--radix-select-content-transform-origin': 'var(--radix-popper-transform-origin)',
889
- '--radix-select-content-available-width': 'var(--radix-popper-available-width)',
890
- '--radix-select-content-available-height': 'var(--radix-popper-available-height)',
891
- '--radix-select-trigger-width': 'var(--radix-popper-anchor-width)',
892
- '--radix-select-trigger-height': 'var(--radix-popper-anchor-height)',
893
- }`
943
+ // The unified vars + placement attrs come from the wrapper (ADR 0012); only `box-sizing` and
944
+ // the deprecated `--radix-select-*` aliases (still consumed by demos) remain here.
945
+ '[style]': 'positionerStyle'
894
946
  }
895
947
  }]
896
- }], propDecorators: { side: [{ type: i0.Input, args: [{ isSignal: true, alias: "side", required: false }] }], sideOffset: [{ type: i0.Input, args: [{ isSignal: true, alias: "sideOffset", required: false }] }], align: [{ type: i0.Input, args: [{ isSignal: true, alias: "align", required: false }] }], alignOffset: [{ type: i0.Input, args: [{ isSignal: true, alias: "alignOffset", required: false }] }], arrowPadding: [{ type: i0.Input, args: [{ isSignal: true, alias: "arrowPadding", required: false }] }], avoidCollisions: [{ type: i0.Input, args: [{ isSignal: true, alias: "avoidCollisions", required: false }] }], collisionBoundary: [{ type: i0.Input, args: [{ isSignal: true, alias: "collisionBoundary", required: false }] }], collisionPadding: [{ type: i0.Input, args: [{ isSignal: true, alias: "collisionPadding", required: false }] }], sticky: [{ type: i0.Input, args: [{ isSignal: true, alias: "sticky", required: false }] }], hideWhenDetached: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideWhenDetached", required: false }] }], updatePositionStrategy: [{ type: i0.Input, args: [{ isSignal: true, alias: "updatePositionStrategy", required: false }] }], placed: [{ type: i0.Output, args: ["placed"] }] } });
948
+ }], ctorParameters: () => [] });
897
949
 
898
950
  /**
899
951
  * A visual divider between groups of items.
@@ -947,18 +999,20 @@ class RdxSelectTrigger {
947
999
  this.fieldRootContext?.setFilled(!this.rootContext.isEmptyModelValue());
948
1000
  });
949
1001
  }
950
- handleOpen() {
1002
+ handleOpen(reason, event) {
951
1003
  if (!this.isDisabled()) {
952
- this.rootContext.onOpenChange(true);
1004
+ return this.rootContext.onOpenChange(true, reason, event);
953
1005
  }
1006
+ return false;
954
1007
  }
955
1008
  handlePointerOpen(event) {
956
1009
  const pointerEvent = event;
957
- this.handleOpen();
958
- this.rootContext.triggerPointerDownPosRef.set({
959
- x: Math.round(pointerEvent.pageX),
960
- y: Math.round(pointerEvent.pageY)
961
- });
1010
+ if (this.handleOpen('trigger-press', event)) {
1011
+ this.rootContext.triggerPointerDownPosRef.set({
1012
+ x: Math.round(pointerEvent.pageX),
1013
+ y: Math.round(pointerEvent.pageY)
1014
+ });
1015
+ }
962
1016
  }
963
1017
  onClickHandler(event) {
964
1018
  // Whilst browsers generally have no issue focusing the trigger when clicking
@@ -996,7 +1050,8 @@ class RdxSelectTrigger {
996
1050
  onKeydown(event) {
997
1051
  const keyEvent = event;
998
1052
  if (OPEN_KEYS.includes(keyEvent.key)) {
999
- this.handleOpen();
1053
+ const reason = keyEvent.key === 'ArrowUp' || keyEvent.key === 'ArrowDown' ? 'list-navigation' : 'trigger-press';
1054
+ this.handleOpen(reason, event);
1000
1055
  event.preventDefault();
1001
1056
  }
1002
1057
  }