'MediaWiki\\Site\\MediaWikiPageNameNormalizer' => __DIR__ . '/includes/site/MediaWikiPageNameNormalizer.php',
'MediaWiki\\User\\UserIdentity' => __DIR__ . '/includes/user/UserIdentity.php',
'MediaWiki\\User\\UserIdentityValue' => __DIR__ . '/includes/user/UserIdentityValue.php',
+ 'MediaWiki\\Widget\\CheckMatrixWidget' => __DIR__ . '/includes/widget/CheckMatrixWidget.php',
'MediaWiki\\Widget\\ComplexNamespaceInputWidget' => __DIR__ . '/includes/widget/ComplexNamespaceInputWidget.php',
'MediaWiki\\Widget\\ComplexTitleInputWidget' => __DIR__ . '/includes/widget/ComplexTitleInputWidget.php',
'MediaWiki\\Widget\\DateInputWidget' => __DIR__ . '/includes/widget/DateInputWidget.php',
$thisAttribs['class'] = 'checkmatrix-forced checkmatrix-forced-on';
}
- $checkbox = $this->getOneCheckbox( $checked, $attribs + $thisAttribs );
+ $checkbox = $this->getOneCheckboxHTML( $checked, $attribs + $thisAttribs );
$rowContents .= Html::rawElement(
'td',
return $html;
}
- protected function getOneCheckbox( $checked, $attribs ) {
- if ( $this->mParent instanceof OOUIHTMLForm ) {
- return new OOUI\CheckboxInputWidget( [
- 'name' => "{$this->mName}[]",
- 'selected' => $checked,
- ] + OOUI\Element::configFromHtmlAttributes(
- $attribs
- ) );
- } else {
- $checkbox = Xml::check( "{$this->mName}[]", $checked, $attribs );
- if ( $this->mParent->getConfig()->get( 'UseMediaWikiUIEverywhere' ) ) {
- $checkbox = Html::openElement( 'div', [ 'class' => 'mw-ui-checkbox' ] ) .
- $checkbox .
- Html::element( 'label', [ 'for' => $attribs['id'] ] ) .
- Html::closeElement( 'div' );
- }
- return $checkbox;
+ public function getInputOOUI( $value ) {
+ $attribs = $this->getAttributes( [ 'disabled', 'tabindex' ] );
+
+ return new MediaWiki\Widget\CheckMatrixWidget(
+ [
+ 'name' => $this->mName,
+ 'infusable' => true,
+ 'id' => $this->mID,
+ 'rows' => $this->mParams['rows'],
+ 'columns' => $this->mParams['columns'],
+ 'tooltips' => $this->mParams['tooltips'],
+ 'forcedOff' => isset( $this->mParams['force-options-off'] ) ?
+ $this->mParams['force-options-off'] : [],
+ 'forcedOn' => isset( $this->mParams['force-options-on'] ) ?
+ $this->mParams['force-options-on'] : [],
+ 'values' => $value
+ ] + OOUI\Element::configFromHtmlAttributes( $attribs )
+ );
+ }
+
+ protected function getOneCheckboxHTML( $checked, $attribs ) {
+ $checkbox = Xml::check( "{$this->mName}[]", $checked, $attribs );
+ if ( $this->mParent->getConfig()->get( 'UseMediaWikiUIEverywhere' ) ) {
+ $checkbox = Html::openElement( 'div', [ 'class' => 'mw-ui-checkbox' ] ) .
+ $checkbox .
+ Html::element( 'label', [ 'for' => $attribs['id'] ] ) .
+ Html::closeElement( 'div' );
}
+ return $checkbox;
}
protected function isTagForcedOff( $tag ) {
return $res;
}
+
+ protected function getOOUIModules() {
+ return [ 'mediawiki.widgets.CheckMatrixWidget' ];
+ }
+
+ protected function shouldInfuseOOUI() {
+ return true;
+ }
}
--- /dev/null
+<?php
+
+namespace MediaWiki\Widget;
+
+/**
+ * Check matrix widget. Displays a matrix of checkboxes for given options
+ *
+ * @copyright 2018 MediaWiki Widgets Team and others; see AUTHORS.txt
+ * @license MIT
+ */
+class CheckMatrixWidget extends \OOUI\Widget {
+
+ protected $name = '';
+ protected $columns = [];
+ protected $rows = [];
+ protected $tooltips = [];
+ protected $values = [];
+ protected $forcedOn = [];
+ protected $forcedOff = [];
+
+ /**
+ * CheckMatrixWidget constructor
+ *
+ * Operates similarly to MultiSelectWidget, but instead of using an array of
+ * options, uses an array of rows and an array of columns to dynamically
+ * construct a matrix of options. The tags used to identify a particular cell
+ * are of the form "columnName-rowName"
+ *
+ * @param array $config Configuration array with the following options:
+ * - columns
+ * - Required list of columns in the matrix.
+ * - rows
+ * - Required list of rows in the matrix.
+ * - force-options-on
+ * - Accepts array of column-row tags to be displayed as enabled but unavailable to change
+ * - force-options-off
+ * - Accepts array of column-row tags to be displayed as disabled but unavailable to change.
+ * - tooltips
+ * - Optional array mapping row label to tooltip content
+ * - tooltip-class
+ * - Optional CSS class used on tooltip container span. Defaults to mw-icon-question.
+ */
+ public function __construct( array $config = [] ) {
+ // Configuration initialization
+
+ parent::__construct( $config );
+
+ $this->name = isset( $config['name'] ) ?
+ $config[ 'name' ] : null;
+ $this->id = isset( $config['id'] ) ?
+ $config['id'] : null;
+
+ // Properties
+ $this->rows = isset( $config['rows'] ) ?
+ $config['rows'] : [];
+ $this->columns = isset( $config['columns'] ) ?
+ $config['columns'] : [];
+ $this->tooltips = isset( $config['tooltips'] ) ?
+ $config['tooltips'] : [];
+
+ $this->values = isset( $config['values'] ) ?
+ $config['values'] : [];
+
+ $this->forcedOn = isset( $config['forcedOn'] ) ?
+ $config['forcedOn'] : [];
+ $this->forcedOff = isset( $config['forcedOff'] ) ?
+ $config['forcedOff'] : [];
+
+ // Build the table
+ $table = new \OOUI\Tag( 'table' );
+ $tr = new \OOUI\Tag( 'tr' );
+ // Build the header
+ $tr->appendContent( $this->getCellTag( "\u{00A0}" ) );
+ foreach ( $this->columns as $columnLabel => $columnTag ) {
+ $tr->appendContent(
+ $this->getCellTag( $columnLabel )
+ );
+ }
+ $table->appendContent( $tr );
+
+ // Build the options matrix
+ foreach ( $this->rows as $rowLabel => $rowTag ) {
+ $table->appendContent(
+ $this->getTableRow( $rowLabel, $rowTag )
+ );
+ }
+
+ // Initialization
+ $this->addClasses( [ 'mw-widget-checkMatrixWidget' ] );
+ $this->appendContent( $table );
+ }
+
+ /**
+ * Get a formatted table row for the option, with
+ * a checkbox widget.
+ *
+ * @param string $label Row label
+ * @param string $tag Row tag name
+ * @return \OOUI\Tag The resulting table row
+ */
+ private function getTableRow( $label, $tag ) {
+ $row = new \OOUI\Tag( 'tr' );
+ $tooltip = $this->getTooltip( $label );
+ $labelFieldConfig = $tooltip ? [ 'help' => $tooltip ] : [];
+ // Build label cell
+ $labelField = new \OOUI\FieldLayout(
+ new \OOUI\Widget(), // Empty widget, since we don't have the checkboxes here
+ [
+ 'label' => $label,
+ 'align' => 'inline',
+ ] + $labelFieldConfig
+ );
+ $row->appendContent( $this->getCellTag( $labelField ) );
+
+ // Build checkbox column cells
+ foreach ( $this->columns as $columnTag ) {
+ $thisTag = "$columnTag-$tag";
+
+ // Construct a checkbox
+ $checkbox = new \OOUI\CheckboxInputWidget( [
+ 'value' => $thisTag,
+ 'name' => $this->name ? "{$this->name}[]" : null,
+ 'id' => $this->id ? "{$this->id}-$thisTag" : null,
+ 'selected' => $this->isTagChecked( $thisTag ),
+ 'disabled' => $this->isTagDisabled( $thisTag ),
+ ] );
+
+ $row->appendContent( $this->getCellTag( $checkbox ) );
+ }
+ return $row;
+ }
+
+ /**
+ * Get an individual cell tag with requested content
+ *
+ * @param string $content Content for the <td> cell
+ * @return \OOUI\Tag Resulting cell
+ */
+ private function getCellTag( $content ) {
+ $cell = new \OOUI\Tag( 'td' );
+ $cell->appendContent( $content );
+ return $cell;
+ }
+
+ /**
+ * Check whether the given tag's checkbox should
+ * be checked
+ *
+ * @param string $tagName Tag name
+ * @return boolean Tag should be checked
+ */
+ private function isTagChecked( $tagName ) {
+ // If the tag is in the value list
+ return in_array( $tagName, (array)$this->values, true ) ||
+ // Or if the tag is forced on
+ in_array( $tagName, (array)$this->forcedOn, true );
+ }
+
+ /**
+ * Check whether the given tag's checkbox should
+ * be disabled
+ *
+ * @param string $tagName Tag name
+ * @return boolean Tag should be disabled
+ */
+ private function isTagDisabled( $tagName ) {
+ return (
+ // If the entire widget is disabled
+ $this->isDisabled() ||
+ // If the tag is 'forced on' or 'forced off'
+ in_array( $tagName, (array)$this->forcedOn, true ) ||
+ in_array( $tagName, (array)$this->forcedOff, true )
+ );
+ }
+
+ /**
+ * Get the tooltip help associated with this row
+ *
+ * @param string $label Label name
+ * @return string Tooltip. Null if none is available.
+ */
+ private function getTooltip( $label ) {
+ return isset( $this->tooltips[ $label ] ) ?
+ $this->tooltips[ $label ] : null;
+ }
+
+ protected function getJavaScriptClassName() {
+ return 'mw.widgets.CheckMatrixWidget';
+ }
+
+ public function getConfig( &$config ) {
+ $config += [
+ 'name' => $this->name,
+ 'id' => $this->id,
+ 'rows' => $this->rows,
+ 'columns' => $this->columns,
+ 'tooltips' => $this->tooltips,
+ 'forcedOff' => $this->forcedOff,
+ 'forcedOn' => $this->forcedOn,
+ 'values' => $this->values,
+ ];
+ return parent::getConfig( $config );
+ }
+}
],
'targets' => [ 'desktop', 'mobile' ],
],
+ 'mediawiki.widgets.CheckMatrixWidget' => [
+ 'scripts' => [
+ 'resources/src/mediawiki.widgets/mw.widgets.CheckMatrixWidget.js',
+ ],
+ 'dependencies' => [
+ 'oojs-ui-core',
+ ],
+ 'targets' => [ 'desktop', 'mobile' ],
+ ],
'mediawiki.widgets.CategoryMultiselectWidget' => [
'scripts' => [
'resources/src/mediawiki.widgets/mw.widgets.CategoryTagItemWidget.js',
--- /dev/null
+( function ( $, mw ) {
+ /**
+ * A JavaScript version of CheckMatrixWidget.
+ *
+ * @class
+ * @extends OO.ui.Widget
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ * @cfg {Object} columns Required object representing the column labels and associated
+ * tags in the matrix.
+ * @cfg {Object} rows Required object representing the row labels and associated
+ * tags in the matrix.
+ * @cfg {string[]} [forcedOn] An array of column-row tags to be displayed as
+ * enabled but unavailable to change
+ * @cfg {string[]} [forcedOff] An array of column-row tags to be displayed as
+ * disnabled but unavailable to change
+ * @cfg {Object} Object mapping row label to tooltip content
+ */
+ mw.widgets.CheckMatrixWidget = function MWWCheckMatrixWidget( config ) {
+ var $headRow = $( '<tr>' ),
+ $table = $( '<table>' ),
+ widget = this;
+ config = config || {};
+
+ // Parent constructor
+ mw.widgets.CheckMatrixWidget.parent.call( this, config );
+ this.checkboxes = {};
+ this.name = config.name;
+ this.id = config.id;
+ this.rows = config.rows || {};
+ this.columns = config.columns || {};
+ this.tooltips = config.tooltips || [];
+ this.values = config.values || [];
+ this.forcedOn = config.forcedOn || [];
+ this.forcedOff = config.forcedOff || [];
+
+ // Build header
+ $headRow.append( $( '<td>' ).html( ' ' ) );
+
+ // Iterate over the columns object (ignore the value)
+ $.each( this.columns, function ( columnLabel ) {
+ $headRow.append( $( '<td>' ).text( columnLabel ) );
+ } );
+ $table.append( $headRow );
+
+ // Build table
+ $.each( this.rows, function ( rowLabel, rowTag ) {
+ var $row = $( '<tr>' ),
+ labelField = new OO.ui.FieldLayout(
+ new OO.ui.Widget(), // Empty widget, since we don't have the checkboxes here
+ {
+ label: rowLabel,
+ help: widget.tooltips[ rowLabel ],
+ align: 'inline'
+ }
+ );
+
+ // Label
+ $row.append( $( '<td>' ).append( labelField.$element ) );
+
+ // Columns
+ $.each( widget.columns, function ( columnLabel, columnTag ) {
+ var thisTag = columnTag + '-' + rowTag,
+ checkbox = new OO.ui.CheckboxInputWidget( {
+ value: thisTag,
+ name: widget.name ? widget.name + '[]' : undefined,
+ id: widget.id ? widget.id + '-' + thisTag : undefined,
+ selected: widget.isTagSelected( thisTag ),
+ disabled: widget.isTagDisabled( thisTag )
+ } );
+
+ widget.checkboxes[ thisTag ] = checkbox;
+ $row.append( $( '<td>' ).append( checkbox.$element ) );
+ } );
+
+ $table.append( $row );
+ } );
+
+ this.$element
+ .addClass( 'mw-widget-checkMatrixWidget' )
+ .append( $table );
+ };
+
+ /* Setup */
+
+ OO.inheritClass( mw.widgets.CheckMatrixWidget, OO.ui.Widget );
+
+ /* Methods */
+
+ /**
+ * Check whether the given tag is selected
+ *
+ * @param {string} tagName Tag name
+ * @return {boolean} Tag is selected
+ */
+ mw.widgets.CheckMatrixWidget.prototype.isTagSelected = function ( tagName ) {
+ return (
+ // If tag is not forced off
+ this.forcedOff.indexOf( tagName ) === -1 &&
+ (
+ // If tag is in values
+ this.values.indexOf( tagName ) > -1 ||
+ // If tag is forced on
+ this.forcedOn.indexOf( tagName ) > -1
+ )
+ );
+ };
+
+ /**
+ * Check whether the given tag is disabled
+ *
+ * @param {string} tagName Tag name
+ * @return {boolean} Tag is disabled
+ */
+ mw.widgets.CheckMatrixWidget.prototype.isTagDisabled = function ( tagName ) {
+ return (
+ // If the entire widget is disabled
+ this.isDisabled() ||
+ // If tag is forced off or forced on
+ this.forcedOff.indexOf( tagName ) > -1 ||
+ this.forcedOn.indexOf( tagName ) > -1
+ );
+ };
+ /**
+ * @inheritdoc
+ */
+ mw.widgets.CheckMatrixWidget.prototype.setDisabled = function ( isDisabled ) {
+ var widget = this;
+
+ // Parent method
+ mw.widgets.CheckMatrixWidget.parent.prototype.setDisabled.call( this, isDisabled );
+
+ // setDisabled sometimes gets called before the widget is ready
+ if ( this.checkboxes && Object.keys( this.checkboxes ).length > 0 ) {
+ // Propagate to all checkboxes and update their disabled state
+ $.each( this.checkboxes, function ( name, checkbox ) {
+ checkbox.setDisabled( widget.isTagDisabled( name ) );
+ } );
+ }
+ };
+}( jQuery, mediaWiki ) );