@angular/cdk 21.0.0-next.9 → 21.0.0-rc.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/_adev_assets/cdk_drag_drop.json +13 -12
- package/_adev_assets/cdk_testing.json +9 -9
- package/_adev_assets/cdk_testing_protractor.json +1 -1
- package/_adev_assets/cdk_testing_selenium_webdriver.json +1 -1
- package/_adev_assets/cdk_testing_testbed.json +1 -1
- package/fesm2022/_a11y-module-chunk.mjs +755 -869
- package/fesm2022/_a11y-module-chunk.mjs.map +1 -1
- package/fesm2022/_activedescendant-key-manager-chunk.mjs +8 -8
- package/fesm2022/_activedescendant-key-manager-chunk.mjs.map +1 -1
- package/fesm2022/_array-chunk.mjs +1 -1
- package/fesm2022/_array-chunk.mjs.map +1 -1
- package/fesm2022/_breakpoints-observer-chunk.mjs +149 -152
- package/fesm2022/_breakpoints-observer-chunk.mjs.map +1 -1
- package/fesm2022/_css-pixel-value-chunk.mjs +4 -5
- package/fesm2022/_css-pixel-value-chunk.mjs.map +1 -1
- package/fesm2022/_data-source-chunk.mjs +2 -8
- package/fesm2022/_data-source-chunk.mjs.map +1 -1
- package/fesm2022/_directionality-chunk.mjs +54 -54
- package/fesm2022/_directionality-chunk.mjs.map +1 -1
- package/fesm2022/_dispose-view-repeater-strategy-chunk.mjs +25 -36
- package/fesm2022/_dispose-view-repeater-strategy-chunk.mjs.map +1 -1
- package/fesm2022/_element-chunk.mjs +6 -17
- package/fesm2022/_element-chunk.mjs.map +1 -1
- package/fesm2022/_fake-event-detection-chunk.mjs +3 -17
- package/fesm2022/_fake-event-detection-chunk.mjs.map +1 -1
- package/fesm2022/_focus-key-manager-chunk.mjs +10 -14
- package/fesm2022/_focus-key-manager-chunk.mjs.map +1 -1
- package/fesm2022/_focus-monitor-chunk.mjs +376 -566
- package/fesm2022/_focus-monitor-chunk.mjs.map +1 -1
- package/fesm2022/_id-generator-chunk.mjs +36 -27
- package/fesm2022/_id-generator-chunk.mjs.map +1 -1
- package/fesm2022/_keycodes-chunk.mjs +9 -9
- package/fesm2022/_keycodes-chunk.mjs.map +1 -1
- package/fesm2022/_list-key-manager-chunk.mjs +248 -336
- package/fesm2022/_list-key-manager-chunk.mjs.map +1 -1
- package/fesm2022/_overlay-module-chunk.mjs +2534 -2948
- package/fesm2022/_overlay-module-chunk.mjs.map +1 -1
- package/fesm2022/_passive-listeners-chunk.mjs +10 -22
- package/fesm2022/_passive-listeners-chunk.mjs.map +1 -1
- package/fesm2022/_platform-chunk.mjs +42 -65
- package/fesm2022/_platform-chunk.mjs.map +1 -1
- package/fesm2022/_recycle-view-repeater-strategy-chunk.mjs +78 -134
- package/fesm2022/_recycle-view-repeater-strategy-chunk.mjs.map +1 -1
- package/fesm2022/_scrolling-chunk.mjs +44 -85
- package/fesm2022/_scrolling-chunk.mjs.map +1 -1
- package/fesm2022/_selection-model-chunk.mjs +138 -209
- package/fesm2022/_selection-model-chunk.mjs.map +1 -1
- package/fesm2022/_shadow-dom-chunk.mjs +21 -35
- package/fesm2022/_shadow-dom-chunk.mjs.map +1 -1
- package/fesm2022/_style-loader-chunk.mjs +50 -37
- package/fesm2022/_style-loader-chunk.mjs.map +1 -1
- package/fesm2022/_test-environment-chunk.mjs +2 -14
- package/fesm2022/_test-environment-chunk.mjs.map +1 -1
- package/fesm2022/_tree-key-manager-chunk.mjs +229 -308
- package/fesm2022/_tree-key-manager-chunk.mjs.map +1 -1
- package/fesm2022/_typeahead-chunk.mjs +52 -74
- package/fesm2022/_typeahead-chunk.mjs.map +1 -1
- package/fesm2022/_unique-selection-dispatcher-chunk.mjs +43 -40
- package/fesm2022/_unique-selection-dispatcher-chunk.mjs.map +1 -1
- package/fesm2022/a11y.mjs +351 -449
- package/fesm2022/a11y.mjs.map +1 -1
- package/fesm2022/accordion.mjs +254 -192
- package/fesm2022/accordion.mjs.map +1 -1
- package/fesm2022/bidi.mjs +121 -64
- package/fesm2022/bidi.mjs.map +1 -1
- package/fesm2022/cdk.mjs +1 -2
- package/fesm2022/cdk.mjs.map +1 -1
- package/fesm2022/clipboard.mjs +208 -186
- package/fesm2022/clipboard.mjs.map +1 -1
- package/fesm2022/coercion-private.mjs +4 -8
- package/fesm2022/coercion-private.mjs.map +1 -1
- package/fesm2022/coercion.mjs +11 -29
- package/fesm2022/coercion.mjs.map +1 -1
- package/fesm2022/dialog.mjs +660 -808
- package/fesm2022/dialog.mjs.map +1 -1
- package/fesm2022/drag-drop.mjs +3347 -4286
- package/fesm2022/drag-drop.mjs.map +1 -1
- package/fesm2022/keycodes.mjs +4 -8
- package/fesm2022/keycodes.mjs.map +1 -1
- package/fesm2022/layout.mjs +44 -26
- package/fesm2022/layout.mjs.map +1 -1
- package/fesm2022/listbox.mjs +841 -895
- package/fesm2022/listbox.mjs.map +1 -1
- package/fesm2022/menu.mjs +1942 -1858
- package/fesm2022/menu.mjs.map +1 -1
- package/fesm2022/observers-private.mjs +88 -106
- package/fesm2022/observers-private.mjs.map +1 -1
- package/fesm2022/observers.mjs +262 -184
- package/fesm2022/observers.mjs.map +1 -1
- package/fesm2022/overlay.mjs +72 -68
- package/fesm2022/overlay.mjs.map +1 -1
- package/fesm2022/platform.mjs +43 -54
- package/fesm2022/platform.mjs.map +1 -1
- package/fesm2022/portal.mjs +402 -560
- package/fesm2022/portal.mjs.map +1 -1
- package/fesm2022/private.mjs +38 -10
- package/fesm2022/private.mjs.map +1 -1
- package/fesm2022/scrolling.mjs +1323 -1400
- package/fesm2022/scrolling.mjs.map +1 -1
- package/fesm2022/stepper.mjs +758 -590
- package/fesm2022/stepper.mjs.map +1 -1
- package/fesm2022/table.mjs +2327 -2319
- package/fesm2022/table.mjs.map +1 -1
- package/fesm2022/testing-selenium-webdriver.mjs +252 -325
- package/fesm2022/testing-selenium-webdriver.mjs.map +1 -1
- package/fesm2022/testing-testbed.mjs +592 -709
- package/fesm2022/testing-testbed.mjs.map +1 -1
- package/fesm2022/testing.mjs +368 -889
- package/fesm2022/testing.mjs.map +1 -1
- package/fesm2022/text-field.mjs +459 -388
- package/fesm2022/text-field.mjs.map +1 -1
- package/fesm2022/tree.mjs +1483 -1731
- package/fesm2022/tree.mjs.map +1 -1
- package/overlay/_index.scss +28 -0
- package/overlay-prebuilt.css +1 -1
- package/package.json +1 -1
- package/schematics/ng-add/index.js +1 -1
- package/types/_overlay-module-chunk.d.ts +59 -7
- package/types/_portal-directives-chunk.d.ts +2 -18
- package/types/accordion.d.ts +3 -1
- package/types/dialog.d.ts +1 -1
- package/types/overlay.d.ts +6 -2
- package/types/portal.d.ts +1 -1
|
@@ -8,936 +8,822 @@ import { _VisuallyHiddenLoader } from './private.mjs';
|
|
|
8
8
|
import { BreakpointObserver } from './_breakpoints-observer-chunk.mjs';
|
|
9
9
|
import { ContentObserver, ObserversModule } from './observers.mjs';
|
|
10
10
|
|
|
11
|
-
/**
|
|
12
|
-
* Configuration for the isFocusable method.
|
|
13
|
-
*/
|
|
14
11
|
class IsFocusableConfig {
|
|
15
|
-
|
|
16
|
-
* Whether to count an element as focusable even if it is not currently visible.
|
|
17
|
-
*/
|
|
18
|
-
ignoreVisibility = false;
|
|
12
|
+
ignoreVisibility = false;
|
|
19
13
|
}
|
|
20
|
-
// The InteractivityChecker leans heavily on the ally.js accessibility utilities.
|
|
21
|
-
// Methods like `isTabbable` are only covering specific edge-cases for the browsers which are
|
|
22
|
-
// supported.
|
|
23
|
-
/**
|
|
24
|
-
* Utility for checking the interactivity of an element, such as whether it is focusable or
|
|
25
|
-
* tabbable.
|
|
26
|
-
*/
|
|
27
14
|
class InteractivityChecker {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
* @param element Element to be checked.
|
|
57
|
-
* @returns Whether the element is tabbable.
|
|
58
|
-
*/
|
|
59
|
-
isTabbable(element) {
|
|
60
|
-
// Nothing is tabbable on the server 😎
|
|
61
|
-
if (!this._platform.isBrowser) {
|
|
62
|
-
return false;
|
|
63
|
-
}
|
|
64
|
-
const frameElement = getFrameElement(getWindow(element));
|
|
65
|
-
if (frameElement) {
|
|
66
|
-
// Frame elements inherit their tabindex onto all child elements.
|
|
67
|
-
if (getTabIndexValue(frameElement) === -1) {
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
// Browsers disable tabbing to an element inside of an invisible frame.
|
|
71
|
-
if (!this.isVisible(frameElement)) {
|
|
72
|
-
return false;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
let nodeName = element.nodeName.toLowerCase();
|
|
76
|
-
let tabIndexValue = getTabIndexValue(element);
|
|
77
|
-
if (element.hasAttribute('contenteditable')) {
|
|
78
|
-
return tabIndexValue !== -1;
|
|
79
|
-
}
|
|
80
|
-
if (nodeName === 'iframe' || nodeName === 'object') {
|
|
81
|
-
// The frame or object's content may be tabbable depending on the content, but it's
|
|
82
|
-
// not possibly to reliably detect the content of the frames. We always consider such
|
|
83
|
-
// elements as non-tabbable.
|
|
84
|
-
return false;
|
|
85
|
-
}
|
|
86
|
-
// In iOS, the browser only considers some specific elements as tabbable.
|
|
87
|
-
if (this._platform.WEBKIT && this._platform.IOS && !isPotentiallyTabbableIOS(element)) {
|
|
88
|
-
return false;
|
|
89
|
-
}
|
|
90
|
-
if (nodeName === 'audio') {
|
|
91
|
-
// Audio elements without controls enabled are never tabbable, regardless
|
|
92
|
-
// of the tabindex attribute explicitly being set.
|
|
93
|
-
if (!element.hasAttribute('controls')) {
|
|
94
|
-
return false;
|
|
95
|
-
}
|
|
96
|
-
// Audio elements with controls are by default tabbable unless the
|
|
97
|
-
// tabindex attribute is set to `-1` explicitly.
|
|
98
|
-
return tabIndexValue !== -1;
|
|
99
|
-
}
|
|
100
|
-
if (nodeName === 'video') {
|
|
101
|
-
// For all video elements, if the tabindex attribute is set to `-1`, the video
|
|
102
|
-
// is not tabbable. Note: We cannot rely on the default `HTMLElement.tabIndex`
|
|
103
|
-
// property as that one is set to `-1` in Chrome, Edge and Safari v13.1. The
|
|
104
|
-
// tabindex attribute is the source of truth here.
|
|
105
|
-
if (tabIndexValue === -1) {
|
|
106
|
-
return false;
|
|
107
|
-
}
|
|
108
|
-
// If the tabindex is explicitly set, and not `-1` (as per check before), the
|
|
109
|
-
// video element is always tabbable (regardless of whether it has controls or not).
|
|
110
|
-
if (tabIndexValue !== null) {
|
|
111
|
-
return true;
|
|
112
|
-
}
|
|
113
|
-
// Otherwise (when no explicit tabindex is set), a video is only tabbable if it
|
|
114
|
-
// has controls enabled. Firefox is special as videos are always tabbable regardless
|
|
115
|
-
// of whether there are controls or not.
|
|
116
|
-
return this._platform.FIREFOX || element.hasAttribute('controls');
|
|
117
|
-
}
|
|
118
|
-
return element.tabIndex >= 0;
|
|
119
|
-
}
|
|
120
|
-
/**
|
|
121
|
-
* Gets whether an element can be focused by the user.
|
|
122
|
-
*
|
|
123
|
-
* @param element Element to be checked.
|
|
124
|
-
* @param config The config object with options to customize this method's behavior
|
|
125
|
-
* @returns Whether the element is focusable.
|
|
126
|
-
*/
|
|
127
|
-
isFocusable(element, config) {
|
|
128
|
-
// Perform checks in order of left to most expensive.
|
|
129
|
-
// Again, naive approach that does not capture many edge cases and browser quirks.
|
|
130
|
-
return (isPotentiallyFocusable(element) &&
|
|
131
|
-
!this.isDisabled(element) &&
|
|
132
|
-
(config?.ignoreVisibility || this.isVisible(element)));
|
|
133
|
-
}
|
|
134
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: InteractivityChecker, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
135
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: InteractivityChecker, providedIn: 'root' });
|
|
136
|
-
}
|
|
137
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: InteractivityChecker, decorators: [{
|
|
138
|
-
type: Injectable,
|
|
139
|
-
args: [{ providedIn: 'root' }]
|
|
140
|
-
}], ctorParameters: () => [] });
|
|
141
|
-
/**
|
|
142
|
-
* Returns the frame element from a window object. Since browsers like MS Edge throw errors if
|
|
143
|
-
* the frameElement property is being accessed from a different host address, this property
|
|
144
|
-
* should be accessed carefully.
|
|
145
|
-
*/
|
|
146
|
-
function getFrameElement(window) {
|
|
147
|
-
try {
|
|
148
|
-
return window.frameElement;
|
|
15
|
+
_platform = inject(Platform);
|
|
16
|
+
constructor() {}
|
|
17
|
+
isDisabled(element) {
|
|
18
|
+
return element.hasAttribute('disabled');
|
|
19
|
+
}
|
|
20
|
+
isVisible(element) {
|
|
21
|
+
return hasGeometry(element) && getComputedStyle(element).visibility === 'visible';
|
|
22
|
+
}
|
|
23
|
+
isTabbable(element) {
|
|
24
|
+
if (!this._platform.isBrowser) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
const frameElement = getFrameElement(getWindow(element));
|
|
28
|
+
if (frameElement) {
|
|
29
|
+
if (getTabIndexValue(frameElement) === -1) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
if (!this.isVisible(frameElement)) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
let nodeName = element.nodeName.toLowerCase();
|
|
37
|
+
let tabIndexValue = getTabIndexValue(element);
|
|
38
|
+
if (element.hasAttribute('contenteditable')) {
|
|
39
|
+
return tabIndexValue !== -1;
|
|
40
|
+
}
|
|
41
|
+
if (nodeName === 'iframe' || nodeName === 'object') {
|
|
42
|
+
return false;
|
|
149
43
|
}
|
|
150
|
-
|
|
151
|
-
|
|
44
|
+
if (this._platform.WEBKIT && this._platform.IOS && !isPotentiallyTabbableIOS(element)) {
|
|
45
|
+
return false;
|
|
152
46
|
}
|
|
47
|
+
if (nodeName === 'audio') {
|
|
48
|
+
if (!element.hasAttribute('controls')) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
return tabIndexValue !== -1;
|
|
52
|
+
}
|
|
53
|
+
if (nodeName === 'video') {
|
|
54
|
+
if (tabIndexValue === -1) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
if (tabIndexValue !== null) {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
return this._platform.FIREFOX || element.hasAttribute('controls');
|
|
61
|
+
}
|
|
62
|
+
return element.tabIndex >= 0;
|
|
63
|
+
}
|
|
64
|
+
isFocusable(element, config) {
|
|
65
|
+
return isPotentiallyFocusable(element) && !this.isDisabled(element) && (config?.ignoreVisibility || this.isVisible(element));
|
|
66
|
+
}
|
|
67
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
68
|
+
minVersion: "12.0.0",
|
|
69
|
+
version: "20.2.0-next.2",
|
|
70
|
+
ngImport: i0,
|
|
71
|
+
type: InteractivityChecker,
|
|
72
|
+
deps: [],
|
|
73
|
+
target: i0.ɵɵFactoryTarget.Injectable
|
|
74
|
+
});
|
|
75
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({
|
|
76
|
+
minVersion: "12.0.0",
|
|
77
|
+
version: "20.2.0-next.2",
|
|
78
|
+
ngImport: i0,
|
|
79
|
+
type: InteractivityChecker,
|
|
80
|
+
providedIn: 'root'
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
84
|
+
minVersion: "12.0.0",
|
|
85
|
+
version: "20.2.0-next.2",
|
|
86
|
+
ngImport: i0,
|
|
87
|
+
type: InteractivityChecker,
|
|
88
|
+
decorators: [{
|
|
89
|
+
type: Injectable,
|
|
90
|
+
args: [{
|
|
91
|
+
providedIn: 'root'
|
|
92
|
+
}]
|
|
93
|
+
}],
|
|
94
|
+
ctorParameters: () => []
|
|
95
|
+
});
|
|
96
|
+
function getFrameElement(window) {
|
|
97
|
+
try {
|
|
98
|
+
return window.frameElement;
|
|
99
|
+
} catch {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
153
102
|
}
|
|
154
|
-
/** Checks whether the specified element has any geometry / rectangles. */
|
|
155
103
|
function hasGeometry(element) {
|
|
156
|
-
|
|
157
|
-
// See https://github.com/jquery/jquery/blob/master/src/css/hiddenVisibleSelectors.js#L12
|
|
158
|
-
return !!(element.offsetWidth ||
|
|
159
|
-
element.offsetHeight ||
|
|
160
|
-
(typeof element.getClientRects === 'function' && element.getClientRects().length));
|
|
104
|
+
return !!(element.offsetWidth || element.offsetHeight || typeof element.getClientRects === 'function' && element.getClientRects().length);
|
|
161
105
|
}
|
|
162
|
-
/** Gets whether an element's */
|
|
163
106
|
function isNativeFormElement(element) {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
nodeName === 'select' ||
|
|
167
|
-
nodeName === 'button' ||
|
|
168
|
-
nodeName === 'textarea');
|
|
107
|
+
let nodeName = element.nodeName.toLowerCase();
|
|
108
|
+
return nodeName === 'input' || nodeName === 'select' || nodeName === 'button' || nodeName === 'textarea';
|
|
169
109
|
}
|
|
170
|
-
/** Gets whether an element is an `<input type="hidden">`. */
|
|
171
110
|
function isHiddenInput(element) {
|
|
172
|
-
|
|
111
|
+
return isInputElement(element) && element.type == 'hidden';
|
|
173
112
|
}
|
|
174
|
-
/** Gets whether an element is an anchor that has an href attribute. */
|
|
175
113
|
function isAnchorWithHref(element) {
|
|
176
|
-
|
|
114
|
+
return isAnchorElement(element) && element.hasAttribute('href');
|
|
177
115
|
}
|
|
178
|
-
/** Gets whether an element is an input element. */
|
|
179
116
|
function isInputElement(element) {
|
|
180
|
-
|
|
117
|
+
return element.nodeName.toLowerCase() == 'input';
|
|
181
118
|
}
|
|
182
|
-
/** Gets whether an element is an anchor element. */
|
|
183
119
|
function isAnchorElement(element) {
|
|
184
|
-
|
|
120
|
+
return element.nodeName.toLowerCase() == 'a';
|
|
185
121
|
}
|
|
186
|
-
/** Gets whether an element has a valid tabindex. */
|
|
187
122
|
function hasValidTabIndex(element) {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
123
|
+
if (!element.hasAttribute('tabindex') || element.tabIndex === undefined) {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
let tabIndex = element.getAttribute('tabindex');
|
|
127
|
+
return !!(tabIndex && !isNaN(parseInt(tabIndex, 10)));
|
|
193
128
|
}
|
|
194
|
-
/**
|
|
195
|
-
* Returns the parsed tabindex from the element attributes instead of returning the
|
|
196
|
-
* evaluated tabindex from the browsers defaults.
|
|
197
|
-
*/
|
|
198
129
|
function getTabIndexValue(element) {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
return isNaN(tabIndex) ? -1 : tabIndex;
|
|
130
|
+
if (!hasValidTabIndex(element)) {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
const tabIndex = parseInt(element.getAttribute('tabindex') || '', 10);
|
|
134
|
+
return isNaN(tabIndex) ? -1 : tabIndex;
|
|
205
135
|
}
|
|
206
|
-
/** Checks whether the specified element is potentially tabbable on iOS */
|
|
207
136
|
function isPotentiallyTabbableIOS(element) {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
inputType === 'password' ||
|
|
212
|
-
nodeName === 'select' ||
|
|
213
|
-
nodeName === 'textarea');
|
|
137
|
+
let nodeName = element.nodeName.toLowerCase();
|
|
138
|
+
let inputType = nodeName === 'input' && element.type;
|
|
139
|
+
return inputType === 'text' || inputType === 'password' || nodeName === 'select' || nodeName === 'textarea';
|
|
214
140
|
}
|
|
215
|
-
/**
|
|
216
|
-
* Gets whether an element is potentially focusable without taking current visible/disabled state
|
|
217
|
-
* into account.
|
|
218
|
-
*/
|
|
219
141
|
function isPotentiallyFocusable(element) {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
return (isNativeFormElement(element) ||
|
|
225
|
-
isAnchorWithHref(element) ||
|
|
226
|
-
element.hasAttribute('contenteditable') ||
|
|
227
|
-
hasValidTabIndex(element));
|
|
142
|
+
if (isHiddenInput(element)) {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
return isNativeFormElement(element) || isAnchorWithHref(element) || element.hasAttribute('contenteditable') || hasValidTabIndex(element);
|
|
228
146
|
}
|
|
229
|
-
/** Gets the parent window of a DOM node with regards of being inside of an iframe. */
|
|
230
147
|
function getWindow(node) {
|
|
231
|
-
|
|
232
|
-
return (node.ownerDocument && node.ownerDocument.defaultView) || window;
|
|
148
|
+
return node.ownerDocument && node.ownerDocument.defaultView || window;
|
|
233
149
|
}
|
|
234
150
|
|
|
235
|
-
/**
|
|
236
|
-
* Class that allows for trapping focus within a DOM element.
|
|
237
|
-
*
|
|
238
|
-
* This class currently uses a relatively simple approach to focus trapping.
|
|
239
|
-
* It assumes that the tab order is the same as DOM order, which is not necessarily true.
|
|
240
|
-
* Things like `tabIndex > 0`, flex `order`, and shadow roots can cause the two to be misaligned.
|
|
241
|
-
*/
|
|
242
151
|
class FocusTrap {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
*/
|
|
429
|
-
focusLastTabbableElement(options) {
|
|
430
|
-
const redirectToElement = this._getRegionBoundary('end');
|
|
431
|
-
if (redirectToElement) {
|
|
432
|
-
redirectToElement.focus(options);
|
|
433
|
-
}
|
|
434
|
-
return !!redirectToElement;
|
|
435
|
-
}
|
|
436
|
-
/**
|
|
437
|
-
* Checks whether the focus trap has successfully been attached.
|
|
438
|
-
*/
|
|
439
|
-
hasAttached() {
|
|
440
|
-
return this._hasAttached;
|
|
441
|
-
}
|
|
442
|
-
/** Get the first tabbable element from a DOM subtree (inclusive). */
|
|
443
|
-
_getFirstTabbableElement(root) {
|
|
444
|
-
if (this._checker.isFocusable(root) && this._checker.isTabbable(root)) {
|
|
445
|
-
return root;
|
|
446
|
-
}
|
|
447
|
-
const children = root.children;
|
|
448
|
-
for (let i = 0; i < children.length; i++) {
|
|
449
|
-
const tabbableChild = children[i].nodeType === this._document.ELEMENT_NODE
|
|
450
|
-
? this._getFirstTabbableElement(children[i])
|
|
451
|
-
: null;
|
|
452
|
-
if (tabbableChild) {
|
|
453
|
-
return tabbableChild;
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
return null;
|
|
457
|
-
}
|
|
458
|
-
/** Get the last tabbable element from a DOM subtree (inclusive). */
|
|
459
|
-
_getLastTabbableElement(root) {
|
|
460
|
-
if (this._checker.isFocusable(root) && this._checker.isTabbable(root)) {
|
|
461
|
-
return root;
|
|
462
|
-
}
|
|
463
|
-
// Iterate in reverse DOM order.
|
|
464
|
-
const children = root.children;
|
|
465
|
-
for (let i = children.length - 1; i >= 0; i--) {
|
|
466
|
-
const tabbableChild = children[i].nodeType === this._document.ELEMENT_NODE
|
|
467
|
-
? this._getLastTabbableElement(children[i])
|
|
468
|
-
: null;
|
|
469
|
-
if (tabbableChild) {
|
|
470
|
-
return tabbableChild;
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
return null;
|
|
474
|
-
}
|
|
475
|
-
/** Creates an anchor element. */
|
|
476
|
-
_createAnchor() {
|
|
477
|
-
const anchor = this._document.createElement('div');
|
|
478
|
-
this._toggleAnchorTabIndex(this._enabled, anchor);
|
|
479
|
-
anchor.classList.add('cdk-visually-hidden');
|
|
480
|
-
anchor.classList.add('cdk-focus-trap-anchor');
|
|
481
|
-
anchor.setAttribute('aria-hidden', 'true');
|
|
482
|
-
return anchor;
|
|
483
|
-
}
|
|
484
|
-
/**
|
|
485
|
-
* Toggles the `tabindex` of an anchor, based on the enabled state of the focus trap.
|
|
486
|
-
* @param isEnabled Whether the focus trap is enabled.
|
|
487
|
-
* @param anchor Anchor on which to toggle the tabindex.
|
|
488
|
-
*/
|
|
489
|
-
_toggleAnchorTabIndex(isEnabled, anchor) {
|
|
490
|
-
// Remove the tabindex completely, rather than setting it to -1, because if the
|
|
491
|
-
// element has a tabindex, the user might still hit it when navigating with the arrow keys.
|
|
492
|
-
isEnabled ? anchor.setAttribute('tabindex', '0') : anchor.removeAttribute('tabindex');
|
|
493
|
-
}
|
|
494
|
-
/**
|
|
495
|
-
* Toggles the`tabindex` of both anchors to either trap Tab focus or allow it to escape.
|
|
496
|
-
* @param enabled: Whether the anchors should trap Tab.
|
|
497
|
-
*/
|
|
498
|
-
toggleAnchors(enabled) {
|
|
499
|
-
if (this._startAnchor && this._endAnchor) {
|
|
500
|
-
this._toggleAnchorTabIndex(enabled, this._startAnchor);
|
|
501
|
-
this._toggleAnchorTabIndex(enabled, this._endAnchor);
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
/** Executes a function when the zone is stable. */
|
|
505
|
-
_executeOnStable(fn) {
|
|
506
|
-
// TODO: remove this conditional when injector is required in the constructor.
|
|
507
|
-
if (this._injector) {
|
|
508
|
-
afterNextRender(fn, { injector: this._injector });
|
|
509
|
-
}
|
|
510
|
-
else {
|
|
511
|
-
setTimeout(fn);
|
|
512
|
-
}
|
|
513
|
-
}
|
|
152
|
+
_element;
|
|
153
|
+
_checker;
|
|
154
|
+
_ngZone;
|
|
155
|
+
_document;
|
|
156
|
+
_injector;
|
|
157
|
+
_startAnchor;
|
|
158
|
+
_endAnchor;
|
|
159
|
+
_hasAttached = false;
|
|
160
|
+
startAnchorListener = () => this.focusLastTabbableElement();
|
|
161
|
+
endAnchorListener = () => this.focusFirstTabbableElement();
|
|
162
|
+
get enabled() {
|
|
163
|
+
return this._enabled;
|
|
164
|
+
}
|
|
165
|
+
set enabled(value) {
|
|
166
|
+
this._enabled = value;
|
|
167
|
+
if (this._startAnchor && this._endAnchor) {
|
|
168
|
+
this._toggleAnchorTabIndex(value, this._startAnchor);
|
|
169
|
+
this._toggleAnchorTabIndex(value, this._endAnchor);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
_enabled = true;
|
|
173
|
+
constructor(_element, _checker, _ngZone, _document, deferAnchors = false, _injector) {
|
|
174
|
+
this._element = _element;
|
|
175
|
+
this._checker = _checker;
|
|
176
|
+
this._ngZone = _ngZone;
|
|
177
|
+
this._document = _document;
|
|
178
|
+
this._injector = _injector;
|
|
179
|
+
if (!deferAnchors) {
|
|
180
|
+
this.attachAnchors();
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
destroy() {
|
|
184
|
+
const startAnchor = this._startAnchor;
|
|
185
|
+
const endAnchor = this._endAnchor;
|
|
186
|
+
if (startAnchor) {
|
|
187
|
+
startAnchor.removeEventListener('focus', this.startAnchorListener);
|
|
188
|
+
startAnchor.remove();
|
|
189
|
+
}
|
|
190
|
+
if (endAnchor) {
|
|
191
|
+
endAnchor.removeEventListener('focus', this.endAnchorListener);
|
|
192
|
+
endAnchor.remove();
|
|
193
|
+
}
|
|
194
|
+
this._startAnchor = this._endAnchor = null;
|
|
195
|
+
this._hasAttached = false;
|
|
196
|
+
}
|
|
197
|
+
attachAnchors() {
|
|
198
|
+
if (this._hasAttached) {
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
this._ngZone.runOutsideAngular(() => {
|
|
202
|
+
if (!this._startAnchor) {
|
|
203
|
+
this._startAnchor = this._createAnchor();
|
|
204
|
+
this._startAnchor.addEventListener('focus', this.startAnchorListener);
|
|
205
|
+
}
|
|
206
|
+
if (!this._endAnchor) {
|
|
207
|
+
this._endAnchor = this._createAnchor();
|
|
208
|
+
this._endAnchor.addEventListener('focus', this.endAnchorListener);
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
if (this._element.parentNode) {
|
|
212
|
+
this._element.parentNode.insertBefore(this._startAnchor, this._element);
|
|
213
|
+
this._element.parentNode.insertBefore(this._endAnchor, this._element.nextSibling);
|
|
214
|
+
this._hasAttached = true;
|
|
215
|
+
}
|
|
216
|
+
return this._hasAttached;
|
|
217
|
+
}
|
|
218
|
+
focusInitialElementWhenReady(options) {
|
|
219
|
+
return new Promise(resolve => {
|
|
220
|
+
this._executeOnStable(() => resolve(this.focusInitialElement(options)));
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
focusFirstTabbableElementWhenReady(options) {
|
|
224
|
+
return new Promise(resolve => {
|
|
225
|
+
this._executeOnStable(() => resolve(this.focusFirstTabbableElement(options)));
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
focusLastTabbableElementWhenReady(options) {
|
|
229
|
+
return new Promise(resolve => {
|
|
230
|
+
this._executeOnStable(() => resolve(this.focusLastTabbableElement(options)));
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
_getRegionBoundary(bound) {
|
|
234
|
+
const markers = this._element.querySelectorAll(`[cdk-focus-region-${bound}], ` + `[cdkFocusRegion${bound}], ` + `[cdk-focus-${bound}]`);
|
|
235
|
+
if (typeof ngDevMode === 'undefined' || ngDevMode) {
|
|
236
|
+
for (let i = 0; i < markers.length; i++) {
|
|
237
|
+
if (markers[i].hasAttribute(`cdk-focus-${bound}`)) {
|
|
238
|
+
console.warn(`Found use of deprecated attribute 'cdk-focus-${bound}', ` + `use 'cdkFocusRegion${bound}' instead. The deprecated ` + `attribute will be removed in 8.0.0.`, markers[i]);
|
|
239
|
+
} else if (markers[i].hasAttribute(`cdk-focus-region-${bound}`)) {
|
|
240
|
+
console.warn(`Found use of deprecated attribute 'cdk-focus-region-${bound}', ` + `use 'cdkFocusRegion${bound}' instead. The deprecated attribute ` + `will be removed in 8.0.0.`, markers[i]);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (bound == 'start') {
|
|
245
|
+
return markers.length ? markers[0] : this._getFirstTabbableElement(this._element);
|
|
246
|
+
}
|
|
247
|
+
return markers.length ? markers[markers.length - 1] : this._getLastTabbableElement(this._element);
|
|
248
|
+
}
|
|
249
|
+
focusInitialElement(options) {
|
|
250
|
+
const redirectToElement = this._element.querySelector(`[cdk-focus-initial], ` + `[cdkFocusInitial]`);
|
|
251
|
+
if (redirectToElement) {
|
|
252
|
+
if ((typeof ngDevMode === 'undefined' || ngDevMode) && redirectToElement.hasAttribute(`cdk-focus-initial`)) {
|
|
253
|
+
console.warn(`Found use of deprecated attribute 'cdk-focus-initial', ` + `use 'cdkFocusInitial' instead. The deprecated attribute ` + `will be removed in 8.0.0`, redirectToElement);
|
|
254
|
+
}
|
|
255
|
+
if ((typeof ngDevMode === 'undefined' || ngDevMode) && !this._checker.isFocusable(redirectToElement)) {
|
|
256
|
+
console.warn(`Element matching '[cdkFocusInitial]' is not focusable.`, redirectToElement);
|
|
257
|
+
}
|
|
258
|
+
if (!this._checker.isFocusable(redirectToElement)) {
|
|
259
|
+
const focusableChild = this._getFirstTabbableElement(redirectToElement);
|
|
260
|
+
focusableChild?.focus(options);
|
|
261
|
+
return !!focusableChild;
|
|
262
|
+
}
|
|
263
|
+
redirectToElement.focus(options);
|
|
264
|
+
return true;
|
|
265
|
+
}
|
|
266
|
+
return this.focusFirstTabbableElement(options);
|
|
267
|
+
}
|
|
268
|
+
focusFirstTabbableElement(options) {
|
|
269
|
+
const redirectToElement = this._getRegionBoundary('start');
|
|
270
|
+
if (redirectToElement) {
|
|
271
|
+
redirectToElement.focus(options);
|
|
272
|
+
}
|
|
273
|
+
return !!redirectToElement;
|
|
274
|
+
}
|
|
275
|
+
focusLastTabbableElement(options) {
|
|
276
|
+
const redirectToElement = this._getRegionBoundary('end');
|
|
277
|
+
if (redirectToElement) {
|
|
278
|
+
redirectToElement.focus(options);
|
|
279
|
+
}
|
|
280
|
+
return !!redirectToElement;
|
|
281
|
+
}
|
|
282
|
+
hasAttached() {
|
|
283
|
+
return this._hasAttached;
|
|
284
|
+
}
|
|
285
|
+
_getFirstTabbableElement(root) {
|
|
286
|
+
if (this._checker.isFocusable(root) && this._checker.isTabbable(root)) {
|
|
287
|
+
return root;
|
|
288
|
+
}
|
|
289
|
+
const children = root.children;
|
|
290
|
+
for (let i = 0; i < children.length; i++) {
|
|
291
|
+
const tabbableChild = children[i].nodeType === this._document.ELEMENT_NODE ? this._getFirstTabbableElement(children[i]) : null;
|
|
292
|
+
if (tabbableChild) {
|
|
293
|
+
return tabbableChild;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
298
|
+
_getLastTabbableElement(root) {
|
|
299
|
+
if (this._checker.isFocusable(root) && this._checker.isTabbable(root)) {
|
|
300
|
+
return root;
|
|
301
|
+
}
|
|
302
|
+
const children = root.children;
|
|
303
|
+
for (let i = children.length - 1; i >= 0; i--) {
|
|
304
|
+
const tabbableChild = children[i].nodeType === this._document.ELEMENT_NODE ? this._getLastTabbableElement(children[i]) : null;
|
|
305
|
+
if (tabbableChild) {
|
|
306
|
+
return tabbableChild;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
_createAnchor() {
|
|
312
|
+
const anchor = this._document.createElement('div');
|
|
313
|
+
this._toggleAnchorTabIndex(this._enabled, anchor);
|
|
314
|
+
anchor.classList.add('cdk-visually-hidden');
|
|
315
|
+
anchor.classList.add('cdk-focus-trap-anchor');
|
|
316
|
+
anchor.setAttribute('aria-hidden', 'true');
|
|
317
|
+
return anchor;
|
|
318
|
+
}
|
|
319
|
+
_toggleAnchorTabIndex(isEnabled, anchor) {
|
|
320
|
+
isEnabled ? anchor.setAttribute('tabindex', '0') : anchor.removeAttribute('tabindex');
|
|
321
|
+
}
|
|
322
|
+
toggleAnchors(enabled) {
|
|
323
|
+
if (this._startAnchor && this._endAnchor) {
|
|
324
|
+
this._toggleAnchorTabIndex(enabled, this._startAnchor);
|
|
325
|
+
this._toggleAnchorTabIndex(enabled, this._endAnchor);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
_executeOnStable(fn) {
|
|
329
|
+
if (this._injector) {
|
|
330
|
+
afterNextRender(fn, {
|
|
331
|
+
injector: this._injector
|
|
332
|
+
});
|
|
333
|
+
} else {
|
|
334
|
+
setTimeout(fn);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
514
337
|
}
|
|
515
|
-
/**
|
|
516
|
-
* Factory that allows easy instantiation of focus traps.
|
|
517
|
-
*/
|
|
518
338
|
class FocusTrapFactory {
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
339
|
+
_checker = inject(InteractivityChecker);
|
|
340
|
+
_ngZone = inject(NgZone);
|
|
341
|
+
_document = inject(DOCUMENT);
|
|
342
|
+
_injector = inject(Injector);
|
|
343
|
+
constructor() {
|
|
344
|
+
inject(_CdkPrivateStyleLoader).load(_VisuallyHiddenLoader);
|
|
345
|
+
}
|
|
346
|
+
create(element, deferCaptureElements = false) {
|
|
347
|
+
return new FocusTrap(element, this._checker, this._ngZone, this._document, deferCaptureElements, this._injector);
|
|
348
|
+
}
|
|
349
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
350
|
+
minVersion: "12.0.0",
|
|
351
|
+
version: "20.2.0-next.2",
|
|
352
|
+
ngImport: i0,
|
|
353
|
+
type: FocusTrapFactory,
|
|
354
|
+
deps: [],
|
|
355
|
+
target: i0.ɵɵFactoryTarget.Injectable
|
|
356
|
+
});
|
|
357
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({
|
|
358
|
+
minVersion: "12.0.0",
|
|
359
|
+
version: "20.2.0-next.2",
|
|
360
|
+
ngImport: i0,
|
|
361
|
+
type: FocusTrapFactory,
|
|
362
|
+
providedIn: 'root'
|
|
363
|
+
});
|
|
538
364
|
}
|
|
539
|
-
i0.ɵɵngDeclareClassMetadata({
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
365
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
366
|
+
minVersion: "12.0.0",
|
|
367
|
+
version: "20.2.0-next.2",
|
|
368
|
+
ngImport: i0,
|
|
369
|
+
type: FocusTrapFactory,
|
|
370
|
+
decorators: [{
|
|
371
|
+
type: Injectable,
|
|
372
|
+
args: [{
|
|
373
|
+
providedIn: 'root'
|
|
374
|
+
}]
|
|
375
|
+
}],
|
|
376
|
+
ctorParameters: () => []
|
|
377
|
+
});
|
|
544
378
|
class CdkTrapFocus {
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
379
|
+
_elementRef = inject(ElementRef);
|
|
380
|
+
_focusTrapFactory = inject(FocusTrapFactory);
|
|
381
|
+
focusTrap;
|
|
382
|
+
_previouslyFocusedElement = null;
|
|
383
|
+
get enabled() {
|
|
384
|
+
return this.focusTrap?.enabled || false;
|
|
385
|
+
}
|
|
386
|
+
set enabled(value) {
|
|
387
|
+
if (this.focusTrap) {
|
|
388
|
+
this.focusTrap.enabled = value;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
autoCapture;
|
|
392
|
+
constructor() {
|
|
393
|
+
const platform = inject(Platform);
|
|
394
|
+
if (platform.isBrowser) {
|
|
395
|
+
this.focusTrap = this._focusTrapFactory.create(this._elementRef.nativeElement, true);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
ngOnDestroy() {
|
|
399
|
+
this.focusTrap?.destroy();
|
|
400
|
+
if (this._previouslyFocusedElement) {
|
|
401
|
+
this._previouslyFocusedElement.focus();
|
|
402
|
+
this._previouslyFocusedElement = null;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
ngAfterContentInit() {
|
|
406
|
+
this.focusTrap?.attachAnchors();
|
|
407
|
+
if (this.autoCapture) {
|
|
408
|
+
this._captureFocus();
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
ngDoCheck() {
|
|
412
|
+
if (this.focusTrap && !this.focusTrap.hasAttached()) {
|
|
413
|
+
this.focusTrap.attachAnchors();
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
ngOnChanges(changes) {
|
|
417
|
+
const autoCaptureChange = changes['autoCapture'];
|
|
418
|
+
if (autoCaptureChange && !autoCaptureChange.firstChange && this.autoCapture && this.focusTrap?.hasAttached()) {
|
|
419
|
+
this._captureFocus();
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
_captureFocus() {
|
|
423
|
+
this._previouslyFocusedElement = _getFocusedElementPierceShadowDom();
|
|
424
|
+
this.focusTrap?.focusInitialElementWhenReady();
|
|
425
|
+
}
|
|
426
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
427
|
+
minVersion: "12.0.0",
|
|
428
|
+
version: "20.2.0-next.2",
|
|
429
|
+
ngImport: i0,
|
|
430
|
+
type: CdkTrapFocus,
|
|
431
|
+
deps: [],
|
|
432
|
+
target: i0.ɵɵFactoryTarget.Directive
|
|
433
|
+
});
|
|
434
|
+
static ɵdir = i0.ɵɵngDeclareDirective({
|
|
435
|
+
minVersion: "16.1.0",
|
|
436
|
+
version: "20.2.0-next.2",
|
|
437
|
+
type: CdkTrapFocus,
|
|
438
|
+
isStandalone: true,
|
|
439
|
+
selector: "[cdkTrapFocus]",
|
|
440
|
+
inputs: {
|
|
441
|
+
enabled: ["cdkTrapFocus", "enabled", booleanAttribute],
|
|
442
|
+
autoCapture: ["cdkTrapFocusAutoCapture", "autoCapture", booleanAttribute]
|
|
443
|
+
},
|
|
444
|
+
exportAs: ["cdkTrapFocus"],
|
|
445
|
+
usesOnChanges: true,
|
|
446
|
+
ngImport: i0
|
|
447
|
+
});
|
|
606
448
|
}
|
|
607
|
-
i0.ɵɵngDeclareClassMetadata({
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
449
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
450
|
+
minVersion: "12.0.0",
|
|
451
|
+
version: "20.2.0-next.2",
|
|
452
|
+
ngImport: i0,
|
|
453
|
+
type: CdkTrapFocus,
|
|
454
|
+
decorators: [{
|
|
455
|
+
type: Directive,
|
|
456
|
+
args: [{
|
|
457
|
+
selector: '[cdkTrapFocus]',
|
|
458
|
+
exportAs: 'cdkTrapFocus'
|
|
459
|
+
}]
|
|
460
|
+
}],
|
|
461
|
+
ctorParameters: () => [],
|
|
462
|
+
propDecorators: {
|
|
463
|
+
enabled: [{
|
|
464
|
+
type: Input,
|
|
465
|
+
args: [{
|
|
466
|
+
alias: 'cdkTrapFocus',
|
|
467
|
+
transform: booleanAttribute
|
|
468
|
+
}]
|
|
469
|
+
}],
|
|
470
|
+
autoCapture: [{
|
|
471
|
+
type: Input,
|
|
472
|
+
args: [{
|
|
473
|
+
alias: 'cdkTrapFocusAutoCapture',
|
|
474
|
+
transform: booleanAttribute
|
|
475
|
+
}]
|
|
476
|
+
}]
|
|
477
|
+
}
|
|
478
|
+
});
|
|
620
479
|
|
|
621
480
|
const LIVE_ANNOUNCER_ELEMENT_TOKEN = new InjectionToken('liveAnnouncerElement', {
|
|
622
|
-
|
|
623
|
-
|
|
481
|
+
providedIn: 'root',
|
|
482
|
+
factory: () => null
|
|
624
483
|
});
|
|
625
|
-
/** Injection token that can be used to configure the default options for the LiveAnnouncer. */
|
|
626
484
|
const LIVE_ANNOUNCER_DEFAULT_OPTIONS = new InjectionToken('LIVE_ANNOUNCER_DEFAULT_OPTIONS');
|
|
627
485
|
|
|
628
486
|
let uniqueIds = 0;
|
|
629
487
|
class LiveAnnouncer {
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
488
|
+
_ngZone = inject(NgZone);
|
|
489
|
+
_defaultOptions = inject(LIVE_ANNOUNCER_DEFAULT_OPTIONS, {
|
|
490
|
+
optional: true
|
|
491
|
+
});
|
|
492
|
+
_liveElement;
|
|
493
|
+
_document = inject(DOCUMENT);
|
|
494
|
+
_previousTimeout;
|
|
495
|
+
_currentPromise;
|
|
496
|
+
_currentResolve;
|
|
497
|
+
constructor() {
|
|
498
|
+
const elementToken = inject(LIVE_ANNOUNCER_ELEMENT_TOKEN, {
|
|
499
|
+
optional: true
|
|
633
500
|
});
|
|
634
|
-
_liveElement;
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
501
|
+
this._liveElement = elementToken || this._createLiveElement();
|
|
502
|
+
}
|
|
503
|
+
announce(message, ...args) {
|
|
504
|
+
const defaultOptions = this._defaultOptions;
|
|
505
|
+
let politeness;
|
|
506
|
+
let duration;
|
|
507
|
+
if (args.length === 1 && typeof args[0] === 'number') {
|
|
508
|
+
duration = args[0];
|
|
509
|
+
} else {
|
|
510
|
+
[politeness, duration] = args;
|
|
511
|
+
}
|
|
512
|
+
this.clear();
|
|
513
|
+
clearTimeout(this._previousTimeout);
|
|
514
|
+
if (!politeness) {
|
|
515
|
+
politeness = defaultOptions && defaultOptions.politeness ? defaultOptions.politeness : 'polite';
|
|
516
|
+
}
|
|
517
|
+
if (duration == null && defaultOptions) {
|
|
518
|
+
duration = defaultOptions.duration;
|
|
519
|
+
}
|
|
520
|
+
this._liveElement.setAttribute('aria-live', politeness);
|
|
521
|
+
if (this._liveElement.id) {
|
|
522
|
+
this._exposeAnnouncerToModals(this._liveElement.id);
|
|
523
|
+
}
|
|
524
|
+
return this._ngZone.runOutsideAngular(() => {
|
|
525
|
+
if (!this._currentPromise) {
|
|
526
|
+
this._currentPromise = new Promise(resolve => this._currentResolve = resolve);
|
|
527
|
+
}
|
|
528
|
+
clearTimeout(this._previousTimeout);
|
|
529
|
+
this._previousTimeout = setTimeout(() => {
|
|
530
|
+
this._liveElement.textContent = message;
|
|
531
|
+
if (typeof duration === 'number') {
|
|
532
|
+
this._previousTimeout = setTimeout(() => this.clear(), duration);
|
|
658
533
|
}
|
|
659
|
-
if (duration == null && defaultOptions) {
|
|
660
|
-
duration = defaultOptions.duration;
|
|
661
|
-
}
|
|
662
|
-
// TODO: ensure changing the politeness works on all environments we support.
|
|
663
|
-
this._liveElement.setAttribute('aria-live', politeness);
|
|
664
|
-
if (this._liveElement.id) {
|
|
665
|
-
this._exposeAnnouncerToModals(this._liveElement.id);
|
|
666
|
-
}
|
|
667
|
-
// This 100ms timeout is necessary for some browser + screen-reader combinations:
|
|
668
|
-
// - Both JAWS and NVDA over IE11 will not announce anything without a non-zero timeout.
|
|
669
|
-
// - With Chrome and IE11 with NVDA or JAWS, a repeated (identical) message won't be read a
|
|
670
|
-
// second time without clearing and then using a non-zero delay.
|
|
671
|
-
// (using JAWS 17 at time of this writing).
|
|
672
|
-
return this._ngZone.runOutsideAngular(() => {
|
|
673
|
-
if (!this._currentPromise) {
|
|
674
|
-
this._currentPromise = new Promise(resolve => (this._currentResolve = resolve));
|
|
675
|
-
}
|
|
676
|
-
clearTimeout(this._previousTimeout);
|
|
677
|
-
this._previousTimeout = setTimeout(() => {
|
|
678
|
-
this._liveElement.textContent = message;
|
|
679
|
-
if (typeof duration === 'number') {
|
|
680
|
-
this._previousTimeout = setTimeout(() => this.clear(), duration);
|
|
681
|
-
}
|
|
682
|
-
// For some reason in tests this can be undefined
|
|
683
|
-
// Probably related to ZoneJS and every other thing that patches browser APIs in tests
|
|
684
|
-
this._currentResolve?.();
|
|
685
|
-
this._currentPromise = this._currentResolve = undefined;
|
|
686
|
-
}, 100);
|
|
687
|
-
return this._currentPromise;
|
|
688
|
-
});
|
|
689
|
-
}
|
|
690
|
-
/**
|
|
691
|
-
* Clears the current text from the announcer element. Can be used to prevent
|
|
692
|
-
* screen readers from reading the text out again while the user is going
|
|
693
|
-
* through the page landmarks.
|
|
694
|
-
*/
|
|
695
|
-
clear() {
|
|
696
|
-
if (this._liveElement) {
|
|
697
|
-
this._liveElement.textContent = '';
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
ngOnDestroy() {
|
|
701
|
-
clearTimeout(this._previousTimeout);
|
|
702
|
-
this._liveElement?.remove();
|
|
703
|
-
this._liveElement = null;
|
|
704
534
|
this._currentResolve?.();
|
|
705
535
|
this._currentPromise = this._currentResolve = undefined;
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
536
|
+
}, 100);
|
|
537
|
+
return this._currentPromise;
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
clear() {
|
|
541
|
+
if (this._liveElement) {
|
|
542
|
+
this._liveElement.textContent = '';
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
ngOnDestroy() {
|
|
546
|
+
clearTimeout(this._previousTimeout);
|
|
547
|
+
this._liveElement?.remove();
|
|
548
|
+
this._liveElement = null;
|
|
549
|
+
this._currentResolve?.();
|
|
550
|
+
this._currentPromise = this._currentResolve = undefined;
|
|
551
|
+
}
|
|
552
|
+
_createLiveElement() {
|
|
553
|
+
const elementClass = 'cdk-live-announcer-element';
|
|
554
|
+
const previousElements = this._document.getElementsByClassName(elementClass);
|
|
555
|
+
const liveEl = this._document.createElement('div');
|
|
556
|
+
for (let i = 0; i < previousElements.length; i++) {
|
|
557
|
+
previousElements[i].remove();
|
|
558
|
+
}
|
|
559
|
+
liveEl.classList.add(elementClass);
|
|
560
|
+
liveEl.classList.add('cdk-visually-hidden');
|
|
561
|
+
liveEl.setAttribute('aria-atomic', 'true');
|
|
562
|
+
liveEl.setAttribute('aria-live', 'polite');
|
|
563
|
+
liveEl.id = `cdk-live-announcer-${uniqueIds++}`;
|
|
564
|
+
this._document.body.appendChild(liveEl);
|
|
565
|
+
return liveEl;
|
|
566
|
+
}
|
|
567
|
+
_exposeAnnouncerToModals(id) {
|
|
568
|
+
const modals = this._document.querySelectorAll('body > .cdk-overlay-container [aria-modal="true"]');
|
|
569
|
+
for (let i = 0; i < modals.length; i++) {
|
|
570
|
+
const modal = modals[i];
|
|
571
|
+
const ariaOwns = modal.getAttribute('aria-owns');
|
|
572
|
+
if (!ariaOwns) {
|
|
573
|
+
modal.setAttribute('aria-owns', id);
|
|
574
|
+
} else if (ariaOwns.indexOf(id) === -1) {
|
|
575
|
+
modal.setAttribute('aria-owns', ariaOwns + ' ' + id);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
580
|
+
minVersion: "12.0.0",
|
|
581
|
+
version: "20.2.0-next.2",
|
|
582
|
+
ngImport: i0,
|
|
583
|
+
type: LiveAnnouncer,
|
|
584
|
+
deps: [],
|
|
585
|
+
target: i0.ɵɵFactoryTarget.Injectable
|
|
586
|
+
});
|
|
587
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({
|
|
588
|
+
minVersion: "12.0.0",
|
|
589
|
+
version: "20.2.0-next.2",
|
|
590
|
+
ngImport: i0,
|
|
591
|
+
type: LiveAnnouncer,
|
|
592
|
+
providedIn: 'root'
|
|
593
|
+
});
|
|
749
594
|
}
|
|
750
|
-
i0.ɵɵngDeclareClassMetadata({
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
595
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
596
|
+
minVersion: "12.0.0",
|
|
597
|
+
version: "20.2.0-next.2",
|
|
598
|
+
ngImport: i0,
|
|
599
|
+
type: LiveAnnouncer,
|
|
600
|
+
decorators: [{
|
|
601
|
+
type: Injectable,
|
|
602
|
+
args: [{
|
|
603
|
+
providedIn: 'root'
|
|
604
|
+
}]
|
|
605
|
+
}],
|
|
606
|
+
ctorParameters: () => []
|
|
607
|
+
});
|
|
758
608
|
class CdkAriaLive {
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
609
|
+
_elementRef = inject(ElementRef);
|
|
610
|
+
_liveAnnouncer = inject(LiveAnnouncer);
|
|
611
|
+
_contentObserver = inject(ContentObserver);
|
|
612
|
+
_ngZone = inject(NgZone);
|
|
613
|
+
get politeness() {
|
|
614
|
+
return this._politeness;
|
|
615
|
+
}
|
|
616
|
+
set politeness(value) {
|
|
617
|
+
this._politeness = value === 'off' || value === 'assertive' ? value : 'polite';
|
|
618
|
+
if (this._politeness === 'off') {
|
|
619
|
+
if (this._subscription) {
|
|
620
|
+
this._subscription.unsubscribe();
|
|
621
|
+
this._subscription = null;
|
|
622
|
+
}
|
|
623
|
+
} else if (!this._subscription) {
|
|
624
|
+
this._subscription = this._ngZone.runOutsideAngular(() => {
|
|
625
|
+
return this._contentObserver.observe(this._elementRef).subscribe(() => {
|
|
626
|
+
const elementText = this._elementRef.nativeElement.textContent;
|
|
627
|
+
if (elementText !== this._previousAnnouncedText) {
|
|
628
|
+
this._liveAnnouncer.announce(elementText, this._politeness, this.duration);
|
|
629
|
+
this._previousAnnouncedText = elementText;
|
|
630
|
+
}
|
|
631
|
+
});
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
_politeness = 'polite';
|
|
636
|
+
duration;
|
|
637
|
+
_previousAnnouncedText;
|
|
638
|
+
_subscription;
|
|
639
|
+
constructor() {
|
|
640
|
+
inject(_CdkPrivateStyleLoader).load(_VisuallyHiddenLoader);
|
|
641
|
+
}
|
|
642
|
+
ngOnDestroy() {
|
|
643
|
+
if (this._subscription) {
|
|
644
|
+
this._subscription.unsubscribe();
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
648
|
+
minVersion: "12.0.0",
|
|
649
|
+
version: "20.2.0-next.2",
|
|
650
|
+
ngImport: i0,
|
|
651
|
+
type: CdkAriaLive,
|
|
652
|
+
deps: [],
|
|
653
|
+
target: i0.ɵɵFactoryTarget.Directive
|
|
654
|
+
});
|
|
655
|
+
static ɵdir = i0.ɵɵngDeclareDirective({
|
|
656
|
+
minVersion: "14.0.0",
|
|
657
|
+
version: "20.2.0-next.2",
|
|
658
|
+
type: CdkAriaLive,
|
|
659
|
+
isStandalone: true,
|
|
660
|
+
selector: "[cdkAriaLive]",
|
|
661
|
+
inputs: {
|
|
662
|
+
politeness: ["cdkAriaLive", "politeness"],
|
|
663
|
+
duration: ["cdkAriaLiveDuration", "duration"]
|
|
664
|
+
},
|
|
665
|
+
exportAs: ["cdkAriaLive"],
|
|
666
|
+
ngImport: i0
|
|
667
|
+
});
|
|
805
668
|
}
|
|
806
|
-
i0.ɵɵngDeclareClassMetadata({
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
669
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
670
|
+
minVersion: "12.0.0",
|
|
671
|
+
version: "20.2.0-next.2",
|
|
672
|
+
ngImport: i0,
|
|
673
|
+
type: CdkAriaLive,
|
|
674
|
+
decorators: [{
|
|
675
|
+
type: Directive,
|
|
676
|
+
args: [{
|
|
677
|
+
selector: '[cdkAriaLive]',
|
|
678
|
+
exportAs: 'cdkAriaLive'
|
|
679
|
+
}]
|
|
680
|
+
}],
|
|
681
|
+
ctorParameters: () => [],
|
|
682
|
+
propDecorators: {
|
|
683
|
+
politeness: [{
|
|
684
|
+
type: Input,
|
|
685
|
+
args: ['cdkAriaLive']
|
|
686
|
+
}],
|
|
687
|
+
duration: [{
|
|
688
|
+
type: Input,
|
|
689
|
+
args: ['cdkAriaLiveDuration']
|
|
690
|
+
}]
|
|
691
|
+
}
|
|
692
|
+
});
|
|
819
693
|
|
|
820
|
-
/** Set of possible high-contrast mode backgrounds. */
|
|
821
694
|
var HighContrastMode;
|
|
822
695
|
(function (HighContrastMode) {
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
696
|
+
HighContrastMode[HighContrastMode["NONE"] = 0] = "NONE";
|
|
697
|
+
HighContrastMode[HighContrastMode["BLACK_ON_WHITE"] = 1] = "BLACK_ON_WHITE";
|
|
698
|
+
HighContrastMode[HighContrastMode["WHITE_ON_BLACK"] = 2] = "WHITE_ON_BLACK";
|
|
826
699
|
})(HighContrastMode || (HighContrastMode = {}));
|
|
827
|
-
/** CSS class applied to the document body when in black-on-white high-contrast mode. */
|
|
828
700
|
const BLACK_ON_WHITE_CSS_CLASS = 'cdk-high-contrast-black-on-white';
|
|
829
|
-
/** CSS class applied to the document body when in white-on-black high-contrast mode. */
|
|
830
701
|
const WHITE_ON_BLACK_CSS_CLASS = 'cdk-high-contrast-white-on-black';
|
|
831
|
-
/** CSS class applied to the document body when in high-contrast mode. */
|
|
832
702
|
const HIGH_CONTRAST_MODE_ACTIVE_CSS_CLASS = 'cdk-high-contrast-active';
|
|
833
|
-
/**
|
|
834
|
-
* Service to determine whether the browser is currently in a high-contrast-mode environment.
|
|
835
|
-
*
|
|
836
|
-
* Microsoft Windows supports an accessibility feature called "High Contrast Mode". This mode
|
|
837
|
-
* changes the appearance of all applications, including web applications, to dramatically increase
|
|
838
|
-
* contrast.
|
|
839
|
-
*
|
|
840
|
-
* IE, Edge, and Firefox currently support this mode. Chrome does not support Windows High Contrast
|
|
841
|
-
* Mode. This service does not detect high-contrast mode as added by the Chrome "High Contrast"
|
|
842
|
-
* browser extension.
|
|
843
|
-
*/
|
|
844
703
|
class HighContrastModeDetector {
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
bodyClasses.add(HIGH_CONTRAST_MODE_ACTIVE_CSS_CLASS, BLACK_ON_WHITE_CSS_CLASS);
|
|
912
|
-
}
|
|
913
|
-
else if (mode === HighContrastMode.WHITE_ON_BLACK) {
|
|
914
|
-
bodyClasses.add(HIGH_CONTRAST_MODE_ACTIVE_CSS_CLASS, WHITE_ON_BLACK_CSS_CLASS);
|
|
915
|
-
}
|
|
916
|
-
}
|
|
917
|
-
}
|
|
918
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: HighContrastModeDetector, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
919
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.0-next.2", ngImport: i0, type: HighContrastModeDetector, providedIn: 'root' });
|
|
704
|
+
_platform = inject(Platform);
|
|
705
|
+
_hasCheckedHighContrastMode;
|
|
706
|
+
_document = inject(DOCUMENT);
|
|
707
|
+
_breakpointSubscription;
|
|
708
|
+
constructor() {
|
|
709
|
+
this._breakpointSubscription = inject(BreakpointObserver).observe('(forced-colors: active)').subscribe(() => {
|
|
710
|
+
if (this._hasCheckedHighContrastMode) {
|
|
711
|
+
this._hasCheckedHighContrastMode = false;
|
|
712
|
+
this._applyBodyHighContrastModeCssClasses();
|
|
713
|
+
}
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
getHighContrastMode() {
|
|
717
|
+
if (!this._platform.isBrowser) {
|
|
718
|
+
return HighContrastMode.NONE;
|
|
719
|
+
}
|
|
720
|
+
const testElement = this._document.createElement('div');
|
|
721
|
+
testElement.style.backgroundColor = 'rgb(1,2,3)';
|
|
722
|
+
testElement.style.position = 'absolute';
|
|
723
|
+
this._document.body.appendChild(testElement);
|
|
724
|
+
const documentWindow = this._document.defaultView || window;
|
|
725
|
+
const computedStyle = documentWindow && documentWindow.getComputedStyle ? documentWindow.getComputedStyle(testElement) : null;
|
|
726
|
+
const computedColor = (computedStyle && computedStyle.backgroundColor || '').replace(/ /g, '');
|
|
727
|
+
testElement.remove();
|
|
728
|
+
switch (computedColor) {
|
|
729
|
+
case 'rgb(0,0,0)':
|
|
730
|
+
case 'rgb(45,50,54)':
|
|
731
|
+
case 'rgb(32,32,32)':
|
|
732
|
+
return HighContrastMode.WHITE_ON_BLACK;
|
|
733
|
+
case 'rgb(255,255,255)':
|
|
734
|
+
case 'rgb(255,250,239)':
|
|
735
|
+
return HighContrastMode.BLACK_ON_WHITE;
|
|
736
|
+
}
|
|
737
|
+
return HighContrastMode.NONE;
|
|
738
|
+
}
|
|
739
|
+
ngOnDestroy() {
|
|
740
|
+
this._breakpointSubscription.unsubscribe();
|
|
741
|
+
}
|
|
742
|
+
_applyBodyHighContrastModeCssClasses() {
|
|
743
|
+
if (!this._hasCheckedHighContrastMode && this._platform.isBrowser && this._document.body) {
|
|
744
|
+
const bodyClasses = this._document.body.classList;
|
|
745
|
+
bodyClasses.remove(HIGH_CONTRAST_MODE_ACTIVE_CSS_CLASS, BLACK_ON_WHITE_CSS_CLASS, WHITE_ON_BLACK_CSS_CLASS);
|
|
746
|
+
this._hasCheckedHighContrastMode = true;
|
|
747
|
+
const mode = this.getHighContrastMode();
|
|
748
|
+
if (mode === HighContrastMode.BLACK_ON_WHITE) {
|
|
749
|
+
bodyClasses.add(HIGH_CONTRAST_MODE_ACTIVE_CSS_CLASS, BLACK_ON_WHITE_CSS_CLASS);
|
|
750
|
+
} else if (mode === HighContrastMode.WHITE_ON_BLACK) {
|
|
751
|
+
bodyClasses.add(HIGH_CONTRAST_MODE_ACTIVE_CSS_CLASS, WHITE_ON_BLACK_CSS_CLASS);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
756
|
+
minVersion: "12.0.0",
|
|
757
|
+
version: "20.2.0-next.2",
|
|
758
|
+
ngImport: i0,
|
|
759
|
+
type: HighContrastModeDetector,
|
|
760
|
+
deps: [],
|
|
761
|
+
target: i0.ɵɵFactoryTarget.Injectable
|
|
762
|
+
});
|
|
763
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({
|
|
764
|
+
minVersion: "12.0.0",
|
|
765
|
+
version: "20.2.0-next.2",
|
|
766
|
+
ngImport: i0,
|
|
767
|
+
type: HighContrastModeDetector,
|
|
768
|
+
providedIn: 'root'
|
|
769
|
+
});
|
|
920
770
|
}
|
|
921
|
-
i0.ɵɵngDeclareClassMetadata({
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
771
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
772
|
+
minVersion: "12.0.0",
|
|
773
|
+
version: "20.2.0-next.2",
|
|
774
|
+
ngImport: i0,
|
|
775
|
+
type: HighContrastModeDetector,
|
|
776
|
+
decorators: [{
|
|
777
|
+
type: Injectable,
|
|
778
|
+
args: [{
|
|
779
|
+
providedIn: 'root'
|
|
780
|
+
}]
|
|
781
|
+
}],
|
|
782
|
+
ctorParameters: () => []
|
|
783
|
+
});
|
|
925
784
|
|
|
926
785
|
class A11yModule {
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
786
|
+
constructor() {
|
|
787
|
+
inject(HighContrastModeDetector)._applyBodyHighContrastModeCssClasses();
|
|
788
|
+
}
|
|
789
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
790
|
+
minVersion: "12.0.0",
|
|
791
|
+
version: "20.2.0-next.2",
|
|
792
|
+
ngImport: i0,
|
|
793
|
+
type: A11yModule,
|
|
794
|
+
deps: [],
|
|
795
|
+
target: i0.ɵɵFactoryTarget.NgModule
|
|
796
|
+
});
|
|
797
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({
|
|
798
|
+
minVersion: "14.0.0",
|
|
799
|
+
version: "20.2.0-next.2",
|
|
800
|
+
ngImport: i0,
|
|
801
|
+
type: A11yModule,
|
|
802
|
+
imports: [ObserversModule, CdkAriaLive, CdkTrapFocus, CdkMonitorFocus],
|
|
803
|
+
exports: [CdkAriaLive, CdkTrapFocus, CdkMonitorFocus]
|
|
804
|
+
});
|
|
805
|
+
static ɵinj = i0.ɵɵngDeclareInjector({
|
|
806
|
+
minVersion: "12.0.0",
|
|
807
|
+
version: "20.2.0-next.2",
|
|
808
|
+
ngImport: i0,
|
|
809
|
+
type: A11yModule,
|
|
810
|
+
imports: [ObserversModule]
|
|
811
|
+
});
|
|
933
812
|
}
|
|
934
|
-
i0.ɵɵngDeclareClassMetadata({
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
813
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
814
|
+
minVersion: "12.0.0",
|
|
815
|
+
version: "20.2.0-next.2",
|
|
816
|
+
ngImport: i0,
|
|
817
|
+
type: A11yModule,
|
|
818
|
+
decorators: [{
|
|
819
|
+
type: NgModule,
|
|
820
|
+
args: [{
|
|
821
|
+
imports: [ObserversModule, CdkAriaLive, CdkTrapFocus, CdkMonitorFocus],
|
|
822
|
+
exports: [CdkAriaLive, CdkTrapFocus, CdkMonitorFocus]
|
|
823
|
+
}]
|
|
824
|
+
}],
|
|
825
|
+
ctorParameters: () => []
|
|
826
|
+
});
|
|
941
827
|
|
|
942
828
|
export { A11yModule, CdkAriaLive, CdkTrapFocus, FocusTrap, FocusTrapFactory, HighContrastMode, HighContrastModeDetector, InteractivityChecker, IsFocusableConfig, LIVE_ANNOUNCER_DEFAULT_OPTIONS, LIVE_ANNOUNCER_ELEMENT_TOKEN, LiveAnnouncer };
|
|
943
829
|
//# sourceMappingURL=_a11y-module-chunk.mjs.map
|