@stonecrop/utilities 0.13.4 → 0.13.5
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/dist/src/composables/keyboard.d.ts.map +1 -1
- package/dist/src/composables/keyboard.js +118 -88
- package/dist/utilities.js +198 -177
- package/dist/utilities.js.map +1 -1
- package/package.json +6 -9
- package/src/composables/keyboard.ts +111 -91
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keyboard.d.ts","sourceRoot":"","sources":["../../../src/composables/keyboard.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,yBAAyB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"keyboard.d.ts","sourceRoot":"","sources":["../../../src/composables/keyboard.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,yBAAyB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAmN3E;;;GAGG;AACH,eAAO,MAAM,uBAAuB,EAAE,gBA+HrC,CAAA;AAoBD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,yBAAyB,EAAE,QA+HlE"}
|
|
@@ -3,63 +3,66 @@ import { useFocusWithin } from '@vueuse/core';
|
|
|
3
3
|
import { useElementVisibility } from './visibility';
|
|
4
4
|
// helper functions
|
|
5
5
|
const isVisible = (element) => {
|
|
6
|
-
let
|
|
7
|
-
|
|
8
|
-
return
|
|
6
|
+
let visible = useElementVisibility(element).value;
|
|
7
|
+
visible = visible && element.offsetHeight > 0;
|
|
8
|
+
return visible;
|
|
9
9
|
};
|
|
10
10
|
const isFocusable = (element) => {
|
|
11
11
|
return element.tabIndex >= 0;
|
|
12
12
|
};
|
|
13
13
|
// navigation functions
|
|
14
14
|
const getUpCell = (event) => {
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
if (!(event.target instanceof HTMLElement))
|
|
16
|
+
return undefined;
|
|
17
|
+
return getUpCellEl(event.target);
|
|
17
18
|
};
|
|
18
|
-
const
|
|
19
|
+
const getUpCellEl = (element) => {
|
|
19
20
|
let $upCell;
|
|
20
21
|
if (element instanceof HTMLTableCellElement) {
|
|
21
|
-
const $
|
|
22
|
-
if ($
|
|
23
|
-
const $prevRowCells = Array.from($
|
|
24
|
-
const $
|
|
25
|
-
if ($
|
|
26
|
-
$upCell = $
|
|
22
|
+
const $prevSibling = element.parentElement?.previousElementSibling;
|
|
23
|
+
if ($prevSibling instanceof HTMLTableRowElement) {
|
|
24
|
+
const $prevRowCells = Array.from($prevSibling.children);
|
|
25
|
+
const $cellEl = $prevRowCells[element.cellIndex];
|
|
26
|
+
if ($cellEl instanceof HTMLElement) {
|
|
27
|
+
$upCell = $cellEl;
|
|
27
28
|
}
|
|
28
29
|
}
|
|
29
30
|
}
|
|
30
31
|
else if (element instanceof HTMLTableRowElement) {
|
|
31
|
-
const $
|
|
32
|
-
if ($
|
|
33
|
-
$upCell = $
|
|
32
|
+
const $prevSibling = element.previousElementSibling;
|
|
33
|
+
if ($prevSibling instanceof HTMLTableRowElement) {
|
|
34
|
+
$upCell = $prevSibling;
|
|
34
35
|
}
|
|
35
36
|
}
|
|
36
37
|
else {
|
|
37
38
|
// handle other contexts
|
|
38
39
|
}
|
|
39
40
|
if ($upCell && (!isFocusable($upCell) || !isVisible($upCell))) {
|
|
40
|
-
return
|
|
41
|
+
return getUpCellEl($upCell);
|
|
41
42
|
}
|
|
42
43
|
return $upCell;
|
|
43
44
|
};
|
|
44
45
|
const getTopCell = (event) => {
|
|
46
|
+
if (!(event.target instanceof HTMLElement))
|
|
47
|
+
return undefined;
|
|
45
48
|
const $target = event.target;
|
|
46
49
|
let $topCell;
|
|
47
50
|
if ($target instanceof HTMLTableCellElement) {
|
|
48
51
|
const $table = $target.parentElement?.parentElement;
|
|
49
52
|
if ($table) {
|
|
50
53
|
const $firstRow = $table.firstElementChild;
|
|
51
|
-
const $
|
|
52
|
-
if ($
|
|
53
|
-
$topCell = $
|
|
54
|
+
const $navEl = $firstRow?.children[$target.cellIndex];
|
|
55
|
+
if ($navEl instanceof HTMLElement) {
|
|
56
|
+
$topCell = $navEl;
|
|
54
57
|
}
|
|
55
58
|
}
|
|
56
59
|
}
|
|
57
60
|
else if ($target instanceof HTMLTableRowElement) {
|
|
58
|
-
const $
|
|
59
|
-
if ($
|
|
60
|
-
const $
|
|
61
|
-
if ($
|
|
62
|
-
$topCell = $
|
|
61
|
+
const $parent = $target.parentElement;
|
|
62
|
+
if ($parent) {
|
|
63
|
+
const $firstEl = $parent.firstElementChild;
|
|
64
|
+
if ($firstEl instanceof HTMLTableRowElement) {
|
|
65
|
+
$topCell = $firstEl;
|
|
63
66
|
}
|
|
64
67
|
}
|
|
65
68
|
}
|
|
@@ -67,59 +70,62 @@ const getTopCell = (event) => {
|
|
|
67
70
|
// handle other contexts
|
|
68
71
|
}
|
|
69
72
|
if ($topCell && (!isFocusable($topCell) || !isVisible($topCell))) {
|
|
70
|
-
return
|
|
73
|
+
return getDownCellEl($topCell);
|
|
71
74
|
}
|
|
72
75
|
return $topCell;
|
|
73
76
|
};
|
|
74
77
|
const getDownCell = (event) => {
|
|
75
|
-
|
|
76
|
-
|
|
78
|
+
if (!(event.target instanceof HTMLElement))
|
|
79
|
+
return undefined;
|
|
80
|
+
return getDownCellEl(event.target);
|
|
77
81
|
};
|
|
78
|
-
const
|
|
82
|
+
const getDownCellEl = (element) => {
|
|
79
83
|
let $downCell;
|
|
80
84
|
if (element instanceof HTMLTableCellElement) {
|
|
81
|
-
const $
|
|
82
|
-
if ($
|
|
83
|
-
const $nextRowCells = Array.from($
|
|
84
|
-
const $
|
|
85
|
-
if ($
|
|
86
|
-
$downCell = $
|
|
85
|
+
const $nextSibling = element.parentElement?.nextElementSibling;
|
|
86
|
+
if ($nextSibling) {
|
|
87
|
+
const $nextRowCells = Array.from($nextSibling.children);
|
|
88
|
+
const $cellEl = $nextRowCells[element.cellIndex];
|
|
89
|
+
if ($cellEl instanceof HTMLElement) {
|
|
90
|
+
$downCell = $cellEl;
|
|
87
91
|
}
|
|
88
92
|
}
|
|
89
93
|
}
|
|
90
94
|
else if (element instanceof HTMLTableRowElement) {
|
|
91
|
-
const $
|
|
92
|
-
if ($
|
|
93
|
-
$downCell = $
|
|
95
|
+
const $nextSibling = element.nextElementSibling;
|
|
96
|
+
if ($nextSibling instanceof HTMLTableRowElement) {
|
|
97
|
+
$downCell = $nextSibling;
|
|
94
98
|
}
|
|
95
99
|
}
|
|
96
100
|
else {
|
|
97
101
|
// handle other contexts
|
|
98
102
|
}
|
|
99
103
|
if ($downCell && (!isFocusable($downCell) || !isVisible($downCell))) {
|
|
100
|
-
return
|
|
104
|
+
return getDownCellEl($downCell);
|
|
101
105
|
}
|
|
102
106
|
return $downCell;
|
|
103
107
|
};
|
|
104
108
|
const getBottomCell = (event) => {
|
|
109
|
+
if (!(event.target instanceof HTMLElement))
|
|
110
|
+
return undefined;
|
|
105
111
|
const $target = event.target;
|
|
106
112
|
let $bottomCell;
|
|
107
113
|
if ($target instanceof HTMLTableCellElement) {
|
|
108
114
|
const $table = $target.parentElement?.parentElement;
|
|
109
115
|
if ($table) {
|
|
110
116
|
const $lastRow = $table.lastElementChild;
|
|
111
|
-
const $
|
|
112
|
-
if ($
|
|
113
|
-
$bottomCell = $
|
|
117
|
+
const $navEl = $lastRow?.children[$target.cellIndex];
|
|
118
|
+
if ($navEl instanceof HTMLElement) {
|
|
119
|
+
$bottomCell = $navEl;
|
|
114
120
|
}
|
|
115
121
|
}
|
|
116
122
|
}
|
|
117
123
|
else if ($target instanceof HTMLTableRowElement) {
|
|
118
|
-
const $
|
|
119
|
-
if ($
|
|
120
|
-
const $
|
|
121
|
-
if ($
|
|
122
|
-
$bottomCell = $
|
|
124
|
+
const $parent = $target.parentElement;
|
|
125
|
+
if ($parent) {
|
|
126
|
+
const $lastEl = $parent.lastElementChild;
|
|
127
|
+
if ($lastEl instanceof HTMLTableRowElement) {
|
|
128
|
+
$bottomCell = $lastEl;
|
|
123
129
|
}
|
|
124
130
|
}
|
|
125
131
|
}
|
|
@@ -127,61 +133,75 @@ const getBottomCell = (event) => {
|
|
|
127
133
|
// handle other contexts
|
|
128
134
|
}
|
|
129
135
|
if ($bottomCell && (!isFocusable($bottomCell) || !isVisible($bottomCell))) {
|
|
130
|
-
return
|
|
136
|
+
return getUpCellEl($bottomCell);
|
|
131
137
|
}
|
|
132
138
|
return $bottomCell;
|
|
133
139
|
};
|
|
134
140
|
const getPrevCell = (event) => {
|
|
135
|
-
|
|
136
|
-
|
|
141
|
+
if (!(event.target instanceof HTMLElement))
|
|
142
|
+
return undefined;
|
|
143
|
+
return getPrevCellEl(event.target);
|
|
137
144
|
};
|
|
138
|
-
const
|
|
145
|
+
const getPrevCellEl = (element) => {
|
|
139
146
|
let $prevCell;
|
|
140
|
-
|
|
141
|
-
|
|
147
|
+
const $prevSibling = element.previousElementSibling;
|
|
148
|
+
if ($prevSibling instanceof HTMLElement) {
|
|
149
|
+
$prevCell = $prevSibling;
|
|
142
150
|
}
|
|
143
151
|
else {
|
|
144
152
|
const $prevRow = element.parentElement?.previousElementSibling;
|
|
145
|
-
$
|
|
153
|
+
const $lastEl = $prevRow?.lastElementChild;
|
|
154
|
+
if ($lastEl instanceof HTMLElement) {
|
|
155
|
+
$prevCell = $lastEl;
|
|
156
|
+
}
|
|
146
157
|
}
|
|
147
158
|
if ($prevCell && (!isFocusable($prevCell) || !isVisible($prevCell))) {
|
|
148
|
-
return
|
|
159
|
+
return getPrevCellEl($prevCell);
|
|
149
160
|
}
|
|
150
161
|
return $prevCell;
|
|
151
162
|
};
|
|
152
163
|
const getNextCell = (event) => {
|
|
153
|
-
|
|
154
|
-
|
|
164
|
+
if (!(event.target instanceof HTMLElement))
|
|
165
|
+
return undefined;
|
|
166
|
+
return getNextCellEl(event.target);
|
|
155
167
|
};
|
|
156
|
-
const
|
|
168
|
+
const getNextCellEl = (element) => {
|
|
157
169
|
let $nextCell;
|
|
158
|
-
|
|
159
|
-
|
|
170
|
+
const $nextSibling = element.nextElementSibling;
|
|
171
|
+
if ($nextSibling instanceof HTMLElement) {
|
|
172
|
+
$nextCell = $nextSibling;
|
|
160
173
|
}
|
|
161
174
|
else {
|
|
162
175
|
const $nextRow = element.parentElement?.nextElementSibling;
|
|
163
|
-
$
|
|
176
|
+
const $firstEl = $nextRow?.firstElementChild;
|
|
177
|
+
if ($firstEl instanceof HTMLElement) {
|
|
178
|
+
$nextCell = $firstEl;
|
|
179
|
+
}
|
|
164
180
|
}
|
|
165
181
|
if ($nextCell && (!isFocusable($nextCell) || !isVisible($nextCell))) {
|
|
166
|
-
return
|
|
182
|
+
return getNextCellEl($nextCell);
|
|
167
183
|
}
|
|
168
184
|
return $nextCell;
|
|
169
185
|
};
|
|
170
186
|
const getFirstCell = (event) => {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
const $
|
|
187
|
+
if (!(event.target instanceof HTMLElement))
|
|
188
|
+
return undefined;
|
|
189
|
+
const $parent = event.target.parentElement;
|
|
190
|
+
const $firstEl = $parent?.firstElementChild;
|
|
191
|
+
const $firstCell = $firstEl instanceof HTMLElement ? $firstEl : null;
|
|
174
192
|
if ($firstCell && (!isFocusable($firstCell) || !isVisible($firstCell))) {
|
|
175
|
-
return
|
|
193
|
+
return getNextCellEl($firstCell);
|
|
176
194
|
}
|
|
177
195
|
return $firstCell;
|
|
178
196
|
};
|
|
179
197
|
const getLastCell = (event) => {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
const $
|
|
198
|
+
if (!(event.target instanceof HTMLElement))
|
|
199
|
+
return undefined;
|
|
200
|
+
const $parent = event.target.parentElement;
|
|
201
|
+
const $lastEl = $parent?.lastElementChild;
|
|
202
|
+
const $lastCell = $lastEl instanceof HTMLElement ? $lastEl : null;
|
|
183
203
|
if ($lastCell && (!isFocusable($lastCell) || !isVisible($lastCell))) {
|
|
184
|
-
return
|
|
204
|
+
return getPrevCellEl($lastCell);
|
|
185
205
|
}
|
|
186
206
|
return $lastCell;
|
|
187
207
|
};
|
|
@@ -272,6 +292,8 @@ export const defaultKeypressHandlers = {
|
|
|
272
292
|
}
|
|
273
293
|
},
|
|
274
294
|
'keydown.enter': (event) => {
|
|
295
|
+
if (!(event.target instanceof HTMLElement))
|
|
296
|
+
return;
|
|
275
297
|
const $target = event.target;
|
|
276
298
|
if ($target instanceof HTMLTableCellElement) {
|
|
277
299
|
event.preventDefault();
|
|
@@ -286,6 +308,8 @@ export const defaultKeypressHandlers = {
|
|
|
286
308
|
}
|
|
287
309
|
},
|
|
288
310
|
'keydown.shift.enter': (event) => {
|
|
311
|
+
if (!(event.target instanceof HTMLElement))
|
|
312
|
+
return;
|
|
289
313
|
const $target = event.target;
|
|
290
314
|
if ($target instanceof HTMLTableCellElement) {
|
|
291
315
|
event.preventDefault();
|
|
@@ -324,27 +348,31 @@ export const defaultKeypressHandlers = {
|
|
|
324
348
|
}
|
|
325
349
|
},
|
|
326
350
|
};
|
|
351
|
+
/**
|
|
352
|
+
* Gets the parent element from a keyboard navigation option.
|
|
353
|
+
* @internal
|
|
354
|
+
*/
|
|
355
|
+
function getParentElement(option) {
|
|
356
|
+
let $parent = null;
|
|
357
|
+
if (option.parent) {
|
|
358
|
+
if (typeof option.parent === 'string') {
|
|
359
|
+
$parent = document.querySelector(option.parent);
|
|
360
|
+
}
|
|
361
|
+
else if (option.parent instanceof HTMLElement) {
|
|
362
|
+
$parent = option.parent;
|
|
363
|
+
}
|
|
364
|
+
else {
|
|
365
|
+
$parent = option.parent.value;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return $parent;
|
|
369
|
+
}
|
|
327
370
|
/**
|
|
328
371
|
* Keyboard navigation composable
|
|
329
372
|
* @param options - Keyboard navigation options
|
|
330
373
|
* @public
|
|
331
374
|
*/
|
|
332
375
|
export function useKeyboardNav(options) {
|
|
333
|
-
const getParentElement = (option) => {
|
|
334
|
-
let $parent = null;
|
|
335
|
-
if (option.parent) {
|
|
336
|
-
if (typeof option.parent === 'string') {
|
|
337
|
-
$parent = document.querySelector(option.parent);
|
|
338
|
-
}
|
|
339
|
-
else if (option.parent instanceof HTMLElement) {
|
|
340
|
-
$parent = option.parent;
|
|
341
|
-
}
|
|
342
|
-
else {
|
|
343
|
-
$parent = option.parent.value;
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
return $parent;
|
|
347
|
-
};
|
|
348
376
|
const getSelectorsFromOption = (option) => {
|
|
349
377
|
// assumes that option.selectors is provided
|
|
350
378
|
const $parent = getParentElement(option);
|
|
@@ -360,6 +388,7 @@ export function useKeyboardNav(options) {
|
|
|
360
388
|
selectors.push(element);
|
|
361
389
|
}
|
|
362
390
|
else {
|
|
391
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- Vue component $el is typed as any; shape guaranteed by VNode contract
|
|
363
392
|
selectors.push(element.$el);
|
|
364
393
|
}
|
|
365
394
|
}
|
|
@@ -374,6 +403,7 @@ export function useKeyboardNav(options) {
|
|
|
374
403
|
selectors.push(element);
|
|
375
404
|
}
|
|
376
405
|
else {
|
|
406
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- Vue component $el is typed as any; shape guaranteed by VNode contract
|
|
377
407
|
selectors.push(element.$el);
|
|
378
408
|
}
|
|
379
409
|
}
|
|
@@ -392,7 +422,7 @@ export function useKeyboardNav(options) {
|
|
|
392
422
|
}
|
|
393
423
|
else if ($parent) {
|
|
394
424
|
// TODO: what should happen if no parent or selectors are provided?
|
|
395
|
-
const $children = Array.from($parent.children);
|
|
425
|
+
const $children = Array.from($parent.children).filter((el) => el instanceof HTMLElement);
|
|
396
426
|
selectors = $children.filter(selector => {
|
|
397
427
|
// ignore elements not in the tab order or are not visible
|
|
398
428
|
return isFocusable(selector) && isVisible(selector);
|
|
@@ -415,9 +445,9 @@ export function useKeyboardNav(options) {
|
|
|
415
445
|
const listener = handlers[key];
|
|
416
446
|
// check if the handler has modifiers, and if the modifier is active;
|
|
417
447
|
// this is to ensure exact key-press matches
|
|
418
|
-
const hasModifier = keys.filter(
|
|
419
|
-
const isModifierActive = modifierKeys.some(
|
|
420
|
-
const modifierKey =
|
|
448
|
+
const hasModifier = keys.filter(k => modifierKeys.includes(k));
|
|
449
|
+
const isModifierActive = modifierKeys.some(mk => {
|
|
450
|
+
const modifierKey = mk.charAt(0).toUpperCase() + mk.slice(1);
|
|
421
451
|
return event.getModifierState(modifierKey);
|
|
422
452
|
});
|
|
423
453
|
if (hasModifier.length > 0) {
|