Skip to content

Commit 004f9c6

Browse files
committed
Add selection border
Signed-off-by: Hollow Man <hollowman@opensuse.org>
1 parent 514e825 commit 004f9c6

File tree

5 files changed

+290
-14
lines changed

5 files changed

+290
-14
lines changed

src/DOM_FOCUS_IMPLEMENTATION.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# DOM-Based Focus Management Implementation
2+
3+
This implementation follows Christopher's suggestion to create a visible DOM element for the MultiselectDraggable that can hold browser focus, addressing the focus management issues in Blockly v12.
4+
5+
## Key Changes
6+
7+
### 1. Visual Selection Outline
8+
- Added a rectangular outline around all selected items (similar to Google Slides)
9+
- The outline is a visible SVG element that can receive browser focus
10+
- Styled with dashed border and proper focus indicators
11+
12+
### 2. DOM Focus Management
13+
- `getFocusableElement()` now returns the actual DOM element (`selectionOutline_`)
14+
- Added proper `tabindex="0"` and accessibility attributes
15+
- Implemented keyboard event handling for accessibility
16+
17+
### 3. Real-time Updates
18+
- Outline position and size updates automatically when:
19+
- Items are added/removed from selection
20+
- Items are dragged
21+
- Selection changes
22+
- Visual feedback when gaining/losing focus
23+
24+
### 4. Integration with Existing System
25+
- All existing `updateFocusedNode()` calls now trigger outline visibility
26+
- Added `onBecomeFocused()` method to ensure outline is shown
27+
- Proper cleanup in `dispose()` method
28+
29+
## Technical Details
30+
31+
### Visual Elements
32+
- **Container**: `.blocklyMultiselectOutline` - Transparent container
33+
- **Outline**: `.blocklyMultiselectOutlineRect` - Focusable dashed rectangle
34+
- **Styling**: Blue dashed border (#4285f4) with rounded corners
35+
36+
### Focus States
37+
- **Normal**: 2px dashed border
38+
- **Focused**: 3px border with enhanced visibility
39+
- **Hover**: Reduced opacity for better UX
40+
41+
### Accessibility
42+
- Proper `role="button"` and `aria-label`
43+
- Keyboard navigation support (Enter, Space, Escape)
44+
- Focus indicators follow web accessibility standards
45+
46+
## Benefits
47+
48+
1. **Solves v12 Focus Issues**: Provides a real DOM element that focus manager can work with
49+
2. **Visual Feedback**: Users can clearly see what's selected
50+
3. **Accessibility**: Screen readers and keyboard navigation work properly
51+
4. **Backward Compatible**: Doesn't break existing functionality
52+
53+
## Usage
54+
55+
The implementation is automatic - when multiple blocks are selected:
56+
1. The outline appears around the selection
57+
2. The outline can receive focus from the focus manager
58+
3. Users can interact with it via keyboard or mouse
59+
4. The outline updates in real-time during operations
60+
61+
This follows the recommended approach (2) from Ben's suggestions and implements Christopher's DOM element idea successfully.

src/multiselect_contextmenu.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,10 @@ const registerDuplicate = function() {
235235
connectionDB[0].connect(connectionDB[1]);
236236
});
237237
Blockly.getFocusManager().updateFocusedNode(multiDraggable);
238+
// Call the new method to ensure outline is visible
239+
if (multiDraggable.onBecomeFocused) {
240+
multiDraggable.onBecomeFocused();
241+
}
238242
Blockly.Events.setGroup(false);
239243
},
240244
scopeType: Blockly.ContextMenuRegistry.ScopeType.BLOCK,
@@ -802,6 +806,10 @@ const registerPaste = function(useCopyPasteCrossTab) {
802806
});
803807
Blockly.Events.setGroup(false);
804808
Blockly.getFocusManager().updateFocusedNode(multiDraggable);
809+
// Call the new method to ensure outline is visible
810+
if (multiDraggable.onBecomeFocused) {
811+
multiDraggable.onBecomeFocused();
812+
}
805813
return true;
806814
},
807815
scopeType: Blockly.ContextMenuRegistry.ScopeType.WORKSPACE,
@@ -869,6 +877,10 @@ const registerSelectAll = function() {
869877
});
870878

871879
Blockly.getFocusManager().updateFocusedNode(multiDraggable);
880+
// Call the new method to ensure outline is visible
881+
if (multiDraggable.onBecomeFocused) {
882+
multiDraggable.onBecomeFocused();
883+
}
872884
},
873885
scopeType: Blockly.ContextMenuRegistry.ScopeType.WORKSPACE,
874886
id,
@@ -1148,6 +1160,10 @@ const registerCommentDuplicate = function() {
11481160
}
11491161
}
11501162
Blockly.getFocusManager().updateFocusedNode(multiDraggable);
1163+
// Call the new method to ensure outline is visible
1164+
if (multiDraggable.onBecomeFocused) {
1165+
multiDraggable.onBecomeFocused();
1166+
}
11511167
Blockly.Events.setGroup(false);
11521168
},
11531169
scopeType: Blockly.ContextMenuRegistry.ScopeType.COMMENT,

src/multiselect_controls.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,10 @@ export class MultiselectControls {
411411
if (this.dragSelection.size && !(Blockly.getSelected() instanceof
412412
MultiselectDraggable)) {
413413
Blockly.getFocusManager().updateFocusedNode(this.multiDraggable);
414+
// Call the new method to ensure outline is visible
415+
if (this.multiDraggable.onBecomeFocused) {
416+
this.multiDraggable.onBecomeFocused();
417+
}
414418
} else if (this.lastSelectedElement_ &&
415419
!inPasteShortcut.get(this.workspace_)) {
416420
this.updateDraggables_(this.lastSelectedElement_);
@@ -534,6 +538,10 @@ export class MultiselectControls {
534538
// our set is not empty.
535539
if (this.dragSelection.size && !Blockly.getSelected()) {
536540
Blockly.getFocusManager().updateFocusedNode(this.multiDraggable);
541+
// Call the new method to ensure outline is visible
542+
if (this.multiDraggable.onBecomeFocused) {
543+
this.multiDraggable.onBecomeFocused();
544+
}
537545
}
538546
if (this.hasDisableWorkspaceDrag_) {
539547
this.workspace_.options.moveOptions.drag = true;
@@ -576,4 +584,22 @@ Blockly.Css.register(`
576584
.blocklyMultiselect>image:active, .blocklyMultiselect>svg>image:active {
577585
opacity: .8;
578586
}
587+
588+
.blocklyMultiselectOutline {
589+
pointer-events: none;
590+
}
591+
592+
.blocklyMultiselectOutlineRect {
593+
pointer-events: all;
594+
cursor: move;
595+
}
596+
597+
.blocklyMultiselectOutlineRect:focus {
598+
outline: none;
599+
stroke-width: 3;
600+
}
601+
602+
.blocklyMultiselectOutlineRect:hover {
603+
stroke-opacity: 0.8;
604+
}
579605
`);

0 commit comments

Comments
 (0)