Skip to content

Commit

Permalink
New: -api select.selectable() to allow dynamic assignment of the ro…
Browse files Browse the repository at this point in the history
…w selectable function

Finishing up row selectable work
  • Loading branch information
AllanJard committed Sep 11, 2024
1 parent 7d1e3a0 commit ab9d6ae
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 17 deletions.
46 changes: 46 additions & 0 deletions docs/api/select.selectable().xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" ?>
<dt-api library="Select">
<name>select.selectable()</name>
<summary>Get / set the function used to determine if a row should be selectable</summary>
<since>2.1.0</since>

<type type="function">
<signature>select.selectable()</signature>
<returns type="function|undefined">
Returns the function set using this method or `-init select.selectable`. If no function is defined, `undefined` is returned.
</returns>
<description>
Get the function that is used to determine if a row is selectable.
</description>
</type>

<type type="function">
<signature>select.selectable( set )</signature>
<parameter type="function" name="set">
Function used to determine if a row should be selectable or not. See `-init select.selectable` for the function's signature.
</parameter>
<returns type="DataTables.Api">
DataTables API instance for chaining.
</returns>
<description>
Set the table's selectable function.
</description>
</type>

<description>
This method allows dynamic assignment of the `-init select.selectable` function, which is used to determine if a row should be selectable or not.
</description>

<example title="Let the user select items using the _os_ selection style"><![CDATA[
let table = new DataTable('#myTable', {
select: true
});
table.select.selectable((data, tr, idx) => {
return data.selectable;
});
]]></example>

<related>-init select.selectable</related>
<related>-event user-select</related>
</dt-api>
5 changes: 5 additions & 0 deletions docs/event/user-select.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@

Note that this event will not be triggered when the API methods for item selection are used (e.g. `se-api row().select()`). It will only be triggered by an end user action defined by `se-init select.selector`.

If you wish to make a row totally unselectable, use the `-init select.selectable` option.

Additionally, as with all DataTables emitted events, this event is triggered with the `dt` namespace. As such, to listen for this event, you must also use the `dt` namespace by simply appending `.dt` to your event name (this is done automatically when using `dt-api on()` and `dt-api one()`).
</description>

Expand All @@ -45,4 +47,7 @@ table.on('user-select', function (e, dt, type, cell, originalEvent) {
}
});
]]></example>

<related>-init select.selectable</related>
<related>-api select.selectable()</related>
</dt-event>
59 changes: 59 additions & 0 deletions docs/option/select.selectable.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8" ?>
<dt-option library="Select">
<name>select.selectable</name>
<summary>Set a function that will determine if a row should be selectable.</summary>
<since>2.1.0</since>

<default value="undefined"/>

<type type="function">
<signature>selectable( data, tr, index )</signature>
<parameter type="object|array" name="row">
The row's data source object or array (depending on `-init columns.data`).
</parameter>
<parameter type="node|null" name="tr">
The `-tag tr` element for the node. Please note that this might be `null` if Ajax loading data and the row has not yet been rendered (see `-init deferRender`).
</parameter>
<parameter type="integer" name="index">
The row's data index in the DataTable (`-api row().index()`).
</parameter>
<returns type="boolean">
Return `true` if the row is selectable and `false` if not.
</returns>
</type>

<description>
In some data sets you might find that you wish to disallow selection on some specific rows of data. This option provides exactly that ability by allowing you to define a function that will return a value to indicate if a row should be selectable or not based on whatever logic you wish to define.

The function is passed in information about the row and it is _strongly_ recommended that you use the row data to perform your logic check on only to keep the function operation as fast as possible. You could use the table node or even look the row up in the API, but these operations can slow the table down as this function, if defined, can be called frequently.

If this function is not defined (there is no default function), all rows are considered to be selectable.
</description>

<example title="Use a data point from the row's data as the selectable flag"><![CDATA[
new DataTable('#example', {
select: {
selectable: rowData => rowData.outdated
}
});
]]></example>

<example title="Check data in the row to decide it it should be selectable. Also add a class based on the same logic"><![CDATA[
new DataTable('#example', {
rowCallback: function (tr, rowData) {
if (rowData[2] === 'New York') {
tr.classList.add('unselectable');
}
},
select: {
selectable: function (rowData) {
return rowData[2] !== 'New York';
}
}
});
]]></example>

<related>-api select.selectable()</related>
<related>-event user-select()</related>
</dt-option>
8 changes: 1 addition & 7 deletions examples/initialisation/selectable.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,7 @@ new DataTable('#example', {
selectable: function (rowData) {
return rowData[2] !== 'New York';
}
},
columnDefs: [
{
target: 0,
render: DataTable.render.select()
}
]
}
});
]]>
Expand Down
66 changes: 56 additions & 10 deletions js/dataTables.select.js
Original file line number Diff line number Diff line change
Expand Up @@ -527,9 +527,13 @@ function info(api, node) {
return;
}

var rows = api.settings()[0]._select_set.length;
var columns = api.columns({ selected: true }).flatten().length;
var cells = api.cells({ selected: true }).flatten().length;
// If _select_set has any length, then ids are available and should be used
// as the counter. Otherwise use the API to workout how many rows are
// selected.
var rowSetLength = api.settings()[0]._select_set.length;
var rows = rowSetLength ? rowSetLength : api.rows({ selected: true }).count();
var columns = api.columns({ selected: true }).count();
var cells = api.cells({ selected: true }).count();

var add = function (el, name, num) {
el.append(
Expand Down Expand Up @@ -611,16 +615,14 @@ function initCheckboxHeader( dt, headerCheckbox ) {
// table changes
dt.on('draw select deselect', function (e, pass, type) {
if (type === 'row' || ! type) {
var count = dt.rows({selected: true}).count();
var search = dt.rows({search: 'applied', selected: true}).count();
var available = headerCheckbox == 'select-page' ? dt.rows({page: 'current'}).count() : dt.rows({search: 'applied'}).count();
var nums = headerCheckboxState(dt, headerCheckbox);

if (search && search <= count && search === available) {
if (nums.search && nums.search <= nums.count && nums.search === nums.available) {
input
.prop('checked', true)
.prop('indeterminate', false);
}
else if (search === 0 && count === 0) {
else if (nums.search === 0 && nums.count === 0) {
input
.prop('checked', false)
.prop('indeterminate', false);
Expand All @@ -636,6 +638,50 @@ function initCheckboxHeader( dt, headerCheckbox ) {
});
}

/**
* Determine the counts used to define the header checkbox's state
*
* @param {*} dt DT API
* @param {*} headerCheckbox Configuration for what the header checkbox does
* @returns Counts object
*/
function headerCheckboxState(dt, headerCheckbox) {
var ctx = dt.settings()[0];
var selectable = ctx._select.selectable;
var count = dt.rows({selected: true}).count()
var search = dt.rows({search: 'applied', selected: true}).count();
var available = 0;

if (! selectable) {
available = headerCheckbox == 'select-page'
? dt.rows({page: 'current'}).count()
: dt.rows({search: 'applied'}).count();
}
else {
// Need to count how many rows are actually selectable to know if all selectable
// rows are selected or not
var indexes = headerCheckbox == 'select-page'
? dt.rows({page: 'current'}).indexes()
: dt.rows({search: 'applied'}).indexes();

for (var i=0 ; i<indexes.length ; i++) {
// For speed I use the internal DataTables object.
var rowInternal = ctx.aoData[indexes[i]];
var result = selectable(rowInternal._aData, rowInternal.nTr, indexes[i]);

if (result) {
available++;
}
}
}

return {
available: available,
count: count,
search: search
}
}

/**
* Initialisation of a new table. Attach event handlers and callbacks to allow
* Select to operate correctly.
Expand Down Expand Up @@ -896,8 +942,8 @@ function _cumulativeEvents(api) {
function _add(api, arr, indexes) {
for (var i=0 ; i<indexes.length ; i++) {
var id = api.row(indexes[i]).id();
if (! arr.includes(id)) {

if (id && id !== 'undefined' && ! arr.includes(id)) {
arr.push(id);
}
}
Expand Down

0 comments on commit ab9d6ae

Please sign in to comment.