adore-datatable 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +75 -0
- package/package.json +78 -0
- package/src/body-renderer.js +163 -0
- package/src/cellmanager.js +934 -0
- package/src/columnmanager.js +496 -0
- package/src/dark.css +11 -0
- package/src/datamanager.js +630 -0
- package/src/datatable.js +296 -0
- package/src/defaults.js +73 -0
- package/src/dom.js +235 -0
- package/src/filterRows.js +209 -0
- package/src/icons.js +10 -0
- package/src/index.js +5 -0
- package/src/keyboard.js +59 -0
- package/src/rowmanager.js +369 -0
- package/src/style.css +304 -0
- package/src/style.js +379 -0
- package/src/translationmanager.js +30 -0
- package/src/translations/de.json +15 -0
- package/src/translations/en.json +15 -0
- package/src/translations/fr.json +15 -0
- package/src/translations/index.js +13 -0
- package/src/translations/it.json +15 -0
- package/src/utils.js +167 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { isNumber, stripHTML } from './utils';
|
|
2
|
+
import CellManager from './cellmanager';
|
|
3
|
+
|
|
4
|
+
export default function filterRows(rows, filters, data) {
|
|
5
|
+
let filteredRowIndices = [];
|
|
6
|
+
|
|
7
|
+
if (Object.keys(filters).length === 0) {
|
|
8
|
+
return rows.map(row => row.meta.rowIndex);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
for (let colIndex in filters) {
|
|
12
|
+
const keyword = filters[colIndex];
|
|
13
|
+
|
|
14
|
+
const filteredRows = filteredRowIndices.length ?
|
|
15
|
+
filteredRowIndices.map(i => rows[i]) :
|
|
16
|
+
rows;
|
|
17
|
+
|
|
18
|
+
const cells = filteredRows.map(row => row[colIndex]);
|
|
19
|
+
|
|
20
|
+
let filter = guessFilter(keyword);
|
|
21
|
+
let filterMethod = getFilterMethod(rows, data, filter);
|
|
22
|
+
|
|
23
|
+
if (filterMethod) {
|
|
24
|
+
filteredRowIndices = filterMethod(filter.text, cells);
|
|
25
|
+
} else {
|
|
26
|
+
filteredRowIndices = cells.map(cell => cell.rowIndex);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return filteredRowIndices;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
function getFilterMethod(rows, allData, filter) {
|
|
34
|
+
const getFormattedValue = cell => {
|
|
35
|
+
let formatter = CellManager.getCustomCellFormatter(cell);
|
|
36
|
+
let rowData = rows[cell.rowIndex];
|
|
37
|
+
if (allData && allData.data && allData.data.length) {
|
|
38
|
+
rowData = allData.data[cell.rowIndex];
|
|
39
|
+
}
|
|
40
|
+
if (formatter && cell.content) {
|
|
41
|
+
cell.html = formatter(cell.content, rows[cell.rowIndex], cell.column, rowData, filter);
|
|
42
|
+
return stripHTML(cell.html);
|
|
43
|
+
}
|
|
44
|
+
return cell.content || '';
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const stringCompareValue = cell =>
|
|
48
|
+
String(stripHTML(cell.html || '') || getFormattedValue(cell)).toLowerCase();
|
|
49
|
+
|
|
50
|
+
const numberCompareValue = cell => parseFloat(cell.content);
|
|
51
|
+
|
|
52
|
+
const getCompareValues = (cell, keyword) => {
|
|
53
|
+
if (cell.column.compareValue) {
|
|
54
|
+
const compareValues = cell.column.compareValue(cell, keyword);
|
|
55
|
+
if (compareValues && Array.isArray(compareValues)) return compareValues;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// check if it can be converted to number
|
|
59
|
+
const float = numberCompareValue(cell);
|
|
60
|
+
if (!isNaN(float)) {
|
|
61
|
+
return [float, keyword];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return [stringCompareValue(cell), keyword];
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
let filterMethodMap = {
|
|
68
|
+
contains(keyword, cells) {
|
|
69
|
+
return cells
|
|
70
|
+
.filter(cell => {
|
|
71
|
+
const needle = (keyword || '').toLowerCase();
|
|
72
|
+
return !needle ||
|
|
73
|
+
(cell.content || '').toLowerCase().includes(needle) ||
|
|
74
|
+
stringCompareValue(cell).includes(needle);
|
|
75
|
+
})
|
|
76
|
+
.map(cell => cell.rowIndex);
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
greaterThan(keyword, cells) {
|
|
80
|
+
return cells
|
|
81
|
+
.filter(cell => {
|
|
82
|
+
const [compareValue, keywordValue] = getCompareValues(cell, keyword);
|
|
83
|
+
return compareValue > keywordValue;
|
|
84
|
+
})
|
|
85
|
+
.map(cell => cell.rowIndex);
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
lessThan(keyword, cells) {
|
|
89
|
+
return cells
|
|
90
|
+
.filter(cell => {
|
|
91
|
+
const [compareValue, keywordValue] = getCompareValues(cell, keyword);
|
|
92
|
+
return compareValue < keywordValue;
|
|
93
|
+
})
|
|
94
|
+
.map(cell => cell.rowIndex);
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
equals(keyword, cells) {
|
|
98
|
+
return cells
|
|
99
|
+
.filter(cell => {
|
|
100
|
+
const value = parseFloat(cell.content);
|
|
101
|
+
return value === keyword;
|
|
102
|
+
})
|
|
103
|
+
.map(cell => cell.rowIndex);
|
|
104
|
+
},
|
|
105
|
+
|
|
106
|
+
notEquals(keyword, cells) {
|
|
107
|
+
return cells
|
|
108
|
+
.filter(cell => {
|
|
109
|
+
const value = parseFloat(cell.content);
|
|
110
|
+
return value !== keyword;
|
|
111
|
+
})
|
|
112
|
+
.map(cell => cell.rowIndex);
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
range(rangeValues, cells) {
|
|
116
|
+
return cells
|
|
117
|
+
.filter(cell => {
|
|
118
|
+
const values1 = getCompareValues(cell, rangeValues[0]);
|
|
119
|
+
const values2 = getCompareValues(cell, rangeValues[1]);
|
|
120
|
+
const value = values1[0];
|
|
121
|
+
return value >= values1[1] && value <= values2[1];
|
|
122
|
+
})
|
|
123
|
+
.map(cell => cell.rowIndex);
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
containsNumber(keyword, cells) {
|
|
127
|
+
return cells
|
|
128
|
+
.filter(cell => {
|
|
129
|
+
let number = parseFloat(keyword, 10);
|
|
130
|
+
let string = keyword;
|
|
131
|
+
let hayNumber = numberCompareValue(cell);
|
|
132
|
+
let hayString = stringCompareValue(cell);
|
|
133
|
+
|
|
134
|
+
return number === hayNumber || hayString.includes(string);
|
|
135
|
+
})
|
|
136
|
+
.map(cell => cell.rowIndex);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
return filterMethodMap[filter.type];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function guessFilter(keyword = '') {
|
|
144
|
+
if (keyword.length === 0) return {};
|
|
145
|
+
|
|
146
|
+
let compareString = keyword;
|
|
147
|
+
|
|
148
|
+
if (['>', '<', '='].includes(compareString[0])) {
|
|
149
|
+
compareString = keyword.slice(1);
|
|
150
|
+
} else if (compareString.startsWith('!=')) {
|
|
151
|
+
compareString = keyword.slice(2);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (keyword.startsWith('>')) {
|
|
155
|
+
if (compareString) {
|
|
156
|
+
return {
|
|
157
|
+
type: 'greaterThan',
|
|
158
|
+
text: compareString.trim()
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (keyword.startsWith('<')) {
|
|
164
|
+
if (compareString) {
|
|
165
|
+
return {
|
|
166
|
+
type: 'lessThan',
|
|
167
|
+
text: compareString.trim()
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (keyword.startsWith('=')) {
|
|
173
|
+
if (isNumber(compareString)) {
|
|
174
|
+
return {
|
|
175
|
+
type: 'equals',
|
|
176
|
+
text: Number(keyword.slice(1).trim())
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (isNumber(compareString)) {
|
|
182
|
+
return {
|
|
183
|
+
type: 'containsNumber',
|
|
184
|
+
text: compareString
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (keyword.startsWith('!=')) {
|
|
189
|
+
if (isNumber(compareString)) {
|
|
190
|
+
return {
|
|
191
|
+
type: 'notEquals',
|
|
192
|
+
text: Number(keyword.slice(2).trim())
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (keyword.split(':').length === 2 && keyword.split(':').every(v => isNumber(v.trim()))) {
|
|
198
|
+
compareString = keyword.split(':');
|
|
199
|
+
return {
|
|
200
|
+
type: 'range',
|
|
201
|
+
text: compareString.map(v => v.trim())
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
type: 'contains',
|
|
207
|
+
text: compareString.toLowerCase()
|
|
208
|
+
};
|
|
209
|
+
}
|
package/src/icons.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/* eslint-disable max-len */
|
|
2
|
+
|
|
3
|
+
// Icons from https://feathericons.com/
|
|
4
|
+
|
|
5
|
+
let icons = {
|
|
6
|
+
chevronDown: '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-down"><polyline points="6 9 12 15 18 9"></polyline></svg>',
|
|
7
|
+
chevronRight: '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-right"><polyline points="9 18 15 12 9 6"></polyline></svg>'
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default icons;
|
package/src/index.js
ADDED
package/src/keyboard.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import $ from './dom';
|
|
2
|
+
|
|
3
|
+
const KEYCODES = {
|
|
4
|
+
13: 'enter',
|
|
5
|
+
91: 'meta',
|
|
6
|
+
16: 'shift',
|
|
7
|
+
17: 'ctrl',
|
|
8
|
+
18: 'alt',
|
|
9
|
+
37: 'left',
|
|
10
|
+
38: 'up',
|
|
11
|
+
39: 'right',
|
|
12
|
+
40: 'down',
|
|
13
|
+
9: 'tab',
|
|
14
|
+
27: 'esc',
|
|
15
|
+
67: 'c',
|
|
16
|
+
70: 'f',
|
|
17
|
+
86: 'v'
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export default class Keyboard {
|
|
21
|
+
constructor(element) {
|
|
22
|
+
this.listeners = {};
|
|
23
|
+
$.on(element, 'keydown', this.handler.bind(this));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
handler(e) {
|
|
27
|
+
let key = KEYCODES[e.keyCode];
|
|
28
|
+
|
|
29
|
+
if (e.shiftKey && key !== 'shift') {
|
|
30
|
+
key = 'shift+' + key;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if ((e.ctrlKey && key !== 'ctrl') || (e.metaKey && key !== 'meta')) {
|
|
34
|
+
key = 'ctrl+' + key;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const listeners = this.listeners[key];
|
|
38
|
+
|
|
39
|
+
if (listeners && listeners.length > 0) {
|
|
40
|
+
for (let listener of listeners) {
|
|
41
|
+
const preventBubbling = listener(e);
|
|
42
|
+
if (preventBubbling === undefined || preventBubbling === true) {
|
|
43
|
+
e.preventDefault();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
on(key, listener) {
|
|
50
|
+
const keys = key.split(',').map(k => k.trim());
|
|
51
|
+
|
|
52
|
+
keys.map(key => {
|
|
53
|
+
this.listeners[key] = this.listeners[key] || [];
|
|
54
|
+
this.listeners[key].push(listener);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export let keyCode = KEYCODES;
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
import $ from './dom';
|
|
2
|
+
import {
|
|
3
|
+
makeDataAttributeString,
|
|
4
|
+
nextTick,
|
|
5
|
+
ensureArray,
|
|
6
|
+
linkProperties,
|
|
7
|
+
uniq,
|
|
8
|
+
numberSortAsc
|
|
9
|
+
} from './utils';
|
|
10
|
+
|
|
11
|
+
export default class RowManager {
|
|
12
|
+
constructor(instance) {
|
|
13
|
+
this.instance = instance;
|
|
14
|
+
linkProperties(this, this.instance, [
|
|
15
|
+
'options',
|
|
16
|
+
'fireEvent',
|
|
17
|
+
'wrapper',
|
|
18
|
+
'bodyScrollable',
|
|
19
|
+
'bodyRenderer',
|
|
20
|
+
'style'
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
this.bindEvents();
|
|
24
|
+
this.refreshRows = nextTick(this.refreshRows, this);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get datamanager() {
|
|
28
|
+
return this.instance.datamanager;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
get cellmanager() {
|
|
32
|
+
return this.instance.cellmanager;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
bindEvents() {
|
|
36
|
+
this.bindCheckbox();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
bindCheckbox() {
|
|
40
|
+
if (!this.options.checkboxColumn) return;
|
|
41
|
+
|
|
42
|
+
// map of checked rows
|
|
43
|
+
this.checkMap = [];
|
|
44
|
+
|
|
45
|
+
$.on(this.wrapper, 'click', '.dt-cell--col-0 [type="checkbox"]', (e, $checkbox) => {
|
|
46
|
+
const $cell = $checkbox.closest('.dt-cell');
|
|
47
|
+
const {
|
|
48
|
+
rowIndex,
|
|
49
|
+
isHeader
|
|
50
|
+
} = $.data($cell);
|
|
51
|
+
const checked = $checkbox.checked;
|
|
52
|
+
|
|
53
|
+
if (isHeader) {
|
|
54
|
+
this.checkAll(checked);
|
|
55
|
+
} else {
|
|
56
|
+
this.checkRow(rowIndex, checked);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
refreshRows() {
|
|
62
|
+
this.instance.renderBody();
|
|
63
|
+
this.instance.setDimensions();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
refreshRow(row, rowIndex) {
|
|
67
|
+
const _row = this.datamanager.updateRow(row, rowIndex);
|
|
68
|
+
|
|
69
|
+
_row.forEach(cell => {
|
|
70
|
+
this.cellmanager.refreshCell(cell, true);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
getCheckedRows() {
|
|
75
|
+
if (!this.checkMap) {
|
|
76
|
+
return [];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let out = [];
|
|
80
|
+
for (let rowIndex in this.checkMap) {
|
|
81
|
+
const checked = this.checkMap[rowIndex];
|
|
82
|
+
if (checked === 1) {
|
|
83
|
+
out.push(rowIndex);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return out;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
highlightCheckedRows() {
|
|
91
|
+
this.getCheckedRows()
|
|
92
|
+
.map(rowIndex => this.checkRow(rowIndex, true));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
checkRow(rowIndex, toggle) {
|
|
96
|
+
const value = toggle ? 1 : 0;
|
|
97
|
+
const selector = rowIndex => `.dt-cell--0-${rowIndex} [type="checkbox"]`;
|
|
98
|
+
// update internal map
|
|
99
|
+
this.checkMap[rowIndex] = value;
|
|
100
|
+
// set checkbox value explicitly
|
|
101
|
+
$.each(selector(rowIndex), this.bodyScrollable)
|
|
102
|
+
.map(input => {
|
|
103
|
+
input.checked = toggle;
|
|
104
|
+
});
|
|
105
|
+
// highlight row
|
|
106
|
+
this.highlightRow(rowIndex, toggle);
|
|
107
|
+
this.showCheckStatus();
|
|
108
|
+
this.fireEvent('onCheckRow', this.datamanager.getRow(rowIndex));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
checkAll(toggle) {
|
|
112
|
+
const value = toggle ? 1 : 0;
|
|
113
|
+
|
|
114
|
+
// update internal map
|
|
115
|
+
if (toggle) {
|
|
116
|
+
if (this.datamanager._filteredRows) {
|
|
117
|
+
this.datamanager._filteredRows.forEach(f => {
|
|
118
|
+
this.checkRow(f, toggle);
|
|
119
|
+
});
|
|
120
|
+
} else {
|
|
121
|
+
this.checkMap = Array.from(Array(this.getTotalRows())).map(c => value);
|
|
122
|
+
}
|
|
123
|
+
} else {
|
|
124
|
+
this.checkMap = [];
|
|
125
|
+
}
|
|
126
|
+
// set checkbox value
|
|
127
|
+
$.each('.dt-cell--col-0 [type="checkbox"]', this.bodyScrollable)
|
|
128
|
+
.map(input => {
|
|
129
|
+
input.checked = toggle;
|
|
130
|
+
});
|
|
131
|
+
// highlight all
|
|
132
|
+
this.highlightAll(toggle);
|
|
133
|
+
this.showCheckStatus();
|
|
134
|
+
this.fireEvent('onCheckRow');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
showCheckStatus() {
|
|
138
|
+
if (!this.options.checkedRowStatus) return;
|
|
139
|
+
const checkedRows = this.getCheckedRows();
|
|
140
|
+
const count = checkedRows.length;
|
|
141
|
+
if (count > 0) {
|
|
142
|
+
let message = this.instance.translate('{count} rows selected', {
|
|
143
|
+
count: count
|
|
144
|
+
});
|
|
145
|
+
this.bodyRenderer.showToastMessage(message);
|
|
146
|
+
} else {
|
|
147
|
+
this.bodyRenderer.clearToastMessage();
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
highlightRow(rowIndex, toggle = true) {
|
|
152
|
+
const $row = this.getRow$(rowIndex);
|
|
153
|
+
if (!$row) return;
|
|
154
|
+
|
|
155
|
+
if (!toggle && this.bodyScrollable.classList.contains('dt-scrollable--highlight-all')) {
|
|
156
|
+
$row.classList.add('dt-row--unhighlight');
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (toggle && $row.classList.contains('dt-row--unhighlight')) {
|
|
161
|
+
$row.classList.remove('dt-row--unhighlight');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
this._highlightedRows = this._highlightedRows || {};
|
|
165
|
+
|
|
166
|
+
if (toggle) {
|
|
167
|
+
$row.classList.add('dt-row--highlight');
|
|
168
|
+
this._highlightedRows[rowIndex] = $row;
|
|
169
|
+
} else {
|
|
170
|
+
$row.classList.remove('dt-row--highlight');
|
|
171
|
+
delete this._highlightedRows[rowIndex];
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
highlightAll(toggle = true) {
|
|
176
|
+
if (toggle) {
|
|
177
|
+
this.bodyScrollable.classList.add('dt-scrollable--highlight-all');
|
|
178
|
+
} else {
|
|
179
|
+
this.bodyScrollable.classList.remove('dt-scrollable--highlight-all');
|
|
180
|
+
for (const rowIndex in this._highlightedRows) {
|
|
181
|
+
const $row = this._highlightedRows[rowIndex];
|
|
182
|
+
$row.classList.remove('dt-row--highlight');
|
|
183
|
+
}
|
|
184
|
+
this._highlightedRows = {};
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
showRows(rowIndices) {
|
|
189
|
+
rowIndices = ensureArray(rowIndices);
|
|
190
|
+
const rows = rowIndices.map(rowIndex => this.datamanager.getRow(rowIndex));
|
|
191
|
+
this.bodyRenderer.renderRows(rows);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
showAllRows() {
|
|
195
|
+
const rowIndices = this.datamanager.getAllRowIndices();
|
|
196
|
+
this.showRows(rowIndices);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
getChildrenToShowForNode(rowIndex) {
|
|
200
|
+
const row = this.datamanager.getRow(rowIndex);
|
|
201
|
+
row.meta.isTreeNodeClose = false;
|
|
202
|
+
|
|
203
|
+
return this.datamanager.getImmediateChildren(rowIndex);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
openSingleNode(rowIndex) {
|
|
207
|
+
const childrenToShow = this.getChildrenToShowForNode(rowIndex);
|
|
208
|
+
const visibleRowIndices = this.bodyRenderer.visibleRowIndices;
|
|
209
|
+
const rowsToShow = uniq([...childrenToShow, ...visibleRowIndices]).sort(numberSortAsc);
|
|
210
|
+
|
|
211
|
+
this.showRows(rowsToShow);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
getChildrenToHideForNode(rowIndex) {
|
|
215
|
+
const row = this.datamanager.getRow(rowIndex);
|
|
216
|
+
row.meta.isTreeNodeClose = true;
|
|
217
|
+
|
|
218
|
+
const rowsToHide = this.datamanager.getChildren(rowIndex);
|
|
219
|
+
rowsToHide.forEach(rowIndex => {
|
|
220
|
+
const row = this.datamanager.getRow(rowIndex);
|
|
221
|
+
if (!row.meta.isLeaf) {
|
|
222
|
+
row.meta.isTreeNodeClose = true;
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
return rowsToHide;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
closeSingleNode(rowIndex) {
|
|
230
|
+
const rowsToHide = this.getChildrenToHideForNode(rowIndex);
|
|
231
|
+
const visibleRows = this.bodyRenderer.visibleRowIndices;
|
|
232
|
+
const rowsToShow = visibleRows
|
|
233
|
+
.filter(rowIndex => !rowsToHide.includes(rowIndex))
|
|
234
|
+
.sort(numberSortAsc);
|
|
235
|
+
|
|
236
|
+
this.showRows(rowsToShow);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
expandAllNodes() {
|
|
240
|
+
let rows = this.datamanager.getRows();
|
|
241
|
+
let rootNodes = rows.filter(row => !row.meta.isLeaf);
|
|
242
|
+
|
|
243
|
+
const childrenToShow = rootNodes.map(row => this.getChildrenToShowForNode(row.meta.rowIndex)).flat();
|
|
244
|
+
const visibleRowIndices = this.bodyRenderer.visibleRowIndices;
|
|
245
|
+
const rowsToShow = uniq([...childrenToShow, ...visibleRowIndices]).sort(numberSortAsc);
|
|
246
|
+
|
|
247
|
+
this.showRows(rowsToShow);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
collapseAllNodes() {
|
|
251
|
+
let rows = this.datamanager.getRows();
|
|
252
|
+
let rootNodes = rows.filter(row => row.meta.indent === 0);
|
|
253
|
+
|
|
254
|
+
const rowsToHide = rootNodes.map(row => this.getChildrenToHideForNode(row.meta.rowIndex)).flat();
|
|
255
|
+
const visibleRows = this.bodyRenderer.visibleRowIndices;
|
|
256
|
+
const rowsToShow = visibleRows
|
|
257
|
+
.filter(rowIndex => !rowsToHide.includes(rowIndex))
|
|
258
|
+
.sort(numberSortAsc);
|
|
259
|
+
|
|
260
|
+
this.showRows(rowsToShow);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
setTreeDepth(depth) {
|
|
264
|
+
let rows = this.datamanager.getRows();
|
|
265
|
+
|
|
266
|
+
const rowsToOpen = rows.filter(row => row.meta.indent < depth);
|
|
267
|
+
const rowsToClose = rows.filter(row => row.meta.indent >= depth);
|
|
268
|
+
const rowsToHide = rowsToClose.filter(row => row.meta.indent > depth);
|
|
269
|
+
|
|
270
|
+
rowsToClose.forEach(row => {
|
|
271
|
+
if (!row.meta.isLeaf) {
|
|
272
|
+
row.meta.isTreeNodeClose = true;
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
rowsToOpen.forEach(row => {
|
|
276
|
+
if (!row.meta.isLeaf) {
|
|
277
|
+
row.meta.isTreeNodeClose = false;
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
const rowsToShow = rows
|
|
282
|
+
.filter(row => !rowsToHide.includes(row))
|
|
283
|
+
.map(row => row.meta.rowIndex)
|
|
284
|
+
.sort(numberSortAsc);
|
|
285
|
+
this.showRows(rowsToShow);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
getRow$(rowIndex) {
|
|
289
|
+
return $(this.selector(rowIndex), this.bodyScrollable);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
getTotalRows() {
|
|
293
|
+
return this.datamanager.getRowCount();
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
getFirstRowIndex() {
|
|
297
|
+
return 0;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
getLastRowIndex() {
|
|
301
|
+
return this.datamanager.getRowCount() - 1;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
scrollToRow(rowIndex) {
|
|
305
|
+
rowIndex = +rowIndex;
|
|
306
|
+
this._lastScrollTo = this._lastScrollTo || 0;
|
|
307
|
+
const $row = this.getRow$(rowIndex);
|
|
308
|
+
if ($.inViewport($row, this.bodyScrollable)) return;
|
|
309
|
+
|
|
310
|
+
const {
|
|
311
|
+
height
|
|
312
|
+
} = $row.getBoundingClientRect();
|
|
313
|
+
const {
|
|
314
|
+
top,
|
|
315
|
+
bottom
|
|
316
|
+
} = this.bodyScrollable.getBoundingClientRect();
|
|
317
|
+
const rowsInView = Math.floor((bottom - top) / height);
|
|
318
|
+
|
|
319
|
+
let offset = 0;
|
|
320
|
+
if (rowIndex > this._lastScrollTo) {
|
|
321
|
+
offset = height * ((rowIndex + 1) - rowsInView);
|
|
322
|
+
} else {
|
|
323
|
+
offset = height * ((rowIndex + 1) - 1);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
this._lastScrollTo = rowIndex;
|
|
327
|
+
$.scrollTop(this.bodyScrollable, offset);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
getRowHTML(row, props) {
|
|
331
|
+
const dataAttr = makeDataAttributeString(props);
|
|
332
|
+
let rowIdentifier = props.rowIndex;
|
|
333
|
+
|
|
334
|
+
if (props.isFilter) {
|
|
335
|
+
row = row.map(cell => (Object.assign({}, cell, {
|
|
336
|
+
content: this.getFilterInput({
|
|
337
|
+
colIndex: cell.colIndex,
|
|
338
|
+
name: cell.name
|
|
339
|
+
}),
|
|
340
|
+
isFilter: 1,
|
|
341
|
+
isHeader: undefined,
|
|
342
|
+
editable: false
|
|
343
|
+
})));
|
|
344
|
+
|
|
345
|
+
rowIdentifier = 'filter';
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (props.isHeader) {
|
|
349
|
+
rowIdentifier = 'header';
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return `
|
|
353
|
+
<div class="dt-row dt-row-${rowIdentifier}" ${dataAttr}>
|
|
354
|
+
${row.map(cell => this.cellmanager.getCellHTML(cell)).join('')}
|
|
355
|
+
</div>
|
|
356
|
+
`;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
getFilterInput(props) {
|
|
360
|
+
let title = `title="Filter based on ${props.name || 'Index'}"`;
|
|
361
|
+
const dataAttr = makeDataAttributeString(props);
|
|
362
|
+
return `<input class="dt-filter dt-input" type="text" ${dataAttr} tabindex="1"
|
|
363
|
+
${props.colIndex === 0 ? 'disabled' : title} />`;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
selector(rowIndex) {
|
|
367
|
+
return `.dt-row-${rowIndex}`;
|
|
368
|
+
}
|
|
369
|
+
}
|