-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnavigation.js
279 lines (279 loc) · 10.5 KB
/
navigation.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
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
let maxVertical = 0;
let minVertical = 0;
/**
* Focus the next focusable element based on the actualHorizontal and actualVertical indexes
*/
const carouselHorizontalMovement = () => {
const carouselCard = document?.querySelectorAll('.carousel-container')[window.actualVertical]?.querySelectorAll('.focusable-element')[
window.actualHorizontal
];
checkBlockedCarousels();
carouselCard.focus();
};
/**
* Check the next focusable element on a grid carousel
*
* actualGridRow indicates the current row of the carousel
* actualGridCell indicates the current cell of the row
*
* @param movement = down, up, left or right
*/
const carouselGridMovement = (movement = 'down | up | left | right') => {
const carouselGridRows = document
?.querySelectorAll('.carousel-container')
[window.actualVertical]?.querySelectorAll('.carousel-container-row');
const actualRow = carouselGridRows[window.actualGridRow].childElementCount;
if (movement === 'down' || movement === 'up') {
try {
// Getting quanity of childs on previous or next row
let rowToFocus;
if (movement === 'down') {
rowToFocus = carouselGridRows[window.actualGridRow + 1].childElementCount - 1;
if (!Number.isNaN(rowToFocus)) {
window.actualGridRow += 1;
}
} else if (movement === 'up') {
rowToFocus = carouselGridRows[window.actualGridRow - 1].childElementCount - 1;
if (!Number.isNaN(rowToFocus)) {
window.actualGridRow -= 1;
}
}
if (actualRow - 1 === rowToFocus || window.actualGridCell === 0) {
// Focusing on same position on next row
carouselGridRows[window.actualGridRow].querySelectorAll('.focusable-element')[window.actualGridCell].focus();
} else {
try {
// Calculating neareast aligned child
const actualChildrenPos = Math.round((rowToFocus * window.actualGridCell + 1) / actualRow);
carouselGridRows[window.actualGridRow].querySelectorAll('.focusable-element')[actualChildrenPos].focus();
window.actualGridCell = actualChildrenPos;
} catch (e2) {
// Focusing first child
carouselGridRows[window.actualGridRow].querySelectorAll('.focusable-element')[0].focus();
window.actualGridCell = 0;
}
}
// Vertical center of the focused row
carouselVerticalCenterGrid();
} catch (e) {
// There is not a next row, check if there is another carousel
focusNextCarousel(movement);
// Check if there are any open dialogs
closeCarousel();
}
} else if (movement === 'left' || movement === 'right') {
try {
let cellToFocus;
// Getting next or previous cell
if (movement === 'right') {
cellToFocus = carouselGridRows[window.actualGridRow].querySelectorAll('.focusable-element')[window.actualGridCell + 1];
cellToFocus.focus();
window.actualGridCell += 1;
} else if (movement === 'left') {
cellToFocus = carouselGridRows[window.actualGridRow].querySelectorAll('.focusable-element')[window.actualGridCell - 1];
cellToFocus.focus();
window.actualGridCell -= 1;
}
} catch (e) {
try {
let rowToFocus;
// Try getting previous or next row
// Getting quanity of childs on previous or next row
if (movement === 'right') {
if (focusNextCarousel(movement)) {
// Check if there are any open dialogs
closeDialogs();
} else {
rowToFocus = carouselGridRows[window.actualGridRow + 1];
if (rowToFocus) {
window.actualGridRow += 1;
// Focus first child of new row
window.actualGridCell = 0;
}
}
} else if (movement === 'left') {
rowToFocus = carouselGridRows[window.actualGridRow - 1];
if (rowToFocus) {
window.actualGridRow -= 1;
// Focus last child of new row
window.actualGridCell = rowToFocus.childElementCount - 1;
}
}
rowToFocus?.querySelectorAll('.focusable-element')[window.actualGridCell].focus();
} catch (e2) {
// There is not a previous or next row
}
}
}
};
/**
* If the user goes down or right, sum +1 to actualVertical index
* If the user goes up or left, substract -1 to actualVertical index
*
* Check the type of the carousel to focus
*
* @param movement = down, up, left or right
* @returns true if there is a next carousel, false if there is not a next carousel based on the movement
*/
const focusNextCarousel = (movement = 'down | up | left | right') => {
const listOfCarousels = document?.querySelectorAll('.carousel-container');
if (movement === 'down' || movement === 'right') {
try {
if ((maxVertical !== 0 && window.actualVertical < maxVertical) || (maxVertical === 0 && minVertical === 0)) {
listOfCarousels[window.actualVertical + 1].focus();
window.actualVertical += 1;
checkTypeOfCarousel(listOfCarousels);
carouselVerticalCenter();
return true;
} else {
return false;
}
} catch (e) {
return false;
}
} else if (movement === 'up' || movement === 'left') {
try {
if ((maxVertical !== 0 && window.actualVertical > minVertical) || (maxVertical === 0 && minVertical === 0)) {
listOfCarousels[window.actualVertical - 1].focus();
window.actualVertical -= 1;
checkTypeOfCarousel(listOfCarousels);
carouselVerticalCenter();
return true;
} else {
return false;
}
} catch (e) {
return false;
}
}
};
/**
* Check if the currently focused carousel is a grid or a normal carousel (vertical or horizontal)
*
* @param listOfCarousels = List of carousels with the .carousel-container class
*/
const checkTypeOfCarousel = (listOfCarousels) => {
// It is a grid carousel if there are rows on the carousel
if (listOfCarousels[window.actualVertical].querySelectorAll('.carousel-container-row')?.length > 0) {
// Reset variables for grid carousel
window.isInGridCarousel = true;
window.isInNormalCarousel = false;
window.actualHorizontal = 0;
window.actualGridRow = 0;
window.actualGridCell = 0;
// Focus the element based on row and cell
listOfCarousels[window.actualVertical]
.querySelectorAll('.carousel-container-row')
[window.actualGridRow].querySelectorAll('.focusable-element')
[window.actualGridCell].focus();
} // It is a normal carousel
else {
// Reset variables for normal carousel
window.isInNormalCarousel = true;
window.isInGridCarousel = false;
window.actualGridRow = 0;
window.actualGridCell = 0;
window.actualHorizontal = 0;
// Focus the current carousel
carouselHorizontalMovement();
}
};
/**
* Vertical center the next carousel
* If the element height is less than the body or the current vertical position is 0, mantain the scroll on 0
* Else; do the vertical center
*/
const carouselVerticalCenter = () => {
const carousel = document?.querySelectorAll('.carousel-container');
let y =
carousel[window.actualVertical].offsetTop - window.innerHeight / 2 + carousel[window.actualVertical].getBoundingClientRect().height / 2;
y = Math.round(y);
carousel[window.actualVertical].focus();
if (y < 100 || window.actualVertical === 0 || (window.isInGridCarousel && window.actualGridCell === 0)) {
document.getElementsByTagName('body')[0].scrollTop = 0;
} else {
document.getElementsByTagName('body')[0].scrollTop = y;
}
};
/**
* Vertical center the next row on grid carousels
* If the element height is less than the body, mantain the scroll on 0
* Else; do the vertical center
*/
const carouselVerticalCenterGrid = () => {
const carousel = document?.querySelectorAll('.carousel-container')[window.actualVertical]?.querySelectorAll('.carousel-container-row');
let y =
carousel[window.actualGridRow].offsetTop - window.innerHeight / 2 + carousel[window.actualGridRow].getBoundingClientRect().height / 2;
y = Math.round(y);
if (
document?.querySelectorAll('.carousel-container')[window.actualVertical].clientHeight <
document.getElementsByTagName('body')[0].clientHeight
) {
document.getElementsByTagName('body')[0].scrollTop = 0;
} else {
document.getElementsByTagName('body')[0].scrollTop = y;
}
};
/**
* Click the focused element and focus the next element based on the window.actualVertical and window.actualHorizontal
*/
const carouselOK = () => {
document.getElementsByTagName('body')[0].scrollTop = 0;
// Stop double propagation on button elements
if (document.activeElement.tagName !== 'BUTTON') {
document.activeElement.click();
}
focusElement();
};
/**
* If there is not any focus, focus the element based on the window.actualVertical and window.actualHorizontal
*/
const focusElement = () => {
let firstFocusableElementCounter = 0;
const firstFocusableElement = setInterval(() => {
if (document.getElementsByTagName('body')[0] === document.activeElement) {
carouselHorizontalMovement();
clearInterval(firstFocusableElement);
} else if (firstFocusableElementCounter === 5) {
clearInterval(firstFocusableElement);
}
firstFocusableElementCounter += 1;
}, 200);
};
/**
* Close any closable carousel
*
* @returns true if there is any closable carousel, false if there is not
*/
const closeCarousel = () => {
if (document.querySelectorAll('.closable-carousel')[0]) {
document.querySelectorAll('.closable-carousel')[0].click();
return true;
}
return false;
};
/*
* If it is a blocked container
* * Get all the quantity of blocked containers on the page
* * The minVertical will be the actualVertical
* * The maxVertical will be all the quantity of blocked containers followed by actualVertical index
*
* Else if
* * The user is not longer focusing a blocked-container
* * * Reset the minVertical and maxVertical to 0
*/
const checkBlockedCarousels = () => {
const carousel = document?.querySelectorAll('.carousel-container');
if (carousel[window.actualVertical]?.classList.contains('blocked-container') && minVertical === 0 && maxVertical === 0) {
if (window.actualVertical === 0) {
minVertical = 0;
} else {
minVertical = window.actualVertical - 1;
}
maxVertical = minVertical + document.querySelectorAll('.blocked-container').length - 1;
} else if (!carousel[window.actualVertical]?.classList.contains('blocked-container')) {
minVertical = 0;
maxVertical = 0;
}
};
export { carouselHorizontalMovement, carouselOK, carouselGridMovement, focusNextCarousel, focusElement, closeCarousel };