Skip to content

Commit 19fed27

Browse files
authored
feat: Allow disposing of a KeyboardNavigation instance. (#190)
1 parent 1974c93 commit 19fed27

File tree

3 files changed

+92
-19
lines changed

3 files changed

+92
-19
lines changed

src/index.ts

Lines changed: 82 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,24 @@ export class KeyboardNavigation {
1313
/** The workspace. */
1414
protected workspace: Blockly.WorkspaceSvg;
1515

16+
/** Event handler run when the workspace gains focus. */
17+
private focusListener: () => void;
18+
19+
/** Event handler run when the workspace loses focus. */
20+
private blurListener: () => void;
21+
22+
/** Keyboard navigation controller instance for the workspace. */
23+
private navigationController: NavigationController;
24+
25+
/**
26+
* These fields are used to preserve the workspace's initial state to restore
27+
* it when/if keyboard navigation is disabled.
28+
*/
29+
private injectionDivTabIndex: string | null;
30+
private workspaceParentTabIndex: string | null;
31+
private originalTheme: Blockly.Theme;
32+
private originalCursor: Blockly.Cursor | null;
33+
1634
/**
1735
* Constructs the keyboard navigation.
1836
*
@@ -22,33 +40,80 @@ export class KeyboardNavigation {
2240
constructor(workspace: Blockly.WorkspaceSvg) {
2341
this.workspace = workspace;
2442

25-
const navigationController = new NavigationController();
26-
navigationController.init();
27-
navigationController.addWorkspace(workspace);
28-
navigationController.enable(workspace);
29-
navigationController.listShortcuts();
43+
this.navigationController = new NavigationController();
44+
this.navigationController.init();
45+
this.navigationController.addWorkspace(workspace);
46+
this.navigationController.enable(workspace);
47+
this.navigationController.listShortcuts();
3048

49+
this.originalTheme = workspace.getTheme();
3150
this.setGlowTheme();
51+
this.originalCursor = workspace.getMarkerManager().getCursor();
3252
installCursor(workspace.getMarkerManager());
3353

3454
// Ensure that only the root SVG G (group) has a tab index.
55+
this.injectionDivTabIndex = workspace
56+
.getInjectionDiv()
57+
.getAttribute('tabindex');
3558
workspace.getInjectionDiv().removeAttribute('tabindex');
59+
this.workspaceParentTabIndex = workspace
60+
.getParentSvg()
61+
.getAttribute('tabindex');
3662
workspace.getParentSvg().removeAttribute('tabindex');
3763

38-
workspace.getSvgGroup().addEventListener('focus', () => {
39-
navigationController.setHasFocus(workspace, true);
40-
});
41-
workspace.getSvgGroup().addEventListener('blur', () => {
42-
navigationController.setHasFocus(workspace, false);
43-
});
64+
this.focusListener = () => {
65+
this.navigationController.setHasFocus(workspace, true);
66+
};
67+
this.blurListener = () => {
68+
this.navigationController.setHasFocus(workspace, false);
69+
};
70+
71+
workspace.getSvgGroup().addEventListener('focus', this.focusListener);
72+
workspace.getSvgGroup().addEventListener('blur', this.blurListener);
4473
// Temporary workaround for #136.
4574
// TODO(#136): fix in core.
46-
workspace.getParentSvg().addEventListener('focus', () => {
47-
navigationController.setHasFocus(workspace, true);
48-
});
49-
workspace.getParentSvg().addEventListener('blur', () => {
50-
navigationController.setHasFocus(workspace, false);
51-
});
75+
workspace.getParentSvg().addEventListener('focus', this.focusListener);
76+
workspace.getParentSvg().addEventListener('blur', this.blurListener);
77+
}
78+
79+
/**
80+
* Disables keyboard navigation for this navigator's workspace.
81+
*/
82+
dispose() {
83+
// Temporary workaround for #136.
84+
// TODO(#136): fix in core.
85+
this.workspace
86+
.getParentSvg()
87+
.removeEventListener('blur', this.blurListener);
88+
this.workspace
89+
.getParentSvg()
90+
.removeEventListener('focus', this.focusListener);
91+
92+
this.workspace.getSvgGroup().removeEventListener('blur', this.blurListener);
93+
this.workspace
94+
.getSvgGroup()
95+
.removeEventListener('focus', this.focusListener);
96+
97+
if (this.workspaceParentTabIndex) {
98+
this.workspace
99+
.getParentSvg()
100+
.setAttribute('tabindex', this.workspaceParentTabIndex);
101+
}
102+
103+
if (this.injectionDivTabIndex) {
104+
this.workspace
105+
.getInjectionDiv()
106+
.setAttribute('tabindex', this.injectionDivTabIndex);
107+
}
108+
109+
if (this.originalCursor) {
110+
const markerManager = this.workspace.getMarkerManager();
111+
markerManager.setCursor(this.originalCursor);
112+
}
113+
114+
this.workspace.setTheme(this.originalTheme);
115+
116+
this.navigationController.dispose();
52117
}
53118

54119
/**

src/navigation.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1109,7 +1109,12 @@ export class Navigation {
11091109
markAtCursor(workspace: Blockly.WorkspaceSvg) {
11101110
const cursor = workspace.getCursor()!;
11111111
this.markedNode = cursor.getCurNode();
1112-
this.passiveFocusIndicator.show(this.markedNode);
1112+
1113+
// Although it seems like this should never happen, the typings are wrong
1114+
// in the base Marker class and this can therefore be null.
1115+
if (this.markedNode) {
1116+
this.passiveFocusIndicator.show(this.markedNode);
1117+
}
11131118
}
11141119

11151120
/**

src/navigation_controller.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ export class NavigationController {
287287
* @returns True iff `deleteCallbackFn` function should be called.
288288
*/
289289
protected blockCopyPreconditionFn(workspace: WorkspaceSvg) {
290-
if (!this.canCurrentlyEdit(workspace)) return false;
290+
if (!this.canCurrentlyEdit(workspace)) return false;
291291
switch (this.navigation.getState(workspace)) {
292292
case Constants.STATE.WORKSPACE:
293293
const curNode = workspace?.getCursor()?.getCurNode();
@@ -977,6 +977,9 @@ export class NavigationController {
977977
ShortcutRegistry.registry.unregister(shortcut.name);
978978
}
979979

980+
ContextMenuRegistry.registry.unregister('blockDeleteFromContextMenu');
981+
ContextMenuRegistry.registry.unregister('blockCopyFromContextMenu');
982+
980983
this.removeShortcutHandlers();
981984
this.navigation.dispose();
982985
}

0 commit comments

Comments
 (0)