define([
|
'dojo/_base/declare',
|
'dojo/_base/lang',
|
'dojo/_base/sniff',
|
'dojo/dom-construct',
|
'dojo/dom-class',
|
'./Selection'
|
], function (declare, lang, has, domConstruct, domClass, Selection) {
|
|
return declare(Selection, {
|
// summary:
|
// Adds an input field (checkbox or radio) to a column that when checked, selects the row
|
// that contains the input field. To enable, add a "selector" property to a column definition.
|
//
|
// description:
|
// The selector property should contain "checkbox", "radio", or be a function that renders the input.
|
// If set to "radio", the input field will be a radio button and only one input in the column will be
|
// checked. If the value of selector is a function, then the function signature is
|
// renderSelectorInput(column, value, cell, object) where:
|
// * column - the column definition
|
// * value - the cell's value
|
// * cell - the cell's DOM node
|
// * object - the row's data object
|
// The custom renderSelectorInput function must return an input field.
|
|
postCreate: function () {
|
this.inherited(arguments);
|
|
// Register one listener at the top level that receives events delegated
|
this.on('.dgrid-selector:click,.dgrid-selector:keydown', lang.hitch(this, '_handleSelectorClick'));
|
// Register listeners to the select and deselect events to change the input checked value
|
this.on('dgrid-select', lang.hitch(this, '_changeSelectorInput', true));
|
this.on('dgrid-deselect', lang.hitch(this, '_changeSelectorInput', false));
|
},
|
|
_defaultRenderSelectorInput: function (column, selected, cell, object) {
|
var grid = column.grid;
|
|
domClass.add(cell, 'dgrid-selector');
|
return (cell.input = domConstruct.create('input', {
|
'aria-checked': selected,
|
checked: selected,
|
disabled: !grid.allowSelect(grid.row(object)),
|
tabIndex: isNaN(column.tabIndex) ? -1 : column.tabIndex,
|
type: column.selector
|
}, cell));
|
},
|
|
_configureSelectorColumn: function (column) {
|
var self = this;
|
var selector = column.selector;
|
|
this._selectorColumns.push(column);
|
this._selectorSingleRow = this._selectorSingleRow || column.selector === 'radio';
|
|
var renderSelectorInput = typeof selector === 'function' ?
|
selector : this._defaultRenderSelectorInput;
|
|
column.sortable = false;
|
|
column.renderCell = function (object, value, cell) {
|
var row = object && self.row(object);
|
value = row && self.selection[row.id];
|
renderSelectorInput(column, !!value, cell, object);
|
};
|
|
column.renderHeaderCell = function (th) {
|
var label = 'label' in column ? column.label : column.field || '';
|
|
if (column.selector === 'radio' || !self.allowSelectAll) {
|
th.appendChild(document.createTextNode(label));
|
}
|
else {
|
column._selectorHeaderCheckbox = renderSelectorInput(column, false, th, {});
|
self._hasSelectorHeaderCheckbox = true;
|
}
|
};
|
},
|
|
_handleSelectorClick: function (event) {
|
// Avoid double-triggering code below due to space key on input automatically triggering click (#731)
|
if (event.target.nodeName === 'INPUT' && event.type === 'keydown' && event.keyCode === 32) {
|
return;
|
}
|
|
var cell = this.cell(event);
|
var row = cell.row;
|
|
// We would really only care about click, since other input sources like spacebar
|
// trigger a click, but the click event doesn't provide access to the shift key in firefox, so
|
// listen for keydown as well to get an event in firefox that we can properly retrieve
|
// the shiftKey property
|
if (event.type === 'click' || event.keyCode === 32 ||
|
(!has('opera') && event.keyCode === 13) || event.keyCode === 0) {
|
|
this._selectionTriggerEvent = event;
|
|
if (row) {
|
if (this.allowSelect(row)) {
|
var lastRow = this._lastSelected && this.row(this._lastSelected);
|
|
if (this._selectorSingleRow) {
|
if (!lastRow || lastRow.id !== row.id) {
|
this.clearSelection();
|
this.select(row, null, true);
|
this._lastSelected = row.element;
|
}
|
}
|
else {
|
if (row) {
|
if (event.shiftKey) {
|
// Make sure the last input always ends up checked for shift key
|
this._changeSelectorInput(true, {rows: [row]});
|
}
|
else {
|
// No shift key, so no range selection
|
lastRow = null;
|
}
|
lastRow = event.shiftKey ? lastRow : null;
|
this.select(lastRow || row, row, lastRow ? undefined : null);
|
this._lastSelected = row.element;
|
}
|
}
|
}
|
}
|
else {
|
// No row resolved; must be the select-all checkbox.
|
this[this.allSelected ? 'clearSelection' : 'selectAll']();
|
}
|
|
this._selectionTriggerEvent = null;
|
}
|
},
|
|
_changeSelectorInput: function (value, event) {
|
if (this._selectorColumns.length) {
|
this._updateRowSelectors(value, event);
|
}
|
if (this._hasSelectorHeaderCheckbox) {
|
this._updateHeaderCheckboxes();
|
}
|
},
|
|
_updateRowSelectors: function (value, event) {
|
var rows = event.rows;
|
var lenRows = rows.length;
|
var lenCols = this._selectorColumns.length;
|
|
for (var iRows = 0; iRows < lenRows; iRows++) {
|
for (var iCols = 0; iCols < lenCols; iCols++) {
|
var column = this._selectorColumns[iCols];
|
var element = this.cell(rows[iRows], column.id).element;
|
if (!element) {
|
// Skip if row has been entirely removed
|
continue;
|
}
|
element = (element.contents || element).input;
|
if (element && !element.disabled) {
|
// Only change the value if it is not disabled
|
element.checked = value;
|
element.setAttribute('aria-checked', value);
|
}
|
}
|
}
|
},
|
|
_updateHeaderCheckboxes: function () {
|
/* jshint eqeqeq: false */
|
var lenCols = this._selectorColumns.length;
|
for (var iCols = 0; iCols < lenCols; iCols++) {
|
var column = this._selectorColumns[iCols];
|
var state = 'false';
|
var selection;
|
var mixed;
|
var selectorHeaderCheckbox = column._selectorHeaderCheckbox;
|
if (selectorHeaderCheckbox) {
|
selection = this.selection;
|
mixed = false;
|
// See if the header checkbox needs to be indeterminate
|
for (var i in selection) {
|
// If there is anything in the selection, than it is indeterminate
|
// (Intentionally coerce since selection[i] can be undefined)
|
if (selection[i] != this.allSelected) {
|
mixed = true;
|
break;
|
}
|
}
|
selectorHeaderCheckbox.indeterminate = mixed;
|
selectorHeaderCheckbox.checked = this.allSelected;
|
if (mixed) {
|
state = 'mixed';
|
}
|
else if (this.allSelected) {
|
state = 'true';
|
}
|
selectorHeaderCheckbox.setAttribute('aria-checked', state);
|
}
|
}
|
},
|
|
configStructure: function () {
|
this.inherited(arguments);
|
var columns = this.columns;
|
this._selectorColumns = [];
|
this._hasSelectorHeaderCheckbox = this._selectorSingleRow = false;
|
|
for (var k in columns) {
|
if (columns[k].selector) {
|
this._configureSelectorColumn(columns[k]);
|
}
|
}
|
},
|
|
_handleSelect: function (event) {
|
// Ignore the default select handler for events that originate from the selector column
|
var column = this.cell(event).column;
|
if (!column || !column.selector) {
|
this.inherited(arguments);
|
}
|
}
|
});
|
});
|