@revolist/revogrid 3.0.98 → 3.1.1
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/custom-element/index.js +519 -301
- package/dist/cjs/debounce-e9b040d9.js +575 -0
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/revo-grid.cjs.js +1 -1
- package/dist/cjs/revo-grid_11.cjs.entry.js +195 -645
- package/dist/cjs/revogr-filter-panel.cjs.entry.js +166 -33
- package/dist/collection/components/data/revogr-data-style.css +8 -0
- package/dist/collection/components/header/revogr-header-style.css +8 -0
- package/dist/collection/components/overlay/editors/text.js +1 -1
- package/dist/collection/components/overlay/revogr-edit-style.css +8 -0
- package/dist/collection/components/overlay/revogr-overlay-style.css +8 -0
- package/dist/collection/components/revo-grid/revo-grid-style.css +8 -0
- package/dist/collection/components/revo-grid/revo-grid.js +30 -3
- package/dist/collection/components/revo-grid/viewport.service.js +3 -0
- package/dist/collection/components/scroll/revogr-viewport-scroll-style.css +8 -0
- package/dist/collection/components/scrollable/revogr-scroll-style.css +8 -0
- package/dist/collection/components/selection-focus/revogr-focus-style.css +8 -0
- package/dist/collection/components/selection-temp-range/revogr-temp-range-style.css +8 -0
- package/dist/collection/plugins/filter/conditions/equal.js +5 -1
- package/dist/collection/plugins/filter/filter.button.js +10 -0
- package/dist/collection/plugins/filter/filter.plugin.js +103 -47
- package/dist/collection/plugins/filter/filter.pop.js +227 -38
- package/dist/collection/plugins/filter/filter.style.css +61 -1
- package/dist/collection/plugins/groupingColumn/grouping.col.plugin.js +1 -0
- package/dist/collection/services/selection.store.connector.js +4 -0
- package/dist/esm/debounce-8dadcda7.js +558 -0
- package/dist/esm/loader.js +1 -1
- package/dist/esm/revo-grid.js +1 -1
- package/dist/esm/revo-grid_11.entry.js +149 -599
- package/dist/esm/revogr-filter-panel.entry.js +165 -32
- package/dist/esm-es5/debounce-8dadcda7.js +1 -0
- package/dist/esm-es5/loader.js +1 -1
- package/dist/esm-es5/revo-grid.js +1 -1
- package/dist/esm-es5/revo-grid_11.entry.js +1 -1
- package/dist/esm-es5/revogr-filter-panel.entry.js +1 -1
- package/dist/revo-grid/debounce-d097578d.js +1 -0
- package/dist/revo-grid/debounce-f40a88f6.system.js +1 -0
- package/dist/revo-grid/revo-grid.esm.js +1 -1
- package/dist/revo-grid/revo-grid.system.js +1 -1
- package/dist/revo-grid/revo-grid_11.entry.js +1 -1
- package/dist/revo-grid/revo-grid_11.system.entry.js +1 -1
- package/dist/revo-grid/revogr-filter-panel.entry.js +1 -1
- package/dist/revo-grid/revogr-filter-panel.system.entry.js +1 -1
- package/dist/types/components/revo-grid/revo-grid.d.ts +5 -1
- package/dist/types/components/revo-grid/viewport.service.d.ts +1 -0
- package/dist/types/components.d.ts +12 -4
- package/dist/types/plugins/filter/filter.button.d.ts +4 -0
- package/dist/types/plugins/filter/filter.plugin.d.ts +17 -8
- package/dist/types/plugins/filter/filter.pop.d.ts +31 -7
- package/dist/types/services/selection.store.connector.d.ts +1 -0
- package/package.json +1 -1
- package/dist/cjs/filter.button-2396a488.js +0 -27
- package/dist/esm/filter.button-53ebca66.js +0 -23
- package/dist/esm-es5/filter.button-53ebca66.js +0 -1
- package/dist/revo-grid/filter.button-1509c206.js +0 -1
- package/dist/revo-grid/filter.button-4bd87101.system.js +0 -1
|
@@ -5,9 +5,11 @@ import { filterEntities, filterNames, filterTypes } from './filter.service';
|
|
|
5
5
|
export const FILTER_TRIMMED_TYPE = 'filter';
|
|
6
6
|
export default class FilterPlugin extends BasePlugin {
|
|
7
7
|
constructor(revogrid, uiid, config) {
|
|
8
|
+
var _a;
|
|
8
9
|
super(revogrid);
|
|
9
10
|
this.revogrid = revogrid;
|
|
10
11
|
this.filterCollection = {};
|
|
12
|
+
this.multiFilterItems = {};
|
|
11
13
|
this.possibleFilters = Object.assign({}, filterTypes);
|
|
12
14
|
this.possibleFilterNames = Object.assign({}, filterNames);
|
|
13
15
|
this.possibleFilterEntities = Object.assign({}, filterEntities);
|
|
@@ -15,21 +17,38 @@ export default class FilterPlugin extends BasePlugin {
|
|
|
15
17
|
this.initConfig(config);
|
|
16
18
|
}
|
|
17
19
|
const headerclick = (e) => this.headerclick(e);
|
|
18
|
-
const aftersourceset = () => {
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
const aftersourceset = async () => {
|
|
21
|
+
const filterCollectionProps = Object.keys(this.filterCollection);
|
|
22
|
+
if (filterCollectionProps.length > 0) {
|
|
23
|
+
// handle old way of filtering by reworking FilterCollection to new MultiFilterItem
|
|
24
|
+
filterCollectionProps.forEach((prop, index) => {
|
|
25
|
+
if (!this.multiFilterItems[prop]) {
|
|
26
|
+
this.multiFilterItems[prop] = [
|
|
27
|
+
{
|
|
28
|
+
id: index,
|
|
29
|
+
type: this.filterCollection[prop].type,
|
|
30
|
+
value: this.filterCollection[prop].value,
|
|
31
|
+
relation: 'and',
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
}
|
|
35
|
+
});
|
|
21
36
|
}
|
|
37
|
+
await this.runFiltering();
|
|
22
38
|
};
|
|
23
39
|
this.addEventListener('headerclick', headerclick);
|
|
24
40
|
this.addEventListener('aftersourceset', aftersourceset);
|
|
25
41
|
this.revogrid.registerVNode([
|
|
26
|
-
h("revogr-filter-panel", { uuid: `filter-${uiid}`, filterNames: this.possibleFilterNames, filterEntities: this.possibleFilterEntities, onFilterChange: e => this.onFilterChange(e.detail), ref: e => (this.pop = e) }),
|
|
42
|
+
h("revogr-filter-panel", { uuid: `filter-${uiid}`, filterItems: this.multiFilterItems, filterNames: this.possibleFilterNames, filterEntities: this.possibleFilterEntities, filterCaptions: (_a = config === null || config === void 0 ? void 0 : config.localization) === null || _a === void 0 ? void 0 : _a.captions, onFilterChange: e => this.onFilterChange(e.detail), ref: e => (this.pop = e) }),
|
|
27
43
|
]);
|
|
28
44
|
}
|
|
29
45
|
initConfig(config) {
|
|
30
46
|
if (config.collection) {
|
|
31
47
|
this.filterCollection = Object.assign({}, config.collection);
|
|
32
48
|
}
|
|
49
|
+
if (config.multiFilterItems) {
|
|
50
|
+
this.multiFilterItems = Object.assign({}, config.multiFilterItems);
|
|
51
|
+
}
|
|
33
52
|
if (config.customFilters) {
|
|
34
53
|
for (let cType in config.customFilters) {
|
|
35
54
|
const cFilter = config.customFilters[cType];
|
|
@@ -59,6 +78,15 @@ export default class FilterPlugin extends BasePlugin {
|
|
|
59
78
|
this.possibleFilters = filters;
|
|
60
79
|
}
|
|
61
80
|
}
|
|
81
|
+
if (config.localization) {
|
|
82
|
+
if (config.localization.filterNames) {
|
|
83
|
+
Object.entries(config.localization.filterNames).forEach(([k, v]) => {
|
|
84
|
+
if (this.possibleFilterNames[k] != void 0) {
|
|
85
|
+
this.possibleFilterNames[k] = v;
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
62
90
|
}
|
|
63
91
|
async headerclick(e) {
|
|
64
92
|
var _a;
|
|
@@ -104,43 +132,18 @@ export default class FilterPlugin extends BasePlugin {
|
|
|
104
132
|
return !!(typeof type === 'string' && this.possibleFilters[type]);
|
|
105
133
|
}
|
|
106
134
|
// called on internal component change
|
|
107
|
-
async onFilterChange(
|
|
108
|
-
this.
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Apply filters collection to extend existing one or override
|
|
112
|
-
* @method
|
|
113
|
-
* @param conditions - list of filters to apply
|
|
114
|
-
*/
|
|
115
|
-
async filterByProps(conditions, override = false) {
|
|
116
|
-
if (override) {
|
|
117
|
-
this.filterCollection = {};
|
|
118
|
-
}
|
|
119
|
-
for (const prop in conditions) {
|
|
120
|
-
const { type, value } = conditions[prop];
|
|
121
|
-
if (type === 'none') {
|
|
122
|
-
delete this.filterCollection[prop];
|
|
123
|
-
}
|
|
124
|
-
else {
|
|
125
|
-
const filter = this.possibleFilterEntities[type];
|
|
126
|
-
this.filterCollection[prop] = {
|
|
127
|
-
filter,
|
|
128
|
-
value,
|
|
129
|
-
type,
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
await this.runFiltering();
|
|
135
|
+
async onFilterChange(filterItems) {
|
|
136
|
+
this.multiFilterItems = filterItems;
|
|
137
|
+
this.runFiltering();
|
|
134
138
|
}
|
|
135
139
|
/**
|
|
136
140
|
* Triggers grid filtering
|
|
137
141
|
*/
|
|
138
|
-
async doFiltering(collection, items, columns) {
|
|
142
|
+
async doFiltering(collection, items, columns, filterItems) {
|
|
139
143
|
const columnsToUpdate = [];
|
|
140
|
-
// todo improvement: loop through collection of props
|
|
141
144
|
columns.forEach(rgCol => {
|
|
142
145
|
const column = Object.assign({}, rgCol);
|
|
143
|
-
const hasFilter =
|
|
146
|
+
const hasFilter = filterItems[column.prop];
|
|
144
147
|
if (column[FILTER_PROP] && !hasFilter) {
|
|
145
148
|
delete column[FILTER_PROP];
|
|
146
149
|
columnsToUpdate.push(column);
|
|
@@ -150,9 +153,9 @@ export default class FilterPlugin extends BasePlugin {
|
|
|
150
153
|
column[FILTER_PROP] = true;
|
|
151
154
|
}
|
|
152
155
|
});
|
|
153
|
-
const itemsToFilter = this.getRowFilter(items,
|
|
156
|
+
const itemsToFilter = this.getRowFilter(items, filterItems);
|
|
154
157
|
// check is filter event prevented
|
|
155
|
-
const { defaultPrevented, detail } = this.emit('beforefiltertrimmed', { collection, itemsToFilter, source: items });
|
|
158
|
+
const { defaultPrevented, detail } = this.emit('beforefiltertrimmed', { collection, itemsToFilter, source: items, filterItems });
|
|
156
159
|
if (defaultPrevented) {
|
|
157
160
|
return;
|
|
158
161
|
}
|
|
@@ -161,39 +164,92 @@ export default class FilterPlugin extends BasePlugin {
|
|
|
161
164
|
if (isAddedEvent.defaultPrevented) {
|
|
162
165
|
return;
|
|
163
166
|
}
|
|
167
|
+
// applies the hasFilter to the columns to show filter icon
|
|
164
168
|
await this.revogrid.updateColumns(columnsToUpdate);
|
|
165
169
|
this.emit('afterFilterApply');
|
|
166
170
|
}
|
|
167
171
|
async clearFiltering() {
|
|
168
|
-
this.
|
|
172
|
+
this.multiFilterItems = {};
|
|
169
173
|
await this.runFiltering();
|
|
170
174
|
}
|
|
171
175
|
async runFiltering() {
|
|
176
|
+
const collection = {};
|
|
177
|
+
// handle old filterCollection to return the first filter only (if any) from multiFilterItems
|
|
178
|
+
const filterProps = Object.keys(this.multiFilterItems);
|
|
179
|
+
for (const prop of filterProps) {
|
|
180
|
+
// check if we have any filter for a column
|
|
181
|
+
if (this.multiFilterItems[prop].length > 0) {
|
|
182
|
+
const firstFilterItem = this.multiFilterItems[prop][0];
|
|
183
|
+
collection[prop] = {
|
|
184
|
+
filter: filterEntities[firstFilterItem.type],
|
|
185
|
+
type: firstFilterItem.type,
|
|
186
|
+
value: firstFilterItem.value,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
this.filterCollection = collection;
|
|
172
191
|
const { source, columns } = await this.getData();
|
|
173
|
-
const { defaultPrevented, detail } = this.emit('beforefilterapply', { collection: this.filterCollection, source, columns });
|
|
192
|
+
const { defaultPrevented, detail } = this.emit('beforefilterapply', { collection: this.filterCollection, source, columns, filterItems: this.multiFilterItems });
|
|
174
193
|
if (defaultPrevented) {
|
|
175
194
|
return;
|
|
176
195
|
}
|
|
177
|
-
this.doFiltering(detail.collection, detail.source, detail.columns);
|
|
196
|
+
this.doFiltering(detail.collection, detail.source, detail.columns, detail.filterItems);
|
|
178
197
|
}
|
|
179
198
|
async getData() {
|
|
180
199
|
const source = await this.revogrid.getSource();
|
|
181
200
|
const columns = await this.revogrid.getColumns();
|
|
182
201
|
return {
|
|
183
202
|
source,
|
|
184
|
-
columns
|
|
203
|
+
columns,
|
|
185
204
|
};
|
|
186
205
|
}
|
|
187
|
-
getRowFilter(rows,
|
|
206
|
+
getRowFilter(rows, filterItems) {
|
|
207
|
+
const propKeys = Object.keys(filterItems);
|
|
188
208
|
const trimmed = {};
|
|
209
|
+
let propFilterSatisfiedCount = 0;
|
|
210
|
+
let lastFilterResults = [];
|
|
211
|
+
// each rows
|
|
189
212
|
rows.forEach((model, rowIndex) => {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
const
|
|
193
|
-
|
|
213
|
+
// working on all props
|
|
214
|
+
for (const prop of propKeys) {
|
|
215
|
+
const propFilters = filterItems[prop];
|
|
216
|
+
propFilterSatisfiedCount = 0;
|
|
217
|
+
lastFilterResults = [];
|
|
218
|
+
// testing each filter for a prop
|
|
219
|
+
for (const [filterIndex, filterData] of propFilters.entries()) {
|
|
220
|
+
// the filter LogicFunction based on the type
|
|
221
|
+
const filter = filterEntities[filterData.type];
|
|
222
|
+
// THE MAGIC OF FILTERING IS HERE
|
|
223
|
+
if (filterData.relation === 'or') {
|
|
224
|
+
lastFilterResults = [];
|
|
225
|
+
if (filter(model[prop], filterData.value)) {
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
propFilterSatisfiedCount++;
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
// 'and' relation will need to know the next filter
|
|
232
|
+
// so we save this current filter to include it in the next filter
|
|
233
|
+
lastFilterResults.push(!filter(model[prop], filterData.value));
|
|
234
|
+
// check first if we have a filter on the next index to pair it with this current filter
|
|
235
|
+
const nextFilterData = propFilters[filterIndex + 1];
|
|
236
|
+
// stop the sequence if there is no next filter or if the next filter is not an 'and' relation
|
|
237
|
+
if (!nextFilterData || nextFilterData.relation !== 'and') {
|
|
238
|
+
// let's just continue since for sure propFilterSatisfiedCount cannot be satisfied
|
|
239
|
+
if (lastFilterResults.indexOf(true) === -1) {
|
|
240
|
+
lastFilterResults = [];
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
// we need to add all of the lastFilterResults since we need to satisfy all
|
|
244
|
+
propFilterSatisfiedCount += lastFilterResults.length;
|
|
245
|
+
lastFilterResults = [];
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
} // end of propFilters forEach
|
|
249
|
+
// add to the list of removed/trimmed rows of filter condition is satisfied
|
|
250
|
+
if (propFilterSatisfiedCount === propFilters.length)
|
|
194
251
|
trimmed[rowIndex] = true;
|
|
195
|
-
|
|
196
|
-
}
|
|
252
|
+
} // end of for-of propKeys
|
|
197
253
|
});
|
|
198
254
|
return trimmed;
|
|
199
255
|
}
|
|
@@ -1,13 +1,30 @@
|
|
|
1
1
|
import { Component, h, Host, Listen, Prop, State, Event, Method } from '@stencil/core';
|
|
2
|
-
import { isFilterBtn } from './filter.button';
|
|
2
|
+
import { AndOrButton, isFilterBtn, TrashButton } from './filter.button';
|
|
3
3
|
import { RevoButton } from '../../components/button/button';
|
|
4
4
|
import '../../utils/closestPolifill';
|
|
5
|
+
import debounce from 'lodash/debounce';
|
|
5
6
|
const defaultType = 'none';
|
|
7
|
+
const FILTER_LIST_CLASS = 'multi-filter-list';
|
|
8
|
+
const FILTER_LIST_CLASS_ACTION = 'multi-filter-list-action';
|
|
6
9
|
export class FilterPanel {
|
|
7
10
|
constructor() {
|
|
11
|
+
this.filterCaptionsInternal = {
|
|
12
|
+
title: 'Filter by condition',
|
|
13
|
+
save: 'Save',
|
|
14
|
+
reset: 'Reset',
|
|
15
|
+
cancel: 'Close',
|
|
16
|
+
};
|
|
17
|
+
this.isFilterIdSet = false;
|
|
18
|
+
this.filterId = 0;
|
|
19
|
+
this.currentFilterId = -1;
|
|
20
|
+
this.currentFilterType = defaultType;
|
|
21
|
+
this.filterItems = {};
|
|
8
22
|
this.filterTypes = {};
|
|
9
23
|
this.filterNames = {};
|
|
10
24
|
this.filterEntities = {};
|
|
25
|
+
this.debouncedApplyFilter = debounce(() => {
|
|
26
|
+
this.filterChange.emit(this.filterItems);
|
|
27
|
+
}, 400);
|
|
11
28
|
}
|
|
12
29
|
onMouseDown(e) {
|
|
13
30
|
if (this.changes && !e.defaultPrevented) {
|
|
@@ -26,26 +43,63 @@ export class FilterPanel {
|
|
|
26
43
|
async getChanges() {
|
|
27
44
|
return this.changes;
|
|
28
45
|
}
|
|
29
|
-
|
|
46
|
+
componentWillRender() {
|
|
47
|
+
if (!this.isFilterIdSet) {
|
|
48
|
+
this.isFilterIdSet = true;
|
|
49
|
+
const filterItems = Object.keys(this.filterItems);
|
|
50
|
+
for (const prop of filterItems) {
|
|
51
|
+
// we set the proper filterId so there won't be any conflict when removing filters
|
|
52
|
+
this.filterId += this.filterItems[prop].length;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
renderSelectOptions(type, isDefaultTypeRemoved = false) {
|
|
57
|
+
var _a;
|
|
30
58
|
const options = [];
|
|
59
|
+
const prop = (_a = this.changes) === null || _a === void 0 ? void 0 : _a.prop;
|
|
60
|
+
if (!isDefaultTypeRemoved) {
|
|
61
|
+
options.push(h("option", { selected: this.currentFilterType === defaultType, value: defaultType }, prop && this.filterItems[prop] && this.filterItems[prop].length > 0 ? 'Add more condition...' : this.filterNames[defaultType]));
|
|
62
|
+
}
|
|
31
63
|
for (let gIndex in this.filterTypes) {
|
|
32
|
-
options.push(h("option", { value: defaultType }, this.filterNames[defaultType]));
|
|
33
64
|
options.push(...this.filterTypes[gIndex].map(k => (h("option", { value: k, selected: type === k }, this.filterNames[k]))));
|
|
34
65
|
options.push(h("option", { disabled: true }));
|
|
35
66
|
}
|
|
36
67
|
return options;
|
|
37
68
|
}
|
|
38
|
-
renderExtra(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
69
|
+
renderExtra(prop, index) {
|
|
70
|
+
const currentFilter = this.filterItems[prop];
|
|
71
|
+
if (!currentFilter)
|
|
72
|
+
return '';
|
|
73
|
+
if (this.filterEntities[currentFilter[index].type].extra !== 'input')
|
|
74
|
+
return '';
|
|
75
|
+
return (h("input", { id: `filter-input-${currentFilter[index].id}`, placeholder: "Enter value...", type: "text", value: currentFilter[index].value, onInput: this.onUserInput.bind(this, index, prop), onKeyDown: e => this.onKeyDown(e) }));
|
|
76
|
+
}
|
|
77
|
+
getFilterItemsList() {
|
|
78
|
+
var _a;
|
|
79
|
+
const prop = (_a = this.changes) === null || _a === void 0 ? void 0 : _a.prop;
|
|
80
|
+
if (!(prop || prop === 0))
|
|
81
|
+
return '';
|
|
82
|
+
const propFilters = this.filterItems[prop] || [];
|
|
83
|
+
return (h("div", { key: this.filterId },
|
|
84
|
+
propFilters.map((d, index) => {
|
|
85
|
+
let andOrButton;
|
|
86
|
+
// hide toggle button if there is only one filter and the last one
|
|
87
|
+
if (index !== this.filterItems[prop].length - 1) {
|
|
88
|
+
andOrButton = (h("div", { onClick: () => this.toggleFilterAndOr(d.id) },
|
|
89
|
+
h(AndOrButton, { isAnd: d.relation === 'and' })));
|
|
90
|
+
}
|
|
91
|
+
return (h("div", { key: d.id, class: FILTER_LIST_CLASS },
|
|
92
|
+
h("div", { class: { 'select-input': true } },
|
|
93
|
+
h("select", { class: "select-css select-filter", onChange: e => this.onFilterTypeChange(e, prop, index) }, this.renderSelectOptions(this.filterItems[prop][index].type, true)),
|
|
94
|
+
h("div", { class: FILTER_LIST_CLASS_ACTION }, andOrButton),
|
|
95
|
+
h("div", { onClick: () => this.onRemoveFilter(d.id) },
|
|
96
|
+
h(TrashButton, null))),
|
|
97
|
+
h("div", null, this.renderExtra(prop, index))));
|
|
98
|
+
}),
|
|
99
|
+
propFilters.length > 0 ? h("div", { class: "add-filter-divider" }) : ''));
|
|
46
100
|
}
|
|
47
101
|
render() {
|
|
48
|
-
if (!this.changes
|
|
102
|
+
if (!this.changes) {
|
|
49
103
|
return h(Host, { style: { display: 'none' } });
|
|
50
104
|
}
|
|
51
105
|
const style = {
|
|
@@ -53,29 +107,83 @@ export class FilterPanel {
|
|
|
53
107
|
left: `${this.changes.x}px`,
|
|
54
108
|
top: `${this.changes.y}px`,
|
|
55
109
|
};
|
|
110
|
+
const capts = Object.assign(this.filterCaptionsInternal, this.filterCaptions);
|
|
56
111
|
return (h(Host, { style: style },
|
|
57
|
-
h("label", null,
|
|
58
|
-
h("
|
|
59
|
-
h("div",
|
|
60
|
-
|
|
61
|
-
h(
|
|
112
|
+
h("label", null, capts.title),
|
|
113
|
+
h("div", { class: "filter-holder" }, this.getFilterItemsList()),
|
|
114
|
+
h("div", { class: "add-filter" },
|
|
115
|
+
h("select", { id: "add-filter", class: "select-css", onChange: e => this.onAddNewFilter(e) }, this.renderSelectOptions(this.currentFilterType))),
|
|
116
|
+
h("div", { class: "filter-actions" },
|
|
117
|
+
h(RevoButton, { class: { red: true }, onClick: () => this.onReset() }, capts.reset),
|
|
118
|
+
h(RevoButton, { class: { light: true }, onClick: () => this.onCancel() }, capts.cancel))));
|
|
62
119
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
120
|
+
onFilterTypeChange(e, prop, index) {
|
|
121
|
+
const el = e.target;
|
|
122
|
+
const type = el.value;
|
|
123
|
+
this.filterItems[prop][index].type = type;
|
|
124
|
+
// this re-renders the input to know if we need extra input
|
|
125
|
+
this.filterId++;
|
|
126
|
+
// adding setTimeout will wait for the next tick DOM update then focus on input
|
|
127
|
+
setTimeout(() => {
|
|
128
|
+
const input = document.getElementById('filter-input-' + this.filterItems[prop][index].id);
|
|
129
|
+
if (input)
|
|
130
|
+
input.focus();
|
|
131
|
+
}, 0);
|
|
132
|
+
this.debouncedApplyFilter();
|
|
133
|
+
}
|
|
134
|
+
onAddNewFilter(e) {
|
|
67
135
|
const el = e.target;
|
|
68
136
|
const type = el.value;
|
|
69
|
-
this.
|
|
137
|
+
this.currentFilterType = type;
|
|
138
|
+
this.addNewFilterToProp();
|
|
139
|
+
// reset value after adding new filter
|
|
140
|
+
const select = document.getElementById('add-filter');
|
|
141
|
+
if (select) {
|
|
142
|
+
select.value = defaultType;
|
|
143
|
+
this.currentFilterType = defaultType;
|
|
144
|
+
}
|
|
145
|
+
this.debouncedApplyFilter();
|
|
70
146
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
147
|
+
addNewFilterToProp() {
|
|
148
|
+
var _a;
|
|
149
|
+
const prop = (_a = this.changes) === null || _a === void 0 ? void 0 : _a.prop;
|
|
150
|
+
if (!(prop || prop === 0))
|
|
151
|
+
return;
|
|
152
|
+
if (!this.filterItems[prop]) {
|
|
153
|
+
this.filterItems[prop] = [];
|
|
154
|
+
}
|
|
155
|
+
if (this.currentFilterType === 'none')
|
|
156
|
+
return;
|
|
157
|
+
this.filterId++;
|
|
158
|
+
this.currentFilterId = this.filterId;
|
|
159
|
+
this.filterItems[prop].push({
|
|
160
|
+
id: this.currentFilterId,
|
|
161
|
+
type: this.currentFilterType,
|
|
162
|
+
value: '',
|
|
163
|
+
relation: 'and',
|
|
164
|
+
});
|
|
165
|
+
// adding setTimeout will wait for the next tick DOM update then focus on input
|
|
166
|
+
setTimeout(() => {
|
|
167
|
+
const input = document.getElementById('filter-input-' + this.currentFilterId);
|
|
168
|
+
if (input)
|
|
169
|
+
input.focus();
|
|
170
|
+
}, 0);
|
|
171
|
+
}
|
|
172
|
+
onUserInput(index, prop, event) {
|
|
173
|
+
// update the value of the filter item
|
|
174
|
+
this.filterItems[prop][index].value = event.target.value;
|
|
175
|
+
this.debouncedApplyFilter();
|
|
75
176
|
}
|
|
76
177
|
onKeyDown(e) {
|
|
77
178
|
if (e.key.toLowerCase() === 'enter') {
|
|
78
|
-
|
|
179
|
+
const select = document.getElementById('add-filter');
|
|
180
|
+
if (select) {
|
|
181
|
+
select.value = defaultType;
|
|
182
|
+
this.currentFilterType = defaultType;
|
|
183
|
+
this.addNewFilterToProp();
|
|
184
|
+
select.focus();
|
|
185
|
+
}
|
|
186
|
+
return;
|
|
79
187
|
}
|
|
80
188
|
// keep event local, don't escalate farther to dom
|
|
81
189
|
e.stopPropagation();
|
|
@@ -83,19 +191,56 @@ export class FilterPanel {
|
|
|
83
191
|
onCancel() {
|
|
84
192
|
this.changes = undefined;
|
|
85
193
|
}
|
|
86
|
-
|
|
87
|
-
|
|
194
|
+
onReset() {
|
|
195
|
+
this.assertChanges();
|
|
196
|
+
delete this.filterItems[this.changes.prop];
|
|
197
|
+
// this updates the DOM which is used by getFilterItemsList() key
|
|
198
|
+
this.filterId++;
|
|
199
|
+
this.filterChange.emit(this.filterItems);
|
|
200
|
+
}
|
|
201
|
+
onRemoveFilter(id) {
|
|
202
|
+
this.assertChanges();
|
|
203
|
+
// this is for reactivity issues for getFilterItemsList()
|
|
204
|
+
this.filterId++;
|
|
205
|
+
const prop = this.changes.prop;
|
|
206
|
+
const items = this.filterItems[prop];
|
|
207
|
+
if (!items)
|
|
208
|
+
return;
|
|
209
|
+
const index = items.findIndex(d => d.id === id);
|
|
210
|
+
if (index === -1)
|
|
211
|
+
return;
|
|
212
|
+
items.splice(index, 1);
|
|
213
|
+
// let's remove the prop if no more filters so the filter icon will be removed
|
|
214
|
+
if (items.length === 0)
|
|
215
|
+
delete this.filterItems[prop];
|
|
216
|
+
this.debouncedApplyFilter();
|
|
217
|
+
}
|
|
218
|
+
toggleFilterAndOr(id) {
|
|
219
|
+
this.assertChanges();
|
|
220
|
+
// this is for reactivity issues for getFilterItemsList()
|
|
221
|
+
this.filterId++;
|
|
222
|
+
const prop = this.changes.prop;
|
|
223
|
+
const items = this.filterItems[prop];
|
|
224
|
+
if (!items)
|
|
225
|
+
return;
|
|
226
|
+
const index = items.findIndex(d => d.id === id);
|
|
227
|
+
if (index === -1)
|
|
228
|
+
return;
|
|
229
|
+
items[index].relation = items[index].relation === 'and' ? 'or' : 'and';
|
|
230
|
+
this.debouncedApplyFilter();
|
|
231
|
+
}
|
|
232
|
+
assertChanges() {
|
|
88
233
|
if (!this.changes) {
|
|
89
234
|
throw new Error('Changes required per edit');
|
|
90
235
|
}
|
|
91
|
-
this.filterChange.emit({
|
|
92
|
-
prop: this.changes.prop,
|
|
93
|
-
type: this.changes.type,
|
|
94
|
-
value: (_b = (_a = this.extraElement) === null || _a === void 0 ? void 0 : _a.value) === null || _b === void 0 ? void 0 : _b.trim(),
|
|
95
|
-
});
|
|
96
|
-
this.changes = undefined;
|
|
97
236
|
}
|
|
98
237
|
isOutside(e) {
|
|
238
|
+
const select = document.getElementById('add-filter');
|
|
239
|
+
if (select)
|
|
240
|
+
select.value = defaultType;
|
|
241
|
+
this.currentFilterType = defaultType;
|
|
242
|
+
this.changes.type = defaultType;
|
|
243
|
+
this.currentFilterId = -1;
|
|
99
244
|
if (e.classList.contains(`[uuid="${this.uuid}"]`)) {
|
|
100
245
|
return false;
|
|
101
246
|
}
|
|
@@ -126,6 +271,26 @@ export class FilterPanel {
|
|
|
126
271
|
"attribute": "uuid",
|
|
127
272
|
"reflect": true
|
|
128
273
|
},
|
|
274
|
+
"filterItems": {
|
|
275
|
+
"type": "unknown",
|
|
276
|
+
"mutable": false,
|
|
277
|
+
"complexType": {
|
|
278
|
+
"original": "MultiFilterItem",
|
|
279
|
+
"resolved": "{ [prop: string]: FilterData[]; }",
|
|
280
|
+
"references": {
|
|
281
|
+
"MultiFilterItem": {
|
|
282
|
+
"location": "local"
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
"required": false,
|
|
287
|
+
"optional": false,
|
|
288
|
+
"docs": {
|
|
289
|
+
"tags": [],
|
|
290
|
+
"text": ""
|
|
291
|
+
},
|
|
292
|
+
"defaultValue": "{}"
|
|
293
|
+
},
|
|
129
294
|
"filterTypes": {
|
|
130
295
|
"type": "unknown",
|
|
131
296
|
"mutable": false,
|
|
@@ -189,9 +354,33 @@ export class FilterPanel {
|
|
|
189
354
|
"text": ""
|
|
190
355
|
},
|
|
191
356
|
"defaultValue": "{}"
|
|
357
|
+
},
|
|
358
|
+
"filterCaptions": {
|
|
359
|
+
"type": "unknown",
|
|
360
|
+
"mutable": false,
|
|
361
|
+
"complexType": {
|
|
362
|
+
"original": "FilterCaptions | undefined",
|
|
363
|
+
"resolved": "{ title: string; save: string; reset: string; cancel: string; }",
|
|
364
|
+
"references": {
|
|
365
|
+
"FilterCaptions": {
|
|
366
|
+
"location": "import",
|
|
367
|
+
"path": "./filter.plugin"
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
},
|
|
371
|
+
"required": false,
|
|
372
|
+
"optional": false,
|
|
373
|
+
"docs": {
|
|
374
|
+
"tags": [],
|
|
375
|
+
"text": ""
|
|
376
|
+
}
|
|
192
377
|
}
|
|
193
378
|
}; }
|
|
194
379
|
static get states() { return {
|
|
380
|
+
"isFilterIdSet": {},
|
|
381
|
+
"filterId": {},
|
|
382
|
+
"currentFilterId": {},
|
|
383
|
+
"currentFilterType": {},
|
|
195
384
|
"changes": {}
|
|
196
385
|
}; }
|
|
197
386
|
static get events() { return [{
|
|
@@ -205,10 +394,10 @@ export class FilterPanel {
|
|
|
205
394
|
"text": ""
|
|
206
395
|
},
|
|
207
396
|
"complexType": {
|
|
208
|
-
"original": "
|
|
209
|
-
"resolved": "{ prop
|
|
397
|
+
"original": "MultiFilterItem",
|
|
398
|
+
"resolved": "{ [prop: string]: FilterData[]; }",
|
|
210
399
|
"references": {
|
|
211
|
-
"
|
|
400
|
+
"MultiFilterItem": {
|
|
212
401
|
"location": "local"
|
|
213
402
|
}
|
|
214
403
|
}
|
|
@@ -52,6 +52,14 @@
|
|
|
52
52
|
background-color: #2ee072;
|
|
53
53
|
border: 1px solid #20d565;
|
|
54
54
|
}
|
|
55
|
+
.revo-button.red {
|
|
56
|
+
background-color: #E0662E;
|
|
57
|
+
border: 1px solid #d55920;
|
|
58
|
+
}
|
|
59
|
+
.revo-button:disabled, .revo-button[disabled] {
|
|
60
|
+
cursor: not-allowed !important;
|
|
61
|
+
filter: opacity(0.35) !important;
|
|
62
|
+
}
|
|
55
63
|
.revo-button.light {
|
|
56
64
|
border: 2px solid #cedefa;
|
|
57
65
|
line-height: 32px;
|
|
@@ -73,9 +81,13 @@ revogr-filter-panel {
|
|
|
73
81
|
box-shadow: 0 5px 18px -2px rgba(0, 0, 0, 0.2);
|
|
74
82
|
padding: 10px;
|
|
75
83
|
border-radius: 4px;
|
|
76
|
-
min-width:
|
|
84
|
+
min-width: 220px;
|
|
77
85
|
text-align: left;
|
|
78
86
|
}
|
|
87
|
+
revogr-filter-panel .filter-holder > div {
|
|
88
|
+
display: flex;
|
|
89
|
+
flex-direction: column;
|
|
90
|
+
}
|
|
79
91
|
revogr-filter-panel label {
|
|
80
92
|
color: gray;
|
|
81
93
|
font-size: 13px;
|
|
@@ -94,11 +106,16 @@ revogr-filter-panel input[type=text] {
|
|
|
94
106
|
border-radius: 5px;
|
|
95
107
|
padding: 0 10px;
|
|
96
108
|
box-sizing: border-box;
|
|
109
|
+
width: 100%;
|
|
97
110
|
}
|
|
98
111
|
revogr-filter-panel button {
|
|
99
112
|
margin-top: 10px;
|
|
100
113
|
margin-right: 5px;
|
|
101
114
|
}
|
|
115
|
+
revogr-filter-panel .filter-actions {
|
|
116
|
+
text-align: right;
|
|
117
|
+
margin-right: -5px;
|
|
118
|
+
}
|
|
102
119
|
|
|
103
120
|
.rgHeaderCell:hover .rv-filter {
|
|
104
121
|
transition: opacity 267ms cubic-bezier(0.4, 0, 0.2, 1) 0ms, transform 178ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
|
|
@@ -176,4 +193,47 @@ revogr-filter-panel button {
|
|
|
176
193
|
}
|
|
177
194
|
.select-css:disabled:hover, .select-css[aria-disabled=true] {
|
|
178
195
|
border-color: #f1f1f1;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.multi-filter-list {
|
|
199
|
+
margin-top: 5px;
|
|
200
|
+
margin-bottom: 5px;
|
|
201
|
+
}
|
|
202
|
+
.multi-filter-list div {
|
|
203
|
+
white-space: nowrap;
|
|
204
|
+
}
|
|
205
|
+
.multi-filter-list .multi-filter-list-action {
|
|
206
|
+
display: flex;
|
|
207
|
+
justify-content: space-between;
|
|
208
|
+
align-items: center;
|
|
209
|
+
}
|
|
210
|
+
.multi-filter-list .and-or-button {
|
|
211
|
+
margin: 0 0 0 10px;
|
|
212
|
+
min-width: 58px;
|
|
213
|
+
cursor: pointer;
|
|
214
|
+
}
|
|
215
|
+
.multi-filter-list .trash-button {
|
|
216
|
+
margin: 0 0 -2px 6px;
|
|
217
|
+
cursor: pointer;
|
|
218
|
+
width: 22px;
|
|
219
|
+
height: 22px;
|
|
220
|
+
color: gray;
|
|
221
|
+
font-size: 18px;
|
|
222
|
+
}
|
|
223
|
+
.multi-filter-list .trash-button .trash-img {
|
|
224
|
+
width: 1em;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.add-filter-divider {
|
|
228
|
+
display: block;
|
|
229
|
+
margin: 0 -10px 10px -10px;
|
|
230
|
+
border-bottom: 1px solid #d9d9d9;
|
|
231
|
+
height: 10px;
|
|
232
|
+
box-shadow: 0 4px 5px rgba(0, 0, 0, 0.05);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.select-input {
|
|
236
|
+
display: flex;
|
|
237
|
+
justify-content: space-between;
|
|
238
|
+
align-items: center;
|
|
179
239
|
}
|
|
@@ -27,6 +27,7 @@ export default class GroupingColumnPlugin extends BasePlugin {
|
|
|
27
27
|
res.columnGrouping[key].push(...collectionItem);
|
|
28
28
|
}
|
|
29
29
|
res.maxLevel = Math.max(res.maxLevel, collection.maxLevel);
|
|
30
|
+
res.sort = Object.assign(Object.assign({}, res.sort), collection.sort);
|
|
30
31
|
return res;
|
|
31
32
|
}
|
|
32
33
|
static isColGrouping(colData) {
|