arrowtab 0.0.2 → 0.0.4
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/LICENSE +21 -0
- package/README.md +111 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/index.test.d.ts +1 -0
- package/dist/index.test.js +6 -0
- package/dist/index.test.js.map +1 -0
- package/dist/lib/debugMode.d.ts +8 -0
- package/dist/lib/debugMode.js +68 -0
- package/dist/lib/debugMode.js.map +1 -0
- package/dist/lib/distances.d.ts +13 -0
- package/dist/lib/distances.js +15 -0
- package/dist/lib/distances.js.map +1 -0
- package/dist/lib/getAngle.d.ts +4 -0
- package/dist/lib/getAngle.js +15 -0
- package/dist/lib/getAngle.js.map +1 -0
- package/dist/lib/getFocusable.js +29 -0
- package/dist/lib/getFocusable.js.map +1 -0
- package/dist/lib/getXyDistance.d.ts +7 -0
- package/dist/lib/getXyDistance.js +12 -0
- package/dist/lib/getXyDistance.js.map +1 -0
- package/dist/lib/hasDisabledKeys.d.ts +4 -0
- package/dist/lib/hasDisabledKeys.js +26 -0
- package/dist/lib/hasDisabledKeys.js.map +1 -0
- package/dist/lib/hasTextSelection.d.ts +4 -0
- package/dist/lib/hasTextSelection.js +53 -0
- package/dist/lib/hasTextSelection.js.map +1 -0
- package/dist/lib/initArrowTab.d.ts +5 -0
- package/dist/lib/initArrowTab.js +102 -0
- package/dist/lib/initArrowTab.js.map +1 -0
- package/dist/lib/positions.js +16 -0
- package/dist/lib/positions.js.map +1 -0
- package/dist/lib/preventNativeArrowKeyPresses.d.ts +4 -0
- package/dist/lib/preventNativeArrowKeyPresses.js +24 -0
- package/dist/lib/preventNativeArrowKeyPresses.js.map +1 -0
- package/dist/lib/strategies.d.ts +9 -23
- package/dist/lib/strategies.js +111 -39
- package/dist/lib/strategies.js.map +1 -1
- package/dist/lib/{sumBy.mjs → sumBy.js} +1 -1
- package/dist/lib/sumBy.js.map +1 -0
- package/package.json +37 -10
- package/.prettierrc +0 -5
- package/.vscode/settings.json +0 -4
- package/dist/index.d.mts +0 -1
- package/dist/index.mjs +0 -177
- package/dist/index.mjs.map +0 -1
- package/dist/lib/getFocusable.mjs +0 -15
- package/dist/lib/getFocusable.mjs.map +0 -1
- package/dist/lib/positions.mjs +0 -12
- package/dist/lib/positions.mjs.map +0 -1
- package/dist/lib/sumBy.mjs.map +0 -1
- package/index.html +0 -142
- package/src/index.mts +0 -228
- package/src/lib/getFocusable.mts +0 -13
- package/src/lib/positions.mts +0 -12
- package/src/lib/strategies.ts +0 -62
- package/src/lib/sumBy.mts +0 -4
- package/tsconfig.json +0 -20
- /package/dist/lib/{getFocusable.d.mts → getFocusable.d.ts} +0 -0
- /package/dist/lib/{positions.d.mts → positions.d.ts} +0 -0
- /package/dist/lib/{sumBy.d.mts → sumBy.d.ts} +0 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) Sean Nicholas 2024
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
# ArrowTab
|
|
2
2
|
|
|
3
|
-
**ArrowTab** allows you to navigate a webpage using the arrow keys
|
|
3
|
+
**ArrowTab** allows you to **navigate** a webpage **using the arrow keys**, automatically selecting the nearest focusable element. This enables fast and efficient keyboard-only navigation across the page.
|
|
4
4
|
|
|
5
5
|
ArrowTab is particularly **useful for business applications**, where power users need to prioritize **high productivity**.
|
|
6
6
|
|
|
7
|
-
Instead of hitting `Tab`
|
|
7
|
+
Instead of hitting `Tab` repeatedly, you can simply use the arrow keys to indicate the direction and jump to the next input, button, or link you want to select.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
ArrowTab's default settings work well, but they are not always a plug-and-play solution. It helps if you **build your app with ArrowTab in mind**. For example, aligning your focusable elements into a grid and considering the placement of your navigation. The idea is to integrate ArrowTab into your app from the beginning, not to add it as an afterthought.
|
|
10
|
+
|
|
11
|
+
## Input Behavior Changes
|
|
12
|
+
|
|
13
|
+
ArrowTab slightly modifies the behavior of some input elements to enhance keyboard navigation. For example, number inputs will not change their value when using the up and down arrow keys; instead, the focus will move to the element above or below.
|
|
14
|
+
|
|
15
|
+
To revert to the native behavior, press `Shift` + `Arrow Key`.
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
10
18
|
|
|
11
19
|
pnpm:
|
|
12
20
|
|
|
@@ -28,8 +36,107 @@ npm install arrowtab
|
|
|
28
36
|
|
|
29
37
|
## Usage
|
|
30
38
|
|
|
31
|
-
|
|
39
|
+
Vanilla JS:
|
|
40
|
+
|
|
41
|
+
```ts
|
|
32
42
|
import { initArrowTab } from 'arrowtab'
|
|
33
43
|
|
|
34
44
|
initArrowTab()
|
|
35
45
|
```
|
|
46
|
+
|
|
47
|
+
React:
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
'use client'
|
|
51
|
+
|
|
52
|
+
import { useEffect } from 'react'
|
|
53
|
+
import { initArrowTab } from 'arrowtab'
|
|
54
|
+
|
|
55
|
+
export const ArrowTab = () => {
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
const { cleanup } = initArrowTab()
|
|
58
|
+
|
|
59
|
+
return () => {
|
|
60
|
+
cleanup()
|
|
61
|
+
}
|
|
62
|
+
}, [])
|
|
63
|
+
|
|
64
|
+
return null
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Cleanup
|
|
69
|
+
|
|
70
|
+
If you want to remove the ArrowTab event listener, you can call the `cleanup` function. This is useful if you use ArrowTab in a `useEffect` hook.
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
const { cleanup } = initArrowTab()
|
|
74
|
+
|
|
75
|
+
cleanup()
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Options
|
|
79
|
+
|
|
80
|
+
You can pass options to the `initArrowTab` function. For example:
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
initArrowTab({ debug: true })
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### debug
|
|
87
|
+
|
|
88
|
+
default: `false`
|
|
89
|
+
|
|
90
|
+
If debug is set to `true`, ArrowTab will log debug information to the console. It also enables the visual debug mode. To activate the visual debug mode, press `Ctrl` + `Arrow Key`. For example, `Ctrl` + `Arrow Down` will highlight all the focusable elements.
|
|
91
|
+
|
|
92
|
+
Here is an example:
|
|
93
|
+
|
|
94
|
+
<div style="padding:10px; background:white; border-radius:4px; margin-bottom:1rem">
|
|
95
|
+
<img src="docs/images/visual-debug-mode.jpg" alt="Visual debug mode example">
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
Blue indicates the current element. Green indicates focusable elements that are within reach, and red indicates those that are not within reach.
|
|
99
|
+
|
|
100
|
+
The numbers represent the order of the focusable elements. The numbers in parentheses represent the order of the focusable elements that are within reach. Lower numbers are closer to the current element.
|
|
101
|
+
|
|
102
|
+
If you click on a focusable element, debug information will be logged to the console.
|
|
103
|
+
|
|
104
|
+
To deactivate the visual debug mode, press `Esc`.
|
|
105
|
+
|
|
106
|
+
## data-arrowtab
|
|
107
|
+
|
|
108
|
+
You can control the behavior of ArrowTab by adding a `data-arrowtab` attribute to any element. Separate the keywords with a space. For example: `<div data-arrowtab="disable-left disable-right">`
|
|
109
|
+
|
|
110
|
+
Here are the available keywords:
|
|
111
|
+
|
|
112
|
+
### disable, disable-${key}
|
|
113
|
+
|
|
114
|
+
Sometimes your components are listening to arrow keys themselves. In that case, you can disable ArrowTab for that element by adding the `disable` attribute.
|
|
115
|
+
|
|
116
|
+
If you only want to disable a specific key, then use: `disable-left`, `disable-right`, `disable-up`, `disable-down`.
|
|
117
|
+
|
|
118
|
+
## Development
|
|
119
|
+
|
|
120
|
+
```sh
|
|
121
|
+
pnpm dev
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
A demo page will open in your browser at `http://localhost:8080`.
|
|
125
|
+
|
|
126
|
+
If you want to use the library in your app, you can use the following code:
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
import { initArrowTab } from 'http://localhost:8080/dist/index.js'
|
|
130
|
+
|
|
131
|
+
initArrowTab()
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Or as a script tag:
|
|
135
|
+
|
|
136
|
+
```html
|
|
137
|
+
<script type="module">
|
|
138
|
+
import { initArrowTab } from 'http://localhost:8080/dist/index.js'
|
|
139
|
+
|
|
140
|
+
initArrowTab()
|
|
141
|
+
</script>
|
|
142
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { initArrowTab } from './lib/initArrowTab.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.test.js","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AACrC,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAEzC,IAAI,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACjC,MAAM,CAAC,YAAY,CAAC,CAAC,WAAW,EAAE,CAAA;AACpC,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
let inDebugMode = false;
|
|
2
|
+
export const activateDebugMode = ({ focusableElements, }) => {
|
|
3
|
+
inDebugMode = true;
|
|
4
|
+
if (document.activeElement) {
|
|
5
|
+
addDebugElementToDom({
|
|
6
|
+
element: document.activeElement,
|
|
7
|
+
withinReach: false,
|
|
8
|
+
content: '',
|
|
9
|
+
color: 'rgb(33 150 243 / 80%)',
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
let counter = focusableElements.length;
|
|
13
|
+
let withinReachCounter = focusableElements.filter((focusable) => focusable.withinReach).length;
|
|
14
|
+
for (const focusable of [...focusableElements].reverse()) {
|
|
15
|
+
addDebugElementToDom({
|
|
16
|
+
...focusable,
|
|
17
|
+
content: `<div>${counter--} ${focusable.withinReach ? `(${withinReachCounter--})` : ''}</div>`,
|
|
18
|
+
color: focusable.withinReach
|
|
19
|
+
? 'rgba(76 175 80 / 80%)'
|
|
20
|
+
: 'rgb(244 67 54 / 80%)',
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
export const deactivateDebugMode = () => {
|
|
25
|
+
document.querySelectorAll('[data-arrowtab="debug"]').forEach((div) => {
|
|
26
|
+
div.remove();
|
|
27
|
+
});
|
|
28
|
+
inDebugMode = false;
|
|
29
|
+
return;
|
|
30
|
+
};
|
|
31
|
+
export const isInDebugMode = () => inDebugMode;
|
|
32
|
+
const addDebugElementToDom = (focusable) => {
|
|
33
|
+
if (!(focusable.element instanceof HTMLElement))
|
|
34
|
+
return;
|
|
35
|
+
const div = document.createElement('div');
|
|
36
|
+
const rect = focusable.element.getBoundingClientRect();
|
|
37
|
+
const scrollX = document.documentElement.scrollLeft;
|
|
38
|
+
const scrollY = document.documentElement.scrollTop;
|
|
39
|
+
div.style.position = 'absolute';
|
|
40
|
+
div.style.top = `${rect.top + scrollY}px`;
|
|
41
|
+
div.style.left = `${rect.left + scrollX}px`;
|
|
42
|
+
let width = Math.max(rect.width, 10);
|
|
43
|
+
let height = Math.max(rect.height, 10);
|
|
44
|
+
div.style.width = `${width}px`;
|
|
45
|
+
div.style.height = `${height}px`;
|
|
46
|
+
div.style.color = 'white';
|
|
47
|
+
div.style.backgroundColor = focusable.color;
|
|
48
|
+
div.style.zIndex = '100000';
|
|
49
|
+
div.style.display = 'flex';
|
|
50
|
+
div.style.alignItems = 'center';
|
|
51
|
+
div.style.justifyContent = 'center';
|
|
52
|
+
div.style.cursor = 'pointer';
|
|
53
|
+
div.style.borderRadius = '4px';
|
|
54
|
+
div.innerHTML = focusable.content;
|
|
55
|
+
div.dataset.arrowtab = 'debug';
|
|
56
|
+
// Move the click event to the focusable element
|
|
57
|
+
div.addEventListener('click', (e) => {
|
|
58
|
+
e.preventDefault();
|
|
59
|
+
e.stopPropagation();
|
|
60
|
+
console.log({
|
|
61
|
+
focusable,
|
|
62
|
+
rect,
|
|
63
|
+
debugElement: div,
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
document.body.appendChild(div);
|
|
67
|
+
};
|
|
68
|
+
//# sourceMappingURL=debugMode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debugMode.js","sourceRoot":"","sources":["../../src/lib/debugMode.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare const getEuclideanDistance: ({ xDistance, yDistance, }: {
|
|
2
|
+
xDistance: number;
|
|
3
|
+
yDistance: number;
|
|
4
|
+
}) => number;
|
|
5
|
+
export declare const manhattan: ({ xDistance, yDistance, }: {
|
|
6
|
+
xDistance: number;
|
|
7
|
+
yDistance: number;
|
|
8
|
+
}) => number;
|
|
9
|
+
export declare const manhattanWeighted: ({ xDistance, yDistance, event, }: {
|
|
10
|
+
xDistance: number;
|
|
11
|
+
yDistance: number;
|
|
12
|
+
event: KeyboardEvent;
|
|
13
|
+
}) => number | undefined;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const getEuclideanDistance = ({ xDistance, yDistance, }) => {
|
|
2
|
+
return Math.sqrt(xDistance ** 2 + yDistance ** 2);
|
|
3
|
+
};
|
|
4
|
+
export const manhattan = ({ xDistance, yDistance, }) => {
|
|
5
|
+
return Math.abs(xDistance) + Math.abs(yDistance);
|
|
6
|
+
};
|
|
7
|
+
export const manhattanWeighted = ({ xDistance, yDistance, event, }) => {
|
|
8
|
+
if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
|
|
9
|
+
return Math.abs(xDistance) + Math.abs(yDistance) * 0.1;
|
|
10
|
+
}
|
|
11
|
+
if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') {
|
|
12
|
+
return Math.abs(xDistance) * 0.1 + Math.abs(yDistance);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=distances.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"distances.js","sourceRoot":"","sources":["../../src/lib/distances.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,EACnC,SAAS,EACT,SAAS,GAIV,EAAE,EAAE;IACH,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,GAAG,SAAS,IAAI,CAAC,CAAC,CAAA;AACnD,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,EACxB,SAAS,EACT,SAAS,GAIV,EAAE,EAAE;IACH,OAAO,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;AAClD,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,EAChC,SAAS,EACT,SAAS,EACT,KAAK,GAKN,EAAE,EAAE;IACH,IAAI,KAAK,CAAC,GAAG,KAAK,WAAW,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;QACzD,OAAO,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,GAAG,CAAA;IACxD,CAAC;IACD,IAAI,KAAK,CAAC,GAAG,KAAK,WAAW,IAAI,KAAK,CAAC,GAAG,KAAK,YAAY,EAAE,CAAC;QAC5D,OAAO,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;IACxD,CAAC;AACH,CAAC,CAAA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { getXyDistance } from './getXyDistance.js';
|
|
2
|
+
export const getAngle = ({ activeElement, element, }) => {
|
|
3
|
+
const { xDistance, yDistance } = getXyDistance({
|
|
4
|
+
activeElement,
|
|
5
|
+
element,
|
|
6
|
+
});
|
|
7
|
+
let angle = Math.acos(yDistance / Math.sqrt(xDistance ** 2 + yDistance ** 2)) *
|
|
8
|
+
(180 / Math.PI);
|
|
9
|
+
const direction = xDistance > 0 ? 'right' : 'left';
|
|
10
|
+
if (direction === 'right') {
|
|
11
|
+
angle = 360 - angle;
|
|
12
|
+
}
|
|
13
|
+
return angle;
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=getAngle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getAngle.js","sourceRoot":"","sources":["../../src/lib/getAngle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAElD,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,EACvB,aAAa,EACb,OAAO,GAIR,EAAE,EAAE;IACH,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,aAAa,CAAC;QAC7C,aAAa;QACb,OAAO;KACR,CAAC,CAAA;IAEF,IAAI,KAAK,GACP,IAAI,CAAC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,GAAG,SAAS,IAAI,CAAC,CAAC,CAAC;QACjE,CAAC,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,CAAA;IACjB,MAAM,SAAS,GAAG,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAA;IAClD,IAAI,SAAS,KAAK,OAAO,EAAE,CAAC;QAC1B,KAAK,GAAG,GAAG,GAAG,KAAK,CAAA;IACrB,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC,CAAA"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export const getFocusable = () => {
|
|
2
|
+
// Selectors from https://zellwk.com/blog/keyboard-focusable-elements/
|
|
3
|
+
const allFocusable = document.querySelectorAll('a[href], button, input, textarea, select, details, [tabindex]:not([tabindex^="-"])');
|
|
4
|
+
const focusable = Array.from(allFocusable).filter((element) => {
|
|
5
|
+
if ('disabled' in element && element.disabled)
|
|
6
|
+
return false;
|
|
7
|
+
let currentElement = element;
|
|
8
|
+
while (currentElement) {
|
|
9
|
+
if ('hidden' in currentElement && currentElement.hidden)
|
|
10
|
+
return false;
|
|
11
|
+
if (currentElement.getAttribute('aria-hidden') === 'true')
|
|
12
|
+
return false;
|
|
13
|
+
const style = window.getComputedStyle(currentElement);
|
|
14
|
+
if (style.display === 'none' ||
|
|
15
|
+
style.visibility === 'hidden' ||
|
|
16
|
+
style.opacity === '0') {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
const rect = currentElement.getBoundingClientRect();
|
|
20
|
+
if (rect.width === 0 && rect.height === 0) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
currentElement = currentElement.parentElement;
|
|
24
|
+
}
|
|
25
|
+
return true;
|
|
26
|
+
});
|
|
27
|
+
return focusable;
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=getFocusable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getFocusable.js","sourceRoot":"","sources":["../../src/lib/getFocusable.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,YAAY,GAAG,GAAG,EAAE;IAC/B,sEAAsE;IACtE,MAAM,YAAY,GAAG,QAAQ,CAAC,gBAAgB,CAC5C,oFAAoF,CACrF,CAAA;IACD,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE;QAC5D,IAAI,UAAU,IAAI,OAAO,IAAI,OAAO,CAAC,QAAQ;YAAE,OAAO,KAAK,CAAA;QAE3D,IAAI,cAAc,GAAmB,OAAO,CAAA;QAC5C,OAAO,cAAc,EAAE,CAAC;YACtB,IAAI,QAAQ,IAAI,cAAc,IAAI,cAAc,CAAC,MAAM;gBAAE,OAAO,KAAK,CAAA;YACrE,IAAI,cAAc,CAAC,YAAY,CAAC,aAAa,CAAC,KAAK,MAAM;gBAAE,OAAO,KAAK,CAAA;YAEvE,MAAM,KAAK,GAAG,MAAM,CAAC,gBAAgB,CAAC,cAAc,CAAC,CAAA;YACrD,IACE,KAAK,CAAC,OAAO,KAAK,MAAM;gBACxB,KAAK,CAAC,UAAU,KAAK,QAAQ;gBAC7B,KAAK,CAAC,OAAO,KAAK,GAAG,EACrB,CAAC;gBACD,OAAO,KAAK,CAAA;YACd,CAAC;YAED,MAAM,IAAI,GAAG,cAAc,CAAC,qBAAqB,EAAE,CAAA;YACnD,IAAI,IAAI,CAAC,KAAK,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC1C,OAAO,KAAK,CAAA;YACd,CAAC;YACD,cAAc,GAAG,cAAc,CAAC,aAAa,CAAA;QAC/C,CAAC;QAED,OAAO,IAAI,CAAA;IACb,CAAC,CAAC,CAAA;IACF,OAAO,SAAS,CAAA;AAClB,CAAC,CAAA"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { getMiddle } from './positions.js';
|
|
2
|
+
export const getXyDistance = ({ activeElement, element, }) => {
|
|
3
|
+
const activePosition = getMiddle(activeElement);
|
|
4
|
+
const position = getMiddle(element);
|
|
5
|
+
const xDistance = position.x - activePosition.x;
|
|
6
|
+
const yDistance = position.y - activePosition.y;
|
|
7
|
+
return {
|
|
8
|
+
xDistance,
|
|
9
|
+
yDistance,
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=getXyDistance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getXyDistance.js","sourceRoot":"","sources":["../../src/lib/getXyDistance.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAE1C,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,EAC5B,aAAa,EACb,OAAO,GAIR,EAAE,EAAE;IACH,MAAM,cAAc,GAAG,SAAS,CAAC,aAAa,CAAC,CAAA;IAC/C,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,CAAC,CAAA;IAEnC,MAAM,SAAS,GAAG,QAAQ,CAAC,CAAC,GAAG,cAAc,CAAC,CAAC,CAAA;IAC/C,MAAM,SAAS,GAAG,QAAQ,CAAC,CAAC,GAAG,cAAc,CAAC,CAAC,CAAA;IAE/C,OAAO;QACL,SAAS;QACT,SAAS;KACV,CAAA;AACH,CAAC,CAAA"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export const hasDisabledKeys = ({ event, activeElement, }) => {
|
|
2
|
+
const checkElement = (element) => {
|
|
3
|
+
const data = element.getAttribute('data-arrowtab');
|
|
4
|
+
if (data) {
|
|
5
|
+
const keywords = data.split(' ');
|
|
6
|
+
if (keywords.includes('disable')) {
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
const direction = event.key.toLowerCase().replace('arrow', '');
|
|
10
|
+
console.log({ direction, keywords });
|
|
11
|
+
if (keywords.includes(`disable-${direction}`)) {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return false;
|
|
16
|
+
};
|
|
17
|
+
let currentElement = activeElement;
|
|
18
|
+
while (currentElement) {
|
|
19
|
+
if (checkElement(currentElement)) {
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
currentElement = currentElement.parentElement;
|
|
23
|
+
}
|
|
24
|
+
return false;
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=hasDisabledKeys.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hasDisabledKeys.js","sourceRoot":"","sources":["../../src/lib/hasDisabledKeys.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,EAC9B,KAAK,EACL,aAAa,GAId,EAAE,EAAE;IACH,MAAM,YAAY,GAAG,CAAC,OAAgB,EAAW,EAAE;QACjD,MAAM,IAAI,GAAG,OAAO,CAAC,YAAY,CAAC,eAAe,CAAC,CAAA;QAElD,IAAI,IAAI,EAAE,CAAC;YACT,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;YAChC,IAAI,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;gBACjC,OAAO,IAAI,CAAA;YACb,CAAC;YAED,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAA;YAC9D,OAAO,CAAC,GAAG,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAA;YACpC,IAAI,QAAQ,CAAC,QAAQ,CAAC,WAAW,SAAS,EAAE,CAAC,EAAE,CAAC;gBAC9C,OAAO,IAAI,CAAA;YACb,CAAC;QACH,CAAC;QAED,OAAO,KAAK,CAAA;IACd,CAAC,CAAA;IAED,IAAI,cAAc,GAAmB,aAAa,CAAA;IAClD,OAAO,cAAc,EAAE,CAAC;QACtB,IAAI,YAAY,CAAC,cAAc,CAAC,EAAE,CAAC;YACjC,OAAO,IAAI,CAAA;QACb,CAAC;QACD,cAAc,GAAG,cAAc,CAAC,aAAa,CAAA;IAC/C,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC,CAAA"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { sumBy } from './sumBy.js';
|
|
2
|
+
export const hasTextSelection = ({ activeElement, event, }) => {
|
|
3
|
+
if (activeElement instanceof HTMLInputElement ||
|
|
4
|
+
activeElement instanceof HTMLTextAreaElement) {
|
|
5
|
+
const selectionStart = activeElement.selectionStart;
|
|
6
|
+
const selectionEnd = activeElement.selectionEnd;
|
|
7
|
+
if (event.key === 'ArrowLeft' &&
|
|
8
|
+
selectionStart !== undefined &&
|
|
9
|
+
selectionStart !== null &&
|
|
10
|
+
selectionStart !== 0) {
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
if (event.key === 'ArrowRight' &&
|
|
14
|
+
selectionStart !== undefined &&
|
|
15
|
+
selectionStart !== null &&
|
|
16
|
+
selectionStart !== activeElement.value?.length) {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
if ((event.key === 'ArrowLeft' || event.key === 'ArrowRight') &&
|
|
20
|
+
selectionStart !== undefined &&
|
|
21
|
+
selectionEnd !== undefined &&
|
|
22
|
+
selectionStart !== null &&
|
|
23
|
+
selectionEnd !== null &&
|
|
24
|
+
selectionStart !== selectionEnd) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
if (event.key === 'ArrowUp' &&
|
|
28
|
+
selectionStart !== undefined &&
|
|
29
|
+
selectionStart !== null &&
|
|
30
|
+
activeElement instanceof HTMLTextAreaElement) {
|
|
31
|
+
const rows = activeElement.value?.split('\n');
|
|
32
|
+
const start = selectionStart;
|
|
33
|
+
const firstRow = rows.at(0);
|
|
34
|
+
if (firstRow && start > firstRow.length) {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (event.key === 'ArrowDown' &&
|
|
39
|
+
selectionStart !== undefined &&
|
|
40
|
+
selectionStart !== null &&
|
|
41
|
+
activeElement instanceof HTMLTextAreaElement) {
|
|
42
|
+
const rows = activeElement.value?.split('\n');
|
|
43
|
+
const withoutLastRow = rows.slice(0, -1);
|
|
44
|
+
const lengthUntilLastRow = sumBy(withoutLastRow, (row) => row.length);
|
|
45
|
+
const start = selectionStart;
|
|
46
|
+
if (start <= lengthUntilLastRow + 1) {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return false;
|
|
52
|
+
};
|
|
53
|
+
//# sourceMappingURL=hasTextSelection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hasTextSelection.js","sourceRoot":"","sources":["../../src/lib/hasTextSelection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAElC,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,EAC/B,aAAa,EACb,KAAK,GAIN,EAAE,EAAE;IACH,IACE,aAAa,YAAY,gBAAgB;QACzC,aAAa,YAAY,mBAAmB,EAC5C,CAAC;QACD,MAAM,cAAc,GAAG,aAAa,CAAC,cAAc,CAAA;QACnD,MAAM,YAAY,GAAG,aAAa,CAAC,YAAY,CAAA;QAE/C,IACE,KAAK,CAAC,GAAG,KAAK,WAAW;YACzB,cAAc,KAAK,SAAS;YAC5B,cAAc,KAAK,IAAI;YACvB,cAAc,KAAK,CAAC,EACpB,CAAC;YACD,OAAO,IAAI,CAAA;QACb,CAAC;QAED,IACE,KAAK,CAAC,GAAG,KAAK,YAAY;YAC1B,cAAc,KAAK,SAAS;YAC5B,cAAc,KAAK,IAAI;YACvB,cAAc,KAAK,aAAa,CAAC,KAAK,EAAE,MAAM,EAC9C,CAAC;YACD,OAAO,IAAI,CAAA;QACb,CAAC;QAED,IACE,CAAC,KAAK,CAAC,GAAG,KAAK,WAAW,IAAI,KAAK,CAAC,GAAG,KAAK,YAAY,CAAC;YACzD,cAAc,KAAK,SAAS;YAC5B,YAAY,KAAK,SAAS;YAC1B,cAAc,KAAK,IAAI;YACvB,YAAY,KAAK,IAAI;YACrB,cAAc,KAAK,YAAY,EAC/B,CAAC;YACD,OAAO,IAAI,CAAA;QACb,CAAC;QAED,IACE,KAAK,CAAC,GAAG,KAAK,SAAS;YACvB,cAAc,KAAK,SAAS;YAC5B,cAAc,KAAK,IAAI;YACvB,aAAa,YAAY,mBAAmB,EAC5C,CAAC;YACD,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;YAC7C,MAAM,KAAK,GAAG,cAAc,CAAA;YAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;YAC3B,IAAI,QAAQ,IAAI,KAAK,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC;gBACxC,OAAO,IAAI,CAAA;YACb,CAAC;QACH,CAAC;QAED,IACE,KAAK,CAAC,GAAG,KAAK,WAAW;YACzB,cAAc,KAAK,SAAS;YAC5B,cAAc,KAAK,IAAI;YACvB,aAAa,YAAY,mBAAmB,EAC5C,CAAC;YACD,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;YAC7C,MAAM,cAAc,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;YACxC,MAAM,kBAAkB,GAAG,KAAK,CAAC,cAAc,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;YAErE,MAAM,KAAK,GAAG,cAAc,CAAA;YAC5B,IAAI,KAAK,IAAI,kBAAkB,GAAG,CAAC,EAAE,CAAC;gBACpC,OAAO,IAAI,CAAA;YACb,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC,CAAA"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { activateDebugMode, deactivateDebugMode, isInDebugMode, } from './debugMode.js';
|
|
2
|
+
import { getFocusable } from './getFocusable.js';
|
|
3
|
+
import { hasDisabledKeys } from './hasDisabledKeys.js';
|
|
4
|
+
import { hasTextSelection } from './hasTextSelection.js';
|
|
5
|
+
import { preventNativeArrowKeyPresses } from './preventNativeArrowKeyPresses.js';
|
|
6
|
+
import { getByGrid } from './strategies.js';
|
|
7
|
+
// TODO: Options
|
|
8
|
+
// modifierKey: 'ctrlKey',
|
|
9
|
+
// onlyWithModifierKey: false, // TODO: Better name
|
|
10
|
+
// strategy: arrowTab.XWalkEuclidean,
|
|
11
|
+
// selector: undefined,
|
|
12
|
+
// elementFilter: () => boolean,
|
|
13
|
+
// getElementPosition: (element) => { x: number, y: number },
|
|
14
|
+
// eventTriggerName: 'keydown' | 'keyup',
|
|
15
|
+
// earlyReturn: (event) => boolean, // TODO: Better name
|
|
16
|
+
// TODO: Check if tab focuses the same elements as arrowTab
|
|
17
|
+
// TODO: select, details, summary, iframe
|
|
18
|
+
// TODO: Investigate links that don't show focus outline sometimes
|
|
19
|
+
export const initArrowTab = ({ debug = false } = {}) => {
|
|
20
|
+
const onKeyDown = (event) => {
|
|
21
|
+
if (event.key === 'Escape' && isInDebugMode()) {
|
|
22
|
+
deactivateDebugMode();
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (event.key !== 'ArrowLeft' &&
|
|
26
|
+
event.key !== 'ArrowRight' &&
|
|
27
|
+
event.key !== 'ArrowUp' &&
|
|
28
|
+
event.key !== 'ArrowDown') {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
// TODO: Use modifierKey
|
|
32
|
+
if (event.shiftKey) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const activeElement = document.activeElement;
|
|
36
|
+
if (!activeElement) {
|
|
37
|
+
if (debug) {
|
|
38
|
+
console.log('no active element');
|
|
39
|
+
}
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (hasDisabledKeys({ event, activeElement })) {
|
|
43
|
+
if (debug) {
|
|
44
|
+
console.log('arrow keys are disabled');
|
|
45
|
+
}
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
preventNativeArrowKeyPresses({ event, activeElement });
|
|
49
|
+
const nothingHasFocus = activeElement === document.body;
|
|
50
|
+
if (nothingHasFocus) {
|
|
51
|
+
if (debug) {
|
|
52
|
+
console.log('nothing has focus. using first focusable');
|
|
53
|
+
}
|
|
54
|
+
const firstFocusable = getFocusable()?.[0];
|
|
55
|
+
if (firstFocusable instanceof HTMLElement) {
|
|
56
|
+
firstFocusable.focus();
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (hasTextSelection({ activeElement, event })) {
|
|
61
|
+
if (debug) {
|
|
62
|
+
console.log('element has text selection');
|
|
63
|
+
}
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const allFocusable = getFocusable();
|
|
67
|
+
const withoutActiveElement = allFocusable.filter((element) => element !== activeElement);
|
|
68
|
+
const sorted = getByGrid({
|
|
69
|
+
focusableElements: withoutActiveElement,
|
|
70
|
+
activeElement,
|
|
71
|
+
event,
|
|
72
|
+
});
|
|
73
|
+
const withinReach = sorted.filter(({ withinReach }) => withinReach);
|
|
74
|
+
if (debug && event.ctrlKey) {
|
|
75
|
+
if (isInDebugMode()) {
|
|
76
|
+
deactivateDebugMode();
|
|
77
|
+
}
|
|
78
|
+
activateDebugMode({ focusableElements: sorted });
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
let nearest = withinReach.at(0);
|
|
82
|
+
if (debug) {
|
|
83
|
+
console.log('found nearest element to select', nearest);
|
|
84
|
+
}
|
|
85
|
+
if (!nearest) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (nearest.element instanceof HTMLElement) {
|
|
89
|
+
if (debug) {
|
|
90
|
+
console.log('focusing element', nearest);
|
|
91
|
+
}
|
|
92
|
+
nearest.element.focus();
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
document.addEventListener('keydown', onKeyDown, { capture: true });
|
|
96
|
+
return {
|
|
97
|
+
cleanup: () => {
|
|
98
|
+
document.removeEventListener('keydown', onKeyDown, { capture: true });
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
//# sourceMappingURL=initArrowTab.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"initArrowTab.js","sourceRoot":"","sources":["../../src/lib/initArrowTab.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export const getMiddle = (element) => {
|
|
2
|
+
const { x, y, width, height } = element.getBoundingClientRect();
|
|
3
|
+
const scrollX = document.documentElement.scrollLeft;
|
|
4
|
+
const scrollY = document.documentElement.scrollTop;
|
|
5
|
+
return {
|
|
6
|
+
x: x + width / 2 + scrollX,
|
|
7
|
+
y: y + height / 2 + scrollY,
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
export const getTopLeft = (element) => {
|
|
11
|
+
const { x, y } = element.getBoundingClientRect();
|
|
12
|
+
const scrollX = document.documentElement.scrollLeft;
|
|
13
|
+
const scrollY = document.documentElement.scrollTop;
|
|
14
|
+
return { x: x + scrollX, y: y + scrollY };
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=positions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"positions.js","sourceRoot":"","sources":["../../src/lib/positions.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,OAAgB,EAAE,EAAE;IAC5C,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,qBAAqB,EAAE,CAAA;IAC/D,MAAM,OAAO,GAAG,QAAQ,CAAC,eAAe,CAAC,UAAU,CAAA;IACnD,MAAM,OAAO,GAAG,QAAQ,CAAC,eAAe,CAAC,SAAS,CAAA;IAClD,OAAO;QACL,CAAC,EAAE,CAAC,GAAG,KAAK,GAAG,CAAC,GAAG,OAAO;QAC1B,CAAC,EAAE,CAAC,GAAG,MAAM,GAAG,CAAC,GAAG,OAAO;KAC5B,CAAA;AACH,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,OAAgB,EAAE,EAAE;IAC7C,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,OAAO,CAAC,qBAAqB,EAAE,CAAA;IAChD,MAAM,OAAO,GAAG,QAAQ,CAAC,eAAe,CAAC,UAAU,CAAA;IACnD,MAAM,OAAO,GAAG,QAAQ,CAAC,eAAe,CAAC,SAAS,CAAA;IAClD,OAAO,EAAE,CAAC,EAAE,CAAC,GAAG,OAAO,EAAE,CAAC,EAAE,CAAC,GAAG,OAAO,EAAE,CAAA;AAC3C,CAAC,CAAA"}
|