Skip to content

Commit ebe2710

Browse files
feat: add "move to heading" commands (#51)
1 parent 26d3151 commit ebe2710

15 files changed

+1445
-134
lines changed

src/Virtual.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
ScreenReader,
99
WindowsModifiers,
1010
} from "@guidepup/guidepup";
11-
import { commands, VirtualCommandKey, VirtualCommands } from "./commands";
11+
import { commands, VirtualCommands } from "./commands";
1212
import {
1313
ERR_VIRTUAL_MISSING_CONTAINER,
1414
ERR_VIRTUAL_NOT_STARTED,
@@ -369,11 +369,11 @@ export class Virtual implements ScreenReader {
369369
* ```
370370
*/
371371
get commands() {
372-
return Object.fromEntries<VirtualCommandKey>(
373-
(Object.keys(commands) as VirtualCommandKey[]).map(
374-
(command: VirtualCommandKey) => [command, command]
372+
return Object.fromEntries<keyof VirtualCommands>(
373+
(Object.keys(commands) as (keyof VirtualCommands)[]).map(
374+
(command: keyof VirtualCommands) => [command, command]
375375
)
376-
) as { [K in VirtualCommandKey]: K };
376+
) as { [K in keyof VirtualCommands]: K };
377377
}
378378

379379
/**
@@ -800,7 +800,7 @@ export class Virtual implements ScreenReader {
800800
* @param {object} [options] Command options.
801801
*/
802802
async perform<
803-
T extends VirtualCommandKey,
803+
T extends keyof VirtualCommands,
804804
K extends Omit<Parameters<VirtualCommands[T]>[0], keyof VirtualCommandArgs>
805805
>(command: T, options?: { [L in keyof K]: K[L] } & CommandOptions) {
806806
this.#checkContainer();
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import {
2+
AccessibilityNode,
3+
END_OF_ROLE_PREFIX,
4+
} from "../createAccessibilityTree";
5+
import { matchesAccessibleAttributes, matchesRoles } from "./nodeMatchers";
6+
import type { AriaAttributes } from "./types";
7+
8+
export interface GetIndexFilters {
9+
/** Matches a node only if the node has any of these roles */
10+
roles?: Readonly<string[]>;
11+
12+
/** Matches a node only if the node has **all** of these aria attributes */
13+
ariaAttributes?: Readonly<AriaAttributes>;
14+
}
15+
16+
export function getIndexByRoleAndAttributes({
17+
filters,
18+
reorderedTree,
19+
tree,
20+
}: {
21+
filters: GetIndexFilters;
22+
reorderedTree: AccessibilityNode[];
23+
tree: AccessibilityNode[];
24+
}) {
25+
const accessibilityNode = reorderedTree.find(
26+
(node) =>
27+
!node.spokenRole.startsWith(END_OF_ROLE_PREFIX) &&
28+
matchesRoles(node, filters.roles) &&
29+
matchesAccessibleAttributes(node, filters.ariaAttributes)
30+
);
31+
32+
if (!accessibilityNode) {
33+
return null;
34+
}
35+
36+
return tree.findIndex((node) => node === accessibilityNode);
37+
}

src/commands/getNextIndexByRole.ts

Lines changed: 0 additions & 22 deletions
This file was deleted.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import {
2+
getIndexByRoleAndAttributes,
3+
type GetIndexFilters,
4+
} from "./getIndexByRoleAndAttributes";
5+
import type { VirtualCommandArgs } from "./types";
6+
7+
export type GetNextIndexArgs = Omit<VirtualCommandArgs, "container">;
8+
9+
export function getNextIndexByRoleAndAttributes(
10+
filters: Readonly<GetIndexFilters>
11+
) {
12+
return function getNextIndexByRoleAndAttributesInner({
13+
currentIndex,
14+
tree,
15+
}: GetNextIndexArgs) {
16+
const reorderedTree = tree
17+
.slice(currentIndex + 1)
18+
.concat(tree.slice(0, currentIndex + 1));
19+
20+
return getIndexByRoleAndAttributes({ filters, reorderedTree, tree });
21+
};
22+
}

src/commands/getPreviousIndexByRole.ts

Lines changed: 0 additions & 26 deletions
This file was deleted.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import {
2+
getIndexByRoleAndAttributes,
3+
type GetIndexFilters,
4+
} from "./getIndexByRoleAndAttributes";
5+
import type { GetNextIndexArgs } from "./getNextIndexByRoleAndAttributes";
6+
7+
type GetPreviousIndexArgs = GetNextIndexArgs;
8+
9+
export function getPreviousIndexByRoleAndAttributes(
10+
filters: Readonly<GetIndexFilters>
11+
) {
12+
return function getPreviousIndexInner({
13+
currentIndex,
14+
tree,
15+
}: GetPreviousIndexArgs) {
16+
const reorderedTree = tree
17+
.slice(0, currentIndex)
18+
.reverse()
19+
.concat(tree.slice(currentIndex).reverse());
20+
21+
return getIndexByRoleAndAttributes({ filters, reorderedTree, tree });
22+
};
23+
}

0 commit comments

Comments
 (0)