UNPKG

19.6 kBJavaScriptView Raw
1import {Feature} from '../../feature';
2import {tag} from '../../dom';
3import {INPUT} from '../../const';
4import {defaultsStr} from '../../settings';
5import {root} from '../../root';
6
7const INSTANTIATION_ERROR = `Failed to instantiate EditTable object.
8 \n"ezEditTable" dependency not found.`;
9
10/**
11 * Adapter module for ezEditTable, an external library providing advanced
12 * grid features (selection and edition):
13 * http://codecanyon.net/item/ezedittable-enhance-html-tables/2425123?ref=koalyptus
14 */
15export default class AdapterEzEditTable extends Feature {
16
17 /**
18 * Creates an instance of AdapterEzEditTable
19 *
20 * @param {TableFilter} tf TableFilter instance
21 * @param {Object} cfg Configuration options for ezEditTable library
22 */
23 constructor(tf, cfg) {
24 super(tf, AdapterEzEditTable);
25
26 /**
27 * Module description
28 * @type {String}
29 */
30 this.desc = defaultsStr(cfg.description, 'ezEditTable adapter');
31
32 /**
33 * Filename of ezEditTable library
34 * @type {String}
35 */
36 this.filename = defaultsStr(cfg.filename, 'ezEditTable.js');
37
38 /**
39 * Path to ezEditTable library
40 * @type {String}
41 */
42 this.vendorPath = cfg.vendor_path;
43
44 /**
45 * Load ezEditTable stylesheet
46 * @type {Boolean}
47 */
48 this.loadStylesheet = Boolean(cfg.load_stylesheet);
49
50 /**
51 * Path to ezEditTable stylesheet
52 * @type {String}
53 */
54 this.stylesheet = defaultsStr(cfg.stylesheet,
55 this.vendorPath + 'ezEditTable.css');
56
57 /**
58 * Name of ezEditTable stylesheet
59 * @type {String}
60 */
61 this.stylesheetName = defaultsStr(cfg.stylesheet_name,
62 'ezEditTableCss');
63
64 // Enable the ezEditTable's scroll into view behaviour if grid layout on
65 cfg.scroll_into_view = cfg.scroll_into_view === false ?
66 false : tf.gridLayout;
67
68 /**
69 * ezEditTable instance
70 * @type {EditTable}
71 * @private
72 */
73 this._ezEditTable = null;
74
75 /**
76 * ezEditTable configuration
77 * @private
78 */
79 this.cfg = cfg;
80
81 this.enable();
82 }
83
84 /**
85 * Conditionally load ezEditTable library and set advanced grid
86 */
87 init() {
88 if (this.initialized) {
89 return;
90 }
91 let tf = this.tf;
92 if (root.EditTable) {
93 this._setAdvancedGrid();
94 } else {
95 let path = this.vendorPath + this.filename;
96 tf.import(this.filename, path, () => this._setAdvancedGrid());
97 }
98 if (this.loadStylesheet && !tf.isImported(this.stylesheet, 'link')) {
99 tf.import(this.stylesheetName, this.stylesheet, null, 'link');
100 }
101
102 // TODO: hack to prevent ezEditTable enter key event hijaking.
103 // Needs to be fixed in the vendor's library
104 this.emitter.on(['filter-focus', 'filter-blur'],
105 () => this._toggleForInputFilter());
106
107 /**
108 * @inherited
109 */
110 this.initialized = true;
111 }
112
113 /**
114 * Instantiate ezEditTable component for advanced grid features
115 * @private
116 */
117 _setAdvancedGrid() {
118 let tf = this.tf;
119
120 //start row for EditTable constructor needs to be calculated
121 let startRow,
122 cfg = this.cfg,
123 thead = tag(tf.dom(), 'thead');
124
125 //if thead exists and startRow not specified, startRow is calculated
126 //automatically by EditTable
127 if (thead.length > 0 && !cfg.startRow) {
128 startRow = undefined;
129 }
130 //otherwise startRow config property if any or TableFilter refRow
131 else {
132 startRow = cfg.startRow || tf.refRow;
133 }
134
135 cfg.base_path = cfg.base_path || tf.basePath + 'ezEditTable/';
136 let editable = cfg.editable;
137 let selectable = cfg.selection;
138
139 if (selectable) {
140 cfg.default_selection = cfg.default_selection || 'row';
141 }
142 //CSS Styles
143 cfg.active_cell_css = cfg.active_cell_css || 'ezETSelectedCell';
144
145 let _lastValidRowIndex = 0;
146 let _lastRowIndex = 0;
147
148 if (selectable) {
149 //Row navigation needs to be calculated according to TableFilter's
150 //validRowsIndex array
151 let onAfterSelection = function (et, selectedElm, e) {
152 let slc = et.Selection;
153 //Next valid filtered row needs to be selected
154 let doSelect = function (nextRowIndex) {
155 if (et.defaultSelection === 'row') {
156 /* eslint-disable */
157 slc.SelectRowByIndex(nextRowIndex);
158 /* eslint-enable */
159 } else {
160 /* eslint-disable */
161 et.ClearSelections();
162 /* eslint-enable */
163 let cellIndex = selectedElm.cellIndex,
164 row = tf.dom().rows[nextRowIndex];
165 if (et.defaultSelection === 'both') {
166 /* eslint-disable */
167 slc.SelectRowByIndex(nextRowIndex);
168 /* eslint-enable */
169 }
170 if (row) {
171 /* eslint-disable */
172 slc.SelectCell(row.cells[cellIndex]);
173 /* eslint-enable */
174 }
175 }
176 //Table is filtered
177 if (tf.validRowsIndex.length !== tf.getRowsNb()) {
178 let r = tf.dom().rows[nextRowIndex];
179 if (r) {
180 r.scrollIntoView(false);
181 }
182 if (cell) {
183 if (cell.cellIndex === (tf.getCellsNb() - 1) &&
184 tf.gridLayout) {
185 tf.tblCont.scrollLeft = 100000000;
186 }
187 else if (cell.cellIndex === 0 && tf.gridLayout) {
188 tf.tblCont.scrollLeft = 0;
189 } else {
190 cell.scrollIntoView(false);
191 }
192 }
193 }
194 };
195
196 //table is not filtered
197 if (!tf.validRowsIndex) {
198 return;
199 }
200 let validIndexes = tf.validRowsIndex,
201 validIdxLen = validIndexes.length,
202 row = et.defaultSelection !== 'row' ?
203 selectedElm.parentNode : selectedElm,
204 //cell for default_selection = 'both' or 'cell'
205 cell = selectedElm.nodeName === 'TD' ? selectedElm : null,
206 /* eslint-disable */
207 keyCode = e !== undefined ? et.Event.GetKey(e) : 0,
208 /* eslint-enable */
209 isRowValid = validIndexes.indexOf(row.rowIndex) !== -1,
210 nextRowIndex,
211 paging = tf.feature('paging'),
212 //pgup/pgdown keys
213 d = keyCode === 34 || keyCode === 33 ?
214 (paging && paging.pageLength || et.nbRowsPerPage) :
215 1;
216
217 //If next row is not valid, next valid filtered row needs to be
218 //calculated
219 if (!isRowValid) {
220 //Selection direction up/down
221 if (row.rowIndex > _lastRowIndex) {
222 //last row
223 if (row.rowIndex >= validIndexes[validIdxLen - 1]) {
224 nextRowIndex = validIndexes[validIdxLen - 1];
225 } else {
226 let calcRowIndex = (_lastValidRowIndex + d);
227 if (calcRowIndex > (validIdxLen - 1)) {
228 nextRowIndex = validIndexes[validIdxLen - 1];
229 } else {
230 nextRowIndex = validIndexes[calcRowIndex];
231 }
232 }
233 } else {
234 //first row
235 if (row.rowIndex <= validIndexes[0]) {
236 nextRowIndex = validIndexes[0];
237 } else {
238 let v = validIndexes[_lastValidRowIndex - d];
239 nextRowIndex = v ? v : validIndexes[0];
240 }
241 }
242 _lastRowIndex = row.rowIndex;
243 doSelect(nextRowIndex);
244 } else {
245 //If filtered row is valid, special calculation for
246 //pgup/pgdown keys
247 if (keyCode !== 34 && keyCode !== 33) {
248 _lastValidRowIndex = validIndexes.indexOf(row.rowIndex);
249 _lastRowIndex = row.rowIndex;
250 } else {
251 if (keyCode === 34) { //pgdown
252 //last row
253 if ((_lastValidRowIndex + d) <= (validIdxLen - 1)) {
254 nextRowIndex = validIndexes[
255 _lastValidRowIndex + d];
256 } else {
257 nextRowIndex = [validIdxLen - 1];
258 }
259 } else { //pgup
260 //first row
261 if ((_lastValidRowIndex - d) <= validIndexes[0]) {
262 nextRowIndex = validIndexes[0];
263 } else {
264 nextRowIndex = validIndexes[
265 _lastValidRowIndex - d];
266 }
267 }
268 _lastRowIndex = nextRowIndex;
269 _lastValidRowIndex = validIndexes.indexOf(nextRowIndex);
270 doSelect(nextRowIndex);
271 }
272 }
273 };
274
275 //Page navigation has to be enforced whenever selected row is out of
276 //the current page range
277 let onBeforeSelection = function (et, selectedElm) {
278 let row = et.defaultSelection !== 'row' ?
279 selectedElm.parentNode : selectedElm;
280 if (tf.paging) {
281 if (tf.feature('paging').nbPages > 1) {
282 let paging = tf.feature('paging');
283 //page length is re-assigned in case it has changed
284 et.nbRowsPerPage = paging.pageLength;
285 let validIndexes = tf.validRowsIndex,
286 validIdxLen = validIndexes.length,
287 pagingEndRow = parseInt(paging.startPagingRow, 10) +
288 parseInt(paging.pageLength, 10);
289 let rowIndex = row.rowIndex;
290
291 if ((rowIndex === validIndexes[validIdxLen - 1]) &&
292 paging.currentPageNb !== paging.nbPages) {
293 paging.setPage('last');
294 }
295 else if ((rowIndex === validIndexes[0]) &&
296 paging.currentPageNb !== 1) {
297 paging.setPage('first');
298 }
299 else if (rowIndex > validIndexes[pagingEndRow - 1] &&
300 rowIndex < validIndexes[validIdxLen - 1]) {
301 paging.setPage('next');
302 }
303 else if (
304 rowIndex < validIndexes[paging.startPagingRow] &&
305 rowIndex > validIndexes[0]) {
306 paging.setPage('previous');
307 }
308 }
309 }
310 };
311
312 //Selected row needs to be visible when paging is activated
313 if (tf.paging) {
314 tf.feature('paging').onAfterChangePage = function (paging) {
315 let advGrid = paging.tf.extension('advancedGrid');
316 let et = advGrid._ezEditTable;
317 let slc = et.Selection;
318 /* eslint-disable */
319 let row = slc.GetActiveRow();
320 /* eslint-enable */
321 if (row) {
322 row.scrollIntoView(false);
323 }
324 /* eslint-disable */
325 let cell = slc.GetActiveCell();
326 /* eslint-enable */
327 if (cell) {
328 cell.scrollIntoView(false);
329 }
330 };
331 }
332
333 //Rows navigation when rows are filtered is performed with the
334 //EditTable row selection callback events
335 if (cfg.default_selection === 'row') {
336 let fnB = cfg.on_before_selected_row;
337 cfg.on_before_selected_row = function () {
338 var args = arguments;
339 onBeforeSelection(args[0], args[1], args[2]);
340 if (fnB) {
341 fnB.call(null, args[0], args[1], args[2]);
342 }
343 };
344 let fnA = cfg.on_after_selected_row;
345 cfg.on_after_selected_row = function () {
346 var args = arguments;
347 onAfterSelection(args[0], args[1], args[2]);
348 if (fnA) {
349 fnA.call(null, args[0], args[1], args[2]);
350 }
351 };
352 } else {
353 let fnD = cfg.on_before_selected_cell;
354 cfg.on_before_selected_cell = function () {
355 var args = arguments;
356 onBeforeSelection(args[0], args[1], args[2]);
357 if (fnD) {
358 fnD.call(null, args[0], args[1], args[2]);
359 }
360 };
361 let fnC = cfg.on_after_selected_cell;
362 cfg.on_after_selected_cell = function () {
363 var args = arguments;
364 onAfterSelection(args[0], args[1], args[2]);
365 if (fnC) {
366 fnC.call(null, args[0], args[1], args[2]);
367 }
368 };
369 }
370 }
371 if (editable) {
372 //Added or removed rows, TF rows number needs to be re-calculated
373 let fnE = cfg.on_added_dom_row;
374 cfg.on_added_dom_row = function () {
375 var args = arguments;
376 tf.nbFilterableRows++;
377 if (!tf.paging) {
378 tf.emitter.emit('rows-changed', tf, this);
379 } else {
380 tf.nbFilterableRows++;
381 tf.paging = false;
382 tf.feature('paging').destroy();
383 tf.feature('paging').reset();
384 }
385 if (tf.alternateRows) {
386 tf.feature('alternateRows').init();
387 }
388 if (fnE) {
389 fnE.call(null, args[0], args[1], args[2]);
390 }
391 };
392 if (cfg.actions && cfg.actions['delete']) {
393 let fnF = cfg.actions['delete'].on_after_submit;
394 cfg.actions['delete'].on_after_submit = function () {
395 var args = arguments;
396 tf.nbFilterableRows--;
397 if (!tf.paging) {
398 tf.emitter.emit('rows-changed', tf, this);
399 } else {
400 tf.nbFilterableRows--;
401 tf.paging = false;
402 tf.feature('paging').destroy();
403 tf.feature('paging').reset(false);
404 }
405 if (tf.alternateRows) {
406 tf.feature('alternateRows').init();
407 }
408 if (fnF) {
409 fnF.call(null, args[0], args[1]);
410 }
411 };
412 }
413 }
414
415 try {
416 /* eslint-disable */
417 this._ezEditTable = new EditTable(tf.id, cfg, startRow);
418 this._ezEditTable.Init();
419 /* eslint-enable */
420 } catch (e) { throw new Error(INSTANTIATION_ERROR); }
421
422 this.initialized = true;
423 }
424
425 /**
426 * Reset advanced grid when previously removed
427 */
428 reset() {
429 let ezEditTable = this._ezEditTable;
430 if (ezEditTable) {
431 if (this.cfg.selection) {
432 /* eslint-disable */
433 ezEditTable.Selection.Set();
434 /* eslint-enable */
435 }
436 if (this.cfg.editable) {
437 /* eslint-disable */
438 ezEditTable.Editable.Set();
439 /* eslint-enable */
440 }
441 }
442 }
443
444 /**
445 * Toggle behaviour
446 */
447 toggle() {
448 let ezEditTable = this._ezEditTable;
449 if (ezEditTable.editable) {
450 /* eslint-disable */
451 ezEditTable.Editable.Remove();
452 /* eslint-enable */
453 } else {
454 /* eslint-disable */
455 ezEditTable.Editable.Set();
456 /* eslint-enable */
457 }
458 if (ezEditTable.selection) {
459 /* eslint-disable */
460 ezEditTable.Selection.Remove();
461 /* eslint-enable */
462 } else {
463 /* eslint-disable */
464 ezEditTable.Selection.Set();
465 /* eslint-enable */
466 }
467 }
468
469 _toggleForInputFilter() {
470 let tf = this.tf;
471 if (!tf.getActiveFilterId()) {
472 return;
473 }
474 let colIndex = tf.getColumnIndexFromFilterId(tf.getActiveFilterId());
475 let filterType = tf.getFilterType(colIndex);
476 if (filterType === INPUT) {
477 this.toggle();
478 }
479 }
480
481 /**
482 * Remove advanced grid
483 */
484 destroy() {
485 if (!this.initialized) {
486 return;
487 }
488 let ezEditTable = this._ezEditTable;
489 if (ezEditTable) {
490 if (this.cfg.selection) {
491 /* eslint-disable */
492 ezEditTable.Selection.ClearSelections();
493 ezEditTable.Selection.Remove();
494 /* eslint-enable */
495 }
496 if (this.cfg.editable) {
497 /* eslint-disable */
498 ezEditTable.Editable.Remove();
499 /* eslint-enable */
500 }
501 }
502
503 this.emitter.off(['filter-focus', 'filter-blur'],
504 () => this._toggleForInputFilter());
505 this.initialized = false;
506 }
507}
508
509AdapterEzEditTable.meta = {altName: 'advancedGrid'};