mirror of
https://github.com/google/blockly.git
synced 2026-01-23 16:50:10 +01:00
Handle the finer points for setting focus correctly after deleting blocks from the workspace.
This commit is contained in:
@@ -65,13 +65,16 @@ blocklyApp.BlockOptionsModalComponent = ng.core.Component({
|
||||
this.modalIsVisible = false;
|
||||
this.actionButtonsInfo = [];
|
||||
this.activeActionButtonIndex = 0;
|
||||
this.onCancelCallback = null;
|
||||
|
||||
var that = this;
|
||||
this.blockOptionsModalService.registerPreShowHook(
|
||||
function(newActionButtonsInfo) {
|
||||
function(newActionButtonsInfo, onCancelCallback) {
|
||||
that.modalIsVisible = true;
|
||||
that.actionButtonsInfo = newActionButtonsInfo;
|
||||
that.activeActionButtonIndex = 0;
|
||||
that.onCancelCallback = onCancelCallback;
|
||||
|
||||
that.keyboardInputService.setOverride({
|
||||
// Tab key: no-op.
|
||||
'9': function(evt) {
|
||||
@@ -92,11 +95,15 @@ blocklyApp.BlockOptionsModalComponent = ng.core.Component({
|
||||
if (that.activeActionButtonIndex <
|
||||
that.actionButtonsInfo.length) {
|
||||
that.actionButtonsInfo[that.activeActionButtonIndex].action();
|
||||
} else {
|
||||
that.onCancelCallback();
|
||||
}
|
||||
|
||||
that.hideModal();
|
||||
},
|
||||
// Escape key: closes the modal.
|
||||
'27': function() {
|
||||
that.onCancelCallback();
|
||||
that.hideModal();
|
||||
},
|
||||
// Up key: navigates to the previous item in the list.
|
||||
|
||||
@@ -32,19 +32,19 @@ blocklyApp.BlockOptionsModalService = ng.core.Class({
|
||||
'before it can be shown.');
|
||||
};
|
||||
this.modalIsShown = false;
|
||||
this.onHideCallback = null;
|
||||
this.onCancelCallback = null;
|
||||
}],
|
||||
registerPreShowHook: function(preShowHook) {
|
||||
this.preShowHook = function() {
|
||||
preShowHook(this.actionButtonsInfo);
|
||||
preShowHook(this.actionButtonsInfo, this.onCancelCallback);
|
||||
};
|
||||
},
|
||||
isModalShown: function() {
|
||||
return this.modalIsShown;
|
||||
},
|
||||
showModal: function(actionButtonsInfo, onHideCallback) {
|
||||
showModal: function(actionButtonsInfo, onCancelCallback) {
|
||||
this.actionButtonsInfo = actionButtonsInfo;
|
||||
this.onHideCallback = onHideCallback;
|
||||
this.onCancelCallback = onCancelCallback;
|
||||
|
||||
if (this.preShowHook) {
|
||||
this.preShowHook();
|
||||
@@ -53,8 +53,5 @@ blocklyApp.BlockOptionsModalService = ng.core.Class({
|
||||
},
|
||||
hideModal: function() {
|
||||
this.modalIsShown = false;
|
||||
if (this.onHideCallback) {
|
||||
this.onHideCallback();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -42,34 +42,18 @@ blocklyApp.TreeService = ng.core.Class({
|
||||
}
|
||||
],
|
||||
// Returns a list of all top-level workspace tree nodes on the page.
|
||||
getWorkspaceTreeNodes_: function() {
|
||||
return Array.from(document.querySelectorAll('ol.blocklyWorkspaceTree'));
|
||||
getWorkspaceFocusTargets_: function() {
|
||||
return Array.from(
|
||||
document.querySelectorAll('.blocklyWorkspaceFocusTarget'));
|
||||
},
|
||||
getSidebarButtonNodes_: function() {
|
||||
return Array.from(document.querySelectorAll('button.blocklySidebarButton'));
|
||||
},
|
||||
// Returns a list of all top-level tree nodes on the page.
|
||||
getAllTreeNodes_: function() {
|
||||
return this.getWorkspaceTreeNodes_().concat(
|
||||
return this.getWorkspaceFocusTargets_().concat(
|
||||
this.getSidebarButtonNodes_());
|
||||
},
|
||||
isTopLevelWorkspaceTree: function(treeId) {
|
||||
return this.getWorkspaceTreeNodes_().some(function(tree) {
|
||||
return tree.id == treeId;
|
||||
});
|
||||
},
|
||||
getNodeToFocusOnWhenTreeIsDeleted: function(deletedTreeId) {
|
||||
// This returns the node to focus on after the deletion happens.
|
||||
// We shift focus to the next tree (which may be a button in the sidebar).
|
||||
var trees = this.getAllTreeNodes_();
|
||||
for (var i = 0; i < trees.length; i++) {
|
||||
if (trees[i].id == deletedTreeId) {
|
||||
if (i + 1 < trees.length) {
|
||||
return trees[i + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
focusOnCurrentTree_: function(treeId) {
|
||||
var trees = this.getAllTreeNodes_();
|
||||
for (var i = 0; i < trees.length; i++) {
|
||||
@@ -224,12 +208,13 @@ blocklyApp.TreeService = ng.core.Class({
|
||||
console.error('Could not handle deletion of block.' + blockRootNode);
|
||||
},
|
||||
notifyUserAboutCurrentTree_: function(treeId) {
|
||||
var workspaceTreeNodes = this.getWorkspaceTreeNodes_();
|
||||
for (var i = 0; i < workspaceTreeNodes.length; i++) {
|
||||
if (workspaceTreeNodes[i].id == treeId) {
|
||||
var workspaceFocusTargets = this.getWorkspaceFocusTargets_();
|
||||
for (var i = 0; i < workspaceFocusTargets.length; i++) {
|
||||
if (workspaceFocusTargets[i].tagName == 'OL' &&
|
||||
workspaceFocusTargets[i].id == treeId) {
|
||||
this.notificationsService.setStatusMessage(
|
||||
'Now in workspace group ' + (i + 1) + ' of ' +
|
||||
workspaceTreeNodes.length);
|
||||
workspaceFocusTargets.length);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -252,12 +237,26 @@ blocklyApp.TreeService = ng.core.Class({
|
||||
// - Otherwise, it sets the correct new active desc for the current tree.
|
||||
var treeId = this.getTreeIdForBlock(block.id);
|
||||
if (this.isIsolatedTopLevelBlock_(block)) {
|
||||
var nextNodeToFocusOn = this.getNodeToFocusOnWhenTreeIsDeleted(treeId);
|
||||
// Find the node to focus on after the deletion happens.
|
||||
var nextNodeToFocusOn = null;
|
||||
var focusTargets = this.getWorkspaceFocusTargets_();
|
||||
for (var i = 0; i < focusTargets.length; i++) {
|
||||
if (focusTargets[i].id == treeId) {
|
||||
if (i + 1 < focusTargets.length) {
|
||||
nextNodeToFocusOn = focusTargets[i + 1];
|
||||
} else if (i > 0) {
|
||||
nextNodeToFocusOn = focusTargets[i - 1];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.clearActiveDesc(treeId);
|
||||
deleteBlockFunc();
|
||||
// Invoke a digest cycle, so that the DOM settles.
|
||||
setTimeout(function() {
|
||||
nextNodeToFocusOn = nextNodeToFocusOn || document.getElementById(
|
||||
'blocklyEmptyWorkspaceButton');
|
||||
nextNodeToFocusOn.focus();
|
||||
});
|
||||
} else {
|
||||
@@ -313,6 +312,7 @@ blocklyApp.TreeService = ng.core.Class({
|
||||
translationIdForText: 'MARK_SPOT_BEFORE',
|
||||
action: function() {
|
||||
that.clipboardService.markConnection(block.previousConnection);
|
||||
that.focusOnBlock(block.id);
|
||||
},
|
||||
isDisabled: function() {
|
||||
return !block.previousConnection;
|
||||
@@ -320,6 +320,7 @@ blocklyApp.TreeService = ng.core.Class({
|
||||
}, {
|
||||
action: function() {
|
||||
that.clipboardService.markConnection(block.nextConnection);
|
||||
that.focusOnBlock(block.id);
|
||||
},
|
||||
translationIdForText: 'MARK_SPOT_AFTER',
|
||||
isDisabled: function() {
|
||||
@@ -391,8 +392,6 @@ blocklyApp.TreeService = ng.core.Class({
|
||||
}];
|
||||
|
||||
this.blockOptionsModalService.showModal(actionButtonsInfo, function() {
|
||||
// TODO(sll): If there are no blocks in the workspace, focus on the
|
||||
// entire workspace instead.
|
||||
that.focusOnBlock(block.id);
|
||||
});
|
||||
},
|
||||
|
||||
@@ -32,7 +32,7 @@ blocklyApp.WorkspaceComponent = ng.core.Component({
|
||||
|
||||
<div *ngIf="workspace" class="blocklyWorkspace">
|
||||
<ol #tree *ngFor="#block of workspace.topBlocks_; #i = index"
|
||||
tabindex="0" role="tree" class="blocklyTree blocklyWorkspaceTree"
|
||||
tabindex="0" role="tree" class="blocklyTree blocklyWorkspaceFocusTarget"
|
||||
[attr.aria-activedescendant]="getActiveDescId(tree.id)"
|
||||
[attr.aria-labelledby]="workspaceTitle.id"
|
||||
(keydown)="onKeypress($event, tree)">
|
||||
@@ -43,7 +43,9 @@ blocklyApp.WorkspaceComponent = ng.core.Component({
|
||||
<span *ngIf="workspace.topBlocks_.length === 0">
|
||||
<p>
|
||||
There are no blocks in the workspace.
|
||||
<button (click)="showToolboxModalForCreateNewGroup()">
|
||||
<button (click)="showToolboxModalForCreateNewGroup()"
|
||||
class="blocklyWorkspaceFocusTarget"
|
||||
id="blocklyEmptyWorkspaceButton">
|
||||
Create new block group...
|
||||
</button>
|
||||
</p>
|
||||
|
||||
Reference in New Issue
Block a user