mediawiki-extensions-Visual.../modules/ve-mw/ui/widgets/ve.ui.MWTransclusionOutlinePartWidget.js
Thiemo Kreuz 66f56f6c88 Defer creating template parameter search when it's not needed
From the user's perspective this is the same as before: When a
template doesn't have any parameters, there is no search field. The
moment the first (undocumented) parameter is added the search field
appears.

This is just delayed now. The widgets are only created the moment
they are actually needed.

This saves loading time and memory, especially in a multi-part
transclusion with many zero-parameter templates.

This also makes it a lot easier to change the minimal number of
parameters from 1 to e.g. 4.

Includes reverting the flexible header composition done in
Ib050e30a50ef965c1524e977d3a600c3ff836774

Bug: T298259
Change-Id: Ied7541d8d5c0b478a439dd31ce072e634287f181
2022-01-19 11:23:34 +01:00

165 lines
4.8 KiB
JavaScript

/**
* Common base class for top-level items (a.k.a. "parts") in the template editor sidebar. Subclasses
* should exist for all subclasses of {@see ve.dm.MWTransclusionPartModel}:
* - {@see ve.dm.MWTemplateModel}
* - {@see ve.dm.MWTemplatePlaceholderModel}
* - {@see ve.dm.MWTransclusionContentModel}
*
* This is inspired by and meant to replace {@see OO.ui.DecoratedOptionWidget} in the context of the
* template dialog. Also see {@see OO.ui.ButtonWidget} for inspiration.
*
* @abstract
* @class
* @extends OO.ui.Widget
*
* @constructor
* @param {ve.dm.MWTransclusionPartModel} part
* @param {Object} config
* @cfg {string} [icon=''] Symbolic name of an icon, e.g. "puzzle" or "wikiText"
* @cfg {string} label
* @cfg {string} ariaDescriptionUnselected
* @cfg {string} ariaDescriptionSelected
* @cfg {string} ariaDescriptionSelectedSingle
*/
ve.ui.MWTransclusionOutlinePartWidget = function VeUiMWTransclusionOutlinePartWidget( part, config ) {
this.part = part;
// Parent constructor
ve.ui.MWTransclusionOutlinePartWidget.super.call( this, ve.extendObject( config, {
classes: [ 've-ui-mwTransclusionOutlinePartWidget' ],
data: part.getId()
} ) );
this.header = new ve.ui.MWTransclusionOutlineButtonWidget( config )
.connect( this, {
keyPressed: 'onHeaderKeyPressed',
click: 'onHeaderClick'
} );
if ( config.ariaDescriptionUnselected &&
config.ariaDescriptionSelected &&
config.ariaDescriptionSelectedSingle
) {
this.$ariaDescriptionUnselected = $( '<span>' )
.text( config.ariaDescriptionUnselected )
.addClass( 've-ui-mwTransclusionOutline-ariaHidden' );
this.$ariaDescriptionSelected = $( '<span>' )
.text( config.ariaDescriptionSelected )
.addClass( 've-ui-mwTransclusionOutline-ariaHidden' );
this.$ariaDescriptionSelectedSingle = $( '<span>' )
.text( config.ariaDescriptionSelectedSingle )
.addClass( 've-ui-mwTransclusionOutline-ariaHidden' );
this.header.setAriaDescribedBy( this.$ariaDescriptionUnselected );
this.header.$element.prepend(
this.$ariaDescriptionUnselected,
this.$ariaDescriptionSelected,
this.$ariaDescriptionSelectedSingle
);
}
this.transclusionModel = this.part.getTransclusion().connect( this, {
replace: 'updateButtonAriaDescription'
} );
this.$element.append( this.header.$element );
};
/* Inheritance */
OO.inheritClass( ve.ui.MWTransclusionOutlinePartWidget, OO.ui.Widget );
/* Events */
/**
* "Soft" selection with space.
*
* @event transclusionPartSoftSelected
* @param {string} partId Unique id of the {@see ve.dm.MWTransclusionPartModel}, e.g. something like
* "part_1".
*/
/**
* "Hard" selection with enter or mouse click.
*
* @event transclusionPartSelected
* @param {string} pageName Unique id of the {@see OO.ui.BookletLayout} page, e.g. something like
* "part_1" or "part_1/param1".
*/
/* Methods */
/**
* @private
* @param {number} key Note that some keys only make it here when Ctrl or Ctrl+Shift is pressed
* @fires transclusionPartSoftSelected
*/
ve.ui.MWTransclusionOutlinePartWidget.prototype.onHeaderKeyPressed = function ( key ) {
switch ( key ) {
case OO.ui.Keys.SPACE:
this.emit( 'transclusionPartSoftSelected', this.getData() );
break;
case OO.ui.Keys.UP:
case OO.ui.Keys.DOWN:
// Modelled after {@see ve.ui.MWTransclusionDialog.onOutlineControlsMove}
var transclusion = this.part.getTransclusion(),
parts = transclusion.getParts(),
offset = key === OO.ui.Keys.UP ? -1 : 1,
newIndex = parts.indexOf( this.part ) + offset;
if ( newIndex >= 0 && newIndex < parts.length ) {
transclusion.addPart( this.part, newIndex );
}
break;
case OO.ui.Keys.DELETE:
this.part.remove();
break;
}
};
/**
* @protected
* @fires transclusionPartSelected
*/
ve.ui.MWTransclusionOutlinePartWidget.prototype.onHeaderClick = function () {
this.emit( 'transclusionPartSelected', this.getData() );
};
/**
* Convenience method, modelled after {@see OO.ui.OptionWidget}, but this isn't one.
*
* @return {boolean}
*/
ve.ui.MWTransclusionOutlinePartWidget.prototype.isSelected = function () {
return this.header.isSelected();
};
/**
* Convenience method, modelled after {@see OO.ui.OptionWidget}, but this isn't one.
*
* @param {boolean} state
*/
ve.ui.MWTransclusionOutlinePartWidget.prototype.setSelected = function ( state ) {
this.updateButtonAriaDescription( state );
this.header
.setSelected( state )
.setFlags( { progressive: state } );
};
/**
* @param {boolean} state
*/
ve.ui.MWTransclusionOutlinePartWidget.prototype.updateButtonAriaDescription = function ( state ) {
if ( !this.$ariaDescriptionUnselected ||
!this.$ariaDescriptionSelected ||
!this.$ariaDescriptionSelectedSingle
) {
return;
}
this.header.setAriaDescribedBy( !state ? this.$ariaDescriptionUnselected :
( this.transclusionModel.isSingleTemplate() ? this.$ariaDescriptionSelectedSingle : this.$ariaDescriptionSelected )
);
};