Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit dca8487

Browse files
author
roman.vasilev
committed
feat: Added external focus trap
1 parent 13922d5 commit dca8487

File tree

5 files changed

+14
-81
lines changed

5 files changed

+14
-81
lines changed
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ import { Component } from '@angular/core';
99
(wich match modal with any name, but show ExampleModalComponent 1) but wee need
1010
<br/>
1111
<a [routerLink]="['.', { outlets: { 'lazy_modal': 'mxx'} }]">/ outlets: modal:'mxx' lazy modal</a> <br/>
12-
<a [routerLink]="['./children_lazy_modal_comp']">primary children_lazy_modal_comp</a> <br/>
12+
<a [routerLink]="['./children_lazy_modal_component']">primary children_lazy_modal_comp</a> <br/>
1313
1414
`
1515
})
16-
export class Child1Component {
16+
export class ChildComponent {
1717
}

ng-package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"whitelistedNonPeerDependencies": [
3+
"focus-trap"
34
],
45
"lib": {
56
"entryFile": "./src/index.ts"

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@
3535
"url": "git+https://gitcloud.ert.com/efs/etm/ert-ngx-modal2.git"
3636
},
3737
"homepage": "https://gitcloud.ert.com/efs/etm/ert-ngx-modal2",
38+
"dependencies": {
39+
},
3840
"peerDependencies": {
41+
"focus-trap": "5",
3942
"rxjs": "5.X",
4043
"@angular/core": ">=2.4 <6",
4144
"@angular/common": ">=2.4 <6",

src/modal-library.ts

Lines changed: 0 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,5 @@
11
import { InjectionToken } from '@angular/core';
22

3-
const focusable = [
4-
'a[href]',
5-
'area[href]',
6-
'input:not([disabled])',
7-
'button:not([disabled])',
8-
'select:not([disabled])',
9-
'textarea:not([disabled])',
10-
'iframe',
11-
'object',
12-
'embed',
13-
'*[tabindex]',
14-
'*[contenteditable=true]',
15-
];
16-
17-
export const focusableSelector = focusable.join(',');
18-
193
export const defaultOptions = {
204
popupOpenedClass: 'ngx-modal-popup-opened',
215
isOpenClass: 'ngx-modal-open',
@@ -68,41 +52,3 @@ export const defaultOptions = {
6852
export type ModalOptions = typeof defaultOptions;
6953

7054
export const OPTIONS = new InjectionToken<ModalOptions>('ModalOptions');
71-
72-
export function createFocusManager(container: HTMLElement, newTarget: Node) {
73-
const elements = [...container.querySelectorAll(focusableSelector) as any];
74-
const isFocusInFirst = (): boolean => {
75-
const [element] = elements;
76-
return element && newTarget === element;
77-
};
78-
const isFocusInLast = (): boolean => {
79-
const [element] = elements.slice(-1);
80-
return element && newTarget === element;
81-
};
82-
const isFocusOutside = (): boolean => {
83-
return !container.contains(newTarget);
84-
};
85-
const focusFirst = () => {
86-
if (elements.length > 0) {
87-
const [element] = elements;
88-
element && element.focus && element.focus();
89-
return true;
90-
}
91-
return false;
92-
};
93-
const focusLast = (): boolean => {
94-
if (elements.length > 0) {
95-
const [element] = elements.slice(-1);
96-
element && element.focus && element.focus();
97-
return true;
98-
}
99-
return false;
100-
};
101-
return {
102-
isFocusInFirst,
103-
isFocusInLast,
104-
isFocusOutside,
105-
focusFirst,
106-
focusLast,
107-
};
108-
}

src/modal.component.ts

Lines changed: 8 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { Input, Output, Component, ElementRef, EventEmitter, Inject, OnDestroy, ViewChild, Renderer2, ContentChild, OnInit, HostListener } from '@angular/core';
2-
import { createFocusManager, OPTIONS, ModalOptions } from './modal-library';
2+
import { OPTIONS, ModalOptions } from './modal-library';
33
import { ModalHeaderComponent } from './modal-header.component';
44
import { Subscription } from 'rxjs/Subscription';
55
import { Location } from '@angular/common';
66
import { ActivatedRoute, Router, PRIMARY_OUTLET, UrlSegment, NavigationExtras } from '@angular/router';
77
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
8+
import focusTrap from 'focus-trap';
89

910
@Component({
1011
exportAs: 'modal',
@@ -27,6 +28,7 @@ export class ModalComponent implements OnDestroy, OnInit {
2728
@ViewChild('body') private readonly body: ElementRef;
2829
@ContentChild(ModalHeaderComponent) private readonly header: ModalHeaderComponent;
2930
private closeSubscription: Subscription;
31+
private focusTrap: ReturnType<typeof focusTrap>;
3032

3133
constructor(
3234
@Inject(OPTIONS) private readonly modalOptions: ModalOptions,
@@ -82,8 +84,6 @@ export class ModalComponent implements OnDestroy, OnInit {
8284
case 'Escape':
8385
this.close(e);
8486
break;
85-
case 'Tab':
86-
this.onTabKeyDown(e);
8787
}
8888
}
8989

@@ -94,28 +94,6 @@ export class ModalComponent implements OnDestroy, OnInit {
9494
};
9595
}
9696

97-
private onTabKeyDown(e: KeyboardEvent) {
98-
if (!this.isOpen) {
99-
return;
100-
}
101-
let focusChanged = false;
102-
const fm = createFocusManager(this.body.nativeElement, e.target as Node);
103-
if (e.shiftKey) {
104-
if (fm.isFocusOutside() || fm.isFocusInFirst()) {
105-
focusChanged = fm.focusLast();
106-
}
107-
} else {
108-
if (fm.isFocusOutside() || fm.isFocusInLast()) {
109-
focusChanged = fm.focusFirst();
110-
// focusChanged = true;
111-
}
112-
}
113-
if (focusChanged) {
114-
e.preventDefault();
115-
e.stopPropagation();
116-
}
117-
}
118-
11997
private doOnOpen() {
12098
if (this.header) {
12199
this.closeSubscription = this.header.closeEventEmitter.subscribe((e: Event) => {
@@ -126,6 +104,8 @@ export class ModalComponent implements OnDestroy, OnInit {
126104
const element = this.body.nativeElement;
127105
if (element && typeof element.focus === 'function') {
128106
element.focus();
107+
this.focusTrap = focusTrap(element);
108+
this.focusTrap.activate();
129109
}
130110
});
131111
this.preventBackgroundScrolling();
@@ -138,6 +118,9 @@ export class ModalComponent implements OnDestroy, OnInit {
138118
if (this.closeSubscription) {
139119
this.closeSubscription.unsubscribe();
140120
}
121+
if (this.focusTrap) {
122+
this.focusTrap.deactivate();
123+
}
141124
}
142125

143126
private enableBackgroundScrolling() {

0 commit comments

Comments
 (0)