mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/DiscussionTools
synced 2024-11-24 00:13:36 +00:00
Improve mode selector keyboard interactions
When there are just two modes, using arrow keys to switch between them is not intuitive. The focus moving from the selector to the body widget afterwards is even less intuitive. Override default TabOptionWidget to allow options to be highlightable (not just immediately selectable), and mark the current mode's tab as disabled instead of selected (but make it look selected). This results in intuitive keyboard interactions (tabbing to the widget highlights the other tab rather than the current one, pressing enter switches to it). Bug: T274423 Change-Id: I9d358d5f301cbf081380ef5d34ccc8c4e146652e
This commit is contained in:
parent
1dbe907011
commit
58c078437d
|
@ -118,6 +118,8 @@
|
||||||
"ext.discussionTools.ReplyWidget": {
|
"ext.discussionTools.ReplyWidget": {
|
||||||
"packageFiles": [
|
"packageFiles": [
|
||||||
"dt.ui.ReplyWidget.js",
|
"dt.ui.ReplyWidget.js",
|
||||||
|
"ModeTabSelectWidget.js",
|
||||||
|
"ModeTabOptionWidget.js",
|
||||||
"AbandonCommentDialog.js",
|
"AbandonCommentDialog.js",
|
||||||
"AbandonTopicDialog.js"
|
"AbandonTopicDialog.js"
|
||||||
],
|
],
|
||||||
|
|
12
modules/ModeTabOptionWidget.js
Normal file
12
modules/ModeTabOptionWidget.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
function ModeTabOptionWidget() {
|
||||||
|
// Parent constructor
|
||||||
|
ModeTabOptionWidget.super.apply( this, arguments );
|
||||||
|
|
||||||
|
this.$element.addClass( 'ext-discussiontools-ui-modeTab' );
|
||||||
|
}
|
||||||
|
|
||||||
|
OO.inheritClass( ModeTabOptionWidget, OO.ui.TabOptionWidget );
|
||||||
|
|
||||||
|
ModeTabOptionWidget.static.highlightable = true;
|
||||||
|
|
||||||
|
module.exports = ModeTabOptionWidget;
|
16
modules/ModeTabSelectWidget.js
Normal file
16
modules/ModeTabSelectWidget.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
function ModeTabSelectWidget() {
|
||||||
|
// Parent constructor
|
||||||
|
ModeTabSelectWidget.super.apply( this, arguments );
|
||||||
|
}
|
||||||
|
|
||||||
|
OO.inheritClass( ModeTabSelectWidget, OO.ui.TabSelectWidget );
|
||||||
|
|
||||||
|
ModeTabSelectWidget.prototype.onDocumentKeyDown = function ( e ) {
|
||||||
|
// Handle Space like Enter
|
||||||
|
if ( e.keyCode === OO.ui.Keys.SPACE ) {
|
||||||
|
e = $.Event( e, { keyCode: OO.ui.Keys.ENTER } );
|
||||||
|
}
|
||||||
|
ModeTabSelectWidget.super.prototype.onDocumentKeyDown.call( this, e );
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = ModeTabSelectWidget;
|
|
@ -3,6 +3,8 @@ var controller = require( 'ext.discussionTools.init' ).controller,
|
||||||
utils = require( 'ext.discussionTools.init' ).utils,
|
utils = require( 'ext.discussionTools.init' ).utils,
|
||||||
logger = require( 'ext.discussionTools.init' ).logger,
|
logger = require( 'ext.discussionTools.init' ).logger,
|
||||||
dtConf = require( 'ext.discussionTools.init' ).config,
|
dtConf = require( 'ext.discussionTools.init' ).config,
|
||||||
|
ModeTabSelectWidget = require( './ModeTabSelectWidget.js' ),
|
||||||
|
ModeTabOptionWidget = require( './ModeTabOptionWidget.js' ),
|
||||||
enable2017Wikitext = dtConf.enable2017Wikitext;
|
enable2017Wikitext = dtConf.enable2017Wikitext;
|
||||||
|
|
||||||
require( './AbandonCommentDialog.js' );
|
require( './AbandonCommentDialog.js' );
|
||||||
|
@ -84,22 +86,23 @@ function ReplyWidget( commentController, comment, pageName, oldId, config ) {
|
||||||
)
|
)
|
||||||
} );
|
} );
|
||||||
|
|
||||||
this.modeTabSelect = new OO.ui.TabSelectWidget( {
|
this.modeTabSelect = new ModeTabSelectWidget( {
|
||||||
classes: [ 'ext-discussiontools-ui-replyWidget-modeTabs' ],
|
classes: [ 'ext-discussiontools-ui-replyWidget-modeTabs' ],
|
||||||
items: [
|
items: [
|
||||||
new OO.ui.TabOptionWidget( {
|
new ModeTabOptionWidget( {
|
||||||
label: mw.msg( 'discussiontools-replywidget-mode-visual' ),
|
label: mw.msg( 'discussiontools-replywidget-mode-visual' ),
|
||||||
data: 'visual'
|
data: 'visual'
|
||||||
} ),
|
} ),
|
||||||
new OO.ui.TabOptionWidget( {
|
new ModeTabOptionWidget( {
|
||||||
label: mw.msg( 'discussiontools-replywidget-mode-source' ),
|
label: mw.msg( 'discussiontools-replywidget-mode-source' ),
|
||||||
data: 'source'
|
data: 'source'
|
||||||
} )
|
} )
|
||||||
],
|
],
|
||||||
framed: false
|
framed: false
|
||||||
} );
|
} );
|
||||||
// Initialize to avoid flicker when switching mode
|
// Make the option for the current mode disabled, to make it un-interactable
|
||||||
this.modeTabSelect.selectItemByData( this.getMode() );
|
// (we override the styles to make it look as if it was selected)
|
||||||
|
this.modeTabSelect.findItemFromData( this.getMode() ).setDisabled( true );
|
||||||
|
|
||||||
this.$headerWrapper = $( '<div>' ).addClass( 'ext-discussiontools-ui-replyWidget-headerWrapper' );
|
this.$headerWrapper = $( '<div>' ).addClass( 'ext-discussiontools-ui-replyWidget-headerWrapper' );
|
||||||
this.$headerWrapper.append(
|
this.$headerWrapper.append(
|
||||||
|
@ -429,7 +432,9 @@ ReplyWidget.prototype.setup = function ( data ) {
|
||||||
|
|
||||||
this.bindBeforeUnloadHandler();
|
this.bindBeforeUnloadHandler();
|
||||||
if ( this.modeTabSelect ) {
|
if ( this.modeTabSelect ) {
|
||||||
this.modeTabSelect.selectItemByData( this.getMode() );
|
// Make the option for the current mode disabled, to make it un-interactable
|
||||||
|
// (we override the styles to make it look as if it was selected)
|
||||||
|
this.modeTabSelect.findItemFromData( this.getMode() ).setDisabled( true );
|
||||||
this.saveEditMode( this.getMode() );
|
this.saveEditMode( this.getMode() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,10 +48,26 @@
|
||||||
text-align: right;
|
text-align: right;
|
||||||
// Stretch to all available space
|
// Stretch to all available space
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
// Hide outline that can appear after switching modes via keyboard
|
||||||
|
outline: 0;
|
||||||
|
|
||||||
.oo-ui-tabOptionWidget:last-child {
|
.oo-ui-tabOptionWidget:last-child {
|
||||||
margin-right: 2px;
|
margin-right: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When mode tabs are focussed, the only available option uses the same styles as normal focus
|
||||||
|
.ext-discussiontools-ui-modeTab.oo-ui-optionWidget-highlighted {
|
||||||
|
color: #36c;
|
||||||
|
border-radius: 2px;
|
||||||
|
box-shadow: inset 0 0 0 2px #36c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The unavailable option in mode tabs is disabled, to make it un-interactable, but we want it
|
||||||
|
// to look as if it was selected
|
||||||
|
.ext-discussiontools-ui-modeTab.oo-ui-widget-disabled {
|
||||||
|
color: #36c;
|
||||||
|
box-shadow: inset 0 -2px 0 0 #36c;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&-actionsWrapper {
|
&-actionsWrapper {
|
||||||
|
|
Loading…
Reference in a new issue