@refinitiv-ui/efx-grid 6.0.110 → 6.0.112

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,6 +6,19 @@ import "@refinitiv-ui/elements/item";
6
6
  import "@refinitiv-ui/elements/checkbox";
7
7
  import translation from "./locale/translation.js";
8
8
 
9
+ declare namespace CheckboxList {
10
+
11
+ type itemState = {
12
+ type?: string|null,
13
+ label?: string|null,
14
+ checked?: boolean|null,
15
+ index?: number|null,
16
+ elem?: Element|null,
17
+ value?: (string|boolean)|null
18
+ };
19
+
20
+ }
21
+
9
22
  declare class CheckboxList extends BasicElement {
10
23
 
11
24
  constructor();
@@ -5,6 +5,15 @@ import "@refinitiv-ui/elements/item";
5
5
  import "@refinitiv-ui/elements/checkbox";
6
6
  import translation from "./locale/translation.js";
7
7
 
8
+ /**
9
+ * @typedef {Object} CheckboxList~itemState
10
+ * @property {string=} type
11
+ * @property {string=} label
12
+ * @property {boolean=} checked
13
+ * @property {number=} index
14
+ * @property {Element=} elem
15
+ * @property {(string|boolean)=} value
16
+ */
8
17
  /** @type {number}
9
18
  * @private
10
19
  * @constant
@@ -28,15 +37,18 @@ const _toLabelText = function(val) {
28
37
  * @function
29
38
  * @param {*} val
30
39
  * @param {number} idx
31
- * @return {!Object}
40
+ * @return {CheckboxList~itemState}
32
41
  */
33
42
  const _toItemStates = function(val, idx) {
43
+ let textLabel = _toLabelText(val);
34
44
  return {
35
- label: _toLabelText(val),
45
+ type: val.type,
46
+ label: textLabel,
47
+ title: textLabel,
36
48
  checked: (val && val.checked) ? true : false,
37
49
  index: idx,
38
50
  elem: null,
39
- value: val.value
51
+ value: val.value || textLabel
40
52
  };
41
53
  };
42
54
  /** @private
@@ -77,9 +89,13 @@ const _updateElement = function(elem, txt, checked) {
77
89
  * @param {string} str
78
90
  * @param {number} idx
79
91
  * @param {string} ctx
92
+ * @param {string} type
80
93
  * @returns {boolean}
81
94
  */
82
- const _caseInsensitiveFilter = function(str, idx, ctx) {
95
+ const _caseInsensitiveFilter = function(str, idx, ctx, type) {
96
+ if(type === "header" || type === "divider") {
97
+ return true;
98
+ }
83
99
  return str.toLowerCase().indexOf(ctx) >= 0;
84
100
  };
85
101
 
@@ -108,6 +124,7 @@ class CheckboxList extends BasicElement {
108
124
  this._states = []; // Item states before filtering
109
125
  this._cachedItems = []; // To avoid repeatedly creating the same item element
110
126
  this._items = []; // Actual existing elements (filtered items)
127
+ this._filterStates = []; // Existing item (after filtered)
111
128
  this._inputFilter = null;
112
129
  }
113
130
 
@@ -131,8 +148,8 @@ class CheckboxList extends BasicElement {
131
148
  }
132
149
 
133
150
  render() {
134
- var lang = this._translation[this.lang] ? this.lang : "en";
135
- var t = this._translation[lang];
151
+ let lang = this._translation[this.lang] ? this.lang : "en";
152
+ let t = this._translation[lang];
136
153
  return html`
137
154
  <div class='root'>
138
155
  <div>
@@ -146,10 +163,10 @@ class CheckboxList extends BasicElement {
146
163
 
147
164
  firstUpdated(changedProps) {
148
165
  // Prepare list item template. TODO: This should be static variable
149
- var checkBox = document.createElement("ef-checkbox");
150
- var label = document.createElement("div");
166
+ let checkBox = document.createElement("ef-checkbox");
167
+ let label = document.createElement("div");
151
168
  label.classList.add("item-label");
152
- var itemContainer = document.createElement("div");
169
+ let itemContainer = document.createElement("div");
153
170
  itemContainer.classList.add("item-container");
154
171
  itemContainer.appendChild(checkBox);
155
172
  itemContainer.appendChild(label);
@@ -157,7 +174,7 @@ class CheckboxList extends BasicElement {
157
174
  this._templateItemEl.appendChild(itemContainer);
158
175
 
159
176
  // Elements has been rendered. Cache element references for better performance
160
- var root = this.shadowRoot;
177
+ let root = this.shadowRoot;
161
178
  this._containerEl = root.getElementById("container");
162
179
 
163
180
  this._selectAllBtn = root.getElementById("select_all");
@@ -169,8 +186,8 @@ class CheckboxList extends BasicElement {
169
186
 
170
187
  updated(changedProps) {
171
188
  if (changedProps.has("data")) {
172
- var dataList = this.data;
173
- var len = dataList ? dataList.length : 0;
189
+ let dataList = this.data;
190
+ let len = dataList ? dataList.length : 0;
174
191
  if (this._cachedItems.length < len) {
175
192
  this._cachedItems.length = len;
176
193
  }
@@ -186,15 +203,15 @@ class CheckboxList extends BasicElement {
186
203
  * @return {!Element}
187
204
  */
188
205
  _toItemElement(state) {
189
- var elem = state.elem;
206
+ let elem = state.elem;
190
207
  if(!elem) {
191
- var idx = state.index;
208
+ let idx = state.index;
192
209
  elem = this._cachedItems[idx];
193
210
  if(!elem) {
194
211
  elem = this._cachedItems[idx] = this._templateItemEl.cloneNode(true);
195
212
  elem.addEventListener("click", this._onItemClicked);
196
213
 
197
- var itemContainer = elem.firstChild; // div.item-container
214
+ let itemContainer = elem.firstChild; // div.item-container
198
215
  elem._checkbox = itemContainer.firstChild; // ef-checkbox
199
216
  elem._label = itemContainer.lastChild; // label
200
217
  }
@@ -213,15 +230,30 @@ class CheckboxList extends BasicElement {
213
230
  }
214
231
 
215
232
  // TODO: Optimize appending and removing items by finding appropriate item number
216
- var container = this._containerEl;
233
+ let container = this._containerEl;
217
234
  while(container.lastChild) {
218
235
  container.removeChild(container.lastChild);
219
236
  }
220
237
 
221
- var ary = this._items;
222
- var len = ary.length;
223
- for(var i = 0; i < len; ++i) {
224
- container.appendChild(ary[i]);
238
+ let ary = this._items;
239
+ let filterStates = this._filterStates;
240
+ let len = ary.length;
241
+ for(let i = 0; i < len; ++i) {
242
+ let item = ary[i];
243
+ let state = filterStates[i];
244
+ item.type = "";
245
+ let itemContainer = item.firstChild;
246
+ if(!itemContainer.contains(item._checkbox)){
247
+ itemContainer.insertBefore(item._checkbox, item._label);
248
+ }
249
+ let type = state.type;
250
+ if(type) {
251
+ if(type === "header") {
252
+ itemContainer.removeChild(item._checkbox);
253
+ }
254
+ item.type = type;
255
+ }
256
+ container.appendChild(item);
225
257
  }
226
258
  this._updateSelectAllState();
227
259
  }
@@ -249,9 +281,9 @@ class CheckboxList extends BasicElement {
249
281
  return;
250
282
  }
251
283
 
252
- var states = this._states;
253
- var len = states.length;
254
- for(var i = 0; i < len; ++i) {
284
+ let states = this._states;
285
+ let len = states.length;
286
+ for(let i = 0; i < len; ++i) {
255
287
  _assignItemState(states[i], this.data[i]);
256
288
  }
257
289
 
@@ -263,14 +295,15 @@ class CheckboxList extends BasicElement {
263
295
  * @return {!Array<object>}
264
296
  */
265
297
  getSelectedItems() {
266
- var items = [];
267
- var states = this._states;
268
- var len = states.length;
269
- for(var i = 0; i < len; ++i) {
270
- var state = states[i];
298
+ let items = [];
299
+ let states = this._states;
300
+ let len = states.length;
301
+ for(let i = 0; i < len; ++i) {
302
+ let state = states[i];
271
303
  if(_getCheckedState(state)) {
272
304
  items.push({
273
305
  title: state.label,
306
+ label: state.label,
274
307
  index: i,
275
308
  value: state.value
276
309
  });
@@ -285,9 +318,9 @@ class CheckboxList extends BasicElement {
285
318
  */
286
319
  selectAll(selected) {
287
320
  selected = selected !== false;
288
- var ary = this._items;
289
- var len = ary.length;
290
- for(var i = 0; i < len; ++i) {
321
+ let ary = this._items;
322
+ let len = ary.length;
323
+ for(let i = 0; i < len; ++i) {
291
324
  ary[i]._checkbox.checked = selected;
292
325
  }
293
326
  this._updateSelectAllState(selected);
@@ -308,7 +341,7 @@ class CheckboxList extends BasicElement {
308
341
  if(!inputFilter) {
309
342
  inputFilter = null;
310
343
  }
311
- var dirty = (typeof inputFilter === "function");
344
+ let dirty = (typeof inputFilter === "function");
312
345
  if(this._inputFilter !== inputFilter) {
313
346
  this._inputFilter = inputFilter || null;
314
347
  dirty = true;
@@ -323,14 +356,14 @@ class CheckboxList extends BasicElement {
323
356
  * @private
324
357
  */
325
358
  _filterItems() {
326
- var states = this._states;
327
- var len = states ? states.length : 0;
359
+ let states = this._states;
360
+ let len = states ? states.length : 0;
328
361
  if(!len) {
329
362
  this._items.length = 0;
330
363
  return; // No new copy is generated
331
364
  }
332
365
 
333
- var inputFilter = this._inputFilter;
366
+ let inputFilter = this._inputFilter;
334
367
  if(inputFilter) {
335
368
  if(typeof inputFilter === "function") {
336
369
  this._filterMethod = inputFilter;
@@ -349,6 +382,7 @@ class CheckboxList extends BasicElement {
349
382
  states = states.slice(0, ITEM_LIMIT);
350
383
  }
351
384
 
385
+ this._filterStates = states;
352
386
  this._items = states.map(this._toItemElement);
353
387
  }
354
388
  /**
@@ -358,7 +392,7 @@ class CheckboxList extends BasicElement {
358
392
  * @return {boolean}
359
393
  */
360
394
  _byLabelText(state, idx) {
361
- return this._filterMethod(state.label, idx, this._filterCtx);
395
+ return this._filterMethod(state.label, idx, this._filterCtx, state.type);
362
396
  }
363
397
 
364
398
  /**
@@ -367,7 +401,7 @@ class CheckboxList extends BasicElement {
367
401
  */
368
402
  _onItemClicked(e) {
369
403
  if(e.target.tagName !== "EF-CHECKBOX") { // checkbox may already toggle its state
370
- var itemEl = e.currentTarget;
404
+ let itemEl = e.currentTarget;
371
405
  itemEl._checkbox.checked = !itemEl._checkbox.checked;
372
406
  }
373
407
  this._updateSelectAllState();
@@ -383,10 +417,10 @@ class CheckboxList extends BasicElement {
383
417
  }
384
418
 
385
419
  if(selected == null) {
386
- var ary = this._items;
387
- var len = ary.length;
420
+ let ary = this._items;
421
+ let len = ary.length;
388
422
  selected = len ? false : true;
389
- for (var i = 0; i < len; i++) {
423
+ for (let i = 0; i < len; i++) {
390
424
  if (ary[i]._checkbox.checked) {
391
425
  selected = true;
392
426
  break;
@@ -36,6 +36,11 @@ declare namespace FilterDialog {
36
36
  sortChanged?: ((...params: any[]) => any)|null
37
37
  };
38
38
 
39
+ type ComboBoxItem = {
40
+ label: string,
41
+ value: string
42
+ };
43
+
39
44
  }
40
45
 
41
46
  declare class FilterDialog extends BasicElement {
@@ -34,6 +34,11 @@ import "./checkbox-list.js";
34
34
  * @property {Function=} sortChanged Sort changed handler
35
35
  */
36
36
 
37
+ /**
38
+ * @typedef {Object} FilterDialog~ComboBoxItem
39
+ * @property {string} label text of item
40
+ * @property {string} value value of item when item is selected
41
+ */
37
42
 
38
43
  /** @type {string}
39
44
  * @private
@@ -111,9 +116,9 @@ const NUMBER_FILTERS = [
111
116
  /** @private
112
117
  * @function
113
118
  * @param {Array.<String>} options
114
- * @returns {Object}
119
+ * @returns {FilterDialog~ComboBoxItem}
115
120
  */
116
- var arrayToComboBoxItem = function(options) {
121
+ let arrayToComboBoxItem = function(options) {
117
122
  return {
118
123
  label: options[1],
119
124
  value: options[0]
@@ -124,7 +129,7 @@ var arrayToComboBoxItem = function(options) {
124
129
  * @param {Array.<Array>} conditions
125
130
  * @returns {Array.<Object>}
126
131
  */
127
- var createComboBoxData = function(conditions) {
132
+ let createComboBoxData = function(conditions) {
128
133
  return conditions.map(arrayToComboBoxItem);
129
134
  };
130
135
 
@@ -199,7 +204,7 @@ class FilterDialog extends BasicElement {
199
204
  }
200
205
 
201
206
  if(changedProperties.has("config")) {
202
- var options = this.config;
207
+ let options = this.config;
203
208
  if(options) {
204
209
  if (options.data) {
205
210
  this.data = options.data;
@@ -287,8 +292,8 @@ class FilterDialog extends BasicElement {
287
292
  * @return {*}
288
293
  */
289
294
  render() {
290
- var lang = this._translation[this._lang] ? this._lang : "en";
291
- var t = this._translation[lang];
295
+ let lang = this._translation[this._lang] ? this._lang : "en";
296
+ let t = this._translation[lang];
292
297
  return html`
293
298
  <ef-panel id="root_panel" spacing with-shadow with-border>
294
299
  <div id="filterDialogContent" class="filter-dialog dialog-theme-wrapper">
@@ -420,9 +425,9 @@ class FilterDialog extends BasicElement {
420
425
  */
421
426
  updated(changedProps) {
422
427
  // Update Sort UI
423
- var isHiddenSortUIChanged = changedProps.has("hiddenSortUI");
424
- var isHiddenFilterUIChanged = changedProps.has("hiddenFilterUI");
425
- var isHiddenAdvancedFilterChanged = changedProps.has("hiddenAdvancedFilter");
428
+ let isHiddenSortUIChanged = changedProps.has("hiddenSortUI");
429
+ let isHiddenFilterUIChanged = changedProps.has("hiddenFilterUI");
430
+ let isHiddenAdvancedFilterChanged = changedProps.has("hiddenAdvancedFilter");
426
431
  if (isHiddenSortUIChanged) {
427
432
  let pn = this._sortUI.parentNode;
428
433
  if (this.hiddenSortUI) {
@@ -458,7 +463,7 @@ class FilterDialog extends BasicElement {
458
463
  }
459
464
 
460
465
  if (isHiddenSortUIChanged || isHiddenFilterUIChanged) {
461
- var isHide = this.hiddenSortUI || this.hiddenFilterUI;
466
+ let isHide = this.hiddenSortUI || this.hiddenFilterUI;
462
467
  this._separator.classList.toggle("hide", isHide);
463
468
  }
464
469
  if(isHiddenAdvancedFilterChanged) {
@@ -490,7 +495,7 @@ class FilterDialog extends BasicElement {
490
495
  this._cancelBtn.focus(); // Keep focus on cancel button when open dialog
491
496
  this._updateDialogHeight(); // position of the popup element will always be updated
492
497
 
493
- var popupElem = this._popup.getElement();
498
+ let popupElem = this._popup.getElement();
494
499
  if(popupElem) {// After all changes, ensure that visibility is reset back
495
500
  popupElem.style.visibility = "";
496
501
  }
@@ -508,7 +513,7 @@ class FilterDialog extends BasicElement {
508
513
  * @public
509
514
  */
510
515
  show() {
511
- var popupElem = this._popup.getElement();
516
+ let popupElem = this._popup.getElement();
512
517
  if(popupElem) {// To prevent blinking due to size and position change
513
518
  popupElem.style.visibility = "hidden";
514
519
  }
@@ -664,9 +669,9 @@ class FilterDialog extends BasicElement {
664
669
  if (this._isAdvancedFilterMode()) {
665
670
  // Retrieve filter conditions from UI;
666
671
  let oper1, oper2, val1, val2, connector;
667
- var dateTimeType = toDateTimeType(this.fieldDataType);
668
- var fdt = dateTimeType ? dateTimeType : this.fieldDataType.toLowerCase();
669
- var useUTCTime = false;
672
+ let dateTimeType = toDateTimeType(this.fieldDataType);
673
+ let fdt = dateTimeType ? dateTimeType : this.fieldDataType.toLowerCase();
674
+ let useUTCTime = false;
670
675
  if (fdt === DATE_TIME) {
671
676
  useUTCTime = this.useUTCTime;
672
677
  oper1 = this._dateTimeComboBoxes[0].value;
@@ -699,7 +704,7 @@ class FilterDialog extends BasicElement {
699
704
 
700
705
  // Perform automatic data type conversion for only number field data type
701
706
  if (fdt === "number") {
702
- var num = NaN;
707
+ let num = NaN;
703
708
  if (val1) {
704
709
  num = Number(val1);
705
710
  if(num === num) { // Convert to number only when number is valid
@@ -715,7 +720,7 @@ class FilterDialog extends BasicElement {
715
720
  }
716
721
  }
717
722
  }
718
- var ary2D = [];
723
+ let ary2D = [];
719
724
 
720
725
  if(this._isValidCondition(oper1, val1)) {
721
726
  ary2D.push([oper1, val1]);
@@ -757,9 +762,9 @@ class FilterDialog extends BasicElement {
757
762
  * @private
758
763
  */
759
764
  _clearAdvanceFilterUI() {
760
- var defaultVal = "";
761
- var dateTimeType = toDateTimeType(this.fieldDataType);
762
- var fdt = dateTimeType ? dateTimeType : this.fieldDataType.toLowerCase();
765
+ let defaultVal = "";
766
+ let dateTimeType = toDateTimeType(this.fieldDataType);
767
+ let fdt = dateTimeType ? dateTimeType : this.fieldDataType.toLowerCase();
763
768
  if (fdt === DATE_TIME) {
764
769
  defaultVal = "DT";
765
770
  this._clearValue(this._dateTimeComboBoxes[0], defaultVal);
@@ -785,9 +790,9 @@ class FilterDialog extends BasicElement {
785
790
  */
786
791
  _updateGeneralComboBoxes() {
787
792
  if(this._initialized) {
788
- var filterItems;
789
- var dateTimeType = toDateTimeType(this.fieldDataType);
790
- var fdt = dateTimeType ? dateTimeType : this.fieldDataType.toLowerCase();
793
+ let filterItems;
794
+ let dateTimeType = toDateTimeType(this.fieldDataType);
795
+ let fdt = dateTimeType ? dateTimeType : this.fieldDataType.toLowerCase();
791
796
  if(fdt === "number") {
792
797
  filterItems = NUMBER_FILTERS;
793
798
  } else if(fdt === "string" || fdt === "text") {
@@ -798,7 +803,7 @@ class FilterDialog extends BasicElement {
798
803
  if(this._blankValues) {
799
804
  filterItems = filterItems.concat(BLANK_FILTERS);
800
805
  }
801
- var translated = this._translateFilters(filterItems);
806
+ let translated = this._translateFilters(filterItems);
802
807
  this._generalComboBoxes[0].data = createComboBoxData(translated);
803
808
  this._generalComboBoxes[2].data = createComboBoxData(translated);
804
809
  }
@@ -828,7 +833,7 @@ class FilterDialog extends BasicElement {
828
833
  // So wee need to remove free-text and re-set again
829
834
  // to track bug https://jira.refinitiv.com/browse/ELF-1694
830
835
  // TODO:: need to remove WORKAROUND after ELF-1694 fixed
831
- var hasFreeTextMode = elem.getAttribute("free-text") != null;
836
+ let hasFreeTextMode = elem.getAttribute("free-text") != null;
832
837
  if (hasFreeTextMode) {
833
838
  elem.removeAttribute("free-text");
834
839
  }
@@ -874,10 +879,10 @@ class FilterDialog extends BasicElement {
874
879
  }
875
880
 
876
881
  const con1 = userConditions[0];
877
- var validCon1 = false;
878
- var conOp1 = "";
879
- var conVal1 = "";
880
- var connector = "";
882
+ let validCon1 = false;
883
+ let conOp1 = "";
884
+ let conVal1 = "";
885
+ let connector = "";
881
886
  if(Array.isArray(con1)) {
882
887
  conOp1 = con1[0];
883
888
  if(con1.length && con1[0]) {
@@ -892,9 +897,9 @@ class FilterDialog extends BasicElement {
892
897
  }
893
898
 
894
899
  const con2 = userConditions[1];
895
- var validCon2 = false;
896
- var conOp2 = "";
897
- var conVal2 = "";
900
+ let validCon2 = false;
901
+ let conOp2 = "";
902
+ let conVal2 = "";
898
903
  if(validCon1 && Array.isArray(con2)) {
899
904
  conOp2 = con2[0];
900
905
  if(con2.length && conOp2) {
@@ -907,7 +912,7 @@ class FilterDialog extends BasicElement {
907
912
  }
908
913
  }
909
914
 
910
- var firstRadioState = true;
915
+ let firstRadioState = true;
911
916
  if(validCon1 && validCon2) {
912
917
  firstRadioState = connector !== "OR";
913
918
  }
@@ -966,7 +971,7 @@ class FilterDialog extends BasicElement {
966
971
  const dialogCoverage = position.y + dialogHeight;
967
972
  const heightDiff = dialogCoverage - windowHeight;
968
973
 
969
- var heightChange = false;
974
+ let heightChange = false;
970
975
  if(heightDiff > 0) {
971
976
  heightChange = true;
972
977
  const isAdvancedFilter = this._isAdvancedFilterMode();
@@ -1007,7 +1012,7 @@ class FilterDialog extends BasicElement {
1007
1012
  * @private
1008
1013
  */
1009
1014
  _addBlankItem() {
1010
- var ary = this._dataToArray(this.data);
1015
+ let ary = this._dataToArray(this.data);
1011
1016
  if(this._blankValues) {
1012
1017
  let blankItem = {
1013
1018
  id: "_blank",
@@ -1026,16 +1031,22 @@ class FilterDialog extends BasicElement {
1026
1031
  * @private
1027
1032
  */
1028
1033
  _updateValueSelector() {
1029
- var ary = this._dataToArray(this.data);
1034
+ let ary = this._dataToArray(this.data);
1030
1035
  this._dataSelector.data = ary; // Preserve original referencing
1031
1036
  // Value selector for Filter by Condition
1032
1037
  if(ary.length > ITEM_LIMIT) {
1033
1038
  ary = ary.slice(0, ITEM_LIMIT); // Prevent combo box from over population
1034
1039
  }
1035
1040
  // Advance items
1036
- var advanceItems = ary.map(this._toComboBoxItem);
1041
+ let advanceItems = [];
1042
+ for (let i = 0; i < ary.length; i++) {
1043
+ let item = ary[i];
1044
+ if(!item.type) {
1045
+ advanceItems.push(this._toComboBoxItem(item));
1046
+ }
1047
+ }
1037
1048
  if(this._blankValues) {
1038
- var basicItems = advanceItems.slice(1);
1049
+ let basicItems = advanceItems.slice(1);
1039
1050
  this._generalComboBoxes[1].data = basicItems;
1040
1051
  this._generalComboBoxes[3].data = basicItems;
1041
1052
  } else {
@@ -1046,10 +1057,10 @@ class FilterDialog extends BasicElement {
1046
1057
  /**
1047
1058
  * @private
1048
1059
  * @param {*} obj
1049
- * @returns {Object}
1060
+ * @returns {FilterDialog~ComboBoxItem}
1050
1061
  */
1051
1062
  _toComboBoxItem(obj) {
1052
- let itemVal = (typeof obj === "string") ? obj : obj.title || "";
1063
+ let itemVal = (typeof obj === "string") ? obj : (obj.label || obj.title) || "";
1053
1064
  return {
1054
1065
  "label": itemVal,
1055
1066
  "value": itemVal
@@ -1076,7 +1087,7 @@ class FilterDialog extends BasicElement {
1076
1087
  * @return {Date}
1077
1088
  */
1078
1089
  _getDateObject(dateString) {
1079
- var date = new Date(dateString);
1090
+ let date = new Date(dateString);
1080
1091
  if(date && this.useUTCTime == false){
1081
1092
  date.setTime(date.getTime() + (date.getTimezoneOffset() * 60000)); // convert to local time
1082
1093
  }
@@ -1088,7 +1099,7 @@ class FilterDialog extends BasicElement {
1088
1099
  * @return {Object}
1089
1100
  */
1090
1101
  _getTranslation() {
1091
- var lang = this._translation[this._lang] ? this._lang : "en";
1102
+ let lang = this._translation[this._lang] ? this._lang : "en";
1092
1103
  return this._translation[lang];
1093
1104
  }
1094
1105
 
@@ -1099,10 +1110,10 @@ class FilterDialog extends BasicElement {
1099
1110
  */
1100
1111
  _translateFilters(filters) {
1101
1112
  let trans = this._getTranslation();
1102
- var len = filters.length;
1103
- var items = [];
1104
- var item;
1105
- for (var i = 0; i < len; i++) {
1113
+ let len = filters.length;
1114
+ let items = [];
1115
+ let item;
1116
+ for (let i = 0; i < len; i++) {
1106
1117
  item = filters[i];
1107
1118
  items.push([item[0], trans[item[1]]]);
1108
1119
  }
package/lib/grid/index.js CHANGED
@@ -1,3 +1,3 @@
1
1
  import {Grid} from "./lib/efx-grid.js";
2
2
  export {Grid}
3
- window.EFX_GRID = { version: "6.0.110" };
3
+ window.EFX_GRID = { version: "6.0.112" };
@@ -47,11 +47,24 @@ import { CoralItems } from '../../tr-grid-util/es6/CoralItems.js';
47
47
  * @property {Object} section Section(ILayoutGrid) contains all of the filter inputs
48
48
  */
49
49
 
50
+ /** @private
51
+ * @param {string} str
52
+ * @return {string}
53
+ */
54
+
55
+ function convertToLowerCase(str) {
56
+ if (typeof str === "string") {
57
+ return str.toLowerCase();
58
+ }
59
+
60
+ return "";
61
+ }
50
62
  /** @constructor
51
63
  * @extends {GridPlugin}
52
64
  * @param {FilterInputPlugin~Options=} options
53
65
  */
54
66
 
67
+
55
68
  var FilterInputPlugin = function FilterInputPlugin(options) {
56
69
  this._onInputChanged = this._onInputChanged.bind(this);
57
70
  this._onOpenedChanged = this._onOpenedChanged.bind(this);
@@ -465,14 +478,14 @@ FilterInputPlugin.prototype._createColumnInputs = function (section, host) {
465
478
  var defaultValue = colOpt.defaultValue;
466
479
 
467
480
  if (defaultValue) {
468
- var type = colOpt.type.toLowerCase();
481
+ var elemType = convertToLowerCase(colOpt.type);
469
482
 
470
- if (type == "date") {
483
+ if (elemType === "date") {
471
484
  var dateObj = ElfDate.from(defaultValue);
472
485
  defaultValue = dateObj ? dateObj.toDateString().substr(4) : "";
473
486
  }
474
487
 
475
- if (type == "multiselect") {
488
+ if (elemType === "multiselect") {
476
489
  var textMap = FilterInputPlugin._createMapObject(defaultValue);
477
490
 
478
491
  this.filterColumn(c, "", textMap);
@@ -500,12 +513,7 @@ FilterInputPlugin._uiMap = {
500
513
  */
501
514
 
502
515
  FilterInputPlugin.prototype._createFilterUI = function (colOpt) {
503
- var elemType = colOpt.type;
504
-
505
- if (elemType) {
506
- elemType = elemType.toLowerCase();
507
- }
508
-
516
+ var elemType = convertToLowerCase(colOpt.type);
509
517
  var defaultValue = colOpt.defaultValue;
510
518
  var elemTrigger = colOpt.trigger != null ? colOpt.trigger : this._inputTrigger;
511
519
  var uiTag = FilterInputPlugin._uiMap[elemType] || "input";
@@ -663,11 +671,10 @@ FilterInputPlugin.prototype._retrieveColumnOption = function (colIndex, colDef)
663
671
  var option = this._newExtColumnOption(colIndex);
664
672
 
665
673
  if (filterOption) {
666
- var type = filterOption["type"];
674
+ var elemType = convertToLowerCase(filterOption["type"]);
667
675
 
668
- if (typeof type == "string") {
669
- type = type.toLowerCase();
670
- option["type"] = type;
676
+ if (elemType) {
677
+ option["type"] = elemType;
671
678
  }
672
679
 
673
680
  var defaultLogic = filterOption["filterLogic"] || filterOption["defaultLogic"];
@@ -680,7 +687,7 @@ FilterInputPlugin.prototype._retrieveColumnOption = function (colIndex, colDef)
680
687
  }
681
688
  }
682
689
 
683
- if (type == "multiselect" && option._comparingLogic === FilterInputPlugin._containingFilter) {
690
+ if (elemType === "multiselect" && option._comparingLogic === FilterInputPlugin._containingFilter) {
684
691
  option._comparingLogic = FilterInputPlugin._multiSelectionFilter;
685
692
  }
686
693
 
@@ -700,7 +707,7 @@ FilterInputPlugin.prototype._retrieveColumnOption = function (colIndex, colDef)
700
707
 
701
708
  if (Array.isArray(entries)) {
702
709
  // Add Clear filter option
703
- if (type === "select") {
710
+ if (elemType === "select") {
704
711
  entries.push({
705
712
  label: 'No Filter',
706
713
  value: 'No Filter'
@@ -851,15 +858,15 @@ FilterInputPlugin.prototype.setInputValue = function (colIndex, value) {
851
858
 
852
859
  var colOpt = this._getExtColumnOption(colIndex);
853
860
 
854
- var type = colOpt.type ? colOpt.type.toLowerCase() : "";
861
+ var elemType = convertToLowerCase(colOpt.type);
855
862
  var dateValue;
856
863
 
857
- if (type == "date") {
864
+ if (elemType === "date") {
858
865
  var dateObj = ElfDate.from(value);
859
866
  dateValue = dateObj ? dateObj.toDateString().substr(4) : "";
860
867
  }
861
868
 
862
- if (type == "multiselect") {
869
+ if (elemType === "multiselect") {
863
870
  inputElem.values = value;
864
871
 
865
872
  var textMap = FilterInputPlugin._createMapObject(value);
@@ -867,7 +874,7 @@ FilterInputPlugin.prototype.setInputValue = function (colIndex, value) {
867
874
  this.filterColumn(colIndex, "", textMap);
868
875
  } else {
869
876
  inputElem.value = value;
870
- this.filterColumn(colIndex, type == "date" ? dateValue : value);
877
+ this.filterColumn(colIndex, elemType === "date" ? dateValue : value);
871
878
  }
872
879
  };
873
880
  /** @public
@@ -32,12 +32,20 @@ declare namespace RowFilteringPlugin {
32
32
  rawDataAccessor?: ((...params: any[]) => any)|null,
33
33
  formattedDataAccessor?: ((...params: any[]) => any)|null,
34
34
  sortLogic?: ((...params: any[]) => any)|null,
35
+ groupCriteria?: ((...params: any[]) => any)|null,
36
+ groupSortLogic?: ((...params: any[]) => any)|null,
35
37
  itemList?: any[]|null,
36
38
  additionalItems?: any[]|null,
37
39
  compactMode?: boolean|null,
38
40
  blankValues?: (boolean|string)|null
39
41
  };
40
42
 
43
+ type FilterEntry = {
44
+ type?: string|null,
45
+ label: string,
46
+ checked: boolean
47
+ };
48
+
41
49
  type Options = {
42
50
  emptySegmentFiltering?: boolean|null,
43
51
  separatorFiltering?: boolean|null,
@@ -86,12 +86,21 @@ The expression can take various forms:<br>
86
86
  * @property {Function=} rawDataAccessor In case you have custom data type for the specified field, data getter is needed to retrieve raw value for filtering
87
87
  * @property {Function=} formattedDataAccessor This function will be called on each raw data, allowing formatting data to be displayed on the filter item list
88
88
  * @property {Function=} sortLogic This function for sorting filter item list in the dialog. The comparison will perform on raw values, and not formatted values
89
+ * @property {Function=} groupCriteria This function for specify group of items to show group as headers.
90
+ * @property {Function=} groupSortLogic This function for sorting of group header, used with `groupCriteria` to show as group.
89
91
  * @property {Array=} itemList Item list to be shown in the dialog. If this is not specified, the list will be collected from existing data on the grid
90
92
  * @property {Array=} additionalItems Additional items to be put on the itemList
91
93
  * @property {boolean=} compactMode=false force compact mode in dialog
92
94
  * @property {(boolean|string)=} blankValues Display a "(Blanks)" item in the filter dialog to represent an empty value. If a string is passed, it will be used as the label for the blank item
93
95
  */
94
96
 
97
+ /** @typedef {Object} RowFilteringPlugin~FilterEntry
98
+ * @description item object to rendered element-item
99
+ * @property {string=} type=null Available types are "header" and "divider". If null, it will be shown as a normal item.
100
+ * @property {string} label="" Text of each item. If the type is "header", it will be shown as a header. If the type is "divider", it will not be shown.
101
+ * @property {boolean} checked=false Set to true to check the checkbox item in the filter dialog basic item.
102
+ */
103
+
95
104
  /** @typedef {Object} RowFilteringPlugin~Options
96
105
  * @description The options can be specified by `rowFiltering` property of the main grid's options
97
106
  * @property {boolean=} emptySegmentFiltering=false If enabled, the filter will automatically hide empty segment when all of its member are filtered out. If there is no active filter, any empty segment will not be hidden. Collapsed segment does not count as having a filter. A segment with no child is treated the same way as an empty segment.
@@ -1922,13 +1931,10 @@ RowFilteringPlugin.prototype.openDialog = function(colIndex, runtimeDialogOption
1922
1931
  });
1923
1932
  }
1924
1933
 
1925
- let items = keys.map(function(formattedVal, idx) {
1926
- return {
1927
- id: idx,
1928
- title: formattedVal,
1929
- nodes: [],
1930
- checked: selectedItems[formattedVal] ? true : false
1931
- };
1934
+ let items = this._toListScheme({
1935
+ dialogConfig: dialogConfig,
1936
+ keys: keys,
1937
+ selectedItems: selectedItems
1932
1938
  });
1933
1939
 
1934
1940
  // Adding inputs from conditions to uniqueValues for mapping back from the dialog
@@ -1946,7 +1952,6 @@ RowFilteringPlugin.prototype.openDialog = function(colIndex, runtimeDialogOption
1946
1952
  }
1947
1953
  }
1948
1954
  }
1949
-
1950
1955
  // Initialize dialog
1951
1956
  if(this._filterDialog.init) { // TODO: support initiailization in v1
1952
1957
  this._filterDialog.init(dialogConfig);
@@ -2016,6 +2021,16 @@ RowFilteringPlugin._overrideConfig = function(config, userConfig) {
2016
2021
  config.sortLogic = sortLogic;
2017
2022
  }
2018
2023
 
2024
+ let groupCriteria = userConfig["groupCriteria"];
2025
+ if(typeof groupCriteria === "function" || groupCriteria === null) { // Allow null value
2026
+ config.groupCriteria = groupCriteria;
2027
+ }
2028
+
2029
+ let groupSortLogic = userConfig["groupSortLogic"];
2030
+ if(typeof groupSortLogic === "function" || groupSortLogic === null) { // Allow null value
2031
+ config.groupSortLogic = groupSortLogic;
2032
+ }
2033
+
2019
2034
  let itemList = userConfig["itemList"];
2020
2035
  if(itemList != null) {
2021
2036
  config.itemList = itemList;
@@ -2034,6 +2049,72 @@ RowFilteringPlugin._overrideConfig = function(config, userConfig) {
2034
2049
  }
2035
2050
  }
2036
2051
  };
2052
+
2053
+ /** @private
2054
+ * @param {Object} listObj
2055
+ * @return {Array<Object>}
2056
+ */
2057
+ RowFilteringPlugin.prototype._toListScheme = function(listObj) {
2058
+ let dialogConfig = listObj.dialogConfig;
2059
+ let keys = listObj.keys;
2060
+ let selectedItems = listObj.selectedItems;
2061
+
2062
+ let groupCriteria = dialogConfig.groupCriteria;
2063
+ let items = keys;
2064
+ let groupMap = {};
2065
+ if(groupCriteria) {
2066
+ for (let idx = 0; idx < items.length; idx++) {
2067
+ let formattedVal = items[idx];
2068
+ let item = {
2069
+ id: idx,
2070
+ label: formattedVal,
2071
+ title: formattedVal, // Backward compatibility
2072
+ nodes: [],
2073
+ checked: selectedItems[formattedVal] ? true : false,
2074
+ group: groupCriteria(formattedVal) || null // no group will be null map
2075
+ };
2076
+ if(!groupMap[item.group]) {
2077
+ groupMap[item.group] = [];
2078
+ }
2079
+ groupMap[item.group].push(item);
2080
+ }
2081
+ let groupOrdered = Object.keys(groupMap);
2082
+ let groupSortLogic = dialogConfig.groupSortLogic;
2083
+ if(groupSortLogic) {
2084
+ groupOrdered = groupOrdered.sort(groupSortLogic);
2085
+ }
2086
+ let sortedGroupItems = [];
2087
+ for (let i = 0; i < groupOrdered.length; i++) {
2088
+ let groupName = groupOrdered[i];
2089
+ let groupAry = groupMap[groupName];
2090
+ let validGroup = groupName != "null" ? true : false;
2091
+ if(validGroup) {
2092
+ sortedGroupItems.push({
2093
+ type: "header",
2094
+ label: groupName
2095
+ });
2096
+ }
2097
+ sortedGroupItems = sortedGroupItems.concat(groupAry);
2098
+ if(validGroup) {
2099
+ sortedGroupItems.push({
2100
+ type: "divider"
2101
+ });
2102
+ }
2103
+ }
2104
+ items = sortedGroupItems;
2105
+ } else {
2106
+ items = keys.map(function(formattedVal, idx) {
2107
+ return {
2108
+ id: idx,
2109
+ label: formattedVal,
2110
+ title: formattedVal, // Backward compatibility
2111
+ nodes: [],
2112
+ checked: selectedItems[formattedVal] ? true : false
2113
+ };
2114
+ });
2115
+ }
2116
+ return items;
2117
+ };
2037
2118
  /** @private
2038
2119
  * @param {number} colIndex
2039
2120
  * @return {Function}
@@ -2144,7 +2225,7 @@ RowFilteringPlugin.prototype._onDialogFilterChanged = function(e) {
2144
2225
  let selectedItems = {};
2145
2226
  let atLeastOne = false;
2146
2227
  for(let i = 0; i < selCount; ++i) {
2147
- let formattedVal = selAry[i].title; // title is defined by the multi-select element
2228
+ let formattedVal = selAry[i].label || selAry[i].title; // title is defined by the multi-select element
2148
2229
  // let selIdx = selAry[i].index; // index cannot be used due to filtering
2149
2230
  if(selAry[i].value === BLANKS) {
2150
2231
  if(!ctx) {
package/lib/versions.json CHANGED
@@ -17,14 +17,14 @@
17
17
  "tr-grid-conditional-coloring": "1.0.70",
18
18
  "tr-grid-content-wrap": "1.0.20",
19
19
  "tr-grid-contextmenu": "1.0.41",
20
- "tr-grid-filter-input": "0.9.40",
20
+ "tr-grid-filter-input": "0.9.41",
21
21
  "tr-grid-heat-map": "1.0.29",
22
22
  "tr-grid-in-cell-editing": "1.0.87",
23
23
  "tr-grid-pagination": "1.0.24",
24
24
  "tr-grid-percent-bar": "1.0.24",
25
25
  "tr-grid-range-bar": "2.0.8",
26
26
  "tr-grid-row-dragging": "1.0.35",
27
- "tr-grid-row-filtering": "1.0.78",
27
+ "tr-grid-row-filtering": "1.0.79",
28
28
  "tr-grid-row-grouping": "1.0.88",
29
29
  "tr-grid-row-selection": "1.0.30",
30
30
  "tr-grid-rowcoloring": "1.0.25",
@@ -32,6 +32,6 @@
32
32
  "tr-grid-titlewrap": "1.0.22",
33
33
  "@grid/formatters": "1.0.55",
34
34
  "@grid/column-selection-dialog": "4.0.57",
35
- "@grid/filter-dialog": "4.0.65",
35
+ "@grid/filter-dialog": "4.0.66",
36
36
  "@grid/column-format-dialog": "4.0.45"
37
37
  }
package/package.json CHANGED
@@ -56,6 +56,7 @@
56
56
  "./extensions": "./lib/index.js",
57
57
  "./window-exporter": "./lib/window-exporter.js",
58
58
  "./grid": "./lib/grid/lib/efx-grid.js",
59
+ "./formatters": "./lib/formatters/es6/index.js",
59
60
  "./formatters/*": "./lib/formatters/es6/*.js",
60
61
  "./utils": "./lib/utils/index.js",
61
62
  "./types": "./lib/types/index.d.ts"
@@ -67,5 +68,5 @@
67
68
  "publishConfig": {
68
69
  "access": "public"
69
70
  },
70
- "version": "6.0.110"
71
+ "version": "6.0.112"
71
72
  }