mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-12-04 18:58:37 +00:00
b518e55ef9
This is not great, but it's a start (and unblocks other pull-throughs). New changes: c401efc98 build: Replace jsduck with jsdoc for documentation 16ba162a0 JSDoc: @mixins -> @mixes 9e0a1f53b JSDoc: Fix complex return types 449b6cc0f Prefer arrow function callbacks 1539af2c8 Remove 'this' bindings in arrow functions b760f3b14 Use arrow functions in OO.ui.Process steps 57c24109e Use arrow functions in jQuery callbacks 9622ccef9 Convert some remaining functions callbacks to arrow functions f6c885021 Remove useless local variable 1cd800020 Clear branch node cache when rebuilding tree Bug: T250843 Bug: T363329 Change-Id: I0f4878ca84b95e3f388b358b943f105637e455f9
270 lines
8.5 KiB
JavaScript
270 lines
8.5 KiB
JavaScript
/**
|
|
* List of template parameters, each of which can be added or removed using a
|
|
* checkbox.
|
|
*
|
|
* This is modelled after {@see OO.ui.OutlineSelectWidget}. Currently we use
|
|
* the SelectWidget in multi-select mode, and selection maps to checked
|
|
* checkboxes.
|
|
*
|
|
* @class
|
|
* @extends OO.ui.SelectWidget
|
|
* @mixins OO.ui.mixin.TabIndexedElement
|
|
* @mixins ve.ui.MWAriaDescribe
|
|
*
|
|
* @constructor
|
|
* @param {Object} config
|
|
* @cfg {ve.ui.MWTransclusionOutlineParameterWidget[]} items
|
|
* @property {string|null} activeParameter Name of the currently selected parameter
|
|
* @property {number} stickyHeaderHeight
|
|
*/
|
|
ve.ui.MWTransclusionOutlineParameterSelectWidget = function VeUiMWTransclusionOutlineParameterSelectWidget( config ) {
|
|
// Parent constructor
|
|
ve.ui.MWTransclusionOutlineParameterSelectWidget.super.call( this, ve.extendObject( config, {
|
|
classes: [ 've-ui-mwTransclusionOutlineParameterSelectWidget' ],
|
|
multiselect: true
|
|
} ) );
|
|
|
|
// Mixin constructors
|
|
OO.ui.mixin.TabIndexedElement.call( this, {
|
|
tabIndex: this.isEmpty() ? -1 : 0
|
|
} );
|
|
ve.ui.MWAriaDescribe.call( this, config );
|
|
|
|
this.$element
|
|
.on( {
|
|
focus: this.bindDocumentKeyDownListener.bind( this ),
|
|
blur: this.onBlur.bind( this )
|
|
} );
|
|
|
|
this.activeParameter = null;
|
|
this.stickyHeaderHeight = 0;
|
|
};
|
|
|
|
/* Inheritance */
|
|
|
|
OO.inheritClass( ve.ui.MWTransclusionOutlineParameterSelectWidget, OO.ui.SelectWidget );
|
|
OO.mixinClass( ve.ui.MWTransclusionOutlineParameterSelectWidget, OO.ui.mixin.TabIndexedElement );
|
|
OO.mixinClass( ve.ui.MWTransclusionOutlineParameterSelectWidget, ve.ui.MWAriaDescribe );
|
|
|
|
/* Events */
|
|
|
|
/**
|
|
* This is fired instead of the "choose" event from the {@see OO.ui.SelectWidget} base class when
|
|
* pressing space on a parameter to toggle it or scroll it into view, without losing the focus.
|
|
*
|
|
* @event ve.ui.MWTransclusionOutlineParameterSelectWidget#templateParameterSpaceDown
|
|
* @param {ve.ui.MWTransclusionOutlineParameterWidget} item
|
|
* @param {boolean} selected
|
|
*/
|
|
|
|
/* Static Methods */
|
|
|
|
/**
|
|
* @param {Object} config
|
|
* @param {string} config.data Parameter name
|
|
* @param {string} config.label
|
|
* @param {boolean} [config.required=false] Required parameters can't be unchecked
|
|
* @param {boolean} [config.selected=false] If the parameter is currently used (checked)
|
|
* @return {ve.ui.MWTransclusionOutlineParameterWidget}
|
|
*/
|
|
ve.ui.MWTransclusionOutlineParameterSelectWidget.static.createItem = function ( config ) {
|
|
return new ve.ui.MWTransclusionOutlineParameterWidget( config );
|
|
};
|
|
|
|
/* Methods */
|
|
|
|
/**
|
|
* @inheritDoc OO.ui.mixin.GroupElement
|
|
* @param {ve.ui.MWTransclusionOutlineParameterWidget[]} items
|
|
* @param {number} [index]
|
|
* @return {ve.ui.MWTransclusionOutlineParameterSelectWidget}
|
|
*/
|
|
ve.ui.MWTransclusionOutlineParameterSelectWidget.prototype.addItems = function ( items, index ) {
|
|
var self = this;
|
|
items.forEach( function ( item ) {
|
|
item.connect( self, {
|
|
change: [ 'onCheckboxChange', item ]
|
|
} );
|
|
} );
|
|
|
|
ve.ui.MWTransclusionOutlineParameterSelectWidget.super.prototype.addItems.call( this, items, index );
|
|
this.setTabIndex( this.isEmpty() ? -1 : 0 );
|
|
return this;
|
|
};
|
|
|
|
ve.ui.MWTransclusionOutlineParameterSelectWidget.prototype.ensureVisibilityOfFirstCheckedParameter = function () {
|
|
// TODO: Replace with {@see OO.ui.SelectWidget.findFirstSelectedItem} when available
|
|
var firstChecked = this.findSelectedItems()[ 0 ];
|
|
if ( firstChecked ) {
|
|
firstChecked.ensureVisibility( this.stickyHeaderHeight );
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param {string|null} [paramName] Parameter name to set, e.g. "param1". Omit to remove setting.
|
|
*/
|
|
ve.ui.MWTransclusionOutlineParameterSelectWidget.prototype.setActiveParameter = function ( paramName ) {
|
|
// Note: We know unnamed parameter placeholders never have an item here
|
|
var newItem = paramName ? this.findItemFromData( paramName ) : null;
|
|
// Unhighlight when called with no parameter name
|
|
this.highlightItem( newItem );
|
|
|
|
paramName = paramName || null;
|
|
if ( this.activeParameter === paramName ) {
|
|
return;
|
|
}
|
|
|
|
var currentItem = this.activeParameter ? this.findItemFromData( this.activeParameter ) : null;
|
|
this.activeParameter = paramName;
|
|
|
|
if ( currentItem ) {
|
|
currentItem.toggleActivePageIndicator( false );
|
|
}
|
|
if ( newItem ) {
|
|
newItem.toggleActivePageIndicator( true );
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @inheritDoc OO.ui.SelectWidget
|
|
*/
|
|
ve.ui.MWTransclusionOutlineParameterSelectWidget.prototype.highlightItem = function ( item ) {
|
|
if ( item ) {
|
|
item.ensureVisibility( this.stickyHeaderHeight );
|
|
}
|
|
ve.ui.MWTransclusionOutlineParameterSelectWidget.super.prototype.highlightItem.call( this, item );
|
|
};
|
|
|
|
/**
|
|
* @param {string} paramName
|
|
*/
|
|
ve.ui.MWTransclusionOutlineParameterSelectWidget.prototype.markParameterAsUnused = function ( paramName ) {
|
|
// There is no OO.ui.SelectWidget.unselectItemByData(), we need to do this manually
|
|
/** @type {ve.ui.MWTransclusionOutlineParameterWidget} */
|
|
var item = paramName ? this.findItemFromData( paramName ) : null;
|
|
if ( item ) {
|
|
item.setSelected( false );
|
|
// An unused parameter can't be the active (set) one; it doesn't exist in the content pane
|
|
if ( this.activeParameter === paramName ) {
|
|
this.activeParameter = null;
|
|
item.toggleActivePageIndicator( false );
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @private
|
|
* @param {ve.ui.MWTransclusionOutlineParameterWidget} item
|
|
* @param {boolean} value
|
|
*/
|
|
ve.ui.MWTransclusionOutlineParameterSelectWidget.prototype.onCheckboxChange = function ( item, value ) {
|
|
// This extra check shouldn't be necessary, but better be safe than sorry
|
|
if ( item.isSelected() !== value ) {
|
|
// Note: This should have been named `toggle…` as it toggles the item's selection
|
|
this.chooseItem( item );
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @inheritDoc OO.ui.SelectWidget
|
|
*/
|
|
ve.ui.MWTransclusionOutlineParameterSelectWidget.prototype.onFocus = function ( event ) {
|
|
if ( event.target !== this.$element[ 0 ] || this.findHighlightedItem() ) {
|
|
return;
|
|
}
|
|
|
|
var index = 0;
|
|
if ( event.relatedTarget ) {
|
|
var toolbarClass = 've-ui-mwTransclusionOutlineControlsWidget',
|
|
// The only elements below a parameter list can be another part or the toolbar
|
|
selector = '.ve-ui-mwTransclusionOutlinePartWidget, .' + toolbarClass,
|
|
$fromPart = $( event.relatedTarget ).closest( selector ),
|
|
$toPart = $( event.target ).closest( selector );
|
|
// When shift+tabbing into the list, highlight the last parameter
|
|
// eslint-disable-next-line no-jquery/no-class-state
|
|
if ( $fromPart.hasClass( toolbarClass ) || $fromPart.index() > $toPart.index() ) {
|
|
index = this.getItemCount() - 1;
|
|
}
|
|
}
|
|
this.highlightItem( this.items[ index ] );
|
|
|
|
// Don't call the parent. It makes assumptions what should be done here.
|
|
};
|
|
|
|
/**
|
|
* @inheritDoc OO.ui.SelectWidget
|
|
* @param {jQuery.Event} e
|
|
* @fires choose
|
|
*/
|
|
ve.ui.MWTransclusionOutlineParameterSelectWidget.prototype.onMouseDown = function ( e ) {
|
|
if ( e.which === OO.ui.MouseButtons.LEFT ) {
|
|
var item = this.findTargetItem( e );
|
|
// Same as pressing enter, see below.
|
|
if ( item && item.isSelected() ) {
|
|
this.emit( 'choose', item, item.isSelected() );
|
|
|
|
// Don't call the parent, i.e. can't click to unselect the item
|
|
return false;
|
|
}
|
|
}
|
|
|
|
ve.ui.MWTransclusionOutlineParameterSelectWidget.super.prototype.onMouseDown.call( this, e );
|
|
};
|
|
|
|
/**
|
|
* @inheritDoc OO.ui.SelectWidget
|
|
* @param {KeyboardEvent} e
|
|
* @fires choose
|
|
* @fires templateParameterSpaceDown
|
|
*/
|
|
ve.ui.MWTransclusionOutlineParameterSelectWidget.prototype.onDocumentKeyDown = function ( e ) {
|
|
var item;
|
|
|
|
switch ( e.keyCode ) {
|
|
case OO.ui.Keys.HOME:
|
|
item = this.items[ 0 ];
|
|
if ( item ) {
|
|
this.highlightItem( item );
|
|
}
|
|
break;
|
|
case OO.ui.Keys.END:
|
|
item = this.items[ this.items.length - 1 ];
|
|
if ( item ) {
|
|
this.highlightItem( item );
|
|
}
|
|
break;
|
|
case OO.ui.Keys.SPACE:
|
|
item = this.findHighlightedItem();
|
|
if ( item ) {
|
|
// Warning, this intentionally doesn't call .chooseItem() because we don't want this
|
|
// to fire a "choose" event!
|
|
if ( item.isSelected() ) {
|
|
this.unselectItem( item );
|
|
} else {
|
|
this.selectItem( item );
|
|
}
|
|
this.emit( 'templateParameterSpaceDown', item, item.isSelected() );
|
|
}
|
|
e.preventDefault();
|
|
break;
|
|
case OO.ui.Keys.ENTER:
|
|
item = this.findHighlightedItem();
|
|
// Same as clicking with the mouse, see above.
|
|
if ( item && item.isSelected() ) {
|
|
this.emit( 'choose', item, item.isSelected() );
|
|
e.preventDefault();
|
|
|
|
// Don't call the parent, i.e. can't use enter to unselect the item
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
|
|
ve.ui.MWTransclusionOutlineParameterSelectWidget.super.prototype.onDocumentKeyDown.call( this, e );
|
|
};
|
|
|
|
ve.ui.MWTransclusionOutlineParameterSelectWidget.prototype.onBlur = function () {
|
|
this.highlightItem();
|
|
this.unbindDocumentKeyDownListener();
|
|
};
|