2 * @classdesc Table row widget. A RowWidget is used in conjunction with
3 * {@link mw.widgets.TableWidget table widgets} and should not be instantiated by themselves.
4 * They group together {@link OO.ui.TextInputWidget text input widgets} to form a unified row of
8 * @extends OO.ui.Widget
9 * @mixes OO.ui.mixin.GroupElement
12 * @description Create an instance of `mw.widgets.RowWidget`.
13 * @param {Object} [config] Configuration options
14 * @param {Array} [config.data] The data of the cells
15 * @param {Array} [config.keys] An array of keys for easy cell selection
16 * @param {RegExp|Function|string} [config.validate] Validation pattern to apply on every cell
17 * @param {number} [config.index] The row index.
18 * @param {string} [config.label] The row label to display. If not provided, the row index will
19 * be used be default. If set to null, no label will be displayed.
20 * @param {boolean} [config.showLabel=true] Show row label. Defaults to true.
21 * @param {boolean} [config.deletable=true] Whether the table should provide deletion UI tools
22 * for this row or not. Defaults to true.
24 mw.widgets.RowWidget = function MwWidgetsRowWidget( config ) {
25 config = config || {};
28 mw.widgets.RowWidget.super.call( this, config );
31 OO.ui.mixin.GroupElement.call( this, config );
34 this.model = new mw.widgets.RowWidgetModel( config );
36 // Set up group element
39 .addClass( 'mw-widgets-rowWidget-cells' )
43 this.labelCell = new OO.ui.LabelWidget( {
44 classes: [ 'mw-widgets-rowWidget-label' ]
47 // Set up delete button
48 if ( this.model.getRowProperties().isDeletable ) {
49 this.deleteButton = new OO.ui.ButtonWidget( {
51 classes: [ 'mw-widgets-rowWidget-delete-button' ],
53 title: mw.msg( 'mw-widgets-table-row-delete' )
58 this.model.connect( this, {
59 valueChange: 'onValueChange',
60 insertCell: 'onInsertCell',
61 removeCell: 'onRemoveCell',
63 labelUpdate: 'onLabelUpdate'
71 cellChange: 'onCellChange'
74 if ( this.model.getRowProperties().isDeletable ) {
75 this.deleteButton.connect( this, {
76 click: 'onDeleteButtonClick'
81 this.$element.addClass( 'mw-widgets-rowWidget' );
84 this.labelCell.$element,
88 if ( this.model.getRowProperties().isDeletable ) {
89 this.$element.append( this.deleteButton.$element );
92 this.setLabel( this.model.getRowProperties().label );
94 this.model.setupRow();
99 OO.inheritClass( mw.widgets.RowWidget, OO.ui.Widget );
100 OO.mixinClass( mw.widgets.RowWidget, OO.ui.mixin.GroupElement );
105 * Change when an input contained within the row is updated.
107 * @event mw.widgets.RowWidget.inputChange
108 * @param {number} index The index of the cell that changed
109 * @param {string} value The new value of the cell
113 * Fired when the delete button for the row is pressed.
115 * @event mw.widgets.RowWidget.deleteButtonClick
124 mw.widgets.RowWidget.prototype.addItems = function ( items, index ) {
127 OO.ui.mixin.GroupElement.prototype.addItems.call( this, items, index );
129 for ( i = index, len = items.length; i < len; i++ ) {
130 items[ i ].setData( i );
138 mw.widgets.RowWidget.prototype.removeItems = function ( items ) {
139 OO.ui.mixin.GroupElement.prototype.removeItems.call( this, items );
141 const cells = this.getItems();
142 for ( let i = 0, len = cells.length; i < len; i++ ) {
143 cells[ i ].setData( i );
150 * @return {number} The row index
152 mw.widgets.RowWidget.prototype.getIndex = function () {
153 return this.model.getRowProperties().index;
159 * @param {number} index The new index
161 mw.widgets.RowWidget.prototype.setIndex = function ( index ) {
162 this.model.setIndex( index );
166 * Get the label displayed on the row. If no custom label is set, the
167 * row index is used instead.
169 * @return {string} The row label
171 mw.widgets.RowWidget.prototype.getLabel = function () {
172 const props = this.model.getRowProperties();
174 if ( props.label === null ) {
176 } else if ( !props.label ) {
177 return props.index.toString();
184 * @event mw.widgets.RowWidget.labelUpdate
185 * @param {string} label
189 * Set the label to be displayed on the widget.
191 * @param {string} label The new label
192 * @fires mw.widgets.RowWidget.labelUpdate
194 mw.widgets.RowWidget.prototype.setLabel = function ( label ) {
195 this.model.setLabel( label );
199 * Set the value of a particular cell.
201 * @param {number} index The cell index
202 * @param {string} value The new value
204 mw.widgets.RowWidget.prototype.setValue = function ( index, value ) {
205 this.model.setValue( index, value );
209 * Insert a cell at a specified index.
211 * @param {string} data The cell data
212 * @param {number} index The index to insert the cell at
213 * @param {string} key A key for easy cell selection
215 mw.widgets.RowWidget.prototype.insertCell = function ( data, index, key ) {
216 this.model.insertCell( data, index, key );
220 * Removes a column at a specified index.
222 * @param {number} index The index to removeColumn
224 mw.widgets.RowWidget.prototype.removeCell = function ( index ) {
225 this.model.removeCell( index );
229 * Clear the field values.
231 mw.widgets.RowWidget.prototype.clear = function () {
236 * Handle model value changes.
238 * @param {number} index The column index of the updated cell
239 * @param {number} value The new value
241 * @fires mw.widgets.RowWidget.inputChange
243 mw.widgets.RowWidget.prototype.onValueChange = function ( index, value ) {
244 this.getItems()[ index ].setValue( value );
245 this.emit( 'inputChange', index, value );
249 * Handle model cell insertions.
251 * @param {string} data The initial data
252 * @param {number} index The index in which to insert the new cell
254 mw.widgets.RowWidget.prototype.onInsertCell = function ( data, index ) {
256 new OO.ui.TextInputWidget( {
259 validate: this.model.getValidationPattern()
265 * Handle model cell removals.
267 * @param {number} index The removed cell index
269 mw.widgets.RowWidget.prototype.onRemoveCell = function ( index ) {
270 this.removeItems( [ index ] );
274 * Handle clear requests.
276 mw.widgets.RowWidget.prototype.onClear = function () {
277 const cells = this.getItems();
279 for ( let i = 0, len = cells.length; i < len; i++ ) {
280 cells[ i ].setValue( '' );
285 * Update model label changes.
287 mw.widgets.RowWidget.prototype.onLabelUpdate = function () {
288 this.labelCell.setLabel( this.getLabel() );
292 * React to cell input change.
295 * @param {OO.ui.TextInputWidget} input The input that fired the event
296 * @param {string} value The value of the input
298 mw.widgets.RowWidget.prototype.onCellChange = function ( input, value ) {
299 // FIXME: The table itself should know if it contains invalid data
300 // in order to pass form state to the dialog when it asks if the Apply
301 // button should be enabled or not. This probably requires the table
302 // and each individual row to handle validation through an array of promises
303 // fed from the cells within.
304 // Right now, the table can't know if it's valid or not because the events
305 // don't get passed through.
306 input.getValidity().done( () => {
307 this.model.setValue( input.getData(), value );
312 * Handle delete button clicks.
315 * @fires mw.widgets.RowWidget.deleteButtonClick
317 mw.widgets.RowWidget.prototype.onDeleteButtonClick = function () {
318 this.emit( 'deleteButtonClick' );
324 mw.widgets.RowWidget.prototype.setDisabled = function ( disabled ) {
326 mw.widgets.RowWidget.super.prototype.setDisabled.call( this, disabled );
332 if ( this.model.getRowProperties().isDeletable ) {
333 this.deleteButton.setDisabled( disabled );
336 this.getItems().forEach( ( cell ) => {
337 cell.setDisabled( disabled );