mirror of
https://github.com/google/blockly.git
synced 2026-01-09 18:10:08 +01:00
Make screenreader focus behave correctly when cutting, moving or deleting a block. Unmark the marked spot after a block has been moved or copied to it.
This commit is contained in:
@@ -111,5 +111,7 @@ blocklyApp.ClipboardService = ng.core
|
||||
Blockly.Msg.PASTED_BLOCK_TO_MARKED_SPOT_MSG +
|
||||
reconstitutedBlock.toString());
|
||||
}
|
||||
|
||||
this.markedConnection_ = null;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -117,9 +117,10 @@ blocklyApp.TreeService = ng.core
|
||||
},
|
||||
// Runs the given function while preserving the focus and active descendant
|
||||
// for the given tree.
|
||||
runWhilePreservingFocus: function(func, treeId) {
|
||||
var activeDescId = this.getActiveDescId(treeId);
|
||||
this.unmarkActiveDesc_(activeDescId);
|
||||
runWhilePreservingFocus: function(func, treeId, optionalNewActiveDescId) {
|
||||
var oldDescId = this.getActiveDescId(treeId);
|
||||
var newDescId = optionalNewActiveDescId || oldDescId;
|
||||
this.unmarkActiveDesc_(oldDescId);
|
||||
func();
|
||||
|
||||
// The timeout is needed in order to give the DOM time to stabilize
|
||||
@@ -127,8 +128,8 @@ blocklyApp.TreeService = ng.core
|
||||
// pasteAbove().
|
||||
var that = this;
|
||||
setTimeout(function() {
|
||||
that.markActiveDesc_(activeDescId);
|
||||
that.activeDescendantIds_[treeId] = activeDescId;
|
||||
that.markActiveDesc_(newDescId);
|
||||
that.activeDescendantIds_[treeId] = newDescId;
|
||||
document.getElementById(treeId).focus();
|
||||
}, 0);
|
||||
},
|
||||
@@ -153,11 +154,43 @@ blocklyApp.TreeService = ng.core
|
||||
isButtonOrFieldNode_: function(node) {
|
||||
return ['BUTTON', 'INPUT'].indexOf(node.tagName) != -1;
|
||||
},
|
||||
getNextActiveDescWhenBlockIsDeleted: function(blockRootNode) {
|
||||
// Go up a level, if possible.
|
||||
var nextNode = blockRootNode.parentNode;
|
||||
while (nextNode && nextNode.tagName != 'LI') {
|
||||
nextNode = nextNode.parentNode;
|
||||
}
|
||||
if (nextNode) {
|
||||
return nextNode;
|
||||
}
|
||||
|
||||
// Otherwise, go to the next sibling.
|
||||
var nextSibling = this.getNextSibling(blockRootNode);
|
||||
if (nextSibling) {
|
||||
return nextSibling;
|
||||
}
|
||||
|
||||
// Otherwise, go to the previous sibling.
|
||||
var previousSibling = this.getPreviousSibling(blockRootNode);
|
||||
if (previousSibling) {
|
||||
return previousSibling;
|
||||
}
|
||||
|
||||
// Otherwise, this is a top-level isolated block, which means that
|
||||
// something's gone wrong and this function should not have been called
|
||||
// in the first place.
|
||||
console.error('Could not handle deletion of block.' + blockRootNode);
|
||||
},
|
||||
onKeypress: function(e, tree) {
|
||||
var treeId = tree.id;
|
||||
var activeDesc = document.getElementById(this.getActiveDescId(treeId));
|
||||
if (!activeDesc) {
|
||||
console.log('ERROR: no active descendant for current tree.');
|
||||
console.error('ERROR: no active descendant for current tree.');
|
||||
// TODO(sll): This just gives the focus somewhere to go in the event
|
||||
// of an error, but we need to generalize this to other trees (both
|
||||
// within and outside the workspace).
|
||||
this.setActiveDesc(
|
||||
blocklyApp.workspace.topBlocks_[0].id + 'blockSummary', treeId);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -168,7 +168,7 @@ blocklyApp.WorkspaceTreeComponent = ng.core
|
||||
var elementsNeedingIds = this.getElementsNeedingIds_();
|
||||
|
||||
this.idMap = {}
|
||||
this.idMap['parentList'] = this.utilsService.generateUniqueId();
|
||||
this.idMap['parentList'] = this.block.id + 'parent';
|
||||
for (var i = 0; i < elementsNeedingIds.length; i++) {
|
||||
this.idMap[elementsNeedingIds[i]] =
|
||||
this.block.id + elementsNeedingIds[i];
|
||||
@@ -198,10 +198,15 @@ blocklyApp.WorkspaceTreeComponent = ng.core
|
||||
isCompatibleWithClipboard: function(connection) {
|
||||
return this.clipboardService.isCompatibleWithClipboard(connection);
|
||||
},
|
||||
isTopLevelBlock: function(block) {
|
||||
return blocklyApp.workspace.topBlocks_.some(function(topBlock) {
|
||||
return topBlock.id == block.id;
|
||||
});
|
||||
isIsolatedTopLevelBlock: function(block) {
|
||||
// Returns whether the given block is at the top level, and has no
|
||||
// siblings.
|
||||
return Boolean(
|
||||
!block.nextConnection.targetConnection &&
|
||||
!block.previousConnection.targetConnection &&
|
||||
blocklyApp.workspace.topBlocks_.some(function(topBlock) {
|
||||
return topBlock.id == block.id;
|
||||
}));
|
||||
},
|
||||
generateAriaLabelledByAttr: function(mainLabel, secondLabel, isDisabled) {
|
||||
return this.utilsService.generateAriaLabelledByAttr(
|
||||
@@ -219,40 +224,45 @@ blocklyApp.WorkspaceTreeComponent = ng.core
|
||||
that.clipboardService.pasteFromClipboard(block.nextConnection);
|
||||
}, this.tree.id);
|
||||
},
|
||||
cutToClipboard: function(block) {
|
||||
if (this.isTopLevelBlock(block)) {
|
||||
nextNodeToFocusOn = this.treeService.getNodeToFocusOnWhenTreeIsDeleted(
|
||||
this.tree.id);
|
||||
this.clipboardService.cut(block);
|
||||
getRootNode: function() {
|
||||
// Gets the root HTML node for this component.
|
||||
return document.getElementById(this.idMap['parentList']);
|
||||
},
|
||||
removeBlockAndSetFocus_: function(block, deleteBlockFunc) {
|
||||
// This method runs the given function and then does one of two things:
|
||||
// - If the block is an isolated top-level block, it shifts the tree
|
||||
// focus.
|
||||
// - Otherwise, it sets the correct new active desc for the current tree.
|
||||
if (this.isIsolatedTopLevelBlock(block)) {
|
||||
var nextNodeToFocusOn =
|
||||
this.treeService.getNodeToFocusOnWhenTreeIsDeleted(this.tree.id);
|
||||
deleteBlockFunc();
|
||||
nextNodeToFocusOn.focus();
|
||||
} else {
|
||||
// TODO(sll): Change the active descendant of the tree.
|
||||
this.clipboardService.cut(block);
|
||||
var nextActiveDesc =
|
||||
this.treeService.getNextActiveDescWhenBlockIsDeleted(
|
||||
this.getRootNode());
|
||||
this.treeService.runWhilePreservingFocus(
|
||||
deleteBlockFunc, this.tree.id, nextActiveDesc.id);
|
||||
}
|
||||
},
|
||||
cutToClipboard: function(block) {
|
||||
var that = this;
|
||||
this.removeBlockAndSetFocus_(block, function() {
|
||||
that.clipboardService.cut(block);
|
||||
});
|
||||
},
|
||||
deleteBlock: function(block) {
|
||||
if (this.isTopLevelBlock(block)) {
|
||||
nextNodeToFocusOn = this.treeService.getNodeToFocusOnWhenTreeIsDeleted(
|
||||
this.tree.id);
|
||||
this.removeBlockAndSetFocus_(block, function() {
|
||||
block.dispose(true);
|
||||
nextNodeToFocusOn.focus();
|
||||
} else {
|
||||
// TODO(sll): Change the active descendant of the tree.
|
||||
block.dispose(true);
|
||||
}
|
||||
});
|
||||
},
|
||||
sendToMarkedSpot: function(block) {
|
||||
this.clipboardService.pasteToMarkedConnection(block, false);
|
||||
|
||||
if (this.isTopLevelBlock(block)) {
|
||||
nextNodeToFocusOn = this.treeService.getNodeToFocusOnWhenTreeIsDeleted(
|
||||
this.tree.id);
|
||||
this.removeBlockAndSetFocus_(block, function() {
|
||||
block.dispose(true);
|
||||
nextNodeToFocusOn.focus();
|
||||
} else {
|
||||
// TODO(sll): Change the active descendant of the tree.
|
||||
block.dispose(true);
|
||||
}
|
||||
});
|
||||
|
||||
alert('Block moved to marked spot: ' + block.toString());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user