Set focus correctly when toolbox modal is dismissed.

This commit is contained in:
Sean Lip
2016-11-28 16:34:52 -08:00
parent 3b24391768
commit 664e6dcf7e
6 changed files with 66 additions and 37 deletions

View File

@@ -64,15 +64,15 @@ blocklyApp.BlockOptionsModalComponent = ng.core.Component({
this.modalIsVisible = false;
this.actionButtonsInfo = [];
this.activeActionButtonIndex = 0;
this.onCancelCallback = null;
this.onDismissCallback = null;
var that = this;
this.blockOptionsModalService.registerPreShowHook(
function(newActionButtonsInfo, onCancelCallback) {
function(newActionButtonsInfo, onDismissCallback) {
that.modalIsVisible = true;
that.actionButtonsInfo = newActionButtonsInfo;
that.activeActionButtonIndex = 0;
that.onCancelCallback = onCancelCallback;
that.onDismissCallback = onDismissCallback;
that.keyboardInputService.setOverride({
// Tab key: no-op.
@@ -83,20 +83,23 @@ blocklyApp.BlockOptionsModalComponent = ng.core.Component({
// Enter key: selects an action, performs it, and closes the
// modal.
'13': function(evt) {
evt.preventDefault();
evt.stopPropagation();
var button = document.getElementById(
that.getOptionId(that.activeActionButtonIndex));
if (that.activeActionButtonIndex <
that.actionButtonsInfo.length) {
that.actionButtonsInfo[that.activeActionButtonIndex].action();
} else {
that.onCancelCallback();
that.onDismissCallback();
}
that.hideModal();
},
// Escape key: closes the modal.
'27': function() {
that.onCancelCallback();
that.onDismissCallback();
that.hideModal();
},
// Up key: navigates to the previous item in the list.

View File

@@ -32,19 +32,19 @@ blocklyApp.BlockOptionsModalService = ng.core.Class({
'before it can be shown.');
};
this.modalIsShown = false;
this.onCancelCallback = null;
this.onDismissCallback = null;
}],
registerPreShowHook: function(preShowHook) {
this.preShowHook = function() {
preShowHook(this.actionButtonsInfo, this.onCancelCallback);
preShowHook(this.actionButtonsInfo, this.onDismissCallback);
};
},
isModalShown: function() {
return this.modalIsShown;
},
showModal: function(actionButtonsInfo, onCancelCallback) {
showModal: function(actionButtonsInfo, onDismissCallback) {
this.actionButtonsInfo = actionButtonsInfo;
this.onCancelCallback = onCancelCallback;
this.onDismissCallback = onDismissCallback;
this.preShowHook();
this.modalIsShown = true;

View File

@@ -37,17 +37,19 @@ blocklyApp.SidebarComponent = ng.core.Component({
{{buttonConfig.text}}
</button>
</span>
<button (click)="showToolboxModalForAttachToMarkedConnection()"
<button id="{{ID_FOR_ATTACH_TO_LINK_BUTTON}}"
(click)="showToolboxModalForAttachToMarkedConnection()"
[attr.disabled]="isAnyConnectionMarked() ? undefined : 'disabled'"
[attr.aria-disabled]="!isAnyConnectionMarked()"
class="blocklySidebarButton">
Attach new block to link...
</button>
<button (click)="showToolboxModalForCreateNewGroup()"
<button id="{{ID_FOR_CREATE_NEW_GROUP_BUTTON}}"
(click)="showToolboxModalForCreateNewGroup()"
class="blocklySidebarButton">
Create new block group...
</button>
<button id="clear-workspace" (click)="workspace.clear()"
<button id="clear-workspace" (click)="clearWorkspace()"
[attr.disabled]="isWorkspaceEmpty() ? 'disabled' : undefined"
[attr.aria-disabled]="isWorkspaceEmpty()"
class="blocklySidebarButton">
@@ -79,6 +81,9 @@ blocklyApp.SidebarComponent = ng.core.Component({
this.utilsService = _utilsService;
this.toolboxModalService = _toolboxModalService;
this.clipboardService = _clipboardService;
this.ID_FOR_ATTACH_TO_LINK_BUTTON = 'blocklyAttachToLinkBtn';
this.ID_FOR_CREATE_NEW_GROUP_BUTTON = 'blocklyCreateNewGroupBtn';
}
],
isAnyConnectionMarked: function() {
@@ -91,6 +96,10 @@ blocklyApp.SidebarComponent = ng.core.Component({
buttonConfig.onClickNotification);
}
},
clearWorkspace: function() {
this.workspace.clear();
document.getElementById(this.ID_FOR_CREATE_NEW_GROUP_BUTTON).focus();
},
onSidebarKeypress: function(e) {
this.treeService.onSidebarKeypress(e, document.activeElement.id);
},
@@ -98,9 +107,11 @@ blocklyApp.SidebarComponent = ng.core.Component({
return this.utilsService.isWorkspaceEmpty();
},
showToolboxModalForAttachToMarkedConnection: function() {
this.toolboxModalService.showToolboxModalForAttachToMarkedConnection();
this.toolboxModalService.showToolboxModalForAttachToMarkedConnection(
this.ID_FOR_ATTACH_TO_LINK_BUTTON);
},
showToolboxModalForCreateNewGroup: function() {
this.toolboxModalService.showToolboxModalForCreateNewGroup();
this.toolboxModalService.showToolboxModalForCreateNewGroup(
this.ID_FOR_CREATE_NEW_GROUP_BUTTON);
}
});

View File

@@ -27,7 +27,7 @@ blocklyApp.ToolboxModalComponent = ng.core.Component({
selector: 'blockly-toolbox-modal',
template: `
<div *ngIf="modalIsVisible" id="toolboxModal" role="dialog" tabindex="-1">
<div (click)="hideModal()" class="blocklyModalCurtain">
<div (click)="dismissModal()" class="blocklyModalCurtain">
<!-- The $event.stopPropagation() here prevents the modal from
closing when its interior is clicked. -->
<div class="blocklyModal" (click)="$event.stopPropagation()" role="document">
@@ -48,7 +48,7 @@ blocklyApp.ToolboxModalComponent = ng.core.Component({
</div>
<hr>
<div class="blocklyModalButtonContainer">
<button [id]="getCancelOptionId()" (click)="hideModal()"
<button [id]="getCancelOptionId()" (click)="dismissModal()"
[ngClass]="{activeButton: activeButtonIndex == totalNumBlocks}">
{{'CANCEL'|translate}}
</button>
@@ -83,11 +83,14 @@ blocklyApp.ToolboxModalComponent = ng.core.Component({
var that = this;
this.toolboxModalService.registerPreShowHook(
function(toolboxCategories, isBlockAvailable, onSelectBlockCallback) {
function(
toolboxCategories, isBlockAvailable, onSelectBlockCallback,
onDismissCallback) {
that.modalIsVisible = true;
that.toolboxCategories = toolboxCategories;
that.isBlockAvailable = isBlockAvailable;
that.onSelectBlockCallback = onSelectBlockCallback;
that.onDismissCallback = onDismissCallback;
var cumulativeIndex = 0;
that.toolboxCategories.forEach(function(category) {
@@ -107,15 +110,12 @@ blocklyApp.ToolboxModalComponent = ng.core.Component({
// Enter key: selects an action, performs it, and closes the
// modal.
'13': function(evt) {
evt.preventDefault();
evt.stopPropagation();
var button = document.getElementById(
that.getOptionId(that.activeButtonIndex));
if (button.disabled) {
evt.preventDefault();
evt.stopPropagation();
return;
}
for (var i = 0; i < that.toolboxCategories.length; i++) {
if (that.firstBlockIndexes[i + 1] > that.activeButtonIndex) {
var categoryIndex = i;
@@ -128,11 +128,11 @@ blocklyApp.ToolboxModalComponent = ng.core.Component({
}
// The 'Cancel' button has been pressed.
that.hideModal();
that.dismissModal();
},
// Escape key: closes the modal.
'27': function() {
that.hideModal();
that.dismissModal();
},
// Up key: navigates to the previous item in the list.
'38': function(evt) {
@@ -163,6 +163,12 @@ blocklyApp.ToolboxModalComponent = ng.core.Component({
);
}
],
// Closes the modal (on both success and failure).
hideModal_: function() {
this.modalIsVisible = false;
this.keyboardInputService.clearOverride();
this.toolboxModalService.hideModal();
},
getOverallIndex: function(categoryIndex, blockIndex) {
return this.firstBlockIndexes[categoryIndex] + blockIndex;
},
@@ -191,12 +197,11 @@ blocklyApp.ToolboxModalComponent = ng.core.Component({
},
selectBlock: function(block) {
this.onSelectBlockCallback(block);
this.hideModal();
this.hideModal_();
},
// Closes the modal.
hideModal: function() {
this.modalIsVisible = false;
this.keyboardInputService.clearOverride();
this.toolboxModalService.hideModal();
// Dismisses and closes the modal.
dismissModal: function() {
this.hideModal_();
this.onDismissCallback();
}
});

View File

@@ -39,6 +39,7 @@ blocklyApp.ToolboxModalService = ng.core.Class({
this.isBlockAvailable = null;
this.onSelectBlockCallback = null;
this.onDismissCallback = null;
this.preShowHook = function() {
throw Error(
'A pre-show hook must be defined for the toolbox modal before it ' +
@@ -84,15 +85,17 @@ blocklyApp.ToolboxModalService = ng.core.Class({
this.preShowHook = function() {
preShowHook(
this.toolboxCategories, this.isBlockAvailable,
this.onSelectBlockCallback);
this.onSelectBlockCallback, this.onDismissCallback);
};
},
isModalShown: function() {
return this.modalIsShown;
},
showModal_: function(isBlockAvailable, onSelectBlockCallback) {
showModal_: function(
isBlockAvailable, onSelectBlockCallback, onDismissCallback) {
this.isBlockAvailable = isBlockAvailable;
this.onSelectBlockCallback = onSelectBlockCallback;
this.onDismissCallback = onDismissCallback;
this.preShowHook();
this.modalIsShown = true;
@@ -100,7 +103,7 @@ blocklyApp.ToolboxModalService = ng.core.Class({
hideModal: function() {
this.modalIsShown = false;
},
showToolboxModalForAttachToMarkedConnection: function() {
showToolboxModalForAttachToMarkedConnection: function(sourceButtonId) {
var that = this;
this.showModal_(function(block) {
return that.clipboardService.canBeAttachedToMarkedConnection(block);
@@ -131,9 +134,11 @@ blocklyApp.ToolboxModalService = ng.core.Class({
blockDescription + ' connected. ' +
'Now on copied block in workspace.');
});
}, function() {
document.getElementById(sourceButtonId).focus();
});
},
showToolboxModalForCreateNewGroup: function() {
showToolboxModalForCreateNewGroup: function(sourceButtonId) {
var that = this;
this.showModal_(function(block) {
return true;
@@ -148,6 +153,8 @@ blocklyApp.ToolboxModalService = ng.core.Class({
blockDescription + ' added to workspace. ' +
'Now on added block in workspace.');
});
}, function() {
document.getElementById(sourceButtonId).focus();
});
}
});

View File

@@ -45,7 +45,7 @@ blocklyApp.WorkspaceComponent = ng.core.Component({
There are no blocks in the workspace.
<button (click)="showToolboxModalForCreateNewGroup()"
class="blocklyWorkspaceFocusTarget"
id="blocklyEmptyWorkspaceButton">
id="{{ID_FOR_EMPTY_WORKSPACE_BTN}}">
Create new block group...
</button>
</p>
@@ -63,6 +63,8 @@ blocklyApp.WorkspaceComponent = ng.core.Component({
this.treeService = _treeService;
this.toolboxModalService = _toolboxModalService;
this.workspace = blocklyApp.workspace;
this.ID_FOR_EMPTY_WORKSPACE_BTN = 'blocklyEmptyWorkspaceButton';
}
],
getActiveDescId: function(treeId) {
@@ -72,6 +74,7 @@ blocklyApp.WorkspaceComponent = ng.core.Component({
this.treeService.onKeypress(e, tree);
},
showToolboxModalForCreateNewGroup: function() {
this.toolboxModalService.showToolboxModalForCreateNewGroup();
this.toolboxModalService.showToolboxModalForCreateNewGroup(
this.ID_FOR_EMPTY_WORKSPACE_BTN);
}
});