@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.
- package/README.md +1 -1
- package/fesm2022/radix-ng-primitives-accordion.mjs +5 -3
- package/fesm2022/radix-ng-primitives-accordion.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-alert-dialog.mjs +3 -2
- package/fesm2022/radix-ng-primitives-alert-dialog.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-autocomplete.mjs +617 -659
- package/fesm2022/radix-ng-primitives-autocomplete.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-calendar.mjs +5 -3
- package/fesm2022/radix-ng-primitives-calendar.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-combobox.mjs +1305 -572
- package/fesm2022/radix-ng-primitives-combobox.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-config.mjs +13 -4
- package/fesm2022/radix-ng-primitives-config.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-context-menu.mjs +51 -10
- package/fesm2022/radix-ng-primitives-context-menu.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-core.mjs +1345 -64
- package/fesm2022/radix-ng-primitives-core.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-date-field.mjs +5 -3
- package/fesm2022/radix-ng-primitives-date-field.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-dialog.mjs +240 -112
- package/fesm2022/radix-ng-primitives-dialog.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-direction-provider.mjs +70 -0
- package/fesm2022/radix-ng-primitives-direction-provider.mjs.map +1 -0
- package/fesm2022/radix-ng-primitives-dismissable-layer.mjs +519 -184
- package/fesm2022/radix-ng-primitives-dismissable-layer.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-drawer.mjs +3 -3
- package/fesm2022/radix-ng-primitives-drawer.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-field.mjs +3 -2
- package/fesm2022/radix-ng-primitives-field.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-floating-focus-manager.mjs +517 -0
- package/fesm2022/radix-ng-primitives-floating-focus-manager.mjs.map +1 -0
- package/fesm2022/radix-ng-primitives-focus-scope.mjs +296 -70
- package/fesm2022/radix-ng-primitives-focus-scope.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-menu.mjs +861 -286
- package/fesm2022/radix-ng-primitives-menu.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-menubar.mjs +32 -4
- package/fesm2022/radix-ng-primitives-menubar.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-navigation-menu.mjs +144 -159
- package/fesm2022/radix-ng-primitives-navigation-menu.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-popover.mjs +220 -205
- package/fesm2022/radix-ng-primitives-popover.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-popper.mjs +94 -51
- package/fesm2022/radix-ng-primitives-popper.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-presence.mjs +1 -1
- package/fesm2022/radix-ng-primitives-presence.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-preview-card.mjs +141 -173
- package/fesm2022/radix-ng-primitives-preview-card.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-roving-focus.mjs +4 -2
- package/fesm2022/radix-ng-primitives-roving-focus.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-scroll-area.mjs +5 -4
- package/fesm2022/radix-ng-primitives-scroll-area.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-select.mjs +211 -156
- package/fesm2022/radix-ng-primitives-select.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-slider.mjs +5 -3
- package/fesm2022/radix-ng-primitives-slider.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-stepper.mjs +5 -3
- package/fesm2022/radix-ng-primitives-stepper.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-time-field.mjs +5 -3
- package/fesm2022/radix-ng-primitives-time-field.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-toast.mjs +15 -36
- package/fesm2022/radix-ng-primitives-toast.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-toggle-group.mjs +5 -3
- package/fesm2022/radix-ng-primitives-toggle-group.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-toolbar.mjs +5 -3
- package/fesm2022/radix-ng-primitives-toolbar.mjs.map +1 -1
- package/fesm2022/radix-ng-primitives-tooltip.mjs +73 -110
- package/fesm2022/radix-ng-primitives-tooltip.mjs.map +1 -1
- package/package.json +10 -1
- package/types/radix-ng-primitives-accordion.d.ts +4 -3
- package/types/radix-ng-primitives-autocomplete.d.ts +217 -152
- package/types/radix-ng-primitives-calendar.d.ts +5 -3
- package/types/radix-ng-primitives-combobox.d.ts +672 -283
- package/types/radix-ng-primitives-config.d.ts +1 -1
- package/types/radix-ng-primitives-context-menu.d.ts +15 -5
- package/types/radix-ng-primitives-core.d.ts +762 -14
- package/types/radix-ng-primitives-date-field.d.ts +3 -2
- package/types/radix-ng-primitives-dialog.d.ts +77 -32
- package/types/radix-ng-primitives-direction-provider.d.ts +41 -0
- package/types/radix-ng-primitives-dismissable-layer.d.ts +147 -99
- package/types/radix-ng-primitives-field.d.ts +1 -0
- package/types/radix-ng-primitives-floating-focus-manager.d.ts +175 -0
- package/types/radix-ng-primitives-focus-scope.d.ts +132 -1
- package/types/radix-ng-primitives-menu.d.ts +186 -103
- package/types/radix-ng-primitives-navigation-menu.d.ts +37 -75
- package/types/radix-ng-primitives-popover.d.ts +59 -92
- package/types/radix-ng-primitives-popper.d.ts +39 -9
- package/types/radix-ng-primitives-preview-card.d.ts +39 -72
- package/types/radix-ng-primitives-roving-focus.d.ts +7 -6
- package/types/radix-ng-primitives-scroll-area.d.ts +2 -2
- package/types/radix-ng-primitives-select.d.ts +145 -108
- package/types/radix-ng-primitives-slider.d.ts +5 -4
- package/types/radix-ng-primitives-stepper.d.ts +4 -3
- package/types/radix-ng-primitives-time-field.d.ts +3 -2
- package/types/radix-ng-primitives-toast.d.ts +7 -7
- package/types/radix-ng-primitives-toggle-group.d.ts +5 -4
- package/types/radix-ng-primitives-toolbar.d.ts +3 -2
- package/types/radix-ng-primitives-tooltip.d.ts +24 -67
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { booleanAttribute, inject, DestroyRef, signal, model, input, output, computed, effect, untracked, Directive,
|
|
2
|
+
import { booleanAttribute, inject, DestroyRef, ElementRef, signal, model, input, output, computed, effect, untracked, Directive, isDevMode, numberAttribute, afterNextRender, NgModule } from '@angular/core';
|
|
3
3
|
import * as i1 from '@radix-ng/primitives/popper';
|
|
4
|
-
import { RdxPopper, RdxPopperContentWrapper, RdxPopperArrow, RdxPopperContent, provideRdxPopperContentConfig, RdxPopperAnchor } from '@radix-ng/primitives/popper';
|
|
5
|
-
import
|
|
4
|
+
import { RdxPopper, RdxPopperContentWrapper, RdxPopperArrow, RdxPopperContent, legacyPopperVars, provideRdxPopperContentWrapper, provideRdxPopperContentConfig, RdxPopperAnchor } from '@radix-ng/primitives/popper';
|
|
5
|
+
import * as i2 from '@radix-ng/primitives/core';
|
|
6
|
+
import { createContext, createFloatingRootContext, useTransitionStatus, injectId, createCancelableChangeEventDetails, provideFloatingTree, provideFloatingRootContext, RDX_FLOATING_ROOT_CONTEXT, RDX_FLOATING_REGISTRATION, useAnchoredScrollLock, RdxFloatingNodeRegistration, rdxDevError, useGraceArea } from '@radix-ng/primitives/core';
|
|
6
7
|
import { outputFromObservable, outputToObservable } from '@angular/core/rxjs-interop';
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import { RdxFocusScope
|
|
8
|
+
import { RdxDismiss } from '@radix-ng/primitives/dismissable-layer';
|
|
9
|
+
import * as i3 from '@radix-ng/primitives/floating-focus-manager';
|
|
10
|
+
import { RdxFloatingFocusManager, provideFloatingFocusManagerConfig } from '@radix-ng/primitives/floating-focus-manager';
|
|
11
|
+
import { RdxFocusScope } from '@radix-ng/primitives/focus-scope';
|
|
11
12
|
import * as i1$1 from '@radix-ng/primitives/portal';
|
|
12
13
|
import { RdxPortalPresence } from '@radix-ng/primitives/portal';
|
|
13
14
|
import { provideRdxPresenceContext } from '@radix-ng/primitives/presence';
|
|
@@ -22,6 +23,11 @@ class RdxPopoverRoot {
|
|
|
22
23
|
constructor() {
|
|
23
24
|
this.popper = inject(RdxPopper);
|
|
24
25
|
this.destroyRef = inject(DestroyRef);
|
|
26
|
+
/** Shared per-popup floating context (ADR 0015 §1): `open`, trigger registry, reference / floating els. */
|
|
27
|
+
this.floatingContext = createFloatingRootContext({
|
|
28
|
+
ownerDocument: inject(ElementRef).nativeElement.ownerDocument,
|
|
29
|
+
open: () => this.open()
|
|
30
|
+
});
|
|
25
31
|
this.hasAppliedDefaultOpen = false;
|
|
26
32
|
this.hasAppliedDefaultTriggerId = false;
|
|
27
33
|
this.hoverDelay = 300;
|
|
@@ -65,45 +71,60 @@ class RdxPopoverRoot {
|
|
|
65
71
|
this.triggers = signal([], ...(ngDevMode ? [{ debugName: "triggers" }] : /* istanbul ignore next */ []));
|
|
66
72
|
this.payload = signal(undefined, ...(ngDevMode ? [{ debugName: "payload" }] : /* istanbul ignore next */ []));
|
|
67
73
|
this.isPointerDownOnTrigger = signal(false, ...(ngDevMode ? [{ debugName: "isPointerDownOnTrigger" }] : /* istanbul ignore next */ []));
|
|
74
|
+
/** Whether the current open was initiated by touch (ADR 0016 §3 — gates the anchored scroll lock). */
|
|
75
|
+
this.openedByTouch = signal(false, ...(ngDevMode ? [{ debugName: "openedByTouch" }] : /* istanbul ignore next */ []));
|
|
68
76
|
this.popupCloseCount = signal(0, ...(ngDevMode ? [{ debugName: "popupCloseCount" }] : /* istanbul ignore next */ []));
|
|
77
|
+
this.preventUnmountOnClose = signal(false, ...(ngDevMode ? [{ debugName: "preventUnmountOnClose" }] : /* istanbul ignore next */ []));
|
|
69
78
|
this.onOpenChange = output();
|
|
70
79
|
this.onOpenChangeComplete = output();
|
|
71
80
|
this.registeredTriggers = new Map();
|
|
72
81
|
this.viewportTriggerChange = new Set();
|
|
73
|
-
this.state = computed(() => (this.open() ? 'open' : 'closed'),
|
|
82
|
+
this.state = computed(() => (this.open() ? 'open' : 'closed'), { debugName: 'RdxPopoverRoot.state' });
|
|
83
|
+
this.present = computed(() => this.open() || this.preventUnmountOnClose(), {
|
|
84
|
+
debugName: 'RdxPopoverRoot.present'
|
|
85
|
+
});
|
|
74
86
|
let previousOpen = this.open();
|
|
87
|
+
// Keep the floating context's reference element in sync with the active trigger.
|
|
88
|
+
effect(() => this.floatingContext.setReferenceElement(this.trigger() ?? null));
|
|
89
|
+
effect(() => {
|
|
90
|
+
if (this.open() && this.preventUnmountOnClose()) {
|
|
91
|
+
this.preventUnmountOnClose.set(false);
|
|
92
|
+
}
|
|
93
|
+
}, { debugName: 'RdxPopoverRoot.clearPreventUnmountOnOpen' });
|
|
75
94
|
effect(() => {
|
|
76
95
|
const defaultOpen = this.defaultOpen();
|
|
77
96
|
if (!this.hasAppliedDefaultOpen && defaultOpen) {
|
|
78
97
|
this.hasAppliedDefaultOpen = true;
|
|
79
98
|
this.open.set(defaultOpen);
|
|
80
99
|
}
|
|
81
|
-
});
|
|
100
|
+
}, { debugName: 'RdxPopoverRoot.applyDefaultOpen' });
|
|
82
101
|
effect(() => {
|
|
83
102
|
const defaultTriggerId = this.defaultTriggerId();
|
|
84
103
|
if (!this.hasAppliedDefaultTriggerId && defaultTriggerId !== null) {
|
|
85
104
|
this.hasAppliedDefaultTriggerId = true;
|
|
86
105
|
this.triggerId.set(defaultTriggerId);
|
|
87
106
|
}
|
|
88
|
-
});
|
|
107
|
+
}, { debugName: 'RdxPopoverRoot.applyDefaultTriggerId' });
|
|
89
108
|
effect(() => {
|
|
90
109
|
const triggerId = this.triggerId();
|
|
91
110
|
untracked(() => this.syncTriggerId(triggerId));
|
|
92
|
-
});
|
|
111
|
+
}, { debugName: 'RdxPopoverRoot.syncTriggerId' });
|
|
93
112
|
effect(() => {
|
|
94
113
|
const open = this.open();
|
|
95
114
|
if (open !== previousOpen) {
|
|
96
115
|
previousOpen = open;
|
|
97
116
|
untracked(() => this.transition.start(open));
|
|
98
117
|
}
|
|
99
|
-
});
|
|
118
|
+
}, { debugName: 'RdxPopoverRoot.startTransition' });
|
|
100
119
|
effect((onCleanup) => {
|
|
101
120
|
const handle = this.handle();
|
|
102
121
|
if (handle) {
|
|
103
122
|
onCleanup(untracked(() => handle.registerRoot(contextFor(this))));
|
|
104
123
|
}
|
|
124
|
+
}, { debugName: 'RdxPopoverRoot.registerHandle' });
|
|
125
|
+
effect(() => this.popper.anchorOverride.set(this.trigger()), {
|
|
126
|
+
debugName: 'RdxPopoverRoot.syncAnchorOverride'
|
|
105
127
|
});
|
|
106
|
-
effect(() => this.popper.anchorOverride.set(this.trigger()));
|
|
107
128
|
this.destroyRef.onDestroy(() => {
|
|
108
129
|
this.clearHoverTimers();
|
|
109
130
|
if (this.instantFrame !== undefined) {
|
|
@@ -113,10 +134,25 @@ class RdxPopoverRoot {
|
|
|
113
134
|
}
|
|
114
135
|
show(trigger = this.trigger(), payload, triggerId, reason = 'none', event = new Event('popover.open-change'), fromHover = false) {
|
|
115
136
|
this.clearHoverTimers();
|
|
116
|
-
this.isHoverActive.set(fromHover);
|
|
117
|
-
this.openChangeReason.set(reason);
|
|
118
137
|
const previousTrigger = this.trigger();
|
|
119
138
|
const changedTriggerWhileOpen = this.open() && previousTrigger !== trigger;
|
|
139
|
+
const changed = !this.open() || previousTrigger !== trigger;
|
|
140
|
+
if (!changed) {
|
|
141
|
+
this.isHoverActive.set(fromHover);
|
|
142
|
+
this.openChangeReason.set(reason);
|
|
143
|
+
if (triggerId !== undefined) {
|
|
144
|
+
this.triggerId.set(triggerId);
|
|
145
|
+
}
|
|
146
|
+
this.payload.set(payload);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const change = this.createOpenChangeEvent(true, reason, event, trigger, triggerId ?? this.triggerId());
|
|
150
|
+
this.onOpenChange.emit(change.payload);
|
|
151
|
+
if (change.eventDetails.isCanceled()) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
this.isHoverActive.set(fromHover);
|
|
155
|
+
this.openChangeReason.set(reason);
|
|
120
156
|
this.instant.set(changedTriggerWhileOpen || reason === 'trigger-focus');
|
|
121
157
|
if (changedTriggerWhileOpen) {
|
|
122
158
|
this.scheduleInstantReset();
|
|
@@ -131,22 +167,27 @@ class RdxPopoverRoot {
|
|
|
131
167
|
this.triggerId.set(triggerId);
|
|
132
168
|
}
|
|
133
169
|
this.payload.set(payload);
|
|
134
|
-
|
|
170
|
+
this.preventUnmountOnClose.set(false);
|
|
135
171
|
this.open.set(true);
|
|
136
|
-
|
|
137
|
-
this.emitOpenChange(true, reason, event);
|
|
138
|
-
}
|
|
172
|
+
this.floatingContext.events.emit('openchange', { open: true, reason, event: change.eventDetails.event });
|
|
139
173
|
}
|
|
140
174
|
close(reason = 'none', event = new Event('popover.open-change')) {
|
|
141
175
|
this.clearHoverTimers();
|
|
142
|
-
this.isHoverActive.set(false);
|
|
143
176
|
if (!this.open()) {
|
|
144
177
|
return;
|
|
145
178
|
}
|
|
179
|
+
const change = this.createOpenChangeEvent(false, reason, event, this.trigger(), this.triggerId());
|
|
180
|
+
this.onOpenChange.emit(change.payload);
|
|
181
|
+
if (change.eventDetails.isCanceled()) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
this.isHoverActive.set(false);
|
|
185
|
+
this.openedByTouch.set(false);
|
|
146
186
|
this.instant.set(reason !== 'none' && reason !== 'trigger-hover');
|
|
147
187
|
this.openChangeReason.set(reason);
|
|
188
|
+
this.preventUnmountOnClose.set(change.shouldPreventUnmountOnClose());
|
|
148
189
|
this.open.set(false);
|
|
149
|
-
this.
|
|
190
|
+
this.floatingContext.events.emit('openchange', { open: false, reason, event: change.eventDetails.event });
|
|
150
191
|
}
|
|
151
192
|
toggle(triggerId, trigger, payload, event) {
|
|
152
193
|
this.clearHoverTimers();
|
|
@@ -186,6 +227,8 @@ class RdxPopoverRoot {
|
|
|
186
227
|
registerTrigger(id, trigger, payload) {
|
|
187
228
|
this.registeredTriggers.set(id, { element: trigger, payload });
|
|
188
229
|
this.triggers.update((triggers) => (triggers.includes(trigger) ? triggers : [...triggers, trigger]));
|
|
230
|
+
// Bridge into the floating context's trigger registry (new dismissal/focus inside-element checks).
|
|
231
|
+
this.floatingContext.triggers.add(trigger);
|
|
189
232
|
if (this.triggerId() === id) {
|
|
190
233
|
this.trigger.set(trigger);
|
|
191
234
|
this.payload.set(payload());
|
|
@@ -199,6 +242,7 @@ class RdxPopoverRoot {
|
|
|
199
242
|
this.registeredTriggers.delete(id);
|
|
200
243
|
}
|
|
201
244
|
this.triggers.update((triggers) => triggers.filter((candidate) => candidate !== trigger));
|
|
245
|
+
this.floatingContext.triggers.delete(trigger);
|
|
202
246
|
if (this.destroyRef.destroyed) {
|
|
203
247
|
return;
|
|
204
248
|
}
|
|
@@ -244,14 +288,20 @@ class RdxPopoverRoot {
|
|
|
244
288
|
this.trigger.set(trigger.element);
|
|
245
289
|
this.payload.set(trigger.payload());
|
|
246
290
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
291
|
+
createOpenChangeEvent(open, reason, event, trigger, triggerId) {
|
|
292
|
+
const change = createCancelableChangeEventDetails(reason, event, trigger);
|
|
293
|
+
return {
|
|
294
|
+
eventDetails: change.eventDetails,
|
|
295
|
+
shouldPreventUnmountOnClose: change.shouldPreventUnmountOnClose,
|
|
296
|
+
payload: {
|
|
297
|
+
open,
|
|
298
|
+
triggerId,
|
|
299
|
+
trigger: change.eventDetails.trigger,
|
|
300
|
+
reason: change.eventDetails.reason,
|
|
301
|
+
event: change.eventDetails.event,
|
|
302
|
+
eventDetails: change.eventDetails
|
|
303
|
+
}
|
|
304
|
+
};
|
|
255
305
|
}
|
|
256
306
|
clearHoverTimers() {
|
|
257
307
|
this.clearOpenTimer();
|
|
@@ -281,14 +331,26 @@ class RdxPopoverRoot {
|
|
|
281
331
|
});
|
|
282
332
|
}
|
|
283
333
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxPopoverRoot, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
284
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxPopoverRoot, isStandalone: true, selector: "[rdxPopoverRoot]", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, defaultOpen: { classPropertyName: "defaultOpen", publicName: "defaultOpen", isSignal: true, isRequired: false, transformFunction: null }, triggerId: { classPropertyName: "triggerId", publicName: "triggerId", isSignal: true, isRequired: false, transformFunction: null }, defaultTriggerId: { classPropertyName: "defaultTriggerId", publicName: "defaultTriggerId", isSignal: true, isRequired: false, transformFunction: null }, modal: { classPropertyName: "modal", publicName: "modal", isSignal: true, isRequired: false, transformFunction: null }, handle: { classPropertyName: "handle", publicName: "handle", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange", triggerId: "triggerIdChange", onOpenChange: "onOpenChange", onOpenChangeComplete: "onOpenChangeComplete" }, providers: [
|
|
334
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxPopoverRoot, isStandalone: true, selector: "[rdxPopoverRoot]", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, defaultOpen: { classPropertyName: "defaultOpen", publicName: "defaultOpen", isSignal: true, isRequired: false, transformFunction: null }, triggerId: { classPropertyName: "triggerId", publicName: "triggerId", isSignal: true, isRequired: false, transformFunction: null }, defaultTriggerId: { classPropertyName: "defaultTriggerId", publicName: "defaultTriggerId", isSignal: true, isRequired: false, transformFunction: null }, modal: { classPropertyName: "modal", publicName: "modal", isSignal: true, isRequired: false, transformFunction: null }, handle: { classPropertyName: "handle", publicName: "handle", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange", triggerId: "triggerIdChange", onOpenChange: "onOpenChange", onOpenChangeComplete: "onOpenChangeComplete" }, providers: [
|
|
335
|
+
provideRdxPopoverRootContext(context),
|
|
336
|
+
// New floating foundation (ADR 0015/0017 migration). Inherit-or-create tree (nested sharing);
|
|
337
|
+
// the per-popup root context bridges open / triggers / reference.
|
|
338
|
+
provideFloatingTree(),
|
|
339
|
+
provideFloatingRootContext(() => inject(RdxPopoverRoot).floatingContext)
|
|
340
|
+
], exportAs: ["rdxPopoverRoot"], hostDirectives: [{ directive: i1.RdxPopper }], ngImport: i0 }); }
|
|
285
341
|
}
|
|
286
342
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxPopoverRoot, decorators: [{
|
|
287
343
|
type: Directive,
|
|
288
344
|
args: [{
|
|
289
345
|
selector: '[rdxPopoverRoot]',
|
|
290
346
|
exportAs: 'rdxPopoverRoot',
|
|
291
|
-
providers: [
|
|
347
|
+
providers: [
|
|
348
|
+
provideRdxPopoverRootContext(context),
|
|
349
|
+
// New floating foundation (ADR 0015/0017 migration). Inherit-or-create tree (nested sharing);
|
|
350
|
+
// the per-popup root context bridges open / triggers / reference.
|
|
351
|
+
provideFloatingTree(),
|
|
352
|
+
provideFloatingRootContext(() => inject(RdxPopoverRoot).floatingContext)
|
|
353
|
+
],
|
|
292
354
|
hostDirectives: [RdxPopper]
|
|
293
355
|
}]
|
|
294
356
|
}], ctorParameters: () => [], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], defaultOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultOpen", required: false }] }], triggerId: [{ type: i0.Input, args: [{ isSignal: true, alias: "triggerId", required: false }] }, { type: i0.Output, args: ["triggerIdChange"] }], defaultTriggerId: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultTriggerId", required: false }] }], modal: [{ type: i0.Input, args: [{ isSignal: true, alias: "modal", required: false }] }], handle: [{ type: i0.Input, args: [{ isSignal: true, alias: "handle", required: false }] }], onOpenChange: [{ type: i0.Output, args: ["onOpenChange"] }], onOpenChangeComplete: [{ type: i0.Output, args: ["onOpenChangeComplete"] }] } });
|
|
@@ -297,6 +359,7 @@ function contextFor(root) {
|
|
|
297
359
|
contentId: root.contentId,
|
|
298
360
|
descriptionId: root.descriptionId.asReadonly(),
|
|
299
361
|
isOpen: root.open,
|
|
362
|
+
present: root.present,
|
|
300
363
|
modal: root.modal,
|
|
301
364
|
titleId: root.titleId.asReadonly(),
|
|
302
365
|
trigger: root.trigger.asReadonly(),
|
|
@@ -307,6 +370,7 @@ function contextFor(root) {
|
|
|
307
370
|
instant: root.instant.asReadonly(),
|
|
308
371
|
openChangeReason: root.openChangeReason.asReadonly(),
|
|
309
372
|
isPointerDownOnTrigger: root.isPointerDownOnTrigger.asReadonly(),
|
|
373
|
+
openedByTouch: root.openedByTouch.asReadonly(),
|
|
310
374
|
close: (reason, event) => root.close(reason, event),
|
|
311
375
|
cancelHoverClose: () => root.cancelHoverClose(),
|
|
312
376
|
cancelHoverOpen: () => root.cancelHoverOpen(),
|
|
@@ -317,6 +381,7 @@ function contextFor(root) {
|
|
|
317
381
|
setDescriptionId: (id) => root.descriptionId.set(id),
|
|
318
382
|
setTitleId: (id) => root.titleId.set(id),
|
|
319
383
|
setPointerDownOnTrigger: (pointerDown) => root.isPointerDownOnTrigger.set(pointerDown),
|
|
384
|
+
setOpenedByTouch: (value) => root.openedByTouch.set(value),
|
|
320
385
|
setHoverDelays: (delay, closeDelay) => root.setHoverDelays(delay, closeDelay),
|
|
321
386
|
registerPopupClose: () => {
|
|
322
387
|
root.popupCloseCount.update((count) => count + 1);
|
|
@@ -364,6 +429,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
364
429
|
class RdxPopoverBackdrop {
|
|
365
430
|
constructor() {
|
|
366
431
|
this.rootContext = injectRdxPopoverRootContext();
|
|
432
|
+
// Register the backdrop as owned DOM footprint for primitive-specific checks. The focus manager's
|
|
433
|
+
// marker keep-set stays narrow and does not keep sibling backdrop roots.
|
|
434
|
+
const floatingContext = inject(RDX_FLOATING_ROOT_CONTEXT, { optional: true });
|
|
435
|
+
if (floatingContext) {
|
|
436
|
+
const host = inject(ElementRef).nativeElement;
|
|
437
|
+
floatingContext.addFloatingElement(host);
|
|
438
|
+
inject(DestroyRef).onDestroy(() => floatingContext.removeFloatingElement(host));
|
|
439
|
+
}
|
|
367
440
|
}
|
|
368
441
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxPopoverBackdrop, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
369
442
|
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxPopoverBackdrop, isStandalone: true, selector: "[rdxPopoverBackdrop]", host: { properties: { "attr.data-closed": "rootContext.isOpen() ? undefined : \"\"", "attr.data-ending-style": "rootContext.transitionStatus() === \"ending\" ? \"\" : undefined", "attr.data-instant": "rootContext.instant() ? \"\" : undefined", "attr.data-open": "rootContext.isOpen() ? \"\" : undefined", "attr.data-starting-style": "rootContext.transitionStatus() === \"starting\" ? \"\" : undefined", "attr.data-state": "rootContext.isOpen() ? \"open\" : \"closed\"" } }, ngImport: i0 }); }
|
|
@@ -381,109 +454,126 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
381
454
|
'[attr.data-state]': 'rootContext.isOpen() ? "open" : "closed"'
|
|
382
455
|
}
|
|
383
456
|
}]
|
|
384
|
-
}] });
|
|
457
|
+
}], ctorParameters: () => [] });
|
|
385
458
|
|
|
386
459
|
/**
|
|
387
460
|
* A container for the popover contents.
|
|
461
|
+
*
|
|
462
|
+
* **ADR 0015/0017 Phase-4 migration** onto the new floating dismissal + focus engine (same pattern as
|
|
463
|
+
* Dialog; browser-verified via `popover.behavior` Playwright). Popover-specific:
|
|
464
|
+
* - **Hover-open disables the manager** (`enabled = isOpen && !isHoverActive`) — Base UI parity
|
|
465
|
+
* (`disabled={!mounted || openReason === triggerHover}`); a hover-opened popover does not trap / mark.
|
|
466
|
+
* (The legacy only suppressed auto-focus while still trapping — that Radix divergence is dropped.)
|
|
467
|
+
* - Trap = `'trap-focus' || (modal === true && hasPopupClose())`; scroll lock + real outside `inert`
|
|
468
|
+
* isolation key off the full modal (`modal === true`).
|
|
469
|
+
* - No `disablePointerDismissal` — outside-press + focus-out always close.
|
|
470
|
+
*
|
|
471
|
+
* Note: a positioned popover does **not** auto-focus into the popup on open (pre-existing — the legacy
|
|
472
|
+
* behaved the same; verified). The trap holds focus once it is inside. Auto-focus-on-open + redirecting a
|
|
473
|
+
* Tab from the trigger into the popup needs the deferred portal-focus bridge / guards (ADR 0017 §6a).
|
|
388
474
|
*/
|
|
389
475
|
class RdxPopoverPopup {
|
|
390
476
|
constructor() {
|
|
391
477
|
this.rootContext = injectRdxPopoverRootContext();
|
|
392
|
-
this.
|
|
478
|
+
this.host = inject(ElementRef).nativeElement;
|
|
479
|
+
this.floatingContext = inject(RDX_FLOATING_ROOT_CONTEXT);
|
|
480
|
+
this.registration = inject(RDX_FLOATING_REGISTRATION, { optional: true });
|
|
481
|
+
this.focusManager = inject(RdxFloatingFocusManager);
|
|
393
482
|
this.focusScope = inject(RdxFocusScope);
|
|
394
483
|
this.wrapper = inject(RdxPopperContentWrapper, { optional: true });
|
|
395
484
|
this.align = computed(() => this.wrapper?.placedAlign(), ...(ngDevMode ? [{ debugName: "align" }] : /* istanbul ignore next */ []));
|
|
396
485
|
this.side = computed(() => this.wrapper?.placedSide(), ...(ngDevMode ? [{ debugName: "side" }] : /* istanbul ignore next */ []));
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
/**
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
this.
|
|
405
|
-
/**
|
|
406
|
-
* Event handler called when a pointerdown event happens outside of the popup. Can be prevented.
|
|
407
|
-
*/
|
|
408
|
-
this.pointerDownOutside = outputFromObservable(outputToObservable(this.dismissableLayer.pointerDownOutside));
|
|
409
|
-
/**
|
|
410
|
-
* Event handler called when focus moves outside of the popup. Can be prevented.
|
|
411
|
-
*/
|
|
412
|
-
this.focusOutside = outputFromObservable(outputToObservable(this.dismissableLayer.focusOutside));
|
|
413
|
-
/**
|
|
414
|
-
* Event handler called when an interaction happens outside of the popup. Can be prevented.
|
|
415
|
-
*/
|
|
416
|
-
this.interactOutside = outputFromObservable(outputToObservable(this.dismissableLayer.interactOutside));
|
|
417
|
-
/**
|
|
418
|
-
* Event handler called before focus moves into the popup. Can be prevented.
|
|
419
|
-
*/
|
|
486
|
+
/** Event handler called when the escape key is down. Can be prevented. */
|
|
487
|
+
this.escapeKeyDown = output();
|
|
488
|
+
/** Event handler called when a pointerdown event happens outside of the popup. Can be prevented. */
|
|
489
|
+
this.pointerDownOutside = output();
|
|
490
|
+
/** Event handler called when focus moves outside of the popup. Can be prevented. */
|
|
491
|
+
this.focusOutside = output();
|
|
492
|
+
/** Event handler called when an interaction (pointer / focus) happens outside of the popup. */
|
|
493
|
+
this.interactOutside = output();
|
|
494
|
+
/** Event handler called before focus moves into the popup. Can be prevented. */
|
|
420
495
|
this.openAutoFocus = outputFromObservable(outputToObservable(this.focusScope.mountAutoFocus));
|
|
421
|
-
/**
|
|
422
|
-
* Event handler called before focus returns after the popup is removed. Can be prevented.
|
|
423
|
-
*/
|
|
496
|
+
/** Event handler called before focus returns after the popup is removed. Can be prevented. */
|
|
424
497
|
this.closeAutoFocus = outputFromObservable(outputToObservable(this.focusScope.unmountAutoFocus));
|
|
425
|
-
|
|
426
|
-
|
|
498
|
+
this.floatingContext.setFloatingElement(this.host);
|
|
499
|
+
// Background pointer/AT isolation for a full modal is the focus manager's `inert` pass (finding
|
|
500
|
+
// #4), not a global body lock; only the page scroll lock stays here. Activation policy (ADR 0016
|
|
501
|
+
// §2): lock only while a `modal === true` popover is OPEN and was **not** hover-opened, gated on
|
|
502
|
+
// `open` (not mounted) so it releases at close-start. For a **touch** open the anchored helper only
|
|
503
|
+
// locks when the popup is effectively viewport-width (a small popover stays swipe-to-dismissable on
|
|
504
|
+
// mobile, §3).
|
|
505
|
+
useAnchoredScrollLock(computed(() => this.rootContext.isOpen() && this.rootContext.modal() === true && !this.rootContext.isHoverActive()), {
|
|
506
|
+
touchOpen: () => this.rootContext.openedByTouch(),
|
|
507
|
+
element: () => this.host
|
|
508
|
+
});
|
|
509
|
+
const unregisterTransitionElement = this.rootContext.registerTransitionElement(this.host);
|
|
427
510
|
inject(DestroyRef).onDestroy(unregisterTransitionElement);
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
if (this.rootContext.
|
|
511
|
+
// A hover-opened popover must not steal focus — suppress the composed focus scope's auto-focus.
|
|
512
|
+
this.focusScope.mountAutoFocus.subscribe((event) => {
|
|
513
|
+
if (this.rootContext.isHoverActive()) {
|
|
431
514
|
event.preventDefault();
|
|
432
515
|
}
|
|
433
516
|
});
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
517
|
+
// Dismissal: Escape + outside-press always close (no pointer-dismissal opt-out). Focus-out is
|
|
518
|
+
// owned by the focus manager (below), so the capability's own focus-out is disabled.
|
|
519
|
+
new RdxDismiss(this.floatingContext, () => this.registration?.node() ?? null, {
|
|
520
|
+
escapeKey: () => true,
|
|
521
|
+
outsidePress: () => true,
|
|
522
|
+
focusOutside: () => false,
|
|
523
|
+
onEscapeKeyDown: (event) => this.escapeKeyDown.emit(event),
|
|
524
|
+
onPointerDownOutside: (event) => {
|
|
525
|
+
this.pointerDownOutside.emit(event);
|
|
526
|
+
this.interactOutside.emit(event);
|
|
527
|
+
},
|
|
528
|
+
onDismiss: (reason, event) => {
|
|
529
|
+
this.rootContext.close(reason === 'escape-key' ? 'escape-key' : 'outside-press', event);
|
|
438
530
|
}
|
|
439
531
|
});
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
if (
|
|
445
|
-
|
|
532
|
+
// Focus-out close (ADR 0017 §3) — re-expose as `focusOutside` (preventable) and close unless vetoed.
|
|
533
|
+
this.focusManager.focusOut.subscribe((event) => {
|
|
534
|
+
this.focusOutside.emit(event);
|
|
535
|
+
this.interactOutside.emit(event);
|
|
536
|
+
if (!event.defaultPrevented) {
|
|
537
|
+
this.rootContext.close('focus-out', event);
|
|
446
538
|
}
|
|
447
539
|
});
|
|
448
|
-
this.dismissableLayer.dismiss.subscribe(() => {
|
|
449
|
-
this.rootContext.close(this.dismissDetails.reason, this.dismissDetails.event);
|
|
450
|
-
this.dismissDetails = { reason: 'none', event: new Event('popover.dismiss') };
|
|
451
|
-
});
|
|
452
540
|
}
|
|
453
541
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxPopoverPopup, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
454
542
|
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxPopoverPopup, isStandalone: true, selector: "[rdxPopoverPopup]", outputs: { escapeKeyDown: "escapeKeyDown", pointerDownOutside: "pointerDownOutside", focusOutside: "focusOutside", interactOutside: "interactOutside", openAutoFocus: "openAutoFocus", closeAutoFocus: "closeAutoFocus" }, host: { attributes: { "role": "dialog" }, listeners: { "pointerenter": "rootContext.cancelHoverClose()" }, properties: { "attr.aria-describedby": "rootContext.descriptionId()", "attr.aria-labelledby": "rootContext.titleId()", "attr.data-closed": "rootContext.isOpen() ? undefined : \"\"", "attr.data-ending-style": "rootContext.transitionStatus() === \"ending\" ? \"\" : undefined", "attr.data-instant": "rootContext.instant() ? \"\" : undefined", "attr.data-open": "rootContext.isOpen() ? \"\" : undefined", "attr.data-starting-style": "rootContext.transitionStatus() === \"starting\" ? \"\" : undefined", "attr.data-state": "rootContext.isOpen() ? \"open\" : \"closed\"", "attr.data-align": "align()", "attr.data-side": "side()", "id": "rootContext.contentId" } }, providers: [
|
|
455
|
-
|
|
456
|
-
const rootContext = injectRdxPopoverRootContext();
|
|
457
|
-
return {
|
|
458
|
-
disableOutsidePointerEvents: computed(() => rootContext.modal() === true)
|
|
459
|
-
};
|
|
460
|
-
}),
|
|
461
|
-
provideRdxFocusScopeConfig(() => {
|
|
543
|
+
provideFloatingFocusManagerConfig(() => {
|
|
462
544
|
const rootContext = injectRdxPopoverRootContext();
|
|
463
545
|
return {
|
|
464
|
-
|
|
465
|
-
(rootContext.modal() === true && rootContext.hasPopupClose())
|
|
546
|
+
modal: () => rootContext.modal() === 'trap-focus' ||
|
|
547
|
+
(rootContext.modal() === true && rootContext.hasPopupClose()),
|
|
548
|
+
// Full modal blocks outside pointer interaction; `trap-focus` only traps focus.
|
|
549
|
+
inert: () => rootContext.modal() === true && rootContext.hasPopupClose(),
|
|
550
|
+
// Active for the whole MOUNTED lifetime (Base UI `disabled={!mounted}`, not `open`) for
|
|
551
|
+
// trap/return-focus — including an explicit `preventUnmountOnClose()` cycle after the
|
|
552
|
+
// exit transition. Marker + isolation are additionally gated on `open` inside the
|
|
553
|
+
// manager. Still suppressed while hover-opened.
|
|
554
|
+
enabled: () => rootContext.present() && !rootContext.isHoverActive()
|
|
466
555
|
};
|
|
467
556
|
})
|
|
468
|
-
], hostDirectives: [{ directive: i1.RdxPopperContent }, { directive: i2.
|
|
557
|
+
], hostDirectives: [{ directive: i1.RdxPopperContent }, { directive: i2.RdxFloatingNodeRegistration }, { directive: i3.RdxFloatingFocusManager }], ngImport: i0 }); }
|
|
469
558
|
}
|
|
470
559
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxPopoverPopup, decorators: [{
|
|
471
560
|
type: Directive,
|
|
472
561
|
args: [{
|
|
473
562
|
selector: '[rdxPopoverPopup]',
|
|
474
|
-
hostDirectives: [RdxPopperContent,
|
|
563
|
+
hostDirectives: [RdxPopperContent, RdxFloatingNodeRegistration, RdxFloatingFocusManager],
|
|
475
564
|
providers: [
|
|
476
|
-
|
|
565
|
+
provideFloatingFocusManagerConfig(() => {
|
|
477
566
|
const rootContext = injectRdxPopoverRootContext();
|
|
478
567
|
return {
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
568
|
+
modal: () => rootContext.modal() === 'trap-focus' ||
|
|
569
|
+
(rootContext.modal() === true && rootContext.hasPopupClose()),
|
|
570
|
+
// Full modal blocks outside pointer interaction; `trap-focus` only traps focus.
|
|
571
|
+
inert: () => rootContext.modal() === true && rootContext.hasPopupClose(),
|
|
572
|
+
// Active for the whole MOUNTED lifetime (Base UI `disabled={!mounted}`, not `open`) for
|
|
573
|
+
// trap/return-focus — including an explicit `preventUnmountOnClose()` cycle after the
|
|
574
|
+
// exit transition. Marker + isolation are additionally gated on `open` inside the
|
|
575
|
+
// manager. Still suppressed while hover-opened.
|
|
576
|
+
enabled: () => rootContext.present() && !rootContext.isHoverActive()
|
|
487
577
|
};
|
|
488
578
|
})
|
|
489
579
|
],
|
|
@@ -563,7 +653,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
563
653
|
*/
|
|
564
654
|
class RdxPopoverPortal {
|
|
565
655
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxPopoverPortal, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
566
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxPopoverPortal, isStandalone: true, selector: "ng-template[rdxPopoverPortal]", providers: [provideRdxPresenceContext(() => ({ present: injectRdxPopoverRootContext().
|
|
656
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxPopoverPortal, isStandalone: true, selector: "ng-template[rdxPopoverPortal]", providers: [provideRdxPresenceContext(() => ({ present: injectRdxPopoverRootContext().present }))], exportAs: ["rdxPopoverPortal"], hostDirectives: [{ directive: i1$1.RdxPortalPresence, inputs: ["container", "container"] }], ngImport: i0 }); }
|
|
567
657
|
}
|
|
568
658
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxPopoverPortal, decorators: [{
|
|
569
659
|
type: Directive,
|
|
@@ -571,7 +661,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
571
661
|
selector: 'ng-template[rdxPopoverPortal]',
|
|
572
662
|
exportAs: 'rdxPopoverPortal',
|
|
573
663
|
hostDirectives: [{ directive: RdxPortalPresence, inputs: ['container'] }],
|
|
574
|
-
providers: [provideRdxPresenceContext(() => ({ present: injectRdxPopoverRootContext().
|
|
664
|
+
providers: [provideRdxPresenceContext(() => ({ present: injectRdxPopoverRootContext().present }))]
|
|
575
665
|
}]
|
|
576
666
|
}] });
|
|
577
667
|
/**
|
|
@@ -582,9 +672,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
582
672
|
class RdxPopoverPortalMisuseGuard {
|
|
583
673
|
constructor() {
|
|
584
674
|
if (isDevMode()) {
|
|
585
|
-
|
|
675
|
+
rdxDevError('popover/portal-on-element', '`rdxPopoverPortal` is now a structural directive. ' +
|
|
586
676
|
'Use `*rdxPopoverPortal` on the positioner element or `<ng-template rdxPopoverPortal>`. ' +
|
|
587
|
-
'rdxPopoverPortalPresence has been removed.
|
|
677
|
+
'rdxPopoverPortalPresence has been removed.', 'components/popover');
|
|
588
678
|
}
|
|
589
679
|
}
|
|
590
680
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxPopoverPortalMisuseGuard, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
@@ -599,132 +689,51 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
599
689
|
|
|
600
690
|
/**
|
|
601
691
|
* Positions the popover against its trigger.
|
|
692
|
+
*
|
|
693
|
+
* A "thin" positioner (ADR 0012): it inherits the popper positioning surface (inputs, `placed`
|
|
694
|
+
* output, unified vars + placement attrs) from {@link RdxPopperContentWrapper} and adds popover's own
|
|
695
|
+
* concerns — Base UI-aligned defaults via the config provider, the open/closed/instant state
|
|
696
|
+
* attributes, the deprecated `--radix-popover-*` aliases, and the grace-area hover bridge.
|
|
602
697
|
*/
|
|
603
|
-
class RdxPopoverPositioner {
|
|
698
|
+
class RdxPopoverPositioner extends RdxPopperContentWrapper {
|
|
604
699
|
constructor() {
|
|
700
|
+
super();
|
|
605
701
|
this.rootContext = injectRdxPopoverRootContext();
|
|
606
|
-
this.
|
|
607
|
-
this.
|
|
702
|
+
this.legacyVars = legacyPopperVars('popover');
|
|
703
|
+
this.containerRef = inject(ElementRef);
|
|
608
704
|
this.triggerEl = signal(null, ...(ngDevMode ? [{ debugName: "triggerEl" }] : /* istanbul ignore next */ []));
|
|
609
|
-
this.containerEl = signal(this.
|
|
705
|
+
this.containerEl = signal(this.containerRef.nativeElement, ...(ngDevMode ? [{ debugName: "containerEl" }] : /* istanbul ignore next */ []));
|
|
610
706
|
this.graceArea = useGraceArea(this.triggerEl, this.containerEl);
|
|
611
|
-
/**
|
|
612
|
-
* An element to position the popup against. Defaults to the trigger.
|
|
613
|
-
*/
|
|
614
|
-
this.anchor = input(...(ngDevMode ? [undefined, { debugName: "anchor" }] : /* istanbul ignore next */ []));
|
|
615
|
-
/**
|
|
616
|
-
* The preferred side of the trigger to render against when open.
|
|
617
|
-
*/
|
|
618
|
-
this.side = input('bottom', ...(ngDevMode ? [{ debugName: "side" }] : /* istanbul ignore next */ []));
|
|
619
|
-
/**
|
|
620
|
-
* Distance between the trigger and the popup in pixels.
|
|
621
|
-
*/
|
|
622
|
-
this.sideOffset = input(0, { ...(ngDevMode ? { debugName: "sideOffset" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
623
|
-
/**
|
|
624
|
-
* How to align the popup relative to the specified side.
|
|
625
|
-
*/
|
|
626
|
-
this.align = input('center', ...(ngDevMode ? [{ debugName: "align" }] : /* istanbul ignore next */ []));
|
|
627
|
-
/**
|
|
628
|
-
* An offset in pixels from the `start` or `end` alignment options.
|
|
629
|
-
*/
|
|
630
|
-
this.alignOffset = input(0, { ...(ngDevMode ? { debugName: "alignOffset" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
631
|
-
/**
|
|
632
|
-
* Minimum distance to maintain between the arrow and the edges of the popup.
|
|
633
|
-
*/
|
|
634
|
-
this.arrowPadding = input(5, { ...(ngDevMode ? { debugName: "arrowPadding" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
635
|
-
/**
|
|
636
|
-
* Whether to override side and alignment preferences to prevent collisions.
|
|
637
|
-
*/
|
|
638
|
-
this.avoidCollisions = input(true, { ...(ngDevMode ? { debugName: "avoidCollisions" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
639
|
-
/**
|
|
640
|
-
* The element used as the collision boundary.
|
|
641
|
-
*/
|
|
642
|
-
this.collisionBoundary = input(...(ngDevMode ? [undefined, { debugName: "collisionBoundary" }] : /* istanbul ignore next */ []));
|
|
643
|
-
/**
|
|
644
|
-
* Distance in pixels from the boundary edges where collision detection should occur.
|
|
645
|
-
*/
|
|
646
|
-
this.collisionPadding = input(5, ...(ngDevMode ? [{ debugName: "collisionPadding" }] : /* istanbul ignore next */ []));
|
|
647
|
-
/**
|
|
648
|
-
* The sticky behavior on the alignment axis.
|
|
649
|
-
*/
|
|
650
|
-
this.sticky = input('partial', ...(ngDevMode ? [{ debugName: "sticky" }] : /* istanbul ignore next */ []));
|
|
651
|
-
/**
|
|
652
|
-
* Whether to hide the popup when the trigger becomes fully occluded.
|
|
653
|
-
*/
|
|
654
|
-
this.hideWhenDetached = input(false, { ...(ngDevMode ? { debugName: "hideWhenDetached" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
655
|
-
/**
|
|
656
|
-
* The CSS position strategy used by Floating UI.
|
|
657
|
-
*/
|
|
658
|
-
this.positionStrategy = input('fixed', ...(ngDevMode ? [{ debugName: "positionStrategy" }] : /* istanbul ignore next */ []));
|
|
659
|
-
/**
|
|
660
|
-
* Whether to update position on every animation frame.
|
|
661
|
-
*/
|
|
662
|
-
this.updatePositionStrategy = input('always', ...(ngDevMode ? [{ debugName: "updatePositionStrategy" }] : /* istanbul ignore next */ []));
|
|
663
|
-
/**
|
|
664
|
-
* Emits when the popup has been placed.
|
|
665
|
-
*/
|
|
666
|
-
this.placed = outputFromObservable(outputToObservable(inject(RdxPopperContentWrapper).placed));
|
|
667
707
|
effect(() => this.triggerEl.set(this.rootContext.trigger() ?? null));
|
|
668
708
|
this.graceArea.onPointerExit(() => {
|
|
669
709
|
this.rootContext.closeOnHover();
|
|
670
710
|
});
|
|
671
711
|
}
|
|
672
712
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxPopoverPositioner, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
673
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "
|
|
713
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.9", type: RdxPopoverPositioner, isStandalone: true, selector: "[rdxPopoverPositioner]", host: { properties: { "attr.data-open": "rootContext.isOpen() ? \"\" : undefined", "attr.data-closed": "rootContext.isOpen() ? undefined : \"\"", "attr.data-instant": "rootContext.instant() ? \"\" : undefined", "style": "legacyVars" } }, providers: [
|
|
714
|
+
...provideRdxPopperContentWrapper(RdxPopoverPositioner),
|
|
674
715
|
provideRdxPopperContentConfig({ arrowPadding: 5, collisionPadding: 5, updatePositionStrategy: 'always' })
|
|
675
|
-
],
|
|
716
|
+
], usesInheritance: true, ngImport: i0 }); }
|
|
676
717
|
}
|
|
677
718
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxPopoverPositioner, decorators: [{
|
|
678
719
|
type: Directive,
|
|
679
720
|
args: [{
|
|
680
721
|
selector: '[rdxPopoverPositioner]',
|
|
681
722
|
providers: [
|
|
723
|
+
...provideRdxPopperContentWrapper(RdxPopoverPositioner),
|
|
682
724
|
provideRdxPopperContentConfig({ arrowPadding: 5, collisionPadding: 5, updatePositionStrategy: 'always' })
|
|
683
725
|
],
|
|
684
|
-
hostDirectives: [
|
|
685
|
-
{
|
|
686
|
-
directive: RdxPopperContentWrapper,
|
|
687
|
-
inputs: [
|
|
688
|
-
'anchor',
|
|
689
|
-
'side',
|
|
690
|
-
'sideOffset',
|
|
691
|
-
'align',
|
|
692
|
-
'alignOffset',
|
|
693
|
-
'arrowPadding',
|
|
694
|
-
'avoidCollisions',
|
|
695
|
-
'collisionBoundary',
|
|
696
|
-
'collisionPadding',
|
|
697
|
-
'sticky',
|
|
698
|
-
'hideWhenDetached',
|
|
699
|
-
'positionStrategy',
|
|
700
|
-
'updatePositionStrategy'
|
|
701
|
-
]
|
|
702
|
-
}
|
|
703
|
-
],
|
|
704
726
|
host: {
|
|
705
727
|
'[attr.data-open]': 'rootContext.isOpen() ? "" : undefined',
|
|
706
728
|
'[attr.data-closed]': 'rootContext.isOpen() ? undefined : ""',
|
|
707
|
-
'[attr.data-anchor-hidden]': 'wrapper.anchorHidden() ? "" : undefined',
|
|
708
|
-
'[attr.data-align]': 'wrapper.placedAlign()',
|
|
709
|
-
'[attr.data-side]': 'wrapper.placedSide()',
|
|
710
729
|
'[attr.data-instant]': 'rootContext.instant() ? "" : undefined',
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
'--available-height': 'var(--radix-popper-available-height)',
|
|
716
|
-
'--positioner-width': 'var(--radix-popper-content-wrapper-width)',
|
|
717
|
-
'--positioner-height': 'var(--radix-popper-content-wrapper-height)',
|
|
718
|
-
'--transform-origin': 'var(--radix-popper-transform-origin)',
|
|
719
|
-
'--radix-popover-content-transform-origin': 'var(--radix-popper-transform-origin)',
|
|
720
|
-
'--radix-popover-content-available-width': 'var(--radix-popper-available-width)',
|
|
721
|
-
'--radix-popover-content-available-height': 'var(--radix-popper-available-height)',
|
|
722
|
-
'--radix-popover-trigger-width': 'var(--radix-popper-anchor-width)',
|
|
723
|
-
'--radix-popover-trigger-height': 'var(--radix-popper-anchor-height)'
|
|
724
|
-
}`
|
|
730
|
+
// `data-side`/`data-align`/`data-anchor-hidden` and the unified `--anchor-*`/`--available-*`/
|
|
731
|
+
// `--transform-origin` vars come from the inherited wrapper (ADR 0012); only the deprecated
|
|
732
|
+
// `--radix-popover-*` aliases remain, for one release of back-compat.
|
|
733
|
+
'[style]': 'legacyVars'
|
|
725
734
|
}
|
|
726
735
|
}]
|
|
727
|
-
}], ctorParameters: () => []
|
|
736
|
+
}], ctorParameters: () => [] });
|
|
728
737
|
|
|
729
738
|
/**
|
|
730
739
|
* An accessible title for the popover.
|
|
@@ -756,6 +765,8 @@ class RdxPopoverTrigger {
|
|
|
756
765
|
constructor() {
|
|
757
766
|
this.parentRootContext = injectRdxPopoverRootContext(true);
|
|
758
767
|
this.elementRef = inject(ElementRef);
|
|
768
|
+
/** Pointer type of the most recent `pointerdown`, used to detect a touch open (ADR 0016 §3). */
|
|
769
|
+
this.lastPointerType = '';
|
|
759
770
|
/**
|
|
760
771
|
* Associates this trigger with a detached popover root.
|
|
761
772
|
*/
|
|
@@ -803,6 +814,9 @@ class RdxPopoverTrigger {
|
|
|
803
814
|
if (this.disabled()) {
|
|
804
815
|
return;
|
|
805
816
|
}
|
|
817
|
+
// Record whether this open is a touch tap (ADR 0016 §3). `detail === 0` is a keyboard-activated
|
|
818
|
+
// click (no preceding pointerdown), which must read non-touch regardless of the last pointer type.
|
|
819
|
+
this.rootContext()?.setOpenedByTouch(event.detail !== 0 && this.lastPointerType === 'touch');
|
|
806
820
|
this.rootContext()?.setPointerDownOnTrigger(false);
|
|
807
821
|
if (this.handle()) {
|
|
808
822
|
this.handle().toggle(this.triggerId(), event);
|
|
@@ -825,14 +839,15 @@ class RdxPopoverTrigger {
|
|
|
825
839
|
}
|
|
826
840
|
this.rootContext()?.cancelHoverOpen();
|
|
827
841
|
}
|
|
828
|
-
handlePointerDown() {
|
|
842
|
+
handlePointerDown(event) {
|
|
843
|
+
this.lastPointerType = event.pointerType;
|
|
829
844
|
this.rootContext()?.setPointerDownOnTrigger(true);
|
|
830
845
|
}
|
|
831
846
|
handlePointerUp() {
|
|
832
847
|
this.rootContext()?.setPointerDownOnTrigger(false);
|
|
833
848
|
}
|
|
834
849
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxPopoverTrigger, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
835
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxPopoverTrigger, isStandalone: true, selector: "button[rdxPopoverTrigger]", inputs: { handle: { classPropertyName: "handle", publicName: "handle", isSignal: true, isRequired: false, transformFunction: null }, payload: { classPropertyName: "payload", publicName: "payload", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, openOnHover: { classPropertyName: "openOnHover", publicName: "openOnHover", isSignal: true, isRequired: false, transformFunction: null }, delay: { classPropertyName: "delay", publicName: "delay", isSignal: true, isRequired: false, transformFunction: null }, closeDelay: { classPropertyName: "closeDelay", publicName: "closeDelay", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "type": "button" }, listeners: { "click": "handleClick($event)", "pointerenter": "handlePointerEnter($event)", "pointerleave": "handlePointerLeave($event)", "pointerdown": "handlePointerDown()", "pointerup": "handlePointerUp()", "pointercancel": "handlePointerUp()" }, properties: { "attr.aria-controls": "rootContext()?.contentId", "attr.aria-expanded": "isOpen()", "attr.aria-haspopup": "\"dialog\"", "attr.data-state": "isOpen() ? \"open\" : \"closed\"", "attr.data-popup-open": "isOpen() ? \"\" : undefined", "attr.data-pressed": "isPressed() ? \"\" : undefined", "attr.disabled": "disabled() ? \"\" : undefined", "id": "triggerId()" } }, hostDirectives: [{ directive: i1.RdxPopperAnchor }], ngImport: i0 }); }
|
|
850
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: RdxPopoverTrigger, isStandalone: true, selector: "button[rdxPopoverTrigger]", inputs: { handle: { classPropertyName: "handle", publicName: "handle", isSignal: true, isRequired: false, transformFunction: null }, payload: { classPropertyName: "payload", publicName: "payload", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, openOnHover: { classPropertyName: "openOnHover", publicName: "openOnHover", isSignal: true, isRequired: false, transformFunction: null }, delay: { classPropertyName: "delay", publicName: "delay", isSignal: true, isRequired: false, transformFunction: null }, closeDelay: { classPropertyName: "closeDelay", publicName: "closeDelay", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "type": "button" }, listeners: { "click": "handleClick($event)", "pointerenter": "handlePointerEnter($event)", "pointerleave": "handlePointerLeave($event)", "pointerdown": "handlePointerDown($event)", "pointerup": "handlePointerUp()", "pointercancel": "handlePointerUp()" }, properties: { "attr.aria-controls": "rootContext()?.contentId", "attr.aria-expanded": "isOpen()", "attr.aria-haspopup": "\"dialog\"", "attr.data-state": "isOpen() ? \"open\" : \"closed\"", "attr.data-popup-open": "isOpen() ? \"\" : undefined", "attr.data-pressed": "isPressed() ? \"\" : undefined", "attr.disabled": "disabled() ? \"\" : undefined", "id": "triggerId()" } }, hostDirectives: [{ directive: i1.RdxPopperAnchor }], ngImport: i0 }); }
|
|
836
851
|
}
|
|
837
852
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: RdxPopoverTrigger, decorators: [{
|
|
838
853
|
type: Directive,
|
|
@@ -852,7 +867,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
852
867
|
'(click)': 'handleClick($event)',
|
|
853
868
|
'(pointerenter)': 'handlePointerEnter($event)',
|
|
854
869
|
'(pointerleave)': 'handlePointerLeave($event)',
|
|
855
|
-
'(pointerdown)': 'handlePointerDown()',
|
|
870
|
+
'(pointerdown)': 'handlePointerDown($event)',
|
|
856
871
|
'(pointerup)': 'handlePointerUp()',
|
|
857
872
|
'(pointercancel)': 'handlePointerUp()'
|
|
858
873
|
}
|