ng-primitives 0.91.2 → 0.93.0
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/a11y/index.d.ts +3 -1
- package/accordion/index.d.ts +8 -1
- package/autofill/index.d.ts +4 -0
- package/avatar/index.d.ts +11 -2
- package/breadcrumbs/index.d.ts +21 -7
- package/button/index.d.ts +3 -1
- package/checkbox/index.d.ts +3 -1
- package/fesm2022/ng-primitives-checkbox.mjs +7 -8
- package/fesm2022/ng-primitives-checkbox.mjs.map +1 -1
- package/fesm2022/ng-primitives-file-upload.mjs +209 -202
- package/fesm2022/ng-primitives-file-upload.mjs.map +1 -1
- package/fesm2022/ng-primitives-focus-trap.mjs +107 -138
- package/fesm2022/ng-primitives-focus-trap.mjs.map +1 -1
- package/fesm2022/ng-primitives-form-field.mjs +323 -399
- package/fesm2022/ng-primitives-form-field.mjs.map +1 -1
- package/fesm2022/ng-primitives-interactions.mjs +57 -58
- package/fesm2022/ng-primitives-interactions.mjs.map +1 -1
- package/fesm2022/ng-primitives-listbox.mjs +2 -2
- package/fesm2022/ng-primitives-listbox.mjs.map +1 -1
- package/fesm2022/ng-primitives-menu.mjs.map +1 -1
- package/fesm2022/ng-primitives-portal.mjs +5 -1
- package/fesm2022/ng-primitives-portal.mjs.map +1 -1
- package/fesm2022/ng-primitives-slider.mjs +4 -5
- package/fesm2022/ng-primitives-slider.mjs.map +1 -1
- package/fesm2022/ng-primitives-state.mjs +36 -8
- package/fesm2022/ng-primitives-state.mjs.map +1 -1
- package/fesm2022/ng-primitives-switch.mjs +4 -5
- package/fesm2022/ng-primitives-switch.mjs.map +1 -1
- package/fesm2022/ng-primitives-tabs.mjs +194 -192
- package/fesm2022/ng-primitives-tabs.mjs.map +1 -1
- package/fesm2022/ng-primitives-toggle-group.mjs +4 -5
- package/fesm2022/ng-primitives-toggle-group.mjs.map +1 -1
- package/fesm2022/ng-primitives-toggle.mjs +4 -5
- package/fesm2022/ng-primitives-toggle.mjs.map +1 -1
- package/fesm2022/ng-primitives-tooltip.mjs +4 -4
- package/fesm2022/ng-primitives-tooltip.mjs.map +1 -1
- package/fesm2022/ng-primitives-utils.mjs +23 -14
- package/fesm2022/ng-primitives-utils.mjs.map +1 -1
- package/file-upload/index.d.ts +100 -52
- package/focus-trap/index.d.ts +33 -75
- package/form-field/index.d.ts +320 -123
- package/input/index.d.ts +6 -0
- package/interactions/index.d.ts +16 -16
- package/package.json +1 -1
- package/roving-focus/index.d.ts +6 -2
- package/schematics/ng-generate/templates/tabs/tabs.__fileSuffix@dasherize__.ts.template +2 -2
- package/slider/index.d.ts +14 -6
- package/state/index.d.ts +23 -8
- package/switch/index.d.ts +8 -4
- package/tabs/index.d.ts +333 -84
- package/textarea/index.d.ts +6 -0
- package/toggle/index.d.ts +5 -3
- package/toggle-group/index.d.ts +6 -2
- package/toolbar/index.d.ts +5 -0
- package/utils/index.d.ts +1 -1
|
@@ -1,31 +1,11 @@
|
|
|
1
|
-
import { FocusMonitor, InteractivityChecker } from '@angular/cdk/a11y';
|
|
2
1
|
import * as i0 from '@angular/core';
|
|
3
|
-
import { inject, Injector,
|
|
2
|
+
import { inject, Injector, NgZone, signal, afterNextRender, input, booleanAttribute, Directive } from '@angular/core';
|
|
3
|
+
import { FocusMonitor, InteractivityChecker } from '@angular/cdk/a11y';
|
|
4
|
+
import { injectElementRef } from 'ng-primitives/internal';
|
|
4
5
|
import { NgpOverlay } from 'ng-primitives/portal';
|
|
6
|
+
import { createPrimitive, attrBinding, dataBinding, onDestroy, listener } from 'ng-primitives/state';
|
|
5
7
|
import { safeTakeUntilDestroyed } from 'ng-primitives/utils';
|
|
6
|
-
import { createStateToken, createStateProvider, createStateInjector, createState } from 'ng-primitives/state';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* The state token for the FocusTrap primitive.
|
|
10
|
-
*/
|
|
11
|
-
const NgpFocusTrapStateToken = createStateToken('FocusTrap');
|
|
12
|
-
/**
|
|
13
|
-
* Provides the FocusTrap state.
|
|
14
|
-
*/
|
|
15
|
-
const provideFocusTrapState = createStateProvider(NgpFocusTrapStateToken);
|
|
16
|
-
/**
|
|
17
|
-
* Injects the FocusTrap state.
|
|
18
|
-
*/
|
|
19
|
-
const injectFocusTrapState = createStateInjector(NgpFocusTrapStateToken);
|
|
20
|
-
/**
|
|
21
|
-
* The FocusTrap state registration function.
|
|
22
|
-
*/
|
|
23
|
-
const focusTrapState = createState(NgpFocusTrapStateToken);
|
|
24
8
|
|
|
25
|
-
/**
|
|
26
|
-
* This implementation is based on the Radix UI FocusScope:
|
|
27
|
-
* https://github.com/radix-ui/primitives/blob/main/packages/react/focus-scope/src/FocusScope.tsx#L306
|
|
28
|
-
*/
|
|
29
9
|
class FocusTrap {
|
|
30
10
|
constructor() {
|
|
31
11
|
/**
|
|
@@ -81,149 +61,111 @@ class FocusTrapStack {
|
|
|
81
61
|
}
|
|
82
62
|
// create a global stack of focus traps
|
|
83
63
|
const focusTrapStack = new FocusTrapStack();
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
*/
|
|
108
|
-
this.interactivityChecker = inject(InteractivityChecker);
|
|
109
|
-
/**
|
|
110
|
-
* Get the focus trap container element.
|
|
111
|
-
*/
|
|
112
|
-
this.elementRef = inject(ElementRef);
|
|
113
|
-
/**
|
|
114
|
-
* Access NgZone to run the focus trap events outside of Angular's zone.
|
|
115
|
-
*/
|
|
116
|
-
this.ngZone = inject(NgZone);
|
|
117
|
-
/**
|
|
118
|
-
* Store the mutation observer.
|
|
119
|
-
*/
|
|
120
|
-
this.mutationObserver = null;
|
|
121
|
-
/**
|
|
122
|
-
* Store the last focused element.
|
|
123
|
-
*/
|
|
124
|
-
this.lastFocusedElement = null;
|
|
125
|
-
/**
|
|
126
|
-
* Whether the focus trap is disabled.
|
|
127
|
-
*/
|
|
128
|
-
this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled", alias: 'ngpFocusTrapDisabled',
|
|
129
|
-
transform: booleanAttribute }] : [{
|
|
130
|
-
alias: 'ngpFocusTrapDisabled',
|
|
131
|
-
transform: booleanAttribute,
|
|
132
|
-
}]));
|
|
133
|
-
/**
|
|
134
|
-
* The focus trap state.
|
|
135
|
-
*/
|
|
136
|
-
this.state = focusTrapState(this);
|
|
137
|
-
// if this is used within an overlay we must disable the focus trap as soon as the overlay is closing
|
|
138
|
-
this.overlay?.closing
|
|
139
|
-
.pipe(safeTakeUntilDestroyed())
|
|
140
|
-
.subscribe(() => this.focusTrap.deactivate());
|
|
141
|
-
}
|
|
142
|
-
ngOnInit() {
|
|
143
|
-
focusTrapStack.add(this.focusTrap);
|
|
144
|
-
this.mutationObserver = new MutationObserver(this.handleMutations.bind(this));
|
|
145
|
-
// setup event listeners
|
|
146
|
-
this.ngZone.runOutsideAngular(() => {
|
|
147
|
-
this.mutationObserver.observe(this.elementRef.nativeElement, {
|
|
64
|
+
const [NgpFocusTrapStateToken, ngpFocusTrap, injectFocusTrapState, provideFocusTrapState] = createPrimitive('NgpFocusTrap', ({ disabled = signal(false) }) => {
|
|
65
|
+
const element = injectElementRef();
|
|
66
|
+
const overlay = inject(NgpOverlay, { optional: true });
|
|
67
|
+
const injector = inject(Injector);
|
|
68
|
+
const focusMonitor = inject(FocusMonitor);
|
|
69
|
+
const interactivityChecker = inject(InteractivityChecker);
|
|
70
|
+
const ngZone = inject(NgZone);
|
|
71
|
+
// Create a new focus trap
|
|
72
|
+
const focusTrap = new FocusTrap();
|
|
73
|
+
// Store the mutation observer
|
|
74
|
+
let mutationObserver = null;
|
|
75
|
+
// Store the last focused element
|
|
76
|
+
let lastFocusedElement = null;
|
|
77
|
+
// Host bindings
|
|
78
|
+
attrBinding(element, 'tabindex', '-1');
|
|
79
|
+
dataBinding(element, 'data-focus-trap', () => (disabled() ? null : ''));
|
|
80
|
+
// Setup the focus trap
|
|
81
|
+
function setupFocusTrap() {
|
|
82
|
+
focusTrapStack.add(focusTrap);
|
|
83
|
+
mutationObserver = new MutationObserver(handleMutations);
|
|
84
|
+
// Setup event listeners
|
|
85
|
+
ngZone.runOutsideAngular(() => {
|
|
86
|
+
mutationObserver.observe(element.nativeElement, {
|
|
148
87
|
childList: true,
|
|
149
88
|
subtree: true,
|
|
150
89
|
});
|
|
151
|
-
document.addEventListener('focusin',
|
|
152
|
-
document.addEventListener('focusout',
|
|
90
|
+
document.addEventListener('focusin', handleFocusIn);
|
|
91
|
+
document.addEventListener('focusout', handleFocusOut);
|
|
153
92
|
});
|
|
154
93
|
const previouslyFocusedElement = document.activeElement;
|
|
155
|
-
const hasFocusedCandidate =
|
|
94
|
+
const hasFocusedCandidate = element.nativeElement.contains(previouslyFocusedElement);
|
|
156
95
|
if (!hasFocusedCandidate) {
|
|
157
96
|
// we do this to ensure the content is rendered before we try to find the first focusable element
|
|
158
97
|
// and focus it
|
|
159
98
|
afterNextRender({
|
|
160
99
|
write: () => {
|
|
161
|
-
|
|
100
|
+
focusFirst();
|
|
162
101
|
// if the focus didn't change, focus the container
|
|
163
102
|
if (document.activeElement === previouslyFocusedElement) {
|
|
164
|
-
|
|
103
|
+
focus(element.nativeElement);
|
|
165
104
|
}
|
|
166
105
|
},
|
|
167
|
-
}, { injector
|
|
106
|
+
}, { injector });
|
|
168
107
|
}
|
|
169
108
|
}
|
|
170
|
-
|
|
171
|
-
focusTrapStack.remove(
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
109
|
+
function teardownFocusTrap() {
|
|
110
|
+
focusTrapStack.remove(focusTrap);
|
|
111
|
+
mutationObserver?.disconnect();
|
|
112
|
+
mutationObserver = null;
|
|
113
|
+
focusTrap.deactivate();
|
|
114
|
+
// Remove event listeners
|
|
115
|
+
document.removeEventListener('focusin', handleFocusIn);
|
|
116
|
+
document.removeEventListener('focusout', handleFocusOut);
|
|
175
117
|
}
|
|
176
|
-
handleFocusIn(event) {
|
|
177
|
-
if (!
|
|
118
|
+
function handleFocusIn(event) {
|
|
119
|
+
if (!focusTrap.active || disabled?.()) {
|
|
178
120
|
return;
|
|
179
121
|
}
|
|
180
122
|
const target = event.target;
|
|
181
|
-
if (
|
|
182
|
-
|
|
123
|
+
if (element.nativeElement.contains(target)) {
|
|
124
|
+
lastFocusedElement = target;
|
|
183
125
|
}
|
|
184
126
|
else {
|
|
185
|
-
|
|
127
|
+
focus(lastFocusedElement);
|
|
186
128
|
}
|
|
187
129
|
}
|
|
188
130
|
/**
|
|
189
131
|
* Handles the `focusout` event.
|
|
190
132
|
*/
|
|
191
|
-
handleFocusOut(event) {
|
|
192
|
-
if (!
|
|
133
|
+
function handleFocusOut(event) {
|
|
134
|
+
if (!focusTrap.active || disabled?.() || event.relatedTarget === null) {
|
|
193
135
|
return;
|
|
194
136
|
}
|
|
195
137
|
const relatedTarget = event.relatedTarget;
|
|
196
|
-
if (!
|
|
197
|
-
|
|
138
|
+
if (!element.nativeElement.contains(relatedTarget)) {
|
|
139
|
+
focus(lastFocusedElement);
|
|
198
140
|
}
|
|
199
141
|
}
|
|
200
142
|
/**
|
|
201
143
|
* If the focused element gets removed from the DOM, browsers move focus back to the document.body.
|
|
202
144
|
* We move focus to the container to keep focus trapped correctly.
|
|
203
145
|
*/
|
|
204
|
-
handleMutations(mutations) {
|
|
146
|
+
function handleMutations(mutations) {
|
|
205
147
|
const focusedElement = document.activeElement;
|
|
206
148
|
if (focusedElement !== document.body) {
|
|
207
149
|
return;
|
|
208
150
|
}
|
|
209
151
|
for (const mutation of mutations) {
|
|
210
152
|
if (mutation.removedNodes.length > 0) {
|
|
211
|
-
|
|
153
|
+
focus(element.nativeElement);
|
|
212
154
|
}
|
|
213
155
|
}
|
|
214
156
|
}
|
|
215
157
|
/**
|
|
216
158
|
* Handles the `keydown` event.
|
|
217
159
|
*/
|
|
218
|
-
handleKeyDown(event) {
|
|
219
|
-
if (!
|
|
160
|
+
function handleKeyDown(event) {
|
|
161
|
+
if (!focusTrap.active || disabled?.()) {
|
|
220
162
|
return;
|
|
221
163
|
}
|
|
222
164
|
const isTabKey = event.key === 'Tab' && !event.altKey && !event.ctrlKey && !event.metaKey;
|
|
223
165
|
const focusedElement = document.activeElement;
|
|
224
166
|
if (isTabKey && focusedElement) {
|
|
225
167
|
const container = event.currentTarget;
|
|
226
|
-
const [first, last] =
|
|
168
|
+
const [first, last] = getTabbableEdges(container);
|
|
227
169
|
const hasTabbableElementsInside = first && last;
|
|
228
170
|
// we can only wrap focus if we have tabbable edges
|
|
229
171
|
if (!hasTabbableElementsInside) {
|
|
@@ -234,11 +176,11 @@ class NgpFocusTrap {
|
|
|
234
176
|
else {
|
|
235
177
|
if (!event.shiftKey && focusedElement === last) {
|
|
236
178
|
event.preventDefault();
|
|
237
|
-
|
|
179
|
+
focus(first);
|
|
238
180
|
}
|
|
239
181
|
else if (event.shiftKey && focusedElement === first) {
|
|
240
182
|
event.preventDefault();
|
|
241
|
-
|
|
183
|
+
focus(last);
|
|
242
184
|
}
|
|
243
185
|
}
|
|
244
186
|
}
|
|
@@ -246,19 +188,19 @@ class NgpFocusTrap {
|
|
|
246
188
|
/**
|
|
247
189
|
* Returns the first and last tabbable elements inside a container.
|
|
248
190
|
*/
|
|
249
|
-
getTabbableEdges(container) {
|
|
250
|
-
const candidates =
|
|
251
|
-
const first =
|
|
252
|
-
const last =
|
|
191
|
+
function getTabbableEdges(container) {
|
|
192
|
+
const candidates = getTabbableCandidates(container);
|
|
193
|
+
const first = findVisible(candidates);
|
|
194
|
+
const last = findVisible(candidates.reverse());
|
|
253
195
|
return [first, last];
|
|
254
196
|
}
|
|
255
197
|
/**
|
|
256
198
|
* Returns a list of potential focusable elements inside a container.
|
|
257
199
|
*/
|
|
258
|
-
getTabbableCandidates(container) {
|
|
200
|
+
function getTabbableCandidates(container) {
|
|
259
201
|
const nodes = [];
|
|
260
202
|
const walker = document.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, {
|
|
261
|
-
acceptNode: (node) =>
|
|
203
|
+
acceptNode: (node) => interactivityChecker.isFocusable(node)
|
|
262
204
|
? NodeFilter.FILTER_ACCEPT
|
|
263
205
|
: NodeFilter.FILTER_SKIP,
|
|
264
206
|
});
|
|
@@ -270,30 +212,64 @@ class NgpFocusTrap {
|
|
|
270
212
|
/**
|
|
271
213
|
* Returns the first visible element in a list..
|
|
272
214
|
*/
|
|
273
|
-
findVisible(elements) {
|
|
274
|
-
return elements.find(element =>
|
|
215
|
+
function findVisible(elements) {
|
|
216
|
+
return elements.find(element => interactivityChecker.isVisible(element)) ?? null;
|
|
275
217
|
}
|
|
276
|
-
focus(element) {
|
|
218
|
+
function focus(element) {
|
|
277
219
|
if (!element) {
|
|
278
220
|
return;
|
|
279
221
|
}
|
|
280
222
|
// Its not great that we are relying on an internal API here, but we need to in order to
|
|
281
223
|
// try and best determine the focus origin when it is programmatically closed by the user.
|
|
282
|
-
|
|
224
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
225
|
+
focusMonitor.focusVia(element, focusMonitor._lastFocusOrigin, {
|
|
283
226
|
preventScroll: true,
|
|
284
227
|
});
|
|
285
228
|
}
|
|
286
|
-
focusFirst() {
|
|
229
|
+
function focusFirst() {
|
|
287
230
|
const previouslyFocusedElement = document.activeElement;
|
|
288
|
-
for (const candidate of
|
|
289
|
-
|
|
231
|
+
for (const candidate of getTabbableCandidates(element.nativeElement)) {
|
|
232
|
+
focus(candidate);
|
|
290
233
|
if (document.activeElement !== previouslyFocusedElement) {
|
|
291
234
|
return;
|
|
292
235
|
}
|
|
293
236
|
}
|
|
294
237
|
}
|
|
238
|
+
// Setup the focus trap
|
|
239
|
+
setupFocusTrap();
|
|
240
|
+
// Teardown the focus trap on destroy
|
|
241
|
+
onDestroy(teardownFocusTrap);
|
|
242
|
+
// Listen to keydown events
|
|
243
|
+
listener(element, 'keydown', handleKeyDown);
|
|
244
|
+
// if this is used within an overlay we must disable the focus trap as soon as the overlay is closing
|
|
245
|
+
overlay?.closing.pipe(safeTakeUntilDestroyed()).subscribe(() => focusTrap.deactivate());
|
|
246
|
+
return {};
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* This implementation is based on the Radix UI FocusScope:
|
|
251
|
+
* https://github.com/radix-ui/primitives/blob/main/packages/react/focus-scope/src/FocusScope.tsx#L306
|
|
252
|
+
*/
|
|
253
|
+
/**
|
|
254
|
+
* The `NgpFocusTrap` directive traps focus within the host element.
|
|
255
|
+
*/
|
|
256
|
+
class NgpFocusTrap {
|
|
257
|
+
/**
|
|
258
|
+
* The focus trap state.
|
|
259
|
+
*/
|
|
260
|
+
constructor() {
|
|
261
|
+
/**
|
|
262
|
+
* Whether the focus trap is disabled.
|
|
263
|
+
*/
|
|
264
|
+
this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled", alias: 'ngpFocusTrapDisabled',
|
|
265
|
+
transform: booleanAttribute }] : [{
|
|
266
|
+
alias: 'ngpFocusTrapDisabled',
|
|
267
|
+
transform: booleanAttribute,
|
|
268
|
+
}]));
|
|
269
|
+
ngpFocusTrap({ disabled: this.disabled });
|
|
270
|
+
}
|
|
295
271
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpFocusTrap, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
296
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.9", type: NgpFocusTrap, isStandalone: true, selector: "[ngpFocusTrap]", inputs: { disabled: { classPropertyName: "disabled", publicName: "ngpFocusTrapDisabled", isSignal: true, isRequired: false, transformFunction: null } },
|
|
272
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.3.9", type: NgpFocusTrap, isStandalone: true, selector: "[ngpFocusTrap]", inputs: { disabled: { classPropertyName: "disabled", publicName: "ngpFocusTrapDisabled", isSignal: true, isRequired: false, transformFunction: null } }, providers: [provideFocusTrapState()], exportAs: ["ngpFocusTrap"], ngImport: i0 }); }
|
|
297
273
|
}
|
|
298
274
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: NgpFocusTrap, decorators: [{
|
|
299
275
|
type: Directive,
|
|
@@ -301,19 +277,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImpor
|
|
|
301
277
|
selector: '[ngpFocusTrap]',
|
|
302
278
|
exportAs: 'ngpFocusTrap',
|
|
303
279
|
providers: [provideFocusTrapState()],
|
|
304
|
-
host: {
|
|
305
|
-
'[attr.tabindex]': '-1',
|
|
306
|
-
'[attr.data-focus-trap]': '!disabled() ? "" : null',
|
|
307
|
-
},
|
|
308
280
|
}]
|
|
309
|
-
}], ctorParameters: () => [], propDecorators: { disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpFocusTrapDisabled", required: false }] }]
|
|
310
|
-
type: HostListener,
|
|
311
|
-
args: ['keydown', ['$event']]
|
|
312
|
-
}] } });
|
|
281
|
+
}], ctorParameters: () => [], propDecorators: { disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngpFocusTrapDisabled", required: false }] }] } });
|
|
313
282
|
|
|
314
283
|
/**
|
|
315
284
|
* Generated bundle index. Do not edit.
|
|
316
285
|
*/
|
|
317
286
|
|
|
318
|
-
export { NgpFocusTrap, injectFocusTrapState, provideFocusTrapState };
|
|
287
|
+
export { NgpFocusTrap, injectFocusTrapState, ngpFocusTrap, provideFocusTrapState };
|
|
319
288
|
//# sourceMappingURL=ng-primitives-focus-trap.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ng-primitives-focus-trap.mjs","sources":["../../../../packages/ng-primitives/focus-trap/src/focus-trap/focus-trap-state.ts","../../../../packages/ng-primitives/focus-trap/src/focus-trap/focus-trap.ts","../../../../packages/ng-primitives/focus-trap/src/ng-primitives-focus-trap.ts"],"sourcesContent":["import {\n createState,\n createStateInjector,\n createStateProvider,\n createStateToken,\n} from 'ng-primitives/state';\nimport type { NgpFocusTrap } from './focus-trap';\n\n/**\n * The state token for the FocusTrap primitive.\n */\nexport const NgpFocusTrapStateToken = createStateToken<NgpFocusTrap>('FocusTrap');\n\n/**\n * Provides the FocusTrap state.\n */\nexport const provideFocusTrapState = createStateProvider(NgpFocusTrapStateToken);\n\n/**\n * Injects the FocusTrap state.\n */\nexport const injectFocusTrapState = createStateInjector<NgpFocusTrap>(NgpFocusTrapStateToken);\n\n/**\n * The FocusTrap state registration function.\n */\nexport const focusTrapState = createState(NgpFocusTrapStateToken);\n","import { FocusMonitor, InteractivityChecker } from '@angular/cdk/a11y';\nimport { BooleanInput } from '@angular/cdk/coercion';\nimport {\n afterNextRender,\n booleanAttribute,\n Directive,\n ElementRef,\n HostListener,\n inject,\n Injector,\n input,\n NgZone,\n OnDestroy,\n OnInit,\n} from '@angular/core';\nimport { NgpOverlay } from 'ng-primitives/portal';\nimport { safeTakeUntilDestroyed } from 'ng-primitives/utils';\nimport { focusTrapState, provideFocusTrapState } from './focus-trap-state';\n\n/**\n * This implementation is based on the Radix UI FocusScope:\n * https://github.com/radix-ui/primitives/blob/main/packages/react/focus-scope/src/FocusScope.tsx#L306\n */\n\nclass FocusTrap {\n /**\n * Whether the focus trap is active.\n */\n active: boolean = false;\n\n /**\n * Activates the focus trap.\n */\n activate(): void {\n this.active = true;\n }\n\n /**\n * Deactivates the focus trap.\n */\n deactivate(): void {\n this.active = false;\n }\n}\n\nclass FocusTrapStack {\n /**\n * The stack of focus traps.\n */\n private readonly stack: FocusTrap[] = [];\n\n /**\n * Adds a focus trap to the stack.\n */\n add(focusTrap: FocusTrap): void {\n // deactivate the previous focus trap\n this.stack.forEach(t => t.deactivate());\n\n // add the new focus trap and activate it\n this.stack.push(focusTrap);\n focusTrap.activate();\n }\n\n /**\n * Removes a focus trap from the stack.\n */\n remove(focusTrap: FocusTrap): void {\n // remove the focus trap\n const index = this.stack.indexOf(focusTrap);\n\n if (index >= 0) {\n this.stack.splice(index, 1);\n }\n\n // activate the previous focus trap\n const previous = this.stack[this.stack.length - 1];\n\n if (previous) {\n previous.activate();\n }\n }\n}\n\n// create a global stack of focus traps\nconst focusTrapStack = new FocusTrapStack();\n\n/**\n * The `NgpFocusTrap` directive traps focus within the host element.\n */\n@Directive({\n selector: '[ngpFocusTrap]',\n exportAs: 'ngpFocusTrap',\n providers: [provideFocusTrapState()],\n host: {\n '[attr.tabindex]': '-1',\n '[attr.data-focus-trap]': '!disabled() ? \"\" : null',\n },\n})\nexport class NgpFocusTrap implements OnInit, OnDestroy {\n /**\n * Access any parent overlay.\n */\n private readonly overlay = inject(NgpOverlay, { optional: true });\n\n /**\n * Create a new focus trap.\n */\n private readonly focusTrap = new FocusTrap();\n\n /**\n * Access the injector.\n */\n private readonly injector = inject(Injector);\n\n /**\n * Access the focus monitor.\n */\n private readonly focusMonitor = inject(FocusMonitor);\n\n /**\n * Access the interactivity checker.\n */\n private readonly interactivityChecker = inject(InteractivityChecker);\n\n /**\n * Get the focus trap container element.\n */\n private readonly elementRef = inject<ElementRef<HTMLElement>>(ElementRef);\n\n /**\n * Access NgZone to run the focus trap events outside of Angular's zone.\n */\n private readonly ngZone = inject(NgZone);\n\n /**\n * Store the mutation observer.\n */\n private mutationObserver: MutationObserver | null = null;\n\n /**\n * Store the last focused element.\n */\n private lastFocusedElement: HTMLElement | null = null;\n\n /**\n * Whether the focus trap is disabled.\n */\n readonly disabled = input<boolean, BooleanInput>(false, {\n alias: 'ngpFocusTrapDisabled',\n transform: booleanAttribute,\n });\n\n /**\n * The focus trap state.\n */\n protected readonly state = focusTrapState<NgpFocusTrap>(this);\n\n constructor() {\n // if this is used within an overlay we must disable the focus trap as soon as the overlay is closing\n this.overlay?.closing\n .pipe(safeTakeUntilDestroyed())\n .subscribe(() => this.focusTrap.deactivate());\n }\n\n ngOnInit(): void {\n focusTrapStack.add(this.focusTrap);\n\n this.mutationObserver = new MutationObserver(this.handleMutations.bind(this));\n\n // setup event listeners\n this.ngZone.runOutsideAngular(() => {\n this.mutationObserver!.observe(this.elementRef.nativeElement, {\n childList: true,\n subtree: true,\n });\n document.addEventListener('focusin', this.handleFocusIn.bind(this));\n document.addEventListener('focusout', this.handleFocusOut.bind(this));\n });\n\n const previouslyFocusedElement = document.activeElement as HTMLElement | null;\n const hasFocusedCandidate = this.elementRef.nativeElement.contains(previouslyFocusedElement);\n\n if (!hasFocusedCandidate) {\n // we do this to ensure the content is rendered before we try to find the first focusable element\n // and focus it\n afterNextRender(\n {\n write: () => {\n this.focusFirst();\n\n // if the focus didn't change, focus the container\n if (document.activeElement === previouslyFocusedElement) {\n this.focus(this.elementRef.nativeElement);\n }\n },\n },\n { injector: this.injector },\n );\n }\n }\n\n ngOnDestroy(): void {\n focusTrapStack.remove(this.focusTrap);\n this.mutationObserver?.disconnect();\n this.mutationObserver = null;\n this.focusTrap.deactivate();\n }\n\n private handleFocusIn(event: FocusEvent): void {\n if (!this.focusTrap.active || this.state.disabled()) {\n return;\n }\n\n const target = event.target as HTMLElement | null;\n\n if (this.elementRef.nativeElement.contains(target)) {\n this.lastFocusedElement = target;\n } else {\n this.focus(this.lastFocusedElement);\n }\n }\n\n /**\n * Handles the `focusout` event.\n */\n private handleFocusOut(event: FocusEvent) {\n if (!this.focusTrap.active || this.state.disabled() || event.relatedTarget === null) {\n return;\n }\n\n const relatedTarget = event.relatedTarget as HTMLElement;\n\n if (!this.elementRef.nativeElement.contains(relatedTarget)) {\n this.focus(this.lastFocusedElement);\n }\n }\n\n /**\n * If the focused element gets removed from the DOM, browsers move focus back to the document.body.\n * We move focus to the container to keep focus trapped correctly.\n */\n private handleMutations(mutations: MutationRecord[]): void {\n const focusedElement = document.activeElement as HTMLElement | null;\n\n if (focusedElement !== document.body) {\n return;\n }\n\n for (const mutation of mutations) {\n if (mutation.removedNodes.length > 0) {\n this.focus(this.elementRef.nativeElement);\n }\n }\n }\n\n /**\n * Handles the `keydown` event.\n */\n @HostListener('keydown', ['$event'])\n protected handleKeyDown(event: KeyboardEvent): void {\n if (!this.focusTrap.active || this.state.disabled()) {\n return;\n }\n\n const isTabKey = event.key === 'Tab' && !event.altKey && !event.ctrlKey && !event.metaKey;\n const focusedElement = document.activeElement as HTMLElement | null;\n\n if (isTabKey && focusedElement) {\n const container = event.currentTarget as HTMLElement;\n const [first, last] = this.getTabbableEdges(container);\n const hasTabbableElementsInside = first && last;\n\n // we can only wrap focus if we have tabbable edges\n if (!hasTabbableElementsInside) {\n if (focusedElement === container) {\n event.preventDefault();\n }\n } else {\n if (!event.shiftKey && focusedElement === last) {\n event.preventDefault();\n this.focus(first);\n } else if (event.shiftKey && focusedElement === first) {\n event.preventDefault();\n this.focus(last);\n }\n }\n }\n }\n\n /**\n * Returns the first and last tabbable elements inside a container.\n */\n private getTabbableEdges(container: HTMLElement) {\n const candidates = this.getTabbableCandidates(container);\n const first = this.findVisible(candidates);\n const last = this.findVisible(candidates.reverse());\n return [first, last] as const;\n }\n\n /**\n * Returns a list of potential focusable elements inside a container.\n */\n private getTabbableCandidates(container: HTMLElement) {\n const nodes: HTMLElement[] = [];\n const walker = document.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, {\n acceptNode: (node: HTMLElement) =>\n this.interactivityChecker.isFocusable(node)\n ? NodeFilter.FILTER_ACCEPT\n : NodeFilter.FILTER_SKIP,\n });\n while (walker.nextNode()) {\n nodes.push(walker.currentNode as HTMLElement);\n }\n return nodes;\n }\n\n /**\n * Returns the first visible element in a list..\n */\n private findVisible(elements: HTMLElement[]) {\n return elements.find(element => this.interactivityChecker.isVisible(element)) ?? null;\n }\n\n private focus(element: HTMLElement | null): void {\n if (!element) {\n return;\n }\n // Its not great that we are relying on an internal API here, but we need to in order to\n // try and best determine the focus origin when it is programmatically closed by the user.\n this.focusMonitor.focusVia(element, (this.focusMonitor as any)._lastFocusOrigin, {\n preventScroll: true,\n });\n }\n\n private focusFirst(): void {\n const previouslyFocusedElement = document.activeElement;\n\n for (const candidate of this.getTabbableCandidates(this.elementRef.nativeElement)) {\n this.focus(candidate);\n\n if (document.activeElement !== previouslyFocusedElement) {\n return;\n }\n }\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ng-primitives-focus-trap.mjs","sources":["../../../../packages/ng-primitives/focus-trap/src/focus-trap/focus-trap-state.ts","../../../../packages/ng-primitives/focus-trap/src/focus-trap/focus-trap.ts","../../../../packages/ng-primitives/focus-trap/src/ng-primitives-focus-trap.ts"],"sourcesContent":["import { FocusMonitor, InteractivityChecker } from '@angular/cdk/a11y';\nimport { afterNextRender, inject, Injector, NgZone, signal, Signal } from '@angular/core';\nimport { injectElementRef } from 'ng-primitives/internal';\nimport { NgpOverlay } from 'ng-primitives/portal';\nimport {\n attrBinding,\n createPrimitive,\n dataBinding,\n listener,\n onDestroy,\n} from 'ng-primitives/state';\nimport { safeTakeUntilDestroyed } from 'ng-primitives/utils';\n\nclass FocusTrap {\n /**\n * Whether the focus trap is active.\n */\n active: boolean = false;\n\n /**\n * Activates the focus trap.\n */\n activate(): void {\n this.active = true;\n }\n\n /**\n * Deactivates the focus trap.\n */\n deactivate(): void {\n this.active = false;\n }\n}\n\nclass FocusTrapStack {\n /**\n * The stack of focus traps.\n */\n private readonly stack: FocusTrap[] = [];\n\n /**\n * Adds a focus trap to the stack.\n */\n add(focusTrap: FocusTrap): void {\n // deactivate the previous focus trap\n this.stack.forEach(t => t.deactivate());\n\n // add the new focus trap and activate it\n this.stack.push(focusTrap);\n focusTrap.activate();\n }\n\n /**\n * Removes a focus trap from the stack.\n */\n remove(focusTrap: FocusTrap): void {\n // remove the focus trap\n const index = this.stack.indexOf(focusTrap);\n\n if (index >= 0) {\n this.stack.splice(index, 1);\n }\n\n // activate the previous focus trap\n const previous = this.stack[this.stack.length - 1];\n\n if (previous) {\n previous.activate();\n }\n }\n}\n\n// create a global stack of focus traps\nconst focusTrapStack = new FocusTrapStack();\n\n/**\n * The state for the FocusTrap primitive.\n */\nexport interface NgpFocusTrapState {\n // No public API\n}\n\n/**\n * The props for the FocusTrap primitive.\n */\nexport interface NgpFocusTrapProps {\n /**\n * Whether the focus trap is disabled.\n */\n readonly disabled?: Signal<boolean>;\n}\n\nexport const [NgpFocusTrapStateToken, ngpFocusTrap, injectFocusTrapState, provideFocusTrapState] =\n createPrimitive('NgpFocusTrap', ({ disabled = signal(false) }: NgpFocusTrapProps) => {\n const element = injectElementRef();\n const overlay = inject(NgpOverlay, { optional: true });\n const injector = inject(Injector);\n const focusMonitor = inject(FocusMonitor);\n const interactivityChecker = inject(InteractivityChecker);\n const ngZone = inject(NgZone);\n\n // Create a new focus trap\n const focusTrap = new FocusTrap();\n\n // Store the mutation observer\n let mutationObserver: MutationObserver | null = null;\n\n // Store the last focused element\n let lastFocusedElement: HTMLElement | null = null;\n\n // Host bindings\n attrBinding(element, 'tabindex', '-1');\n dataBinding(element, 'data-focus-trap', () => (disabled() ? null : ''));\n\n // Setup the focus trap\n function setupFocusTrap(): void {\n focusTrapStack.add(focusTrap);\n\n mutationObserver = new MutationObserver(handleMutations);\n\n // Setup event listeners\n ngZone.runOutsideAngular(() => {\n mutationObserver!.observe(element.nativeElement, {\n childList: true,\n subtree: true,\n });\n document.addEventListener('focusin', handleFocusIn);\n document.addEventListener('focusout', handleFocusOut);\n });\n\n const previouslyFocusedElement = document.activeElement as HTMLElement | null;\n const hasFocusedCandidate = element.nativeElement.contains(previouslyFocusedElement);\n\n if (!hasFocusedCandidate) {\n // we do this to ensure the content is rendered before we try to find the first focusable element\n // and focus it\n afterNextRender(\n {\n write: () => {\n focusFirst();\n\n // if the focus didn't change, focus the container\n if (document.activeElement === previouslyFocusedElement) {\n focus(element.nativeElement);\n }\n },\n },\n { injector },\n );\n }\n }\n\n function teardownFocusTrap(): void {\n focusTrapStack.remove(focusTrap);\n mutationObserver?.disconnect();\n mutationObserver = null;\n focusTrap.deactivate();\n\n // Remove event listeners\n document.removeEventListener('focusin', handleFocusIn);\n document.removeEventListener('focusout', handleFocusOut);\n }\n\n function handleFocusIn(event: FocusEvent): void {\n if (!focusTrap.active || disabled?.()) {\n return;\n }\n\n const target = event.target as HTMLElement | null;\n\n if (element.nativeElement.contains(target)) {\n lastFocusedElement = target;\n } else {\n focus(lastFocusedElement);\n }\n }\n\n /**\n * Handles the `focusout` event.\n */\n function handleFocusOut(event: FocusEvent) {\n if (!focusTrap.active || disabled?.() || event.relatedTarget === null) {\n return;\n }\n\n const relatedTarget = event.relatedTarget as HTMLElement;\n\n if (!element.nativeElement.contains(relatedTarget)) {\n focus(lastFocusedElement);\n }\n }\n\n /**\n * If the focused element gets removed from the DOM, browsers move focus back to the document.body.\n * We move focus to the container to keep focus trapped correctly.\n */\n function handleMutations(mutations: MutationRecord[]): void {\n const focusedElement = document.activeElement as HTMLElement | null;\n\n if (focusedElement !== document.body) {\n return;\n }\n\n for (const mutation of mutations) {\n if (mutation.removedNodes.length > 0) {\n focus(element.nativeElement);\n }\n }\n }\n\n /**\n * Handles the `keydown` event.\n */\n function handleKeyDown(event: KeyboardEvent): void {\n if (!focusTrap.active || disabled?.()) {\n return;\n }\n\n const isTabKey = event.key === 'Tab' && !event.altKey && !event.ctrlKey && !event.metaKey;\n const focusedElement = document.activeElement as HTMLElement | null;\n\n if (isTabKey && focusedElement) {\n const container = event.currentTarget as HTMLElement;\n const [first, last] = getTabbableEdges(container);\n const hasTabbableElementsInside = first && last;\n\n // we can only wrap focus if we have tabbable edges\n if (!hasTabbableElementsInside) {\n if (focusedElement === container) {\n event.preventDefault();\n }\n } else {\n if (!event.shiftKey && focusedElement === last) {\n event.preventDefault();\n focus(first);\n } else if (event.shiftKey && focusedElement === first) {\n event.preventDefault();\n focus(last);\n }\n }\n }\n }\n\n /**\n * Returns the first and last tabbable elements inside a container.\n */\n function getTabbableEdges(container: HTMLElement) {\n const candidates = getTabbableCandidates(container);\n const first = findVisible(candidates);\n const last = findVisible(candidates.reverse());\n return [first, last] as const;\n }\n\n /**\n * Returns a list of potential focusable elements inside a container.\n */\n function getTabbableCandidates(container: HTMLElement) {\n const nodes: HTMLElement[] = [];\n const walker = document.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, {\n acceptNode: (node: HTMLElement) =>\n interactivityChecker.isFocusable(node)\n ? NodeFilter.FILTER_ACCEPT\n : NodeFilter.FILTER_SKIP,\n });\n while (walker.nextNode()) {\n nodes.push(walker.currentNode as HTMLElement);\n }\n return nodes;\n }\n\n /**\n * Returns the first visible element in a list..\n */\n function findVisible(elements: HTMLElement[]) {\n return elements.find(element => interactivityChecker.isVisible(element)) ?? null;\n }\n\n function focus(element: HTMLElement | null): void {\n if (!element) {\n return;\n }\n // Its not great that we are relying on an internal API here, but we need to in order to\n // try and best determine the focus origin when it is programmatically closed by the user.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n focusMonitor.focusVia(element, (focusMonitor as any)._lastFocusOrigin, {\n preventScroll: true,\n });\n }\n\n function focusFirst(): void {\n const previouslyFocusedElement = document.activeElement;\n\n for (const candidate of getTabbableCandidates(element.nativeElement)) {\n focus(candidate);\n\n if (document.activeElement !== previouslyFocusedElement) {\n return;\n }\n }\n }\n\n // Setup the focus trap\n setupFocusTrap();\n\n // Teardown the focus trap on destroy\n onDestroy(teardownFocusTrap);\n\n // Listen to keydown events\n listener(element, 'keydown', handleKeyDown);\n\n // if this is used within an overlay we must disable the focus trap as soon as the overlay is closing\n overlay?.closing.pipe(safeTakeUntilDestroyed()).subscribe(() => focusTrap.deactivate());\n\n return {};\n });\n","import { BooleanInput } from '@angular/cdk/coercion';\nimport { booleanAttribute, Directive, input } from '@angular/core';\nimport { ngpFocusTrap, provideFocusTrapState } from './focus-trap-state';\n\n/**\n * This implementation is based on the Radix UI FocusScope:\n * https://github.com/radix-ui/primitives/blob/main/packages/react/focus-scope/src/FocusScope.tsx#L306\n */\n\n/**\n * The `NgpFocusTrap` directive traps focus within the host element.\n */\n@Directive({\n selector: '[ngpFocusTrap]',\n exportAs: 'ngpFocusTrap',\n providers: [provideFocusTrapState()],\n})\nexport class NgpFocusTrap {\n /**\n * Whether the focus trap is disabled.\n */\n readonly disabled = input<boolean, BooleanInput>(false, {\n alias: 'ngpFocusTrapDisabled',\n transform: booleanAttribute,\n });\n\n /**\n * The focus trap state.\n */\n constructor() {\n ngpFocusTrap({ disabled: this.disabled });\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|