mirror of
https://github.com/google/blockly.git
synced 2026-01-05 08:00:09 +01:00
Use Tab keys instead of arrow keys for dialog boxes. Set role=alertdialog and read out the header/text automatically. Ensure that Esc key actually closes dialogs and that all keystrokes are captured.
This commit is contained in:
@@ -27,14 +27,14 @@ blocklyApp.BlockOptionsModalComponent = ng.core.Component({
|
||||
selector: 'blockly-block-options-modal',
|
||||
template: `
|
||||
<div *ngIf="modalIsVisible" class="blocklyModalCurtain"
|
||||
(click)="hideModal()">
|
||||
(click)="dismissModal()">
|
||||
<!-- $event.stopPropagation() prevents the modal from closing when its
|
||||
interior is clicked. -->
|
||||
<div id="blockOptionsModal" class="blocklyModal" role="dialog"
|
||||
(click)="$event.stopPropagation()" tabindex="-1">
|
||||
<div id="blockOptionsModal" class="blocklyModal" role="alertdialog"
|
||||
(click)="$event.stopPropagation()" tabindex="-1"
|
||||
aria-labelledby="blockOptionsModalHeading">
|
||||
<h3 id="blockOptionsModalHeading">{{'BLOCK_OPTIONS'|translate}}</h3>
|
||||
<div role="document">
|
||||
<h3>{{'BLOCK_OPTIONS'|translate}}</h3>
|
||||
|
||||
<div class="blocklyModalButtonContainer"
|
||||
*ngFor="#buttonInfo of actionButtonsInfo; #i=index">
|
||||
<button [id]="getOptionId(i)"
|
||||
@@ -43,13 +43,14 @@ blocklyApp.BlockOptionsModalComponent = ng.core.Component({
|
||||
{{buttonInfo.translationIdForText|translate}}
|
||||
</button>
|
||||
</div>
|
||||
<div class="blocklyModalButtonContainer">
|
||||
<button [id]="getCancelOptionId()"
|
||||
(click)="hideModal()"
|
||||
[ngClass]="{activeButton: activeActionButtonIndex == actionButtonsInfo.length}">
|
||||
{{'CANCEL'|translate}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="blocklyModalButtonContainer">
|
||||
<button [id]="getCancelOptionId()"
|
||||
(click)="dismissModal()"
|
||||
[ngClass]="{activeButton: activeActionButtonIndex == actionButtonsInfo.length}">
|
||||
{{'CANCEL'|translate}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -67,7 +68,7 @@ blocklyApp.BlockOptionsModalComponent = ng.core.Component({
|
||||
|
||||
this.modalIsVisible = false;
|
||||
this.actionButtonsInfo = [];
|
||||
this.activeActionButtonIndex = 0;
|
||||
this.activeActionButtonIndex = -1;
|
||||
this.onDismissCallback = null;
|
||||
|
||||
var that = this;
|
||||
@@ -75,17 +76,36 @@ blocklyApp.BlockOptionsModalComponent = ng.core.Component({
|
||||
function(newActionButtonsInfo, onDismissCallback) {
|
||||
that.modalIsVisible = true;
|
||||
that.actionButtonsInfo = newActionButtonsInfo;
|
||||
that.activeActionButtonIndex = 0;
|
||||
that.activeActionButtonIndex = -1;
|
||||
that.onDismissCallback = onDismissCallback;
|
||||
|
||||
that.keyboardInputService.setOverride({
|
||||
// Tab key: no-op.
|
||||
// Tab key: navigates to the previous or next item in the list.
|
||||
'9': function(evt) {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
|
||||
if (evt.shiftKey) {
|
||||
// Move to the previous item in the list.
|
||||
if (that.activeActionButtonIndex <= 0) {
|
||||
that.activeActionButtonIndex = 0;
|
||||
that.audioService.playOopsSound();
|
||||
} else {
|
||||
that.activeActionButtonIndex--;
|
||||
}
|
||||
} else {
|
||||
// Move to the next item in the list.
|
||||
if (that.activeActionButtonIndex ==
|
||||
that.actionButtonsInfo.length) {
|
||||
that.audioService.playOopsSound();
|
||||
} else {
|
||||
that.activeActionButtonIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
that.focusOnOption(that.activeActionButtonIndex);
|
||||
},
|
||||
// Enter key: selects an action, performs it, and closes the
|
||||
// modal.
|
||||
// Enter key: selects an action, performs it, and closes the modal.
|
||||
'13': function(evt) {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
@@ -96,38 +116,24 @@ blocklyApp.BlockOptionsModalComponent = ng.core.Component({
|
||||
that.actionButtonsInfo.length) {
|
||||
that.actionButtonsInfo[that.activeActionButtonIndex].action();
|
||||
} else {
|
||||
that.onDismissCallback();
|
||||
that.dismissModal();
|
||||
}
|
||||
|
||||
that.hideModal();
|
||||
},
|
||||
// Escape key: closes the modal.
|
||||
'27': function() {
|
||||
that.onDismissCallback();
|
||||
that.hideModal();
|
||||
that.dismissModal();
|
||||
},
|
||||
// Up key: navigates to the previous item in the list.
|
||||
// Up key: no-op.
|
||||
'38': function(evt) {
|
||||
// Prevent the page from scrolling.
|
||||
evt.preventDefault();
|
||||
if (that.activeActionButtonIndex == 0) {
|
||||
that.audioService.playOopsSound();
|
||||
} else {
|
||||
that.activeActionButtonIndex--;
|
||||
that.focusOnOption(that.activeActionButtonIndex);
|
||||
}
|
||||
},
|
||||
// Down key: navigates to the next item in the list.
|
||||
// Down key: no-op.
|
||||
'40': function(evt) {
|
||||
// Prevent the page from scrolling.
|
||||
evt.preventDefault();
|
||||
if (that.activeActionButtonIndex ==
|
||||
that.actionButtonsInfo.length) {
|
||||
that.audioService.playOopsSound();
|
||||
} else {
|
||||
that.activeActionButtonIndex++;
|
||||
that.focusOnOption(that.activeActionButtonIndex);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -150,6 +156,10 @@ blocklyApp.BlockOptionsModalComponent = ng.core.Component({
|
||||
getCancelOptionId: function() {
|
||||
return this.getOptionId(this.actionButtonsInfo.length);
|
||||
},
|
||||
dismissModal: function() {
|
||||
this.onDismissCallback();
|
||||
this.hideModal();
|
||||
},
|
||||
// Closes the modal.
|
||||
hideModal: function() {
|
||||
this.modalIsVisible = false;
|
||||
|
||||
@@ -30,30 +30,29 @@ blocklyApp.ToolboxModalComponent = ng.core.Component({
|
||||
(click)="dismissModal()">
|
||||
<!-- $event.stopPropagation() prevents the modal from closing when its
|
||||
interior is clicked. -->
|
||||
<div id="toolboxModal" class="blocklyModal" role="dialog"
|
||||
(click)="$event.stopPropagation()" tabindex="-1">
|
||||
<div role="document">
|
||||
<h3>{{'SELECT_A_BLOCK'|translate}}</h3>
|
||||
<div id="toolboxModal" class="blocklyModal" role="alertdialog"
|
||||
(click)="$event.stopPropagation()" tabindex="-1"
|
||||
aria-labelledby="toolboxModalHeading">
|
||||
<h3 id="toolboxModalHeading">{{'SELECT_A_BLOCK'|translate}}</h3>
|
||||
|
||||
<div *ngFor="#toolboxCategory of toolboxCategories; #categoryIndex=index">
|
||||
<h4 *ngIf="toolboxCategory.categoryName">{{toolboxCategory.categoryName}}</h4>
|
||||
<div class="blocklyModalButtonContainer"
|
||||
*ngFor="#block of toolboxCategory.blocks; #blockIndex=index">
|
||||
<button [id]="getOptionId(getOverallIndex(categoryIndex, blockIndex))"
|
||||
(click)="selectBlock(getBlock(categoryIndex, blockIndex))"
|
||||
[ngClass]="{activeButton: activeButtonIndex == getOverallIndex(categoryIndex, blockIndex)}">
|
||||
{{getBlockDescription(block)}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="blocklyModalButtonContainer">
|
||||
<button [id]="getCancelOptionId()" (click)="dismissModal()"
|
||||
[ngClass]="{activeButton: activeButtonIndex == totalNumBlocks}">
|
||||
{{'CANCEL'|translate}}
|
||||
<div *ngFor="#toolboxCategory of toolboxCategories; #categoryIndex=index">
|
||||
<h4 *ngIf="toolboxCategory.categoryName">{{toolboxCategory.categoryName}}</h4>
|
||||
<div class="blocklyModalButtonContainer"
|
||||
*ngFor="#block of toolboxCategory.blocks; #blockIndex=index">
|
||||
<button [id]="getOptionId(getOverallIndex(categoryIndex, blockIndex))"
|
||||
(click)="selectBlock(getBlock(categoryIndex, blockIndex))"
|
||||
[ngClass]="{activeButton: activeButtonIndex == getOverallIndex(categoryIndex, blockIndex)}">
|
||||
{{getBlockDescription(block)}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="blocklyModalButtonContainer">
|
||||
<button [id]="getCancelOptionId()" (click)="dismissModal()"
|
||||
[ngClass]="{activeButton: activeButtonIndex == totalNumBlocks}">
|
||||
{{'CANCEL'|translate}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
@@ -78,7 +77,7 @@ blocklyApp.ToolboxModalComponent = ng.core.Component({
|
||||
this.onDismissCallback = null;
|
||||
|
||||
this.firstBlockIndexes = [];
|
||||
this.activeButtonIndex = 0;
|
||||
this.activeButtonIndex = -1;
|
||||
this.totalNumBlocks = 0;
|
||||
|
||||
var that = this;
|
||||
@@ -91,7 +90,7 @@ blocklyApp.ToolboxModalComponent = ng.core.Component({
|
||||
that.onDismissCallback = onDismissCallback;
|
||||
|
||||
that.firstBlockIndexes = [];
|
||||
that.activeButtonIndex = 0;
|
||||
that.activeButtonIndex = -1;
|
||||
that.totalNumBlocks = 0;
|
||||
|
||||
var cumulativeIndex = 0;
|
||||
@@ -102,12 +101,30 @@ blocklyApp.ToolboxModalComponent = ng.core.Component({
|
||||
that.firstBlockIndexes.push(cumulativeIndex);
|
||||
that.totalNumBlocks = cumulativeIndex;
|
||||
|
||||
that.activeButtonIndex = 0;
|
||||
that.keyboardInputService.setOverride({
|
||||
// Tab key: no-op.
|
||||
// Tab key: navigates to the previous or next item in the list.
|
||||
'9': function(evt) {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
|
||||
if (evt.shiftKey) {
|
||||
// Move to the previous item in the list.
|
||||
if (that.activeButtonIndex <= 0) {
|
||||
that.activeActionButtonIndex = 0;
|
||||
that.audioService.playOopsSound();
|
||||
} else {
|
||||
that.activeButtonIndex--;
|
||||
}
|
||||
} else {
|
||||
// Move to the next item in the list.
|
||||
if (that.activeButtonIndex == that.totalNumBlocks) {
|
||||
that.audioService.playOopsSound();
|
||||
} else {
|
||||
that.activeButtonIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
that.focusOnOption(that.activeButtonIndex);
|
||||
},
|
||||
// Enter key: selects an action, performs it, and closes the modal.
|
||||
'13': function(evt) {
|
||||
@@ -135,25 +152,13 @@ blocklyApp.ToolboxModalComponent = ng.core.Component({
|
||||
'27': function() {
|
||||
that.dismissModal();
|
||||
},
|
||||
// Up key: navigates to the previous item in the list.
|
||||
// Up key: no-op.
|
||||
'38': function(evt) {
|
||||
evt.preventDefault();
|
||||
if (that.activeButtonIndex == 0) {
|
||||
that.audioService.playOopsSound();
|
||||
} else {
|
||||
that.activeButtonIndex--;
|
||||
}
|
||||
that.focusOnOption(that.activeButtonIndex);
|
||||
},
|
||||
// Down key: navigates to the next item in the list.
|
||||
// Down key: no-op.
|
||||
'40': function(evt) {
|
||||
evt.preventDefault();
|
||||
if (that.activeButtonIndex == that.totalNumBlocks) {
|
||||
that.audioService.playOopsSound();
|
||||
} else {
|
||||
that.activeButtonIndex++;
|
||||
}
|
||||
that.focusOnOption(that.activeButtonIndex);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user