Skip to content

Commit 92c9b39

Browse files
Passive focus tweaks
1 parent c9d8689 commit 92c9b39

File tree

2 files changed

+82
-46
lines changed

2 files changed

+82
-46
lines changed

src/index.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,25 @@ export class KeyboardNavigation {
3535
*/
3636
private originalTheme: Blockly.Theme;
3737

38+
/**
39+
* Layer for the workspace.
40+
*/
41+
private workspaceFocusRingDiv: HTMLElement | null = null;
42+
43+
/**
44+
* Updates the focus ring layer for the workspace when focus moves to the
45+
* workspace.
46+
*/
47+
private workspaceFocusInListener = () => {
48+
this.workspaceFocusRingDiv?.classList.add('blocklyWorkspaceFocused');
49+
};
50+
/**
51+
* Updates the focus ring layer for the workspace when focus moves elsewhere.
52+
*/
53+
private workspaceFocusOutListener = () => {
54+
this.workspaceFocusRingDiv?.classList.remove('blocklyWorkspaceFocused');
55+
};
56+
3857
/**
3958
* Constructs the keyboard navigation.
4059
*
@@ -75,12 +94,40 @@ export class KeyboardNavigation {
7594
workspace.getParentSvg(),
7695
);
7796
}
97+
98+
// Add a layer for a workspace outline and wire up events.
99+
this.workspaceFocusRingDiv = workspace
100+
.getInjectionDiv()
101+
.appendChild(document.createElement('div')) as HTMLElement;
102+
this.workspaceFocusRingDiv.className = 'blocklyWorkspaceFocusRingLayer';
103+
Object.assign(this.workspaceFocusRingDiv.style, {
104+
position: 'absolute',
105+
top: '0px',
106+
left: '0px',
107+
bottom: '0px',
108+
right: '0px',
109+
pointerEvents: 'none',
110+
zIndex: '999',
111+
} satisfies Partial<CSSStyleDeclaration>);
112+
workspace
113+
.getSvgGroup()
114+
.addEventListener('focusin', this.workspaceFocusInListener);
115+
workspace
116+
.getSvgGroup()
117+
.addEventListener('focusout', this.workspaceFocusOutListener);
78118
}
79119

80120
/**
81121
* Disables keyboard navigation for this navigator's workspace.
82122
*/
83123
dispose() {
124+
this.workspace
125+
.getSvgGroup()
126+
.removeEventListener('focusin', this.workspaceFocusInListener);
127+
this.workspace
128+
.getSvgGroup()
129+
.removeEventListener('focusout', this.workspaceFocusOutListener);
130+
84131
// Remove the event listener that enables blocks on drag
85132
this.workspace.removeChangeListener(enableBlocksOnDrag);
86133

test/index.html

Lines changed: 35 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,6 @@
3131
width: 100%;
3232
max-height: 100%;
3333
position: relative;
34-
--outline-width: 5px;
35-
}
36-
37-
.blocklyFlyout {
38-
top: var(--outline-width);
39-
left: var(--outline-width);
40-
height: calc(100% - calc(var(--outline-width) * 2));
4134
}
4235

4336
.blocklyToolboxDiv ~ .blocklyFlyout:focus {
@@ -97,53 +90,49 @@
9790
font-weight: bold;
9891
}
9992

100-
.blocklyActiveFocus:is(
101-
.blocklyField,
102-
.blocklyPath,
103-
.blocklyHighlightedConnectionPath
104-
) {
105-
stroke: #ffa200;
106-
stroke-width: 3px;
93+
html {
94+
--blockly-active-node-color: #ffa200;
95+
--blockly-active-tree-color: rgb(97, 91, 127);
96+
--blockly-selection-width: 3px;
10797
}
108-
.blocklyActiveFocus > .blocklyFlyoutBackground,
109-
.blocklyActiveFocus > .blocklyMainBackground {
110-
stroke: #ffa200;
111-
stroke-width: 3px;
98+
* {
99+
box-sizing: border-box;
112100
}
113-
.blocklyActiveFocus:is(
114-
.blocklyToolbox,
115-
.blocklyToolboxCategoryContainer
116-
) {
117-
outline: 3px solid #ffa200;
101+
102+
/* Blocks, connections and fields. */
103+
.blocklyActiveFocus:is(.blocklyPath, .blocklyHighlightedConnectionPath),
104+
.blocklyActiveFocus.blocklyField > .blocklyFieldRect {
105+
stroke: var(--blockly-active-node-color);
106+
stroke-width: var(--blockly-selection-width);
118107
}
119108
.blocklyPassiveFocus:is(
120-
.blocklyField,
121-
.blocklyPath,
109+
.blocklyPath:not(.blocklyFlyout .blocklyPath),
122110
.blocklyHighlightedConnectionPath
123-
) {
124-
stroke: #ffa200;
125-
stroke-dasharray: 5px 3px;
126-
stroke-width: 3px;
127-
}
128-
.blocklyPassiveFocus > .blocklyFlyoutBackground,
129-
.blocklyPassiveFocus > .blocklyMainBackground {
130-
stroke: #ffa200;
111+
),
112+
.blocklyPassiveFocus.blocklyField > .blocklyFieldRect {
113+
stroke: var(--blockly-active-node-color);
131114
stroke-dasharray: 5px 3px;
132-
stroke-width: 3px;
115+
stroke-width: var(--blockly-selection-width);
133116
}
134-
.blocklyPassiveFocus:is(
135-
.blocklyToolbox,
136-
.blocklyToolboxCategoryContainer
137-
) {
138-
border: 3px dashed #ffa200;
139-
}
140-
.blocklySelected:is(.blocklyPath) {
141-
stroke: #ffa200;
142-
stroke-width: 5;
143-
}
144-
.blocklySelected > .blocklyPathLight {
145-
display: none;
117+
118+
/* Toolbox and flyout. */
119+
.blocklyFlyout:has(.blocklyActiveFocus),
120+
.blocklyToolbox:has(.blocklyActiveFocus),
121+
.blocklyActiveFocus:is(.blocklyFlyout, .blocklyToolbox) {
122+
outline-offset: calc(var(--blockly-selection-width) * -1);
123+
outline: var(--blockly-selection-width) solid
124+
var(--blockly-active-tree-color);
125+
}
126+
/* Separate layer so we can have a visible outline. */
127+
.blocklyWorkspaceFocusRingLayer.blocklyWorkspaceFocused {
128+
outline-offset: calc(var(--blockly-selection-width) * -1);
129+
outline: var(--blockly-selection-width) solid
130+
var(--blockly-active-tree-color);
131+
}
132+
.blocklyToolboxCategoryContainer:focus-visible {
133+
outline: none;
146134
}
135+
/*TODO: moving a block should have active styling for connection & block */
147136
</style>
148137
</head>
149138

0 commit comments

Comments
 (0)