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