1 | import {BaseDropdown} from './baseDropdown';
|
2 | import {
|
3 | addClass, createCheckItem, createText, createElm, elm, removeClass, tag
|
4 | } from '../dom';
|
5 | import {has} from '../array';
|
6 | import {matchCase, trim, rgxEsc} from '../string';
|
7 | import {addEvt, removeEvt, targetEvt} from '../event';
|
8 | import {isEmpty} from '../types';
|
9 | import {CHECKLIST, NONE} from '../const';
|
10 | import {defaultsStr, defaultsBool} from '../settings';
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 | export class CheckList extends BaseDropdown {
|
19 |
|
20 | |
21 |
|
22 |
|
23 |
|
24 | constructor(tf) {
|
25 | super(tf, 'checkList');
|
26 |
|
27 | let f = this.config;
|
28 |
|
29 | |
30 |
|
31 |
|
32 |
|
33 | this.containers = [];
|
34 |
|
35 | |
36 |
|
37 |
|
38 |
|
39 | this.containerCssClass = defaultsStr(f.div_checklist_css_class,
|
40 | 'div_checklist');
|
41 |
|
42 | |
43 |
|
44 |
|
45 |
|
46 | this.filterCssClass = defaultsStr(f.checklist_css_class,
|
47 | 'flt_checklist');
|
48 |
|
49 | |
50 |
|
51 |
|
52 |
|
53 | this.itemCssClass = defaultsStr(f.checklist_item_css_class,
|
54 | 'flt_checklist_item');
|
55 |
|
56 | |
57 |
|
58 |
|
59 |
|
60 | this.selectedItemCssClass = defaultsStr(
|
61 | f.checklist_selected_item_css_class,
|
62 | 'flt_checklist_slc_item'
|
63 | );
|
64 |
|
65 | |
66 |
|
67 |
|
68 |
|
69 |
|
70 | this.activateText = defaultsStr(
|
71 | f.activate_checklist_text,
|
72 | 'Click to load filter data'
|
73 | );
|
74 |
|
75 | |
76 |
|
77 |
|
78 |
|
79 | this.disabledItemCssClass = defaultsStr(
|
80 | f.checklist_item_disabled_css_class,
|
81 | 'flt_checklist_item_disabled'
|
82 | );
|
83 |
|
84 | |
85 |
|
86 |
|
87 |
|
88 | this.enableResetOption = defaultsBool(f.enable_checklist_reset_filter,
|
89 | true);
|
90 |
|
91 | |
92 |
|
93 |
|
94 |
|
95 |
|
96 | this.prfx = 'chkdiv_';
|
97 | }
|
98 |
|
99 | |
100 |
|
101 |
|
102 |
|
103 |
|
104 | optionClick(evt) {
|
105 | let elm = targetEvt(evt);
|
106 | let tf = this.tf;
|
107 |
|
108 | this.emitter.emit('filter-focus', tf, elm);
|
109 | this.setItemOption(elm);
|
110 | tf.filter();
|
111 | }
|
112 |
|
113 | |
114 |
|
115 |
|
116 |
|
117 |
|
118 | onCheckListClick(evt) {
|
119 | let elm = targetEvt(evt);
|
120 | if (this.tf.loadFltOnDemand && elm.getAttribute('filled') === '0') {
|
121 | let ct = elm.getAttribute('ct');
|
122 | let div = this.containers[ct];
|
123 | this.build(ct);
|
124 | removeEvt(div, 'click', (evt) => this.onCheckListClick(evt));
|
125 | }
|
126 | }
|
127 |
|
128 | |
129 |
|
130 |
|
131 | refreshAll() {
|
132 | let colIdxs = this.tf.getFiltersByType(CHECKLIST, true);
|
133 | this.refreshFilters(colIdxs);
|
134 | }
|
135 |
|
136 | |
137 |
|
138 |
|
139 |
|
140 |
|
141 |
|
142 | init(colIndex, isExternal, container) {
|
143 | let tf = this.tf;
|
144 | let externalFltTgtId = isExternal ?
|
145 | tf.externalFltIds[colIndex] : null;
|
146 |
|
147 | let divCont = createElm('div',
|
148 | ['id', `${this.prfx}${colIndex}_${tf.id}`],
|
149 | ['ct', colIndex], ['filled', '0']);
|
150 | divCont.className = this.containerCssClass;
|
151 |
|
152 |
|
153 | if (externalFltTgtId) {
|
154 | elm(externalFltTgtId).appendChild(divCont);
|
155 | } else {
|
156 | container.appendChild(divCont);
|
157 | }
|
158 |
|
159 | this.containers[colIndex] = divCont;
|
160 | tf.fltIds.push(tf.buildFilterId(colIndex));
|
161 |
|
162 | if (!tf.loadFltOnDemand) {
|
163 | this.build(colIndex);
|
164 | } else {
|
165 | addEvt(divCont, 'click', (evt) => this.onCheckListClick(evt));
|
166 | divCont.appendChild(createText(this.activateText));
|
167 | }
|
168 |
|
169 | this.emitter.on(
|
170 | ['build-checklist-filter'],
|
171 | (tf, colIndex, isLinked) => this.build(colIndex, isLinked)
|
172 | );
|
173 |
|
174 | this.emitter.on(
|
175 | ['select-checklist-options'],
|
176 | (tf, colIndex, values) => this.selectOptions(colIndex, values)
|
177 | );
|
178 |
|
179 | this.emitter.on(['rows-changed'], () => this.refreshAll());
|
180 |
|
181 | this.emitter.on(['after-filtering'], () => this.linkFilters());
|
182 |
|
183 |
|
184 | this.initialized = true;
|
185 | }
|
186 |
|
187 | |
188 |
|
189 |
|
190 |
|
191 |
|
192 | build(colIndex, isLinked = false) {
|
193 | let tf = this.tf;
|
194 | colIndex = Number(colIndex);
|
195 |
|
196 | this.emitter.emit('before-populating-filter', tf, colIndex);
|
197 |
|
198 |
|
199 | this.opts = [];
|
200 |
|
201 | this.optsTxt = [];
|
202 |
|
203 | let flt = this.containers[colIndex];
|
204 | let ul = createElm('ul',
|
205 | ['id', tf.fltIds[colIndex]],
|
206 | ['colIndex', colIndex]);
|
207 | ul.className = this.filterCssClass;
|
208 |
|
209 | let caseSensitive = tf.caseSensitive;
|
210 |
|
211 | this.isCustom = tf.isCustomOptions(colIndex);
|
212 |
|
213 |
|
214 | if (this.isCustom) {
|
215 | let customValues = tf.getCustomOptions(colIndex);
|
216 | this.opts = customValues[0];
|
217 | this.optsTxt = customValues[1];
|
218 | }
|
219 |
|
220 | let activeIdx;
|
221 | let activeFilterId = tf.getActiveFilterId();
|
222 |
|
223 | if (isLinked && activeFilterId) {
|
224 | activeIdx = tf.getColumnIndexFromFilterId(activeFilterId);
|
225 | }
|
226 |
|
227 | let filteredDataCol = [];
|
228 | if (isLinked && tf.disableExcludedOptions) {
|
229 |
|
230 | this.excludedOpts = [];
|
231 | }
|
232 |
|
233 | flt.innerHTML = '';
|
234 |
|
235 | let eachRow = tf.eachRow();
|
236 | eachRow(
|
237 | (row) => {
|
238 | let cellValue = tf.getCellValue(row.cells[colIndex]);
|
239 |
|
240 | let cellString = matchCase(cellValue, caseSensitive);
|
241 |
|
242 | if (!has(this.opts, cellString, caseSensitive)) {
|
243 | this.opts.push(cellValue);
|
244 | }
|
245 | let filteredCol = filteredDataCol[colIndex];
|
246 | if (isLinked && tf.disableExcludedOptions) {
|
247 | if (!filteredCol) {
|
248 | filteredCol = tf.getVisibleColumnValues(colIndex);
|
249 | }
|
250 | if (!has(filteredCol, cellString, caseSensitive) &&
|
251 | !has(this.excludedOpts, cellString, caseSensitive)) {
|
252 | this.excludedOpts.push(cellValue);
|
253 | }
|
254 | }
|
255 | },
|
256 |
|
257 | (row, k) => {
|
258 |
|
259 | if (tf.excludeRows.indexOf(k) !== -1) {
|
260 | return true;
|
261 | }
|
262 |
|
263 |
|
264 | if (row.cells.length !== tf.nbCells || this.isCustom) {
|
265 | return true;
|
266 | }
|
267 |
|
268 | if (isLinked && !this.isValidLinkedValue(k, activeIdx)) {
|
269 | return true;
|
270 | }
|
271 | }
|
272 | );
|
273 |
|
274 |
|
275 | this.opts = this.sortOptions(colIndex, this.opts);
|
276 | if (this.excludedOpts) {
|
277 | this.excludedOpts = this.sortOptions(colIndex, this.excludedOpts);
|
278 | }
|
279 |
|
280 | this.addChecks(colIndex, ul);
|
281 |
|
282 | if (tf.loadFltOnDemand) {
|
283 | flt.innerHTML = '';
|
284 | }
|
285 | flt.appendChild(ul);
|
286 | flt.setAttribute('filled', '1');
|
287 |
|
288 | this.emitter.emit('after-populating-filter', tf, colIndex, flt);
|
289 | }
|
290 |
|
291 | |
292 |
|
293 |
|
294 |
|
295 |
|
296 |
|
297 | addChecks(colIndex, ul) {
|
298 | let tf = this.tf;
|
299 | let chkCt = this.addTChecks(colIndex, ul);
|
300 |
|
301 | for (let y = 0; y < this.opts.length; y++) {
|
302 | let val = this.opts[y];
|
303 | let lbl = this.isCustom ? this.optsTxt[y] : val;
|
304 | let fltId = tf.fltIds[colIndex];
|
305 | let lblIdx = y + chkCt;
|
306 | let li = createCheckItem(`${fltId}_${lblIdx}`, val, lbl,
|
307 | ['data-idx', lblIdx]);
|
308 | li.className = this.itemCssClass;
|
309 |
|
310 | if (tf.linkedFilters && tf.disableExcludedOptions &&
|
311 | has(this.excludedOpts, matchCase(val, tf.caseSensitive),
|
312 | tf.caseSensitive)) {
|
313 | addClass(li, this.disabledItemCssClass);
|
314 | li.check.disabled = true;
|
315 | li.disabled = true;
|
316 | } else {
|
317 | addEvt(li.check, 'click', evt => this.optionClick(evt));
|
318 | }
|
319 | ul.appendChild(li);
|
320 |
|
321 | if (val === '') {
|
322 |
|
323 | li.style.display = NONE;
|
324 | }
|
325 | }
|
326 | }
|
327 |
|
328 | |
329 |
|
330 |
|
331 |
|
332 |
|
333 |
|
334 | addTChecks(colIndex, ul) {
|
335 | let tf = this.tf;
|
336 | let chkCt = 1;
|
337 | let fltId = tf.fltIds[colIndex];
|
338 | let li0 = createCheckItem(`${fltId}_0`, '',
|
339 | tf.getClearFilterText(colIndex), ['data-idx', 0]);
|
340 | li0.className = this.itemCssClass;
|
341 | ul.appendChild(li0);
|
342 |
|
343 | addEvt(li0.check, 'click', evt => this.optionClick(evt));
|
344 |
|
345 | if (!this.enableResetOption) {
|
346 | li0.style.display = NONE;
|
347 | }
|
348 |
|
349 | if (tf.enableEmptyOption) {
|
350 | let li1 = createCheckItem(`${fltId}_1`, tf.emOperator,
|
351 | tf.emptyText, ['data-idx', 1]);
|
352 | li1.className = this.itemCssClass;
|
353 | ul.appendChild(li1);
|
354 | addEvt(li1.check, 'click', evt => this.optionClick(evt));
|
355 | chkCt++;
|
356 | }
|
357 |
|
358 | if (tf.enableNonEmptyOption) {
|
359 | let li2 = createCheckItem(`${fltId}_2`, tf.nmOperator,
|
360 | tf.nonEmptyText, ['data-idx', 2]);
|
361 | li2.className = this.itemCssClass;
|
362 | ul.appendChild(li2);
|
363 | addEvt(li2.check, 'click', evt => this.optionClick(evt));
|
364 | chkCt++;
|
365 | }
|
366 | return chkCt;
|
367 | }
|
368 |
|
369 | |
370 |
|
371 |
|
372 |
|
373 |
|
374 | setItemOption(o) {
|
375 | if (!o) {
|
376 | return;
|
377 | }
|
378 |
|
379 | let tf = this.tf;
|
380 | let chkValue = o.value;
|
381 | let chkIndex = o.dataset.idx;
|
382 | let colIdx = tf.getColumnIndexFromFilterId(o.id);
|
383 | let n = tf.getFilterElement(parseInt(colIdx, 10));
|
384 | let items = n.childNodes;
|
385 | let li = items[chkIndex];
|
386 |
|
387 | let slcValues = n.getAttribute('value') || '';
|
388 |
|
389 | let slcIndexes = n.getAttribute('indexes') || '';
|
390 |
|
391 | if (o.checked) {
|
392 |
|
393 | if (chkValue === '') {
|
394 |
|
395 | let indexes = slcIndexes.split(tf.separator);
|
396 | indexes.forEach(idx => {
|
397 | idx = Number(idx);
|
398 | let li = items[idx];
|
399 | let chx = tag(li, 'input')[0];
|
400 | if (chx && idx > 0) {
|
401 | chx.checked = false;
|
402 | removeClass(li, this.selectedItemCssClass);
|
403 | }
|
404 | });
|
405 |
|
406 | n.setAttribute('value', '');
|
407 | n.setAttribute('indexes', '');
|
408 |
|
409 | } else {
|
410 | let indexes = slcIndexes + chkIndex + tf.separator;
|
411 | let values =
|
412 | trim(slcValues + ' ' + chkValue + ' ' + tf.orOperator);
|
413 |
|
414 | n.setAttribute('value', values);
|
415 | n.setAttribute('indexes', indexes);
|
416 |
|
417 |
|
418 | let chx0 = tag(items[0], 'input')[0];
|
419 | if (chx0) {
|
420 | chx0.checked = false;
|
421 | }
|
422 | }
|
423 |
|
424 | removeClass(items[0], this.selectedItemCssClass);
|
425 | addClass(li, this.selectedItemCssClass);
|
426 | } else {
|
427 | let replaceValue =
|
428 | new RegExp(rgxEsc(chkValue + ' ' + tf.orOperator));
|
429 | let values = slcValues.replace(replaceValue, '');
|
430 | let replaceIndex = new RegExp(rgxEsc(chkIndex + tf.separator));
|
431 | let indexes = slcIndexes.replace(replaceIndex, '');
|
432 |
|
433 | n.setAttribute('value', trim(values));
|
434 | n.setAttribute('indexes', indexes);
|
435 |
|
436 | removeClass(li, this.selectedItemCssClass);
|
437 | }
|
438 | }
|
439 |
|
440 | |
441 |
|
442 |
|
443 |
|
444 |
|
445 | selectOptions(colIndex, values = []) {
|
446 | let tf = this.tf;
|
447 | let flt = tf.getFilterElement(colIndex);
|
448 | if (!flt || values.length === 0) {
|
449 | return;
|
450 | }
|
451 |
|
452 | let lis = tag(flt, 'li');
|
453 |
|
454 | flt.setAttribute('value', '');
|
455 | flt.setAttribute('indexes', '');
|
456 |
|
457 | [].forEach.call(lis, (li) => {
|
458 | let chk = tag(li, 'input')[0];
|
459 | let chkVal = matchCase(chk.value, tf.caseSensitive);
|
460 |
|
461 | if (chkVal !== '' && has(values, chkVal, tf.caseSensitive)) {
|
462 | chk.checked = true;
|
463 | } else {
|
464 |
|
465 | if (values.indexOf(tf.nmOperator) !== -1 &&
|
466 | chkVal === matchCase(tf.nonEmptyText, tf.caseSensitive)) {
|
467 | chk.checked = true;
|
468 | }
|
469 | else if (values.indexOf(tf.emOperator) !== -1 &&
|
470 | chkVal === matchCase(tf.emptyText, tf.caseSensitive)) {
|
471 | chk.checked = true;
|
472 | } else {
|
473 | chk.checked = false;
|
474 | }
|
475 | }
|
476 | this.setItemOption(chk);
|
477 | });
|
478 | }
|
479 |
|
480 | |
481 |
|
482 |
|
483 |
|
484 |
|
485 | getValues(colIndex) {
|
486 | let tf = this.tf;
|
487 | let flt = tf.getFilterElement(colIndex);
|
488 | if (!flt) {
|
489 | return [];
|
490 | }
|
491 |
|
492 | let fltAttr = flt.getAttribute('value');
|
493 | let values = isEmpty(fltAttr) ? '' : fltAttr;
|
494 |
|
495 | values = values.substr(0, values.length - 3);
|
496 |
|
497 | values = values.split(' ' + tf.orOperator + ' ');
|
498 |
|
499 | return values;
|
500 | }
|
501 |
|
502 | |
503 |
|
504 |
|
505 | destroy() {
|
506 | this.emitter.off(
|
507 | ['build-checklist-filter'],
|
508 | (tf, colIndex, isLinked) => this.build(colIndex, isLinked)
|
509 | );
|
510 | this.emitter.off(
|
511 | ['select-checklist-options'],
|
512 | (tf, colIndex, values) => this.selectOptions(colIndex, values)
|
513 | );
|
514 | this.emitter.off(['rows-changed'], () => this.refreshAll());
|
515 | this.emitter.off(['after-filtering'], () => this.linkFilters());
|
516 |
|
517 | this.initialized = false;
|
518 | }
|
519 | }
|