mirror of
https://github.com/google/blockly.git
synced 2026-01-05 16:10:09 +01:00
Adding the common modal class. (#1017)
Centralizes accessible modal behavior.
This commit is contained in:
@@ -36,11 +36,11 @@ blocklyApp.AppComponent = ng.core.Component({
|
||||
<span aria-live="polite" role="status">{{getAriaLiveReadout()}}</span>
|
||||
</div>
|
||||
|
||||
<blockly-block-options-modal></blockly-block-options-modal>
|
||||
<blockly-toolbox-modal></blockly-toolbox-modal>
|
||||
<blockly-add-variable-modal></blockly-add-variable-modal>
|
||||
<blockly-rename-variable-modal></blockly-rename-variable-modal>
|
||||
<blockly-remove-variable-modal></blockly-remove-variable-modal>
|
||||
<blockly-toolbox-modal></blockly-toolbox-modal>
|
||||
<blockly-block-options-modal></blockly-block-options-modal>
|
||||
|
||||
<label id="blockly-translate-button" aria-hidden="true" hidden>
|
||||
{{'BUTTON'|translate}}
|
||||
|
||||
@@ -39,7 +39,7 @@ blocklyApp.BlockOptionsModalComponent = ng.core.Component({
|
||||
*ngFor="#buttonInfo of actionButtonsInfo; #buttonIndex=index">
|
||||
<button [id]="getOptionId(buttonIndex)"
|
||||
(click)="buttonInfo.action(); hideModal();"
|
||||
[ngClass]="{activeButton: activeActionButtonIndex == buttonIndex}">
|
||||
[ngClass]="{activeButton: activeButtonIndex == buttonIndex}">
|
||||
{{buttonInfo.translationIdForText|translate}}
|
||||
</button>
|
||||
</div>
|
||||
@@ -48,7 +48,7 @@ blocklyApp.BlockOptionsModalComponent = ng.core.Component({
|
||||
<div class="blocklyModalButtonContainer">
|
||||
<button [id]="getCancelOptionId()"
|
||||
(click)="dismissModal()"
|
||||
[ngClass]="{activeButton: activeActionButtonIndex == actionButtonsInfo.length}">
|
||||
[ngClass]="{activeButton: activeButtonIndex == actionButtonsInfo.length}">
|
||||
{{'CANCEL'|translate}}
|
||||
</button>
|
||||
</div>
|
||||
@@ -68,7 +68,7 @@ blocklyApp.BlockOptionsModalComponent = ng.core.Component({
|
||||
|
||||
this.modalIsVisible = false;
|
||||
this.actionButtonsInfo = [];
|
||||
this.activeActionButtonIndex = -1;
|
||||
this.activeButtonIndex = -1;
|
||||
this.onDismissCallback = null;
|
||||
|
||||
var that = this;
|
||||
@@ -79,67 +79,26 @@ blocklyApp.BlockOptionsModalComponent = ng.core.Component({
|
||||
that.activeActionButtonIndex = -1;
|
||||
that.onDismissCallback = onDismissCallback;
|
||||
|
||||
that.keyboardInputService.setOverride({
|
||||
// Tab key: navigates to the previous or next item in the list.
|
||||
'9': function(evt) {
|
||||
Blockly.CommonModal.setupKeyboardOverrides(that);
|
||||
that.keyboardInputService.addOverride('13', 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.
|
||||
'13': function(evt) {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
|
||||
if (that.activeActionButtonIndex == -1) {
|
||||
if (that.activeButtonIndex == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
var button = document.getElementById(
|
||||
that.getOptionId(that.activeActionButtonIndex));
|
||||
if (that.activeActionButtonIndex <
|
||||
that.getOptionId(that.activeButtonIndex));
|
||||
if (that.activeButtonIndex <
|
||||
that.actionButtonsInfo.length) {
|
||||
that.actionButtonsInfo[that.activeActionButtonIndex].action();
|
||||
that.actionButtonsInfo[that.activeButtonIndex].action();
|
||||
} else {
|
||||
that.dismissModal();
|
||||
}
|
||||
|
||||
that.hideModal();
|
||||
},
|
||||
// Escape key: closes the modal.
|
||||
'27': function() {
|
||||
that.dismissModal();
|
||||
},
|
||||
// Up key: no-op.
|
||||
'38': function(evt) {
|
||||
// Prevent the page from scrolling.
|
||||
evt.preventDefault();
|
||||
},
|
||||
// Down key: no-op.
|
||||
'40': function(evt) {
|
||||
// Prevent the page from scrolling.
|
||||
evt.preventDefault();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
setTimeout(function() {
|
||||
document.getElementById('blockOptionsModal').focus();
|
||||
@@ -152,6 +111,10 @@ blocklyApp.BlockOptionsModalComponent = ng.core.Component({
|
||||
var button = document.getElementById(this.getOptionId(index));
|
||||
button.focus();
|
||||
},
|
||||
// Counts the number of interactive elements for the modal.
|
||||
numInteractiveElements: function() {
|
||||
return this.actionButtonsInfo.length + 1;
|
||||
},
|
||||
// Returns the ID for the corresponding option button.
|
||||
getOptionId: function(index) {
|
||||
return 'block-options-modal-option-' + index;
|
||||
|
||||
74
accessible/commonModal.js
Normal file
74
accessible/commonModal.js
Normal file
@@ -0,0 +1,74 @@
|
||||
Blockly.CommonModal = function() {};
|
||||
|
||||
Blockly.CommonModal.setupKeyboardOverrides = function(component) {
|
||||
component.keyboardInputService.setOverride({
|
||||
// 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 (component.activeButtonIndex <= 0) {
|
||||
component.activeActionButtonIndex = 0;
|
||||
component.audioService.playOopsSound();
|
||||
} else {
|
||||
component.activeButtonIndex--;
|
||||
}
|
||||
} else {
|
||||
// Move to the next item in the list.
|
||||
if (component.activeButtonIndex == component.numInteractiveElements(component) - 1) {
|
||||
component.audioService.playOopsSound();
|
||||
} else {
|
||||
component.activeButtonIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
component.focusOnOption(component.activeButtonIndex, component);
|
||||
},
|
||||
// Escape key: closes the modal.
|
||||
'27': function() {
|
||||
component.dismissModal();
|
||||
},
|
||||
// Up key: no-op.
|
||||
'38': function(evt) {
|
||||
evt.preventDefault();
|
||||
},
|
||||
// Down key: no-op.
|
||||
'40': function(evt) {
|
||||
evt.preventDefault();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Blockly.CommonModal.getInteractiveElements = function(component) {
|
||||
return Array.prototype.filter.call(
|
||||
component.getInteractiveContainer().elements, function(element) {
|
||||
if (element.type === 'hidden') {
|
||||
return false;
|
||||
}
|
||||
if (element.disabled) {
|
||||
return false;
|
||||
}
|
||||
if (element.tabIndex < 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
};
|
||||
|
||||
Blockly.CommonModal.numInteractiveElements = function(component) {
|
||||
var elements = this.getInteractiveElements(component);
|
||||
return elements.length;
|
||||
};
|
||||
|
||||
Blockly.CommonModal.focusOnOption = function(index, component) {
|
||||
var elements = this.getInteractiveElements(component);
|
||||
var button = elements[index];
|
||||
button.focus();
|
||||
};
|
||||
|
||||
Blockly.CommonModal.hideModal = function() {
|
||||
this.modalIsVisible = false;
|
||||
this.keyboardInputService.clearOverride();
|
||||
};
|
||||
@@ -46,6 +46,9 @@ blocklyApp.KeyboardInputService = ng.core.Class({
|
||||
setOverride: function(newKeysToActions) {
|
||||
this.keysToActionsOverride = newKeysToActions;
|
||||
},
|
||||
addOverride: function(keyCode, action) {
|
||||
this.keysToActionsOverride[keyCode] = action;
|
||||
},
|
||||
clearOverride: function() {
|
||||
this.keysToActionsOverride = null;
|
||||
}
|
||||
|
||||
@@ -103,34 +103,8 @@ blocklyApp.ToolboxModalComponent = ng.core.Component({
|
||||
that.firstBlockIndexes.push(cumulativeIndex);
|
||||
that.totalNumBlocks = cumulativeIndex;
|
||||
|
||||
that.keyboardInputService.setOverride({
|
||||
// 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 a block (or the 'Cancel' button), and closes
|
||||
// the modal.
|
||||
'13': function(evt) {
|
||||
Blockly.CommonModal.setupKeyboardOverrides(that);
|
||||
that.keyboardInputService.addOverride('13', function(evt) {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
|
||||
@@ -154,20 +128,7 @@ blocklyApp.ToolboxModalComponent = ng.core.Component({
|
||||
|
||||
// The 'Cancel' button has been pressed.
|
||||
that.dismissModal();
|
||||
},
|
||||
// Escape key: closes the modal.
|
||||
'27': function() {
|
||||
that.dismissModal();
|
||||
},
|
||||
// Up key: no-op.
|
||||
'38': function(evt) {
|
||||
evt.preventDefault();
|
||||
},
|
||||
// Down key: no-op.
|
||||
'40': function(evt) {
|
||||
evt.preventDefault();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
setTimeout(function() {
|
||||
document.getElementById('toolboxModal').focus();
|
||||
@@ -177,10 +138,15 @@ blocklyApp.ToolboxModalComponent = ng.core.Component({
|
||||
}
|
||||
],
|
||||
// Closes the modal (on both success and failure).
|
||||
hideModal_: function() {
|
||||
this.modalIsVisible = false;
|
||||
this.keyboardInputService.clearOverride();
|
||||
this.toolboxModalService.hideModal();
|
||||
hideModal_: Blockly.CommonModal.hideModal,
|
||||
// Focuses on the button represented by the given index.
|
||||
focusOnOption: function(index) {
|
||||
var button = document.getElementById(this.getOptionId(index));
|
||||
button.focus();
|
||||
},
|
||||
// Counts the number of interactive elements for the modal.
|
||||
numInteractiveElements: function() {
|
||||
return this.totalNumBlocks + 1;
|
||||
},
|
||||
getOverallIndex: function(categoryIndex, blockIndex) {
|
||||
return this.firstBlockIndexes[categoryIndex] + blockIndex;
|
||||
@@ -191,11 +157,6 @@ blocklyApp.ToolboxModalComponent = ng.core.Component({
|
||||
getBlockDescription: function(block) {
|
||||
return this.utilsService.getBlockDescription(block);
|
||||
},
|
||||
// Focuses on the button represented by the given index.
|
||||
focusOnOption: function(index) {
|
||||
var button = document.getElementById(this.getOptionId(index));
|
||||
button.focus();
|
||||
},
|
||||
// Returns the ID for the corresponding option button.
|
||||
getOptionId: function(index) {
|
||||
return 'toolbox-modal-option-' + index;
|
||||
|
||||
@@ -68,44 +68,7 @@ blocklyApp.VariableAddModalComponent = ng.core.Component({
|
||||
function() {
|
||||
that.modalIsVisible = true;
|
||||
|
||||
that.keyboardInputService.setOverride({
|
||||
// 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.numInteractiveElements() - 1) {
|
||||
that.audioService.playOopsSound();
|
||||
} else {
|
||||
that.activeButtonIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
that.focusOnOption(that.activeButtonIndex);
|
||||
},
|
||||
// Escape key: closes the modal.
|
||||
'27': function() {
|
||||
that.dismissModal();
|
||||
},
|
||||
// Up key: no-op.
|
||||
'38': function(evt) {
|
||||
evt.preventDefault();
|
||||
},
|
||||
// Down key: no-op.
|
||||
'40': function(evt) {
|
||||
evt.preventDefault();
|
||||
}
|
||||
});
|
||||
Blockly.CommonModal.setupKeyboardOverrides(that);
|
||||
|
||||
setTimeout(function() {
|
||||
document.getElementById('mainFieldId').focus();
|
||||
@@ -119,36 +82,16 @@ blocklyApp.VariableAddModalComponent = ng.core.Component({
|
||||
this.variableName = newValue;
|
||||
},
|
||||
// Closes the modal (on both success and failure).
|
||||
hideModal_: function() {
|
||||
this.modalIsVisible = false;
|
||||
this.keyboardInputService.clearOverride();
|
||||
},
|
||||
hideModal_: Blockly.CommonModal.hideModal,
|
||||
// Focuses on the button represented by the given index.
|
||||
focusOnOption: function(index) {
|
||||
var elements = this.getInteractiveElements();
|
||||
var button = elements[index];
|
||||
button.focus();
|
||||
},
|
||||
focusOnOption: Blockly.CommonModal.focusOnOption,
|
||||
// Counts the number of interactive elements for the modal.
|
||||
numInteractiveElements: function() {
|
||||
var elements = this.getInteractiveElements();
|
||||
return elements.length;
|
||||
},
|
||||
numInteractiveElements: Blockly.CommonModal.numInteractiveElements,
|
||||
// Gets all the interactive elements for the modal.
|
||||
getInteractiveElements: function() {
|
||||
return Array.prototype.filter.call(
|
||||
document.getElementById("varForm").elements, function(element) {
|
||||
if (element.type === 'hidden') {
|
||||
return false;
|
||||
}
|
||||
if (element.disabled) {
|
||||
return false;
|
||||
}
|
||||
if (element.tabIndex < 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
getInteractiveElements: Blockly.CommonModal.getInteractiveElements,
|
||||
// Gets the container with interactive elements.
|
||||
getInteractiveContainer: function() {
|
||||
return document.getElementById("varForm");
|
||||
},
|
||||
// Submits the name change for the variable.
|
||||
submit: function() {
|
||||
|
||||
@@ -70,44 +70,7 @@ blocklyApp.VariableRemoveModalComponent = ng.core.Component({
|
||||
that.count = count
|
||||
that.modalIsVisible = true;
|
||||
|
||||
that.keyboardInputService.setOverride({
|
||||
// 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.numInteractiveElements() - 1) {
|
||||
that.audioService.playOopsSound();
|
||||
} else {
|
||||
that.activeButtonIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
that.focusOnOption(that.activeButtonIndex);
|
||||
},
|
||||
// Escape key: closes the modal.
|
||||
'27': function() {
|
||||
that.dismissModal();
|
||||
},
|
||||
// Up key: no-op.
|
||||
'38': function(evt) {
|
||||
evt.preventDefault();
|
||||
},
|
||||
// Down key: no-op.
|
||||
'40': function(evt) {
|
||||
evt.preventDefault();
|
||||
}
|
||||
});
|
||||
Blockly.CommonModal.setupKeyboardOverrides(that);
|
||||
|
||||
setTimeout(function() {
|
||||
document.getElementById('label').focus();
|
||||
@@ -116,41 +79,17 @@ blocklyApp.VariableRemoveModalComponent = ng.core.Component({
|
||||
);
|
||||
}
|
||||
],
|
||||
// Caches the current text variable as the user types.
|
||||
setTextValue: function(newValue) {
|
||||
this.variableName = newValue;
|
||||
},
|
||||
// Closes the modal (on both success and failure).
|
||||
hideModal_: function() {
|
||||
this.modalIsVisible = false;
|
||||
this.keyboardInputService.clearOverride();
|
||||
},
|
||||
hideModal_: Blockly.CommonModal.hideModal,
|
||||
// Focuses on the button represented by the given index.
|
||||
focusOnOption: function(index) {
|
||||
var elements = this.getInteractiveElements();
|
||||
var button = elements[index];
|
||||
button.focus();
|
||||
},
|
||||
focusOnOption: Blockly.CommonModal.focusOnOption,
|
||||
// Counts the number of interactive elements for the modal.
|
||||
numInteractiveElements: function() {
|
||||
var elements = this.getInteractiveElements();
|
||||
return elements.length;
|
||||
},
|
||||
numInteractiveElements: Blockly.CommonModal.numInteractiveElements,
|
||||
// Gets all the interactive elements for the modal.
|
||||
getInteractiveElements: function() {
|
||||
return Array.prototype.filter.call(
|
||||
document.getElementById("varForm").elements, function(element) {
|
||||
if (element.type === 'hidden') {
|
||||
return false;
|
||||
}
|
||||
if (element.disabled) {
|
||||
return false;
|
||||
}
|
||||
if (element.tabIndex < 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
getInteractiveElements: Blockly.CommonModal.getInteractiveElements,
|
||||
// Gets the container with interactive elements.
|
||||
getInteractiveContainer: function() {
|
||||
return document.getElementById("varForm");
|
||||
},
|
||||
// Submits the name change for the variable.
|
||||
submit: function() {
|
||||
|
||||
@@ -70,44 +70,7 @@ blocklyApp.VariableRenameModalComponent = ng.core.Component({
|
||||
that.currentVariableName = oldName;
|
||||
that.modalIsVisible = true;
|
||||
|
||||
that.keyboardInputService.setOverride({
|
||||
// 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.numInteractiveElements() - 1) {
|
||||
that.audioService.playOopsSound();
|
||||
} else {
|
||||
that.activeButtonIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
that.focusOnOption(that.activeButtonIndex);
|
||||
},
|
||||
// Escape key: closes the modal.
|
||||
'27': function() {
|
||||
that.dismissModal();
|
||||
},
|
||||
// Up key: no-op.
|
||||
'38': function(evt) {
|
||||
evt.preventDefault();
|
||||
},
|
||||
// Down key: no-op.
|
||||
'40': function(evt) {
|
||||
evt.preventDefault();
|
||||
}
|
||||
});
|
||||
Blockly.CommonModal.setupKeyboardOverrides(that);
|
||||
|
||||
setTimeout(function() {
|
||||
document.getElementById('mainFieldId').focus();
|
||||
@@ -121,36 +84,16 @@ blocklyApp.VariableRenameModalComponent = ng.core.Component({
|
||||
this.variableName = newValue;
|
||||
},
|
||||
// Closes the modal (on both success and failure).
|
||||
hideModal_: function() {
|
||||
this.modalIsVisible = false;
|
||||
this.keyboardInputService.clearOverride();
|
||||
},
|
||||
hideModal_: Blockly.CommonModal.hideModal,
|
||||
// Focuses on the button represented by the given index.
|
||||
focusOnOption: function(index) {
|
||||
var elements = this.getInteractiveElements();
|
||||
var button = elements[index];
|
||||
button.focus();
|
||||
},
|
||||
focusOnOption: Blockly.CommonModal.focusOnOption,
|
||||
// Counts the number of interactive elements for the modal.
|
||||
numInteractiveElements: function() {
|
||||
var elements = this.getInteractiveElements();
|
||||
return elements.length;
|
||||
},
|
||||
numInteractiveElements: Blockly.CommonModal.numInteractiveElements,
|
||||
// Gets all the interactive elements for the modal.
|
||||
getInteractiveElements: function() {
|
||||
return Array.prototype.filter.call(
|
||||
document.getElementById("varForm").elements, function(element) {
|
||||
if (element.type === 'hidden') {
|
||||
return false;
|
||||
}
|
||||
if (element.disabled) {
|
||||
return false;
|
||||
}
|
||||
if (element.tabIndex < 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
getInteractiveElements: Blockly.CommonModal.getInteractiveElements,
|
||||
// Gets the container with interactive elements.
|
||||
getInteractiveContainer: function() {
|
||||
return document.getElementById("varForm");
|
||||
},
|
||||
// Submits the name change for the variable.
|
||||
submit: function() {
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
<script src="../../accessible/translate.pipe.js"></script>
|
||||
<script src="../../accessible/variable-modal.service.js"></script>
|
||||
|
||||
<script src="../../accessible/commonModal.js"></script>
|
||||
<script src="../../accessible/field-segment.component.js"></script>
|
||||
<script src="../../accessible/block-options-modal.component.js"></script>
|
||||
<script src="../../accessible/toolbox-modal.component.js"></script>
|
||||
|
||||
Reference in New Issue
Block a user