1 | import { elementContainsAttribute } from './dom/elementContainsAttribute';
|
2 | import { elementContains } from './dom/elementContains';
|
3 | import { getParent } from './dom/getParent';
|
4 | import { getWindow } from './dom/getWindow';
|
5 | import { getDocument } from './dom/getDocument';
|
6 | var IS_FOCUSABLE_ATTRIBUTE = 'data-is-focusable';
|
7 | var IS_VISIBLE_ATTRIBUTE = 'data-is-visible';
|
8 | var FOCUSZONE_ID_ATTRIBUTE = 'data-focuszone-id';
|
9 | var FOCUSZONE_SUB_ATTRIBUTE = 'data-is-sub-focuszone';
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 | export function getFirstFocusable(rootElement, currentElement, includeElementsInFocusZones) {
|
16 | return getNextElement(rootElement, currentElement, true , false , false , includeElementsInFocusZones);
|
17 | }
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 | export function getLastFocusable(rootElement, currentElement, includeElementsInFocusZones) {
|
24 | return getPreviousElement(rootElement, currentElement, true , false , true , includeElementsInFocusZones);
|
25 | }
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 | export function getFirstTabbable(rootElement, currentElement, includeElementsInFocusZones, checkNode) {
|
37 | if (checkNode === void 0) { checkNode = true; }
|
38 | return getNextElement(rootElement, currentElement, checkNode, false , false , includeElementsInFocusZones, false , true );
|
39 | }
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
47 |
|
48 |
|
49 |
|
50 | export function getLastTabbable(rootElement, currentElement, includeElementsInFocusZones, checkNode) {
|
51 | if (checkNode === void 0) { checkNode = true; }
|
52 | return getPreviousElement(rootElement, currentElement, checkNode, false , true , includeElementsInFocusZones, false , true );
|
53 | }
|
54 |
|
55 |
|
56 |
|
57 |
|
58 |
|
59 |
|
60 |
|
61 | export function focusFirstChild(rootElement) {
|
62 | var element = getNextElement(rootElement, rootElement, true, false, false, true);
|
63 | if (element) {
|
64 | focusAsync(element);
|
65 | return true;
|
66 | }
|
67 | return false;
|
68 | }
|
69 |
|
70 |
|
71 |
|
72 |
|
73 |
|
74 |
|
75 | export function getPreviousElement(rootElement, currentElement, checkNode, suppressParentTraversal, traverseChildren, includeElementsInFocusZones, allowFocusRoot, tabbable) {
|
76 | if (!currentElement || (!allowFocusRoot && currentElement === rootElement)) {
|
77 | return null;
|
78 | }
|
79 | var isCurrentElementVisible = isElementVisible(currentElement);
|
80 |
|
81 | if (traverseChildren &&
|
82 | isCurrentElementVisible &&
|
83 | (includeElementsInFocusZones || !(isElementFocusZone(currentElement) || isElementFocusSubZone(currentElement)))) {
|
84 | var childMatch = getPreviousElement(rootElement, currentElement.lastElementChild, true, true, true, includeElementsInFocusZones, allowFocusRoot, tabbable);
|
85 | if (childMatch) {
|
86 | if ((tabbable && isElementTabbable(childMatch, true)) || !tabbable) {
|
87 | return childMatch;
|
88 | }
|
89 | var childMatchSiblingMatch = getPreviousElement(rootElement, childMatch.previousElementSibling, true, true, true, includeElementsInFocusZones, allowFocusRoot, tabbable);
|
90 | if (childMatchSiblingMatch) {
|
91 | return childMatchSiblingMatch;
|
92 | }
|
93 | var childMatchParent = childMatch.parentElement;
|
94 |
|
95 |
|
96 |
|
97 |
|
98 | while (childMatchParent && childMatchParent !== currentElement) {
|
99 | var childMatchParentMatch = getPreviousElement(rootElement, childMatchParent.previousElementSibling, true, true, true, includeElementsInFocusZones, allowFocusRoot, tabbable);
|
100 | if (childMatchParentMatch) {
|
101 | return childMatchParentMatch;
|
102 | }
|
103 | childMatchParent = childMatchParent.parentElement;
|
104 | }
|
105 | }
|
106 | }
|
107 |
|
108 | if (checkNode && isCurrentElementVisible && isElementTabbable(currentElement, tabbable)) {
|
109 | return currentElement;
|
110 | }
|
111 |
|
112 | var siblingMatch = getPreviousElement(rootElement, currentElement.previousElementSibling, true, true, true, includeElementsInFocusZones, allowFocusRoot, tabbable);
|
113 | if (siblingMatch) {
|
114 | return siblingMatch;
|
115 | }
|
116 |
|
117 | if (!suppressParentTraversal) {
|
118 | return getPreviousElement(rootElement, currentElement.parentElement, true, false, false, includeElementsInFocusZones, allowFocusRoot, tabbable);
|
119 | }
|
120 | return null;
|
121 | }
|
122 |
|
123 |
|
124 |
|
125 |
|
126 |
|
127 |
|
128 |
|
129 | export function getNextElement(rootElement, currentElement, checkNode, suppressParentTraversal, suppressChildTraversal, includeElementsInFocusZones, allowFocusRoot, tabbable) {
|
130 | if (!currentElement || (currentElement === rootElement && suppressChildTraversal && !allowFocusRoot)) {
|
131 | return null;
|
132 | }
|
133 | var isCurrentElementVisible = isElementVisible(currentElement);
|
134 |
|
135 | if (checkNode && isCurrentElementVisible && isElementTabbable(currentElement, tabbable)) {
|
136 | return currentElement;
|
137 | }
|
138 |
|
139 | if (!suppressChildTraversal &&
|
140 | isCurrentElementVisible &&
|
141 | (includeElementsInFocusZones || !(isElementFocusZone(currentElement) || isElementFocusSubZone(currentElement)))) {
|
142 | var childMatch = getNextElement(rootElement, currentElement.firstElementChild, true, true, false, includeElementsInFocusZones, allowFocusRoot, tabbable);
|
143 | if (childMatch) {
|
144 | return childMatch;
|
145 | }
|
146 | }
|
147 | if (currentElement === rootElement) {
|
148 | return null;
|
149 | }
|
150 |
|
151 | var siblingMatch = getNextElement(rootElement, currentElement.nextElementSibling, true, true, false, includeElementsInFocusZones, allowFocusRoot, tabbable);
|
152 | if (siblingMatch) {
|
153 | return siblingMatch;
|
154 | }
|
155 | if (!suppressParentTraversal) {
|
156 | return getNextElement(rootElement, currentElement.parentElement, false, false, true, includeElementsInFocusZones, allowFocusRoot, tabbable);
|
157 | }
|
158 | return null;
|
159 | }
|
160 |
|
161 |
|
162 |
|
163 |
|
164 |
|
165 | export function isElementVisible(element) {
|
166 |
|
167 | if (!element || !element.getAttribute) {
|
168 | return false;
|
169 | }
|
170 | var visibilityAttribute = element.getAttribute(IS_VISIBLE_ATTRIBUTE);
|
171 |
|
172 | if (visibilityAttribute !== null && visibilityAttribute !== undefined) {
|
173 | return visibilityAttribute === 'true';
|
174 | }
|
175 |
|
176 | return (element.offsetHeight !== 0 ||
|
177 | element.offsetParent !== null ||
|
178 |
|
179 | element.isVisible === true);
|
180 | }
|
181 |
|
182 |
|
183 |
|
184 |
|
185 |
|
186 |
|
187 |
|
188 | export function isElementTabbable(element, checkTabIndex) {
|
189 |
|
190 | if (!element || element.disabled) {
|
191 | return false;
|
192 | }
|
193 | var tabIndex = 0;
|
194 | var tabIndexAttributeValue = null;
|
195 | if (element && element.getAttribute) {
|
196 | tabIndexAttributeValue = element.getAttribute('tabIndex');
|
197 | if (tabIndexAttributeValue) {
|
198 | tabIndex = parseInt(tabIndexAttributeValue, 10);
|
199 | }
|
200 | }
|
201 | var isFocusableAttribute = element.getAttribute ? element.getAttribute(IS_FOCUSABLE_ATTRIBUTE) : null;
|
202 | var isTabIndexSet = tabIndexAttributeValue !== null && tabIndex >= 0;
|
203 | var result = !!element &&
|
204 | isFocusableAttribute !== 'false' &&
|
205 | (element.tagName === 'A' ||
|
206 | element.tagName === 'BUTTON' ||
|
207 | element.tagName === 'INPUT' ||
|
208 | element.tagName === 'TEXTAREA' ||
|
209 | element.tagName === 'SELECT' ||
|
210 | isFocusableAttribute === 'true' ||
|
211 | isTabIndexSet);
|
212 | return checkTabIndex ? tabIndex !== -1 && result : result;
|
213 | }
|
214 |
|
215 |
|
216 |
|
217 |
|
218 |
|
219 | export function isElementFocusZone(element) {
|
220 | return !!(element && element.getAttribute && !!element.getAttribute(FOCUSZONE_ID_ATTRIBUTE));
|
221 | }
|
222 |
|
223 |
|
224 |
|
225 |
|
226 |
|
227 | export function isElementFocusSubZone(element) {
|
228 | return !!(element && element.getAttribute && element.getAttribute(FOCUSZONE_SUB_ATTRIBUTE) === 'true');
|
229 | }
|
230 |
|
231 |
|
232 |
|
233 |
|
234 |
|
235 | export function doesElementContainFocus(element) {
|
236 | var document = getDocument(element);
|
237 | var currentActiveElement = document && document.activeElement;
|
238 | if (currentActiveElement && elementContains(element, currentActiveElement)) {
|
239 | return true;
|
240 | }
|
241 | return false;
|
242 | }
|
243 |
|
244 |
|
245 |
|
246 |
|
247 |
|
248 |
|
249 | export function shouldWrapFocus(element, noWrapDataAttribute) {
|
250 | return elementContainsAttribute(element, noWrapDataAttribute) === 'true' ? false : true;
|
251 | }
|
252 | var targetToFocusOnNextRepaint = undefined;
|
253 |
|
254 |
|
255 |
|
256 |
|
257 |
|
258 |
|
259 | export function focusAsync(element) {
|
260 | if (element) {
|
261 |
|
262 | if (targetToFocusOnNextRepaint) {
|
263 | targetToFocusOnNextRepaint = element;
|
264 | return;
|
265 | }
|
266 | targetToFocusOnNextRepaint = element;
|
267 | var win = getWindow(element);
|
268 | if (win) {
|
269 |
|
270 | win.requestAnimationFrame(function () {
|
271 | var focusableElement = targetToFocusOnNextRepaint;
|
272 |
|
273 | targetToFocusOnNextRepaint = undefined;
|
274 | if (focusableElement) {
|
275 | if (focusableElement.getAttribute && focusableElement.getAttribute(IS_FOCUSABLE_ATTRIBUTE) === 'true') {
|
276 |
|
277 |
|
278 |
|
279 | if (!focusableElement.getAttribute('tabindex')) {
|
280 | focusableElement.setAttribute('tabindex', '0');
|
281 | }
|
282 | }
|
283 | focusableElement.focus();
|
284 | }
|
285 | });
|
286 | }
|
287 | }
|
288 | }
|
289 |
|
290 |
|
291 |
|
292 |
|
293 | export function getFocusableByIndexPath(parent, path) {
|
294 | var element = parent;
|
295 | for (var _i = 0, path_1 = path; _i < path_1.length; _i++) {
|
296 | var index = path_1[_i];
|
297 | var nextChild = element.children[Math.min(index, element.children.length - 1)];
|
298 | if (!nextChild) {
|
299 | break;
|
300 | }
|
301 | element = nextChild;
|
302 | }
|
303 | element =
|
304 | isElementTabbable(element) && isElementVisible(element)
|
305 | ? element
|
306 | : getNextElement(parent, element, true) || getPreviousElement(parent, element);
|
307 | return element;
|
308 | }
|
309 |
|
310 |
|
311 |
|
312 |
|
313 |
|
314 |
|
315 | export function getElementIndexPath(fromElement, toElement) {
|
316 | var path = [];
|
317 | while (toElement && fromElement && toElement !== fromElement) {
|
318 | var parent_1 = getParent(toElement, true);
|
319 | if (parent_1 === null) {
|
320 | return [];
|
321 | }
|
322 | path.unshift(Array.prototype.indexOf.call(parent_1.children, toElement));
|
323 | toElement = parent_1;
|
324 | }
|
325 | return path;
|
326 | }
|
327 |
|
\ | No newline at end of file |