Skip to content

Commit

Permalink
Fixed #4881, shift key based selection for TurboTable
Browse files Browse the repository at this point in the history
  • Loading branch information
Çağatay Çivici authored and Çağatay Çivici committed Jan 19, 2018
1 parent 9da19e8 commit f13926c
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 43 deletions.
142 changes: 109 additions & 33 deletions src/app/components/table/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,10 @@ export class Table implements OnInit, AfterContentInit {

_selection: any;

anchorRowIndex: number;

rangeRowIndex: number;

constructor(public el: ElementRef, public domHandler: DomHandler, public objectUtils: ObjectUtils, public zone: NgZone, public tableService: TableService) {}

ngOnInit() {
Expand Down Expand Up @@ -593,44 +597,57 @@ export class Table implements OnInit, AfterContentInit {
this.onRowClick.emit({ originalEvent: event.originalEvent, data: event.rowData });

if(this.selectionMode) {
let rowData = event.rowData;
let dataKeyValue = this.dataKey ? String(this.objectUtils.resolveFieldData(rowData, this.dataKey)) : null;
let selected = this.isSelected(rowData);
this.preventSelectionSetterPropagation = true;

if (this.selectionMode === 'single') {
if (selected) {
this._selection = null;
this.selectionKeys = {};
this.selectionChange.emit(this.selection);
this.onRowUnselect.emit({ originalEvent: event.originalEvent, data: rowData, type: 'row' });
if(this.isMultipleSelectionMode() && event.originalEvent.shiftKey && this.anchorRowIndex != null) {
this.domHandler.clearSelection();
if(this.rangeRowIndex != null) {
this.clearSelectionRange(event.originalEvent);
}
else {
this._selection = rowData;
this.selectionChange.emit(this.selection);
this.onRowSelect.emit({ originalEvent: event.originalEvent, data: rowData, type: 'row' });
if (dataKeyValue) {

this.rangeRowIndex = event.rowIndex;
this.selectRange(event.originalEvent, event.rowIndex);
}
else {
let rowData = event.rowData;
let dataKeyValue = this.dataKey ? String(this.objectUtils.resolveFieldData(rowData, this.dataKey)) : null;
let selected = this.isSelected(rowData);
this.anchorRowIndex = event.rowIndex;
this.rangeRowIndex = event.rowIndex;

if (this.selectionMode === 'single') {
if (selected) {
this._selection = null;
this.selectionKeys = {};
this.selectionKeys[dataKeyValue] = 1;
this.selectionChange.emit(this.selection);
this.onRowUnselect.emit({ originalEvent: event.originalEvent, data: rowData, type: 'row' });
}
}
}
else if (this.selectionMode === 'multiple') {
if (selected) {
let selectionIndex = this.findIndexInSelection(rowData);
this._selection = this.selection.filter((val, i) => i != selectionIndex);
this.selectionChange.emit(this.selection);
this.onRowUnselect.emit({ originalEvent: event.originalEvent, data: rowData, type: 'row' });
if (dataKeyValue) {
delete this.selectionKeys[dataKeyValue];
else {
this._selection = rowData;
this.selectionChange.emit(this.selection);
this.onRowSelect.emit({ originalEvent: event.originalEvent, data: rowData, type: 'row' });
if (dataKeyValue) {
this.selectionKeys = {};
this.selectionKeys[dataKeyValue] = 1;
}
}
}
else {
this._selection = this.selection ? [...this.selection, rowData] : [rowData];
this.selectionChange.emit(this.selection);
this.onRowSelect.emit({ originalEvent: event.originalEvent, data: rowData, type: 'row' });
if (dataKeyValue) {
this.selectionKeys[dataKeyValue] = 1;
else if (this.selectionMode === 'multiple') {
if (selected) {
let selectionIndex = this.findIndexInSelection(rowData);
this._selection = this.selection.filter((val, i) => i != selectionIndex);
this.selectionChange.emit(this.selection);
this.onRowUnselect.emit({ originalEvent: event.originalEvent, data: rowData, type: 'row' });
if (dataKeyValue) {
delete this.selectionKeys[dataKeyValue];
}
}
else {
this._selection = this.selection ? [...this.selection, rowData] : [rowData];
this.selectionChange.emit(this.selection);
this.onRowSelect.emit({ originalEvent: event.originalEvent, data: rowData, type: 'row' });
if (dataKeyValue) {
this.selectionKeys[dataKeyValue] = 1;
}
}
}
}
Expand All @@ -649,6 +666,62 @@ export class Table implements OnInit, AfterContentInit {
}
}

selectRange(event: MouseEvent, rowIndex: number) {
let rangeStart, rangeEnd;

if(this.anchorRowIndex > rowIndex) {
rangeStart = rowIndex;
rangeEnd = this.anchorRowIndex;
}
else if(this.anchorRowIndex < rowIndex) {
rangeStart = this.anchorRowIndex;
rangeEnd = rowIndex;
}
else {
rangeStart = rowIndex;
rangeEnd = rowIndex;
}

for(let i = rangeStart; i <= rangeEnd; i++) {
let rangeRowData = this.value[i];
this._selection = [...this.selection, rangeRowData];
this.selectionChange.emit(this.selection);
let dataKeyValue: string = this.dataKey ? String(this.objectUtils.resolveFieldData(rangeRowData, this.dataKey)) : null;
if(dataKeyValue) {
this.selectionKeys[dataKeyValue] = 1;
}
this.onRowSelect.emit({originalEvent: event, data: rangeRowData, type: 'row'});
}
}

clearSelectionRange(event: MouseEvent) {
let rangeStart, rangeEnd;

if(this.rangeRowIndex > this.anchorRowIndex) {
rangeStart = this.anchorRowIndex;
rangeEnd = this.rangeRowIndex;
}
else if(this.rangeRowIndex < this.anchorRowIndex) {
rangeStart = this.rangeRowIndex;
rangeEnd = this.anchorRowIndex;
}
else {
rangeStart = this.rangeRowIndex;
rangeEnd = this.rangeRowIndex;
}

for(let i = rangeStart; i <= rangeEnd; i++) {
let rangeRowData = this.value[i];
let selectionIndex = this.findIndexInSelection(rangeRowData);
this._selection = this.selection.filter((val,i) => i!=selectionIndex);
let dataKeyValue: string = this.dataKey ? String(this.objectUtils.resolveFieldData(rangeRowData, this.dataKey)) : null;
if(dataKeyValue) {
delete this.selectionKeys[dataKeyValue];
}
this.onRowUnselect.emit({originalEvent: event, data: rangeRowData, type: 'row'});
}
}

isSelected(rowData) {
if (rowData && this.selection) {
if (this.dataKey) {
Expand Down Expand Up @@ -1621,6 +1694,8 @@ export class SelectableRow implements OnInit, OnDestroy {

@Input("pSelectableRow") data: any;

@Input("pSelectableRowIndex") index: number;

selected: boolean;

subscription: Subscription;
Expand All @@ -1639,7 +1714,8 @@ export class SelectableRow implements OnInit, OnDestroy {
onClick(event: Event) {
this.dt.handleRowClick({
originalEvent: event,
rowData: this.data
rowData: this.data,
rowIndex: this.index
});
this.domHandler.clearSelection();
}
Expand Down
12 changes: 6 additions & 6 deletions src/app/showcase/components/table/tabledemo.html
Original file line number Diff line number Diff line change
Expand Up @@ -854,9 +854,9 @@ <h3>Selection</h3>
whose value is the rowData to the rows that can be selected. Alternatively instead of row click, radiobutton or checkbox elements can be used to implement row selection.</p>

<p>When resolving if a row is selected, by default Table compares selection array with the datasource which may cause a performance issue with huge datasets that do not use pagination.
If available the fastest way is to use dataKey property that identifies a unique row so that Table can avoid comparing arrays as internally a map instance is used instead of looping arrays, on the other hand
if dataKey cannot be provided consider using compareSelectionBy property as "equals" which uses reference comparison instead of the default "deepEquals" comparison. Latter is slower since it checks all properties.
</p>
If available the fastest way is to use dataKey property that identifies a unique row so that Table can avoid comparing arrays as internally a map instance is used instead of looping arrays, on the other hand
if dataKey cannot be provided consider using compareSelectionBy property as "equals" which uses reference comparison instead of the default "deepEquals" comparison. Latter is slower since it checks all properties.
</p>

<p>In single mode, selection binding is an object reference.</p>
<pre>
Expand Down Expand Up @@ -897,7 +897,7 @@ <h3>Selection</h3>
</code>
</pre>

<p>In multiple mode, selection binding should be an array.</p>
<p>In multiple mode, selection binding should be an array. Note that if you require shiftKey based range selection, pass the rowIndex to the SelectableRow directive.</p>
<pre>
<code class="language-typescript" pCode ngNonBindable>
export class DataTableDemo implements OnInit &#123;
Expand Down Expand Up @@ -925,8 +925,8 @@ <h3>Selection</h3>
&lt;/th&gt;
&lt;/tr&gt;
&lt;/ng-template&gt;
&lt;ng-template pTemplate="body" let-rowData let-columns="columns"&gt;
&lt;tr [pSelectableRow]="rowData"&gt;
&lt;ng-template pTemplate="body" let-rowData let-columns="columns" let-rowIndex="rowIndex"&gt;
&lt;tr [pSelectableRow]="rowData" [pSelectableRowIndex]="rowIndex"&gt;
&lt;td *ngFor="let col of columns"&gt;
&#123;&#123;rowData[col.field]&#125;&#125;
&lt;/td&gt;
Expand Down
8 changes: 4 additions & 4 deletions src/app/showcase/components/table/tableselectiondemo.html
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ <h3>Multiple Row Selection</h3>
</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-rowData let-columns="columns">
<tr [pSelectableRow]="rowData">
<ng-template pTemplate="body" let-rowData let-columns="columns" let-rowIndex="rowIndex">
<tr [pSelectableRow]="rowData" [pSelectableRowIndex]="rowIndex">
<td *ngFor="let col of columns">
{{rowData[col.field]}}
</td>
Expand Down Expand Up @@ -258,8 +258,8 @@ <h3>Checkbox Selection</h3>
&lt;/th&gt;
&lt;/tr&gt;
&lt;/ng-template&gt;
&lt;ng-template pTemplate="body" let-rowData let-columns="columns"&gt;
&lt;tr [pSelectableRow]="rowData"&gt;
&lt;ng-template pTemplate="body" let-rowData let-columns="columns" let-rowIndex="rowIndex"&gt;
&lt;tr [pSelectableRow]="rowData" [pSelectableRowIndex]="rowIndex"&gt;
&lt;td *ngFor="let col of columns"&gt;
&#123;&#123;rowData[col.field]&#125;&#125;
&lt;/td&gt;
Expand Down

0 comments on commit f13926c

Please sign in to comment.