@revolist/revogrid 4.25.2 → 4.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/{column.drag.plugin-E_K6cIAy.js → column.drag.plugin-CK5HADlW.js} +111 -11
- package/dist/cjs/index.cjs.js +4 -1
- package/dist/cjs/revo-grid.cjs.entry.js +1 -1
- package/dist/collection/plugins/filter/conditions/set.js +13 -2
- package/dist/collection/plugins/filter/filter.blank.js +68 -0
- package/dist/collection/plugins/filter/filter.indexed.js +4 -2
- package/dist/collection/plugins/filter/filter.plugin.js +29 -8
- package/dist/esm/{column.drag.plugin-DKgC_0I1.js → column.drag.plugin-DDBKeTGI.js} +110 -13
- package/dist/esm/index.js +2 -2
- package/dist/esm/revo-grid.entry.js +1 -1
- package/dist/revo-grid/{column.drag.plugin-DKgC_0I1.js → column.drag.plugin-DDBKeTGI.js} +110 -13
- package/dist/revo-grid/index.esm.js +2 -2
- package/dist/revo-grid/revo-grid.entry.js +1 -1
- package/dist/types/plugins/filter/conditions/set.d.ts +1 -1
- package/dist/types/plugins/filter/filter.blank.d.ts +6 -0
- package/dist/types/plugins/filter/filter.plugin.d.ts +1 -0
- package/dist/types/plugins/filter/filter.types.d.ts +32 -2
- package/dist/types/types/interfaces.d.ts +3 -0
- package/hydrate/index.js +108 -11
- package/hydrate/index.mjs +108 -11
- package/package.json +1 -1
- package/readme.md +34 -0
- package/standalone/index.js +1 -1
- package/standalone/revo-grid.js +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* Built by Revolist OU ❤️
|
|
3
3
|
*/
|
|
4
|
-
import { J as reduce, g as getRange, K as baseEach, C as getColumnType, c as columnTypes, L as toInteger, u as isGrouping, t as getGroupingName, r as rowTypes,
|
|
4
|
+
import { J as reduce, g as getRange, K as baseEach, C as getColumnType, c as columnTypes, L as toInteger, u as isGrouping, t as getGroupingName, r as rowTypes, A as getCellRaw, j as GROUP_COLUMN_PROP, I as getColumnByProp, h as GROUP_EXPANDED, x as getParsedGroup, y as isSameGroup, G as GROUP_DEPTH, e as PSEUDO_GROUP_ITEM_VALUE, d as PSEUDO_GROUP_ITEM_ID, o as GROUPING_ROW_TYPE, p as getSource, f as PSEUDO_GROUP_COLUMN, s as gatherGrouping, m as GROUP_EXPAND_EVENT, v as isGroupingColumn, q as getExpanded, E as isColGrouping } from './column.service-Cfw3L0PN.js';
|
|
5
5
|
import { K as createStore, l as setStore, i as calculateDimensionData, L as identity, N as isArray, b as getSourceItem, g as getPhysical, e as setItems, j as getItemByPosition } from './dimension.helpers-fl4HSSK9.js';
|
|
6
6
|
import { j as calculateRowHeaderSize } from './viewport.store-BSXmsiZc.js';
|
|
7
7
|
import { g as getScrollbarSize, t as timeout } from './index-D24ku9ej.js';
|
|
@@ -1223,8 +1223,84 @@ const lsEq = function (value, extra) {
|
|
|
1223
1223
|
};
|
|
1224
1224
|
lsEq.extra = 'input';
|
|
1225
1225
|
|
|
1226
|
-
const
|
|
1227
|
-
|
|
1226
|
+
const DEFAULT_BLANK_SEMANTICS = Object.freeze({
|
|
1227
|
+
null: true,
|
|
1228
|
+
undefined: true,
|
|
1229
|
+
emptyString: true,
|
|
1230
|
+
whitespaceOnlyString: false,
|
|
1231
|
+
emptyArray: false,
|
|
1232
|
+
missingProperty: true,
|
|
1233
|
+
});
|
|
1234
|
+
const BOOLEAN_FIELDS = [
|
|
1235
|
+
'null',
|
|
1236
|
+
'undefined',
|
|
1237
|
+
'emptyString',
|
|
1238
|
+
'whitespaceOnlyString',
|
|
1239
|
+
'emptyArray',
|
|
1240
|
+
'missingProperty',
|
|
1241
|
+
];
|
|
1242
|
+
/** Merge a grid policy and a partial column policy over Core defaults. */
|
|
1243
|
+
function resolveBlankSemantics(gridPolicy, columnPolicy) {
|
|
1244
|
+
const resolved = Object.assign({}, DEFAULT_BLANK_SEMANTICS);
|
|
1245
|
+
for (const policy of [gridPolicy, columnPolicy]) {
|
|
1246
|
+
if (!policy) {
|
|
1247
|
+
continue;
|
|
1248
|
+
}
|
|
1249
|
+
for (const field of BOOLEAN_FIELDS) {
|
|
1250
|
+
if (policy[field] !== undefined) {
|
|
1251
|
+
resolved[field] = policy[field];
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
if (policy.isBlank !== undefined) {
|
|
1255
|
+
resolved.isBlank = policy.isBlank;
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
return resolved;
|
|
1259
|
+
}
|
|
1260
|
+
/** Evaluate blankness from the unparsed source value and property presence. */
|
|
1261
|
+
function isBlankValue(value, context) {
|
|
1262
|
+
const policy = context.blankSemantics;
|
|
1263
|
+
let fallbackResult;
|
|
1264
|
+
if (!context.hasOwnProperty) {
|
|
1265
|
+
fallbackResult = policy.missingProperty === true;
|
|
1266
|
+
}
|
|
1267
|
+
else if (value === null) {
|
|
1268
|
+
fallbackResult = policy.null === true;
|
|
1269
|
+
}
|
|
1270
|
+
else if (value === undefined) {
|
|
1271
|
+
fallbackResult = policy.undefined === true;
|
|
1272
|
+
}
|
|
1273
|
+
else if (value === '') {
|
|
1274
|
+
fallbackResult = policy.emptyString === true;
|
|
1275
|
+
}
|
|
1276
|
+
else if (typeof value === 'string' &&
|
|
1277
|
+
value.length > 0 &&
|
|
1278
|
+
value.trim() === '') {
|
|
1279
|
+
fallbackResult = policy.whitespaceOnlyString === true;
|
|
1280
|
+
}
|
|
1281
|
+
else if (Array.isArray(value) && value.length === 0) {
|
|
1282
|
+
fallbackResult = policy.emptyArray === true;
|
|
1283
|
+
}
|
|
1284
|
+
else {
|
|
1285
|
+
fallbackResult = false;
|
|
1286
|
+
}
|
|
1287
|
+
return policy.isBlank
|
|
1288
|
+
? policy.isBlank(value, context, fallbackResult)
|
|
1289
|
+
: fallbackResult;
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
function blankContext(value, context) {
|
|
1293
|
+
return context !== null && context !== void 0 ? context : {
|
|
1294
|
+
model: {},
|
|
1295
|
+
property: '',
|
|
1296
|
+
sourceValue: value,
|
|
1297
|
+
parsedValue: value,
|
|
1298
|
+
hasOwnProperty: true,
|
|
1299
|
+
blankSemantics: DEFAULT_BLANK_SEMANTICS,
|
|
1300
|
+
};
|
|
1301
|
+
}
|
|
1302
|
+
const notSet = (value, _extra, context) => isBlankValue(context ? context.sourceValue : value, blankContext(value, context));
|
|
1303
|
+
const set = (value, _extra, context) => !notSet(value, _extra, context);
|
|
1228
1304
|
|
|
1229
1305
|
const beginsWith = (value, extra) => {
|
|
1230
1306
|
if (!value) {
|
|
@@ -1284,11 +1360,13 @@ const filterCoreFunctionsIndexedByType = {
|
|
|
1284
1360
|
const filterTypes = {
|
|
1285
1361
|
string: ['notEmpty', 'empty', 'eq', 'notEq', 'begins', 'contains', 'notContains'],
|
|
1286
1362
|
number: ['notEmpty', 'empty', 'eqN', 'neqN', 'gt', 'gte', 'lt', 'lte'],
|
|
1363
|
+
boolean: ['notEmpty', 'empty'],
|
|
1364
|
+
array: ['notEmpty', 'empty'],
|
|
1287
1365
|
};
|
|
1288
1366
|
const filterNames = {
|
|
1289
1367
|
none: 'None',
|
|
1290
|
-
empty: '
|
|
1291
|
-
notEmpty: '
|
|
1368
|
+
empty: 'Is blank',
|
|
1369
|
+
notEmpty: 'Is not blank',
|
|
1292
1370
|
eq: 'Equal',
|
|
1293
1371
|
notEq: 'Not equal',
|
|
1294
1372
|
begins: 'Begins with',
|
|
@@ -1411,6 +1489,7 @@ class FilterPlugin extends BasePlugin {
|
|
|
1411
1489
|
}
|
|
1412
1490
|
initConfig(config) {
|
|
1413
1491
|
var _a;
|
|
1492
|
+
this.config = config;
|
|
1414
1493
|
this.allowDuplicateOperators = (_a = config.allowDuplicateOperators) !== null && _a !== void 0 ? _a : true;
|
|
1415
1494
|
if (this.pop) {
|
|
1416
1495
|
this.pop.allowDuplicateOperators = this.allowDuplicateOperators;
|
|
@@ -1627,38 +1706,56 @@ class FilterPlugin extends BasePlugin {
|
|
|
1627
1706
|
* Get trimmed rows based on filter
|
|
1628
1707
|
*/
|
|
1629
1708
|
getRowFilter(rows, filterItems, columnByProp) {
|
|
1709
|
+
var _a, _b;
|
|
1630
1710
|
const propKeys = Object.keys(filterItems);
|
|
1711
|
+
const blankSemanticsByProp = {};
|
|
1712
|
+
for (const prop of propKeys) {
|
|
1713
|
+
blankSemanticsByProp[prop] = resolveBlankSemantics((_a = this.config) === null || _a === void 0 ? void 0 : _a.blankSemantics, (_b = columnByProp[prop]) === null || _b === void 0 ? void 0 : _b.blankSemantics);
|
|
1714
|
+
}
|
|
1631
1715
|
const trimmed = {};
|
|
1632
1716
|
// each rows
|
|
1633
1717
|
for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
|
|
1634
1718
|
// check filter by column properties
|
|
1635
1719
|
for (const prop of propKeys) {
|
|
1636
1720
|
// add to the list of removed/trimmed rows of filter condition is satisfied
|
|
1637
|
-
if (this.shouldTrimRow(filterItems[prop], prop, columnByProp[prop], rows[rowIndex])) {
|
|
1721
|
+
if (this.shouldTrimRow(filterItems[prop], prop, blankSemanticsByProp[prop], columnByProp[prop], rows[rowIndex])) {
|
|
1638
1722
|
trimmed[rowIndex] = true;
|
|
1639
1723
|
}
|
|
1640
1724
|
} // end of for-of propKeys
|
|
1641
1725
|
}
|
|
1642
1726
|
return trimmed;
|
|
1643
1727
|
}
|
|
1644
|
-
shouldTrimRow(propFilters, prop, column, model = {}) {
|
|
1728
|
+
shouldTrimRow(propFilters, prop, blankSemantics, column, model = {}) {
|
|
1645
1729
|
// reset the count of satisfied filters
|
|
1646
1730
|
let propFilterSatisfiedCount = 0;
|
|
1647
1731
|
// reset the array of last filter results
|
|
1648
1732
|
let lastFilterResults = [];
|
|
1733
|
+
// THE MAGIC OF FILTERING IS HERE
|
|
1734
|
+
// If there is no column but user wants to filter by a property
|
|
1735
|
+
const hasOwnProperty = Object.hasOwn(model, prop);
|
|
1736
|
+
const sourceValue = model[prop];
|
|
1737
|
+
const parsedValue = (column === null || column === void 0 ? void 0 : column.cellParser)
|
|
1738
|
+
? column.cellParser(model, column)
|
|
1739
|
+
: sourceValue;
|
|
1740
|
+
const context = {
|
|
1741
|
+
model,
|
|
1742
|
+
column,
|
|
1743
|
+
property: prop,
|
|
1744
|
+
sourceValue,
|
|
1745
|
+
parsedValue,
|
|
1746
|
+
hasOwnProperty,
|
|
1747
|
+
blankSemantics,
|
|
1748
|
+
};
|
|
1649
1749
|
// testing each filter for a prop
|
|
1650
1750
|
for (const [filterIndex, filterData] of propFilters.entries()) {
|
|
1651
1751
|
// the filter LogicFunction based on the type
|
|
1652
1752
|
const filterFunc = this.filterFunctionsIndexedByType[filterData.type];
|
|
1653
|
-
// THE MAGIC OF FILTERING IS HERE
|
|
1654
|
-
// If there is no column but user wants to filter by a property
|
|
1655
|
-
const value = column ? getCellDataParsed(model, column) : model[prop];
|
|
1656
1753
|
// OR relation
|
|
1657
1754
|
if (filterData.relation === 'or') {
|
|
1658
1755
|
// reset the array of last filter results
|
|
1659
1756
|
lastFilterResults = [];
|
|
1660
1757
|
// if the filter is satisfied, continue to the next filter
|
|
1661
|
-
if (filterFunc(
|
|
1758
|
+
if (filterFunc(parsedValue, filterData.value, context)) {
|
|
1662
1759
|
continue;
|
|
1663
1760
|
}
|
|
1664
1761
|
// if the filter is not satisfied, count it
|
|
@@ -1668,7 +1765,7 @@ class FilterPlugin extends BasePlugin {
|
|
|
1668
1765
|
else {
|
|
1669
1766
|
// 'and' relation will need to know the next filter
|
|
1670
1767
|
// so we save this current filter to include it in the next filter
|
|
1671
|
-
lastFilterResults.push(!filterFunc(
|
|
1768
|
+
lastFilterResults.push(!filterFunc(parsedValue, filterData.value, context));
|
|
1672
1769
|
if (isFinalAndFilter(filterIndex, propFilters)) {
|
|
1673
1770
|
// let's just continue since for sure propFilterSatisfiedCount cannot be satisfied
|
|
1674
1771
|
if (allAndConditionsSatisfied(lastFilterResults)) {
|
|
@@ -2951,4 +3048,4 @@ function getColumnDragPosition(targetItem, startItem, renderOffset, scrollOffset
|
|
|
2951
3048
|
return insertionEdge - renderOffset + scrollOffset;
|
|
2952
3049
|
}
|
|
2953
3050
|
|
|
2954
|
-
export { AutoSizeColumnPlugin as A, BasePlugin as B, ColumnAutoSizeMode as C, DimensionStore as D, ExportFilePlugin as E, FILTER_TRIMMED_TYPE as F, GroupingRowPlugin as G,
|
|
3051
|
+
export { AutoSizeColumnPlugin as A, BasePlugin as B, ColumnAutoSizeMode as C, DimensionStore as D, ExportFilePlugin as E, FILTER_TRIMMED_TYPE as F, GroupingRowPlugin as G, sortIndexByItems as H, defaultCellCompare as I, descCellCompare as J, getNextOrder as K, getComparer as L, SelectionStore as S, StretchColumn as a, ExportCsv as b, FILTER_CONFIG_CHANGED_EVENT as c, defineTheme as d, FILTE_PANEL as e, FilterPlugin as f, filterCoreFunctionsIndexedByType as g, filterTypes as h, isStretchPlugin as i, filterNames as j, DEFAULT_BLANK_SEMANTICS as k, isBlankValue as l, doCollapse as m, doExpand as n, COLUMN_DRAG_MOVE_EVENT as o, COLUMN_DRAG_END_EVENT as p, BEFORE_COLUMN_DRAG_END_EVENT as q, resolveBlankSemantics as r, COLUMN_DRAG_START_EVENT as s, themeTokenCssVariables as t, ColumnMovePlugin as u, getLeftRelative as v, getColumnDragPosition as w, SortingPlugin as x, hasActiveSorting as y, getSortingIndex as z };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* Built by Revolist OU ❤️
|
|
3
3
|
*/
|
|
4
|
-
import { d as defineTheme, B as BasePlugin } from './column.drag.plugin-
|
|
5
|
-
export { A as AutoSizeColumnPlugin,
|
|
4
|
+
import { d as defineTheme, B as BasePlugin } from './column.drag.plugin-DDBKeTGI.js';
|
|
5
|
+
export { A as AutoSizeColumnPlugin, q as BEFORE_COLUMN_DRAG_END_EVENT, p as COLUMN_DRAG_END_EVENT, o as COLUMN_DRAG_MOVE_EVENT, s as COLUMN_DRAG_START_EVENT, C as ColumnAutoSizeMode, u as ColumnMovePlugin, k as DEFAULT_BLANK_SEMANTICS, D as DimensionStore, b as ExportCsv, E as ExportFilePlugin, c as FILTER_CONFIG_CHANGED_EVENT, F as FILTER_TRIMMED_TYPE, e as FILTE_PANEL, f as FilterPlugin, G as GroupingRowPlugin, S as SelectionStore, x as SortingPlugin, a as StretchColumn, I as defaultCellCompare, J as descCellCompare, m as doCollapse, n as doExpand, g as filterCoreFunctionsIndexedByType, j as filterNames, h as filterTypes, w as getColumnDragPosition, L as getComparer, v as getLeftRelative, K as getNextOrder, z as getSortingIndex, y as hasActiveSorting, l as isBlankValue, i as isStretchPlugin, r as resolveBlankSemantics, H as sortIndexByItems, t as themeTokenCssVariables } from './column.drag.plugin-DDBKeTGI.js';
|
|
6
6
|
export { o as GROUPING_ROW_TYPE, j as GROUP_COLUMN_PROP, G as GROUP_DEPTH, h as GROUP_EXPANDED, l as GROUP_EXPAND_BTN, m as GROUP_EXPAND_EVENT, k as GROUP_ORIGINAL_INDEX, f as PSEUDO_GROUP_COLUMN, P as PSEUDO_GROUP_ITEM, d as PSEUDO_GROUP_ITEM_ID, e as PSEUDO_GROUP_ITEM_VALUE, c as columnTypes, a as cropCellToMax, H as gatherGroup, s as gatherGrouping, z as getCellData, B as getCellDataParsed, A as getCellRaw, I as getColumnByProp, D as getColumnSizes, C as getColumnType, F as getColumns, q as getExpanded, t as getGroupingName, x as getParsedGroup, g as getRange, p as getSource, E as isColGrouping, u as isGrouping, v as isGroupingColumn, b as isRangeSingleCell, i as isRowType, y as isSameGroup, w as measureEqualDepth, n as nextCell, r as rowTypes } from './column.service-Cfw3L0PN.js';
|
|
7
7
|
export { d as dispatch, a as dispatchByEvent } from './header-cell-renderer-Cqtg1ETB.js';
|
|
8
8
|
export { C as CellRenderer, G as GroupingRowRenderer, S as SortingSign, e as expandEvent, a as expandSvgIconVNode, r as renderGroupCells } from './cell-renderer-11cf-lu3.js';
|
|
@@ -6,7 +6,7 @@ import { c as columnTypes, J as reduce, C as getColumnType, r as rowTypes, i as
|
|
|
6
6
|
import { D as DataStore, b as getSourceItem, f as getSourceItemVirtualIndexByProp, d as setSourceByPhysicalIndex, s as setSourceByVirtualIndex, a as getVisibleSourceItem, h as gatherTrimmedItems, k as getItemByIndex, R as RESIZE_INTERVAL } from './dimension.helpers-fl4HSSK9.js';
|
|
7
7
|
import { d as debounce } from './debounce-PCRWZliA.js';
|
|
8
8
|
import { g as getScrollDimension, v as viewportDataPartition, F as FOOTER_SLOT, C as CONTENT_SLOT, H as HEADER_SLOT, D as DATA_SLOT } from './viewport.helpers-CoCAvmZs.js';
|
|
9
|
-
import { D as DimensionStore, t as themeTokenCssVariables, S as SelectionStore, B as BasePlugin, G as GroupingRowPlugin, a as StretchColumn, i as isStretchPlugin, A as AutoSizeColumnPlugin, f as FilterPlugin, E as ExportFilePlugin,
|
|
9
|
+
import { D as DimensionStore, t as themeTokenCssVariables, S as SelectionStore, B as BasePlugin, G as GroupingRowPlugin, a as StretchColumn, i as isStretchPlugin, A as AutoSizeColumnPlugin, f as FilterPlugin, E as ExportFilePlugin, x as SortingPlugin, u as ColumnMovePlugin } from './column.drag.plugin-DDBKeTGI.js';
|
|
10
10
|
import { V as ViewportStore } from './viewport.store-BSXmsiZc.js';
|
|
11
11
|
import { t as timeout } from './index-D24ku9ej.js';
|
|
12
12
|
import { g as getPropertyFromEvent } from './events-BvSmBueA.js';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { BlankSemantics, FilterEvaluationContext } from './filter.types';
|
|
2
|
+
export declare const DEFAULT_BLANK_SEMANTICS: Readonly<BlankSemantics>;
|
|
3
|
+
/** Merge a grid policy and a partial column policy over Core defaults. */
|
|
4
|
+
export declare function resolveBlankSemantics(gridPolicy?: BlankSemantics, columnPolicy?: BlankSemantics): BlankSemantics;
|
|
5
|
+
/** Evaluate blankness from the unparsed source value and property presence. */
|
|
6
|
+
export declare function isBlankValue(value: any, context: FilterEvaluationContext): boolean;
|
|
@@ -5,6 +5,7 @@ import type { ColumnFilterConfig, FilterCollectionItem, LogicFunction, MultiFilt
|
|
|
5
5
|
import { TrimmedEntity } from "../../store/index";
|
|
6
6
|
export * from './filter.types';
|
|
7
7
|
export * from './filter.indexed';
|
|
8
|
+
export * from './filter.blank';
|
|
8
9
|
export * from './filter.button';
|
|
9
10
|
export declare const FILTER_TRIMMED_TYPE = "filter";
|
|
10
11
|
export declare const FILTER_CONFIG_CHANGED_EVENT = "filterconfigchanged";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ColumnProp, ColumnRegular, HyperFunc } from "../../types/index";
|
|
1
|
+
import type { ColumnProp, ColumnRegular, DataType, HyperFunc } from "../../types/index";
|
|
2
2
|
import type { FilterType } from './filter.indexed';
|
|
3
3
|
import type { VNode } from '../../stencil-public-runtime';
|
|
4
4
|
export type DateEnum = 'today' | 'yesterday' | 'tomorrow' | 'thisweek' | 'lastweek' | 'nextweek' | 'thismonth' | 'lastmonth' | 'nextmonth' | 'thisyear' | 'lastyear' | 'nextyear';
|
|
@@ -13,8 +13,36 @@ export type ExtraField = 'input' | 'datepicker' | ((h: HyperFunc<VNode>, config:
|
|
|
13
13
|
}) => VNode | VNode[]);
|
|
14
14
|
export type LogicFunctionParam = any;
|
|
15
15
|
export type LogicFunctionExtraParam = 'select' | 'input' | 'multi' | 'datepicker' | number | Date | DateEnum | null | undefined | string | string[] | number[];
|
|
16
|
+
/** Source-aware context supplied while a filter predicate is evaluated. */
|
|
17
|
+
export interface FilterEvaluationContext<TModel extends DataType = DataType, TColumn extends ColumnRegular = ColumnRegular> {
|
|
18
|
+
/** Row model being evaluated. */
|
|
19
|
+
model: TModel;
|
|
20
|
+
/** Column associated with the filter, when one is available. */
|
|
21
|
+
column?: TColumn;
|
|
22
|
+
/** Property the filter is evaluating. */
|
|
23
|
+
property: ColumnProp;
|
|
24
|
+
/** Unparsed value read from the row model. */
|
|
25
|
+
sourceValue: any;
|
|
26
|
+
/** Value after the column cell parser, when configured. */
|
|
27
|
+
parsedValue: any;
|
|
28
|
+
/** Whether the property exists directly on the row model. */
|
|
29
|
+
hasOwnProperty: boolean;
|
|
30
|
+
/** Effective blank policy after grid and column settings are merged. */
|
|
31
|
+
blankSemantics: BlankSemantics;
|
|
32
|
+
}
|
|
33
|
+
/** Configures which source values are treated as blank by blank operators. */
|
|
34
|
+
export interface BlankSemantics {
|
|
35
|
+
null?: boolean;
|
|
36
|
+
undefined?: boolean;
|
|
37
|
+
emptyString?: boolean;
|
|
38
|
+
whitespaceOnlyString?: boolean;
|
|
39
|
+
emptyArray?: boolean;
|
|
40
|
+
missingProperty?: boolean;
|
|
41
|
+
/** Final application override, evaluated after the configured fallback. */
|
|
42
|
+
isBlank?: (value: any, context: FilterEvaluationContext, fallbackResult: boolean) => boolean;
|
|
43
|
+
}
|
|
16
44
|
export interface LogicFunction<T1 = LogicFunctionParam, T2 = LogicFunctionExtraParam> {
|
|
17
|
-
(value: T1, extra?: T2): boolean;
|
|
45
|
+
(value: T1, extra?: T2, context?: FilterEvaluationContext): boolean;
|
|
18
46
|
extra?: ExtraField;
|
|
19
47
|
}
|
|
20
48
|
export interface CustomFilter<T1 = LogicFunctionParam, T2 = LogicFunctionExtraParam> {
|
|
@@ -53,6 +81,8 @@ export interface FilterLocalization {
|
|
|
53
81
|
* Filter configuration for a column. This is the type of the `filter` property on a column.
|
|
54
82
|
*/
|
|
55
83
|
export interface ColumnFilterConfig {
|
|
84
|
+
/** Grid-level blank policy. Individual columns can override fields. */
|
|
85
|
+
blankSemantics?: BlankSemantics;
|
|
56
86
|
/**
|
|
57
87
|
* The collection of filters to be applied to the column.
|
|
58
88
|
*/
|
|
@@ -3,6 +3,7 @@ import type { DimensionCols, DimensionRows, DimensionColPin, DimensionType, Mult
|
|
|
3
3
|
import type { Cell, EditorCtr, FocusedCells, OldNewRangeMapping, RangeArea, SelectionStoreState } from './selection';
|
|
4
4
|
import type { Observable } from '../utils';
|
|
5
5
|
import type { JSXBase } from '../stencil-public-runtime';
|
|
6
|
+
import type { BlankSemantics } from '../plugins/filter/filter.types';
|
|
6
7
|
export type Nullable<T> = {
|
|
7
8
|
[P in keyof T]: T[P] | null;
|
|
8
9
|
};
|
|
@@ -194,6 +195,8 @@ export interface ColumnRegular<P extends ColumnProp = ColumnProp, TModel extends
|
|
|
194
195
|
* Filter. Require filter plugin to be installed and activated through grid config filter.
|
|
195
196
|
*/
|
|
196
197
|
filter?: boolean | string | string[];
|
|
198
|
+
/** Partial column override for the grid-level blank filtering policy. */
|
|
199
|
+
blankSemantics?: BlankSemantics;
|
|
197
200
|
/**
|
|
198
201
|
* Is column can be sorted, check cellCompare function for custom sorting.
|
|
199
202
|
*/
|
package/hydrate/index.js
CHANGED
|
@@ -16158,8 +16158,84 @@ const lsEq = function (value, extra) {
|
|
|
16158
16158
|
};
|
|
16159
16159
|
lsEq.extra = 'input';
|
|
16160
16160
|
|
|
16161
|
-
const
|
|
16162
|
-
|
|
16161
|
+
const DEFAULT_BLANK_SEMANTICS = Object.freeze({
|
|
16162
|
+
null: true,
|
|
16163
|
+
undefined: true,
|
|
16164
|
+
emptyString: true,
|
|
16165
|
+
whitespaceOnlyString: false,
|
|
16166
|
+
emptyArray: false,
|
|
16167
|
+
missingProperty: true,
|
|
16168
|
+
});
|
|
16169
|
+
const BOOLEAN_FIELDS = [
|
|
16170
|
+
'null',
|
|
16171
|
+
'undefined',
|
|
16172
|
+
'emptyString',
|
|
16173
|
+
'whitespaceOnlyString',
|
|
16174
|
+
'emptyArray',
|
|
16175
|
+
'missingProperty',
|
|
16176
|
+
];
|
|
16177
|
+
/** Merge a grid policy and a partial column policy over Core defaults. */
|
|
16178
|
+
function resolveBlankSemantics(gridPolicy, columnPolicy) {
|
|
16179
|
+
const resolved = Object.assign({}, DEFAULT_BLANK_SEMANTICS);
|
|
16180
|
+
for (const policy of [gridPolicy, columnPolicy]) {
|
|
16181
|
+
if (!policy) {
|
|
16182
|
+
continue;
|
|
16183
|
+
}
|
|
16184
|
+
for (const field of BOOLEAN_FIELDS) {
|
|
16185
|
+
if (policy[field] !== undefined) {
|
|
16186
|
+
resolved[field] = policy[field];
|
|
16187
|
+
}
|
|
16188
|
+
}
|
|
16189
|
+
if (policy.isBlank !== undefined) {
|
|
16190
|
+
resolved.isBlank = policy.isBlank;
|
|
16191
|
+
}
|
|
16192
|
+
}
|
|
16193
|
+
return resolved;
|
|
16194
|
+
}
|
|
16195
|
+
/** Evaluate blankness from the unparsed source value and property presence. */
|
|
16196
|
+
function isBlankValue(value, context) {
|
|
16197
|
+
const policy = context.blankSemantics;
|
|
16198
|
+
let fallbackResult;
|
|
16199
|
+
if (!context.hasOwnProperty) {
|
|
16200
|
+
fallbackResult = policy.missingProperty === true;
|
|
16201
|
+
}
|
|
16202
|
+
else if (value === null) {
|
|
16203
|
+
fallbackResult = policy.null === true;
|
|
16204
|
+
}
|
|
16205
|
+
else if (value === undefined) {
|
|
16206
|
+
fallbackResult = policy.undefined === true;
|
|
16207
|
+
}
|
|
16208
|
+
else if (value === '') {
|
|
16209
|
+
fallbackResult = policy.emptyString === true;
|
|
16210
|
+
}
|
|
16211
|
+
else if (typeof value === 'string' &&
|
|
16212
|
+
value.length > 0 &&
|
|
16213
|
+
value.trim() === '') {
|
|
16214
|
+
fallbackResult = policy.whitespaceOnlyString === true;
|
|
16215
|
+
}
|
|
16216
|
+
else if (Array.isArray(value) && value.length === 0) {
|
|
16217
|
+
fallbackResult = policy.emptyArray === true;
|
|
16218
|
+
}
|
|
16219
|
+
else {
|
|
16220
|
+
fallbackResult = false;
|
|
16221
|
+
}
|
|
16222
|
+
return policy.isBlank
|
|
16223
|
+
? policy.isBlank(value, context, fallbackResult)
|
|
16224
|
+
: fallbackResult;
|
|
16225
|
+
}
|
|
16226
|
+
|
|
16227
|
+
function blankContext(value, context) {
|
|
16228
|
+
return context !== null && context !== void 0 ? context : {
|
|
16229
|
+
model: {},
|
|
16230
|
+
property: '',
|
|
16231
|
+
sourceValue: value,
|
|
16232
|
+
parsedValue: value,
|
|
16233
|
+
hasOwnProperty: true,
|
|
16234
|
+
blankSemantics: DEFAULT_BLANK_SEMANTICS,
|
|
16235
|
+
};
|
|
16236
|
+
}
|
|
16237
|
+
const notSet = (value, _extra, context) => isBlankValue(context ? context.sourceValue : value, blankContext(value, context));
|
|
16238
|
+
const set = (value, _extra, context) => !notSet(value, _extra, context);
|
|
16163
16239
|
|
|
16164
16240
|
const beginsWith = (value, extra) => {
|
|
16165
16241
|
if (!value) {
|
|
@@ -16219,11 +16295,13 @@ const filterCoreFunctionsIndexedByType = {
|
|
|
16219
16295
|
const filterTypes = {
|
|
16220
16296
|
string: ['notEmpty', 'empty', 'eq', 'notEq', 'begins', 'contains', 'notContains'],
|
|
16221
16297
|
number: ['notEmpty', 'empty', 'eqN', 'neqN', 'gt', 'gte', 'lt', 'lte'],
|
|
16298
|
+
boolean: ['notEmpty', 'empty'],
|
|
16299
|
+
array: ['notEmpty', 'empty'],
|
|
16222
16300
|
};
|
|
16223
16301
|
const filterNames = {
|
|
16224
16302
|
none: 'None',
|
|
16225
|
-
empty: '
|
|
16226
|
-
notEmpty: '
|
|
16303
|
+
empty: 'Is blank',
|
|
16304
|
+
notEmpty: 'Is not blank',
|
|
16227
16305
|
eq: 'Equal',
|
|
16228
16306
|
notEq: 'Not equal',
|
|
16229
16307
|
begins: 'Begins with',
|
|
@@ -16346,6 +16424,7 @@ class FilterPlugin extends BasePlugin {
|
|
|
16346
16424
|
}
|
|
16347
16425
|
initConfig(config) {
|
|
16348
16426
|
var _a;
|
|
16427
|
+
this.config = config;
|
|
16349
16428
|
this.allowDuplicateOperators = (_a = config.allowDuplicateOperators) !== null && _a !== void 0 ? _a : true;
|
|
16350
16429
|
if (this.pop) {
|
|
16351
16430
|
this.pop.allowDuplicateOperators = this.allowDuplicateOperators;
|
|
@@ -16562,38 +16641,56 @@ class FilterPlugin extends BasePlugin {
|
|
|
16562
16641
|
* Get trimmed rows based on filter
|
|
16563
16642
|
*/
|
|
16564
16643
|
getRowFilter(rows, filterItems, columnByProp) {
|
|
16644
|
+
var _a, _b;
|
|
16565
16645
|
const propKeys = Object.keys(filterItems);
|
|
16646
|
+
const blankSemanticsByProp = {};
|
|
16647
|
+
for (const prop of propKeys) {
|
|
16648
|
+
blankSemanticsByProp[prop] = resolveBlankSemantics((_a = this.config) === null || _a === void 0 ? void 0 : _a.blankSemantics, (_b = columnByProp[prop]) === null || _b === void 0 ? void 0 : _b.blankSemantics);
|
|
16649
|
+
}
|
|
16566
16650
|
const trimmed = {};
|
|
16567
16651
|
// each rows
|
|
16568
16652
|
for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
|
|
16569
16653
|
// check filter by column properties
|
|
16570
16654
|
for (const prop of propKeys) {
|
|
16571
16655
|
// add to the list of removed/trimmed rows of filter condition is satisfied
|
|
16572
|
-
if (this.shouldTrimRow(filterItems[prop], prop, columnByProp[prop], rows[rowIndex])) {
|
|
16656
|
+
if (this.shouldTrimRow(filterItems[prop], prop, blankSemanticsByProp[prop], columnByProp[prop], rows[rowIndex])) {
|
|
16573
16657
|
trimmed[rowIndex] = true;
|
|
16574
16658
|
}
|
|
16575
16659
|
} // end of for-of propKeys
|
|
16576
16660
|
}
|
|
16577
16661
|
return trimmed;
|
|
16578
16662
|
}
|
|
16579
|
-
shouldTrimRow(propFilters, prop, column, model = {}) {
|
|
16663
|
+
shouldTrimRow(propFilters, prop, blankSemantics, column, model = {}) {
|
|
16580
16664
|
// reset the count of satisfied filters
|
|
16581
16665
|
let propFilterSatisfiedCount = 0;
|
|
16582
16666
|
// reset the array of last filter results
|
|
16583
16667
|
let lastFilterResults = [];
|
|
16668
|
+
// THE MAGIC OF FILTERING IS HERE
|
|
16669
|
+
// If there is no column but user wants to filter by a property
|
|
16670
|
+
const hasOwnProperty = Object.hasOwn(model, prop);
|
|
16671
|
+
const sourceValue = model[prop];
|
|
16672
|
+
const parsedValue = (column === null || column === void 0 ? void 0 : column.cellParser)
|
|
16673
|
+
? column.cellParser(model, column)
|
|
16674
|
+
: sourceValue;
|
|
16675
|
+
const context = {
|
|
16676
|
+
model,
|
|
16677
|
+
column,
|
|
16678
|
+
property: prop,
|
|
16679
|
+
sourceValue,
|
|
16680
|
+
parsedValue,
|
|
16681
|
+
hasOwnProperty,
|
|
16682
|
+
blankSemantics,
|
|
16683
|
+
};
|
|
16584
16684
|
// testing each filter for a prop
|
|
16585
16685
|
for (const [filterIndex, filterData] of propFilters.entries()) {
|
|
16586
16686
|
// the filter LogicFunction based on the type
|
|
16587
16687
|
const filterFunc = this.filterFunctionsIndexedByType[filterData.type];
|
|
16588
|
-
// THE MAGIC OF FILTERING IS HERE
|
|
16589
|
-
// If there is no column but user wants to filter by a property
|
|
16590
|
-
const value = column ? getCellDataParsed(model, column) : model[prop];
|
|
16591
16688
|
// OR relation
|
|
16592
16689
|
if (filterData.relation === 'or') {
|
|
16593
16690
|
// reset the array of last filter results
|
|
16594
16691
|
lastFilterResults = [];
|
|
16595
16692
|
// if the filter is satisfied, continue to the next filter
|
|
16596
|
-
if (filterFunc(
|
|
16693
|
+
if (filterFunc(parsedValue, filterData.value, context)) {
|
|
16597
16694
|
continue;
|
|
16598
16695
|
}
|
|
16599
16696
|
// if the filter is not satisfied, count it
|
|
@@ -16603,7 +16700,7 @@ class FilterPlugin extends BasePlugin {
|
|
|
16603
16700
|
else {
|
|
16604
16701
|
// 'and' relation will need to know the next filter
|
|
16605
16702
|
// so we save this current filter to include it in the next filter
|
|
16606
|
-
lastFilterResults.push(!filterFunc(
|
|
16703
|
+
lastFilterResults.push(!filterFunc(parsedValue, filterData.value, context));
|
|
16607
16704
|
if (isFinalAndFilter(filterIndex, propFilters)) {
|
|
16608
16705
|
// let's just continue since for sure propFilterSatisfiedCount cannot be satisfied
|
|
16609
16706
|
if (allAndConditionsSatisfied(lastFilterResults)) {
|