@tanstack/table-core 9.0.0-beta.45 → 9.0.0-beta.47
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/features/column-filtering/columnFilteringFeature.types.d.cts +43 -3
- package/dist/features/column-filtering/columnFilteringFeature.types.d.ts +43 -3
- package/dist/features/column-filtering/columnFilteringFeature.utils.cjs +18 -9
- package/dist/features/column-filtering/columnFilteringFeature.utils.cjs.map +1 -1
- package/dist/features/column-filtering/columnFilteringFeature.utils.d.cts +6 -2
- package/dist/features/column-filtering/columnFilteringFeature.utils.d.ts +6 -2
- package/dist/features/column-filtering/columnFilteringFeature.utils.js +18 -9
- package/dist/features/column-filtering/columnFilteringFeature.utils.js.map +1 -1
- package/dist/features/column-filtering/createFilteredRowModel.cjs +7 -2
- package/dist/features/column-filtering/createFilteredRowModel.cjs.map +1 -1
- package/dist/features/column-filtering/createFilteredRowModel.d.cts +6 -2
- package/dist/features/column-filtering/createFilteredRowModel.d.ts +6 -2
- package/dist/features/column-filtering/createFilteredRowModel.js +7 -2
- package/dist/features/column-filtering/createFilteredRowModel.js.map +1 -1
- package/dist/features/column-grouping/columnGroupingFeature.types.d.cts +41 -3
- package/dist/features/column-grouping/columnGroupingFeature.types.d.ts +41 -3
- package/dist/features/column-grouping/columnGroupingFeature.utils.cjs +17 -3
- package/dist/features/column-grouping/columnGroupingFeature.utils.cjs.map +1 -1
- package/dist/features/column-grouping/columnGroupingFeature.utils.d.cts +5 -0
- package/dist/features/column-grouping/columnGroupingFeature.utils.d.ts +5 -0
- package/dist/features/column-grouping/columnGroupingFeature.utils.js +17 -3
- package/dist/features/column-grouping/columnGroupingFeature.utils.js.map +1 -1
- package/dist/features/column-grouping/createGroupedRowModel.cjs +6 -3
- package/dist/features/column-grouping/createGroupedRowModel.cjs.map +1 -1
- package/dist/features/column-grouping/createGroupedRowModel.d.cts +6 -3
- package/dist/features/column-grouping/createGroupedRowModel.d.ts +6 -3
- package/dist/features/column-grouping/createGroupedRowModel.js +6 -3
- package/dist/features/column-grouping/createGroupedRowModel.js.map +1 -1
- package/dist/features/global-filtering/globalFilteringFeature.utils.cjs +3 -1
- package/dist/features/global-filtering/globalFilteringFeature.utils.cjs.map +1 -1
- package/dist/features/global-filtering/globalFilteringFeature.utils.d.cts +2 -6
- package/dist/features/global-filtering/globalFilteringFeature.utils.d.ts +2 -6
- package/dist/features/global-filtering/globalFilteringFeature.utils.js +3 -1
- package/dist/features/global-filtering/globalFilteringFeature.utils.js.map +1 -1
- package/dist/features/row-sorting/createSortedRowModel.cjs +6 -2
- package/dist/features/row-sorting/createSortedRowModel.cjs.map +1 -1
- package/dist/features/row-sorting/createSortedRowModel.d.cts +6 -2
- package/dist/features/row-sorting/createSortedRowModel.d.ts +6 -2
- package/dist/features/row-sorting/createSortedRowModel.js +6 -2
- package/dist/features/row-sorting/createSortedRowModel.js.map +1 -1
- package/dist/features/row-sorting/rowSortingFeature.types.d.cts +31 -2
- package/dist/features/row-sorting/rowSortingFeature.types.d.ts +31 -2
- package/dist/features/row-sorting/rowSortingFeature.utils.cjs +19 -4
- package/dist/features/row-sorting/rowSortingFeature.utils.cjs.map +1 -1
- package/dist/features/row-sorting/rowSortingFeature.utils.js +19 -4
- package/dist/features/row-sorting/rowSortingFeature.utils.js.map +1 -1
- package/dist/fns/aggregationFns.cjs +128 -57
- package/dist/fns/aggregationFns.cjs.map +1 -1
- package/dist/fns/aggregationFns.d.cts +72 -18
- package/dist/fns/aggregationFns.d.ts +72 -18
- package/dist/fns/aggregationFns.js +126 -58
- package/dist/fns/aggregationFns.js.map +1 -1
- package/dist/fns/filterFns.cjs +254 -97
- package/dist/fns/filterFns.cjs.map +1 -1
- package/dist/fns/filterFns.d.cts +126 -94
- package/dist/fns/filterFns.d.ts +126 -94
- package/dist/fns/filterFns.js +247 -98
- package/dist/fns/filterFns.js.map +1 -1
- package/dist/fns/sortFns.cjs +68 -21
- package/dist/fns/sortFns.cjs.map +1 -1
- package/dist/fns/sortFns.d.cts +48 -15
- package/dist/fns/sortFns.d.ts +48 -15
- package/dist/fns/sortFns.js +68 -22
- package/dist/fns/sortFns.js.map +1 -1
- package/dist/helpers/tableFeatures.cjs +5 -4
- package/dist/helpers/tableFeatures.cjs.map +1 -1
- package/dist/helpers/tableFeatures.d.cts +5 -4
- package/dist/helpers/tableFeatures.d.ts +5 -4
- package/dist/helpers/tableFeatures.js +5 -4
- package/dist/helpers/tableFeatures.js.map +1 -1
- package/dist/index.cjs +12 -0
- package/dist/index.d.cts +8 -8
- package/dist/index.d.ts +8 -8
- package/dist/index.js +4 -4
- package/dist/types/TableFeatures.d.cts +15 -7
- package/dist/types/TableFeatures.d.ts +15 -7
- package/dist/types/type-utils.d.cts +8 -1
- package/dist/types/type-utils.d.ts +8 -1
- package/package.json +1 -1
- package/skills/api-not-found/SKILL.md +1 -1
- package/skills/client-vs-server/SKILL.md +1 -1
- package/skills/column-faceting/SKILL.md +11 -5
- package/skills/column-filtering/SKILL.md +8 -3
- package/skills/column-ordering/SKILL.md +1 -1
- package/skills/column-pinning/SKILL.md +1 -1
- package/skills/column-resizing/SKILL.md +1 -1
- package/skills/column-sizing/SKILL.md +1 -1
- package/skills/column-visibility/SKILL.md +1 -1
- package/skills/core/SKILL.md +1 -1
- package/skills/custom-features/SKILL.md +1 -1
- package/skills/expanding/SKILL.md +1 -1
- package/skills/global-filtering/SKILL.md +3 -3
- package/skills/grouping/SKILL.md +8 -3
- package/skills/migrate-v8-to-v9/SKILL.md +7 -7
- package/skills/pagination/SKILL.md +1 -1
- package/skills/row-pinning/SKILL.md +1 -1
- package/skills/row-selection/SKILL.md +1 -1
- package/skills/sorting/SKILL.md +9 -3
- package/skills/table-features/SKILL.md +16 -13
- package/skills/typescript/SKILL.md +1 -1
- package/src/features/column-filtering/columnFilteringFeature.types.ts +60 -0
- package/src/features/column-filtering/columnFilteringFeature.utils.ts +33 -19
- package/src/features/column-filtering/createFilteredRowModel.ts +13 -3
- package/src/features/column-grouping/columnGroupingFeature.types.ts +63 -8
- package/src/features/column-grouping/columnGroupingFeature.utils.ts +43 -9
- package/src/features/column-grouping/createGroupedRowModel.ts +6 -3
- package/src/features/global-filtering/globalFilteringFeature.utils.ts +8 -0
- package/src/features/row-sorting/createSortedRowModel.ts +6 -2
- package/src/features/row-sorting/rowSortingFeature.types.ts +53 -1
- package/src/features/row-sorting/rowSortingFeature.utils.ts +45 -13
- package/src/fns/aggregationFns.ts +204 -146
- package/src/fns/filterFns.ts +361 -280
- package/src/fns/sortFns.ts +95 -78
- package/src/helpers/tableFeatures.ts +5 -4
- package/src/types/TableFeatures.ts +15 -7
- package/src/types/type-utils.ts +8 -0
package/src/fns/filterFns.ts
CHANGED
|
@@ -1,7 +1,72 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
CreatedFilterFn,
|
|
3
|
+
ExtractFilterMeta,
|
|
4
|
+
FilterFnDef,
|
|
5
|
+
} from '../features/column-filtering/columnFilteringFeature.types'
|
|
1
6
|
import type { RowData } from '../types/type-utils'
|
|
2
7
|
import type { TableFeatures } from '../types/TableFeatures'
|
|
3
8
|
import type { Row } from '../types/Row'
|
|
4
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Builds a `FilterFn` from a value-level comparator plus optional resolvers.
|
|
12
|
+
*
|
|
13
|
+
* The `filter` comparator receives the row's data value (already passed
|
|
14
|
+
* through `resolveDataValue` when one is defined) and the filter value
|
|
15
|
+
* (already passed through `resolveFilterValue` by the table). Keeping
|
|
16
|
+
* normalization in the resolvers means a variant of an existing filter
|
|
17
|
+
* function only has to swap the resolvers, not re-implement the comparison.
|
|
18
|
+
*
|
|
19
|
+
* The definition is attached to the returned function, so a variant can be
|
|
20
|
+
* created by spreading a built-in filter function and overriding what differs:
|
|
21
|
+
*
|
|
22
|
+
* ```ts
|
|
23
|
+
* const normalize = (value: unknown) =>
|
|
24
|
+
* String(value ?? '')
|
|
25
|
+
* .toLowerCase()
|
|
26
|
+
* .normalize('NFD')
|
|
27
|
+
* .replace(/\p{Diacritic}/gu, '')
|
|
28
|
+
*
|
|
29
|
+
* const includesStringIgnoreDiacritics = constructFilterFn({
|
|
30
|
+
* ...filterFn_includesString,
|
|
31
|
+
* resolveFilterValue: normalize,
|
|
32
|
+
* resolveDataValue: normalize,
|
|
33
|
+
* })
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* Note: the table applies `resolveFilterValue` once per filter before any rows
|
|
37
|
+
* are tested. When calling a filter function directly (outside of a table),
|
|
38
|
+
* apply it yourself: `fn(row, columnId, fn.resolveFilterValue?.(value) ?? value)`.
|
|
39
|
+
*/
|
|
40
|
+
export function constructFilterFn<
|
|
41
|
+
TFeatures extends TableFeatures = any,
|
|
42
|
+
TData extends RowData = any,
|
|
43
|
+
>(def: FilterFnDef<TFeatures, TData>): CreatedFilterFn<TFeatures, TData> {
|
|
44
|
+
const filterFn: CreatedFilterFn<TFeatures, TData> = Object.assign(
|
|
45
|
+
<TRowFeatures extends TableFeatures, TRowData extends RowData>(
|
|
46
|
+
row: Row<TRowFeatures, TRowData>,
|
|
47
|
+
columnId: string,
|
|
48
|
+
filterValue: any,
|
|
49
|
+
addMeta?: (meta: ExtractFilterMeta<TRowFeatures>) => void,
|
|
50
|
+
): boolean => {
|
|
51
|
+
const rawValue = row.getValue(columnId)
|
|
52
|
+
const dataValue = filterFn.resolveDataValue
|
|
53
|
+
? filterFn.resolveDataValue(rawValue)
|
|
54
|
+
: rawValue
|
|
55
|
+
// The def's comparator is typed against the factory's TFeatures/TData;
|
|
56
|
+
// the caller's row generics are erased at this boundary.
|
|
57
|
+
return filterFn.filter(
|
|
58
|
+
dataValue,
|
|
59
|
+
filterValue,
|
|
60
|
+
row as any,
|
|
61
|
+
columnId,
|
|
62
|
+
addMeta as any,
|
|
63
|
+
)
|
|
64
|
+
},
|
|
65
|
+
def,
|
|
66
|
+
)
|
|
67
|
+
return filterFn
|
|
68
|
+
}
|
|
69
|
+
|
|
5
70
|
// Basic filters
|
|
6
71
|
|
|
7
72
|
/**
|
|
@@ -9,16 +74,10 @@ import type { Row } from '../types/Row'
|
|
|
9
74
|
*
|
|
10
75
|
* Uses JavaScript `===` comparison and auto-removes empty filter values.
|
|
11
76
|
*/
|
|
12
|
-
export const filterFn_equals =
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
filterValue: unknown,
|
|
17
|
-
) => {
|
|
18
|
-
return row.getValue(columnId) === filterValue
|
|
19
|
-
},
|
|
20
|
-
{ autoRemove: (val: any) => testFalsy(val) },
|
|
21
|
-
)
|
|
77
|
+
export const filterFn_equals = constructFilterFn({
|
|
78
|
+
filter: (dataValue, filterValue) => dataValue === filterValue,
|
|
79
|
+
autoRemove: (val: any) => testFalsy(val),
|
|
80
|
+
})
|
|
22
81
|
|
|
23
82
|
/**
|
|
24
83
|
* Keeps rows whose column value is loosely equal to the filter value.
|
|
@@ -26,16 +85,10 @@ export const filterFn_equals = Object.assign(
|
|
|
26
85
|
* Uses JavaScript `==` comparison and auto-removes empty filter values. This is
|
|
27
86
|
* useful for matching string input against numeric row values.
|
|
28
87
|
*/
|
|
29
|
-
export const filterFn_weakEquals =
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
filterValue: unknown,
|
|
34
|
-
) => {
|
|
35
|
-
return row.getValue(columnId) == filterValue
|
|
36
|
-
},
|
|
37
|
-
{ autoRemove: (val: any) => testFalsy(val) },
|
|
38
|
-
)
|
|
88
|
+
export const filterFn_weakEquals = constructFilterFn({
|
|
89
|
+
filter: (dataValue, filterValue) => dataValue == filterValue,
|
|
90
|
+
autoRemove: (val: any) => testFalsy(val),
|
|
91
|
+
})
|
|
39
92
|
|
|
40
93
|
// String filters
|
|
41
94
|
|
|
@@ -44,21 +97,12 @@ export const filterFn_weakEquals = Object.assign(
|
|
|
44
97
|
*
|
|
45
98
|
* Matching is case-sensitive and empty filter values are auto-removed.
|
|
46
99
|
*/
|
|
47
|
-
export const filterFn_includesStringSensitive =
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
return Boolean(
|
|
54
|
-
row.getValue(columnId)?.toString().includes(String(filterValue)),
|
|
55
|
-
)
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
autoRemove: (val: any) => testFalsy(val),
|
|
59
|
-
resolveFilterValue: (val: any) => String(val),
|
|
60
|
-
},
|
|
61
|
-
)
|
|
100
|
+
export const filterFn_includesStringSensitive = constructFilterFn({
|
|
101
|
+
filter: (dataValue, filterValue) => Boolean(dataValue?.includes(filterValue)),
|
|
102
|
+
autoRemove: (val: any) => testFalsy(val),
|
|
103
|
+
resolveFilterValue: (val: any) => String(val),
|
|
104
|
+
resolveDataValue: (val: any) => (val == null ? undefined : String(val)),
|
|
105
|
+
})
|
|
62
106
|
|
|
63
107
|
/**
|
|
64
108
|
* Keeps rows whose stringified column value includes the filter text.
|
|
@@ -66,25 +110,13 @@ export const filterFn_includesStringSensitive = Object.assign(
|
|
|
66
110
|
* Both values are lowercased before comparison, and empty filter values are
|
|
67
111
|
* auto-removed.
|
|
68
112
|
*/
|
|
69
|
-
export const filterFn_includesString =
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
row
|
|
77
|
-
.getValue(columnId)
|
|
78
|
-
?.toString()
|
|
79
|
-
.toLowerCase()
|
|
80
|
-
.includes(String(filterValue).toLowerCase()),
|
|
81
|
-
)
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
autoRemove: (val: any) => testFalsy(val),
|
|
85
|
-
resolveFilterValue: (val: any) => String(val).toLowerCase(),
|
|
86
|
-
},
|
|
87
|
-
)
|
|
113
|
+
export const filterFn_includesString = constructFilterFn({
|
|
114
|
+
filter: (dataValue, filterValue) => Boolean(dataValue?.includes(filterValue)),
|
|
115
|
+
autoRemove: (val: any) => testFalsy(val),
|
|
116
|
+
resolveFilterValue: (val: any) => String(val).toLowerCase(),
|
|
117
|
+
resolveDataValue: (val: any) =>
|
|
118
|
+
val == null ? undefined : String(val).toLowerCase(),
|
|
119
|
+
})
|
|
88
120
|
|
|
89
121
|
/**
|
|
90
122
|
* Keeps rows whose stringified column value equals the filter text.
|
|
@@ -92,41 +124,80 @@ export const filterFn_includesString = Object.assign(
|
|
|
92
124
|
* Both values are lowercased before comparison, and empty filter values are
|
|
93
125
|
* auto-removed.
|
|
94
126
|
*/
|
|
95
|
-
export const filterFn_equalsString =
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
row.getValue(columnId)?.toString().toLowerCase() ===
|
|
103
|
-
String(filterValue).toLowerCase()
|
|
104
|
-
)
|
|
105
|
-
},
|
|
106
|
-
{
|
|
107
|
-
autoRemove: (val: any) => testFalsy(val),
|
|
108
|
-
resolveFilterValue: (val: any) => String(val).toLowerCase(),
|
|
109
|
-
},
|
|
110
|
-
)
|
|
127
|
+
export const filterFn_equalsString = constructFilterFn({
|
|
128
|
+
filter: (dataValue, filterValue) => dataValue === filterValue,
|
|
129
|
+
autoRemove: (val: any) => testFalsy(val),
|
|
130
|
+
resolveFilterValue: (val: any) => String(val).toLowerCase(),
|
|
131
|
+
resolveDataValue: (val: any) =>
|
|
132
|
+
val == null ? undefined : String(val).toLowerCase(),
|
|
133
|
+
})
|
|
111
134
|
|
|
112
135
|
/**
|
|
113
136
|
* Keeps rows whose stringified column value exactly equals the filter text.
|
|
114
137
|
*
|
|
115
138
|
* Matching is case-sensitive and empty filter values are auto-removed.
|
|
116
139
|
*/
|
|
117
|
-
export const filterFn_equalsStringSensitive =
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
140
|
+
export const filterFn_equalsStringSensitive = constructFilterFn({
|
|
141
|
+
filter: (dataValue, filterValue) => dataValue === filterValue,
|
|
142
|
+
autoRemove: (val: any) => testFalsy(val),
|
|
143
|
+
resolveFilterValue: (val: any) => String(val),
|
|
144
|
+
resolveDataValue: (val: any) => (val == null ? undefined : String(val)),
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Keeps rows whose stringified column value starts with the filter text.
|
|
149
|
+
*
|
|
150
|
+
* Both values are lowercased before comparison, and empty filter values are
|
|
151
|
+
* auto-removed.
|
|
152
|
+
*/
|
|
153
|
+
export const filterFn_startsWith = constructFilterFn({
|
|
154
|
+
filter: (dataValue, filterValue) =>
|
|
155
|
+
Boolean(dataValue?.startsWith(filterValue)),
|
|
156
|
+
autoRemove: (val: any) => testFalsy(val),
|
|
157
|
+
resolveFilterValue: (val: any) => String(val).toLowerCase(),
|
|
158
|
+
resolveDataValue: (val: any) =>
|
|
159
|
+
val == null ? undefined : String(val).toLowerCase(),
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Keeps rows whose stringified column value ends with the filter text.
|
|
164
|
+
*
|
|
165
|
+
* Both values are lowercased before comparison, and empty filter values are
|
|
166
|
+
* auto-removed.
|
|
167
|
+
*/
|
|
168
|
+
export const filterFn_endsWith = constructFilterFn({
|
|
169
|
+
filter: (dataValue, filterValue) => Boolean(dataValue?.endsWith(filterValue)),
|
|
170
|
+
autoRemove: (val: any) => testFalsy(val),
|
|
171
|
+
resolveFilterValue: (val: any) => String(val).toLowerCase(),
|
|
172
|
+
resolveDataValue: (val: any) =>
|
|
173
|
+
val == null ? undefined : String(val).toLowerCase(),
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
// Blank filters
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Keeps rows whose column value is empty.
|
|
180
|
+
*
|
|
181
|
+
* A value is empty when it is nullish or stringifies to whitespace only. The
|
|
182
|
+
* filter value acts as an on/off flag: `false` and blank values are
|
|
183
|
+
* auto-removed.
|
|
184
|
+
*/
|
|
185
|
+
export const filterFn_empty = constructFilterFn({
|
|
186
|
+
filter: (dataValue) => testValueEmpty(dataValue),
|
|
187
|
+
autoRemove: (val: any) => testFalsy(val) || val === false,
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Keeps rows whose column value is not empty.
|
|
192
|
+
*
|
|
193
|
+
* A value is empty when it is nullish or stringifies to whitespace only. The
|
|
194
|
+
* filter value acts as an on/off flag: `false` and blank values are
|
|
195
|
+
* auto-removed.
|
|
196
|
+
*/
|
|
197
|
+
export const filterFn_notEmpty = constructFilterFn({
|
|
198
|
+
filter: (dataValue) => !testValueEmpty(dataValue),
|
|
199
|
+
autoRemove: (val: any) => testFalsy(val) || val === false,
|
|
200
|
+
})
|
|
130
201
|
|
|
131
202
|
// Number filters
|
|
132
203
|
|
|
@@ -136,78 +207,44 @@ export const filterFn_equalsStringSensitive = Object.assign(
|
|
|
136
207
|
* Numeric values are compared numerically when both sides can be coerced to
|
|
137
208
|
* numbers; otherwise normalized strings are compared.
|
|
138
209
|
*/
|
|
139
|
-
export const filterFn_greaterThan =
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
) => {
|
|
145
|
-
const rowValue = row.getValue(columnId)
|
|
146
|
-
const numericRowValue =
|
|
147
|
-
rowValue === null || rowValue === undefined ? 0 : +rowValue
|
|
148
|
-
const numericFilterValue = Number(filterValue)
|
|
149
|
-
|
|
150
|
-
if (!isNaN(numericFilterValue) && !isNaN(numericRowValue)) {
|
|
151
|
-
return numericRowValue > numericFilterValue
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const stringValue = (rowValue ?? '').toString().toLowerCase().trim()
|
|
155
|
-
const stringFilterValue = String(filterValue).toLowerCase().trim()
|
|
156
|
-
return stringValue > stringFilterValue
|
|
157
|
-
},
|
|
158
|
-
{ autoRemove: (val: any) => testFalsy(val) },
|
|
159
|
-
)
|
|
210
|
+
export const filterFn_greaterThan = constructFilterFn({
|
|
211
|
+
filter: (dataValue, filterValue) =>
|
|
212
|
+
compareGreaterThan(dataValue, filterValue),
|
|
213
|
+
autoRemove: (val: any) => testFalsy(val),
|
|
214
|
+
})
|
|
160
215
|
|
|
161
216
|
/**
|
|
162
217
|
* Keeps rows whose value is greater than or equal to the filter value.
|
|
163
218
|
*
|
|
164
|
-
* Delegates to the built-in greater-than and equality comparisons.
|
|
219
|
+
* Delegates to the built-in greater-than and strict-equality comparisons.
|
|
165
220
|
*/
|
|
166
|
-
export const filterFn_greaterThanOrEqualTo =
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
) => {
|
|
172
|
-
return (
|
|
173
|
-
filterFn_greaterThan(row, columnId, filterValue) ||
|
|
174
|
-
filterFn_equals(row, columnId, filterValue)
|
|
175
|
-
)
|
|
176
|
-
},
|
|
177
|
-
{ autoRemove: (val: any) => testFalsy(val) },
|
|
178
|
-
)
|
|
221
|
+
export const filterFn_greaterThanOrEqualTo = constructFilterFn({
|
|
222
|
+
filter: (dataValue, filterValue) =>
|
|
223
|
+
compareGreaterThanOrEqualTo(dataValue, filterValue),
|
|
224
|
+
autoRemove: (val: any) => testFalsy(val),
|
|
225
|
+
})
|
|
179
226
|
|
|
180
227
|
/**
|
|
181
228
|
* Keeps rows whose value is less than the filter value.
|
|
182
229
|
*
|
|
183
230
|
* This is implemented as the inverse of greater-than-or-equal comparison.
|
|
184
231
|
*/
|
|
185
|
-
export const filterFn_lessThan =
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
) => {
|
|
191
|
-
return !filterFn_greaterThanOrEqualTo(row, columnId, filterValue)
|
|
192
|
-
},
|
|
193
|
-
{ autoRemove: (val: any) => testFalsy(val) },
|
|
194
|
-
)
|
|
232
|
+
export const filterFn_lessThan = constructFilterFn({
|
|
233
|
+
filter: (dataValue, filterValue) =>
|
|
234
|
+
!compareGreaterThanOrEqualTo(dataValue, filterValue),
|
|
235
|
+
autoRemove: (val: any) => testFalsy(val),
|
|
236
|
+
})
|
|
195
237
|
|
|
196
238
|
/**
|
|
197
239
|
* Keeps rows whose value is less than or equal to the filter value.
|
|
198
240
|
*
|
|
199
241
|
* This is implemented as the inverse of greater-than comparison.
|
|
200
242
|
*/
|
|
201
|
-
export const filterFn_lessThanOrEqualTo =
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
) => {
|
|
207
|
-
return !filterFn_greaterThan(row, columnId, filterValue)
|
|
208
|
-
},
|
|
209
|
-
{ autoRemove: (val: any) => testFalsy(val) },
|
|
210
|
-
)
|
|
243
|
+
export const filterFn_lessThanOrEqualTo = constructFilterFn({
|
|
244
|
+
filter: (dataValue, filterValue) =>
|
|
245
|
+
!compareGreaterThan(dataValue, filterValue),
|
|
246
|
+
autoRemove: (val: any) => testFalsy(val),
|
|
247
|
+
})
|
|
211
248
|
|
|
212
249
|
// Range filters
|
|
213
250
|
|
|
@@ -216,78 +253,26 @@ export const filterFn_lessThanOrEqualTo = Object.assign(
|
|
|
216
253
|
*
|
|
217
254
|
* Blank range endpoints are treated as open-ended.
|
|
218
255
|
*/
|
|
219
|
-
const filterFn_between =
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
if (min !== '' && min !== undefined) {
|
|
227
|
-
if (!filterFn_greaterThan(row, columnId, min)) {
|
|
228
|
-
return false
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
const max = filterValues[1]
|
|
233
|
-
if (max === '' || max === undefined) {
|
|
234
|
-
return true
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
if (min !== '' && min !== undefined) {
|
|
238
|
-
const numericMin = Number(min)
|
|
239
|
-
const numericMax = Number(max)
|
|
240
|
-
if (!isNaN(numericMin) && !isNaN(numericMax) && numericMin > numericMax) {
|
|
241
|
-
return true
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
return filterFn_lessThan(row, columnId, max)
|
|
246
|
-
},
|
|
247
|
-
{
|
|
248
|
-
autoRemove: (val: any) =>
|
|
249
|
-
testFalsy(val) || (testFalsy(val[0]) && testFalsy(val[1])),
|
|
250
|
-
},
|
|
251
|
-
)
|
|
256
|
+
export const filterFn_between = constructFilterFn({
|
|
257
|
+
filter: (dataValue, filterValues: [unknown, unknown]) =>
|
|
258
|
+
compareBetween(dataValue, filterValues, false),
|
|
259
|
+
autoRemove: (val: any) =>
|
|
260
|
+
testFalsy(val) ||
|
|
261
|
+
(Array.isArray(val) && testFalsy(val[0]) && testFalsy(val[1])),
|
|
262
|
+
})
|
|
252
263
|
|
|
253
264
|
/**
|
|
254
265
|
* Keeps rows whose value falls between an inclusive min/max pair.
|
|
255
266
|
*
|
|
256
267
|
* Blank range endpoints are treated as open-ended.
|
|
257
268
|
*/
|
|
258
|
-
const filterFn_betweenInclusive =
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
if (min !== '' && min !== undefined) {
|
|
266
|
-
if (!filterFn_greaterThanOrEqualTo(row, columnId, min)) {
|
|
267
|
-
return false
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
const max = filterValues[1]
|
|
272
|
-
if (max === '' || max === undefined) {
|
|
273
|
-
return true
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
if (min !== '' && min !== undefined) {
|
|
277
|
-
const numericMin = Number(min)
|
|
278
|
-
const numericMax = Number(max)
|
|
279
|
-
if (!isNaN(numericMin) && !isNaN(numericMax) && numericMin > numericMax) {
|
|
280
|
-
return true
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
return filterFn_lessThanOrEqualTo(row, columnId, max)
|
|
285
|
-
},
|
|
286
|
-
{
|
|
287
|
-
autoRemove: (val: any) =>
|
|
288
|
-
testFalsy(val) || (testFalsy(val[0]) && testFalsy(val[1])),
|
|
289
|
-
},
|
|
290
|
-
)
|
|
269
|
+
export const filterFn_betweenInclusive = constructFilterFn({
|
|
270
|
+
filter: (dataValue, filterValues: [unknown, unknown]) =>
|
|
271
|
+
compareBetween(dataValue, filterValues, true),
|
|
272
|
+
autoRemove: (val: any) =>
|
|
273
|
+
testFalsy(val) ||
|
|
274
|
+
(Array.isArray(val) && testFalsy(val[0]) && testFalsy(val[1])),
|
|
275
|
+
})
|
|
291
276
|
|
|
292
277
|
/**
|
|
293
278
|
* Keeps rows whose numeric value is inside an inclusive `[min, max]` range.
|
|
@@ -295,138 +280,156 @@ const filterFn_betweenInclusive = Object.assign(
|
|
|
295
280
|
* Filter values are normalized so blank endpoints become open-ended and
|
|
296
281
|
* reversed endpoints are swapped.
|
|
297
282
|
*/
|
|
298
|
-
export const filterFn_inNumberRange =
|
|
299
|
-
|
|
300
|
-
row: Row<TFeatures, TData>,
|
|
301
|
-
columnId: string,
|
|
302
|
-
filterValue: [number, number],
|
|
303
|
-
) => {
|
|
283
|
+
export const filterFn_inNumberRange = constructFilterFn({
|
|
284
|
+
filter: (dataValue: number, filterValue: [number, number]) => {
|
|
304
285
|
const [min, max] = filterValue
|
|
286
|
+
return dataValue >= min && dataValue <= max
|
|
287
|
+
},
|
|
288
|
+
resolveFilterValue: (val: [any, any]) => {
|
|
289
|
+
const [unsafeMin, unsafeMax] = val
|
|
290
|
+
|
|
291
|
+
const parsedMin =
|
|
292
|
+
typeof unsafeMin !== 'number' ? parseFloat(unsafeMin) : unsafeMin
|
|
293
|
+
const parsedMax =
|
|
294
|
+
typeof unsafeMax !== 'number' ? parseFloat(unsafeMax) : unsafeMax
|
|
295
|
+
|
|
296
|
+
let min =
|
|
297
|
+
unsafeMin === null || Number.isNaN(parsedMin) ? -Infinity : parsedMin
|
|
298
|
+
let max =
|
|
299
|
+
unsafeMax === null || Number.isNaN(parsedMax) ? Infinity : parsedMax
|
|
300
|
+
|
|
301
|
+
if (min > max) {
|
|
302
|
+
const temp = min
|
|
303
|
+
min = max
|
|
304
|
+
max = temp
|
|
305
|
+
}
|
|
305
306
|
|
|
306
|
-
|
|
307
|
-
return rowValue >= min && rowValue <= max
|
|
307
|
+
return [min, max] as const
|
|
308
308
|
},
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
const parsedMin =
|
|
314
|
-
typeof unsafeMin !== 'number' ? parseFloat(unsafeMin) : unsafeMin
|
|
315
|
-
const parsedMax =
|
|
316
|
-
typeof unsafeMax !== 'number' ? parseFloat(unsafeMax) : unsafeMax
|
|
317
|
-
|
|
318
|
-
let min =
|
|
319
|
-
unsafeMin === null || Number.isNaN(parsedMin) ? -Infinity : parsedMin
|
|
320
|
-
let max =
|
|
321
|
-
unsafeMax === null || Number.isNaN(parsedMax) ? Infinity : parsedMax
|
|
322
|
-
|
|
323
|
-
if (min > max) {
|
|
324
|
-
const temp = min
|
|
325
|
-
min = max
|
|
326
|
-
max = temp
|
|
327
|
-
}
|
|
309
|
+
autoRemove: (val: any) =>
|
|
310
|
+
testFalsy(val) ||
|
|
311
|
+
(Array.isArray(val) && testFalsy(val[0]) && testFalsy(val[1])),
|
|
312
|
+
})
|
|
328
313
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
314
|
+
// Date filters
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Keeps rows whose date value is inside an inclusive `[min, max]` date range.
|
|
318
|
+
*
|
|
319
|
+
* Row values and range endpoints may be `Date` objects, timestamps, or
|
|
320
|
+
* parseable date strings. Blank or invalid endpoints become open-ended and
|
|
321
|
+
* reversed endpoints are swapped. Rows without a valid date never match.
|
|
322
|
+
*/
|
|
323
|
+
export const filterFn_inDateRange = constructFilterFn({
|
|
324
|
+
filter: (dataValue: number, filterValue: [number, number]) => {
|
|
325
|
+
const [min, max] = filterValue
|
|
326
|
+
return dataValue >= min && dataValue <= max
|
|
333
327
|
},
|
|
334
|
-
)
|
|
328
|
+
resolveFilterValue: (val: [any, any]) => {
|
|
329
|
+
const [unsafeMin, unsafeMax] = val
|
|
330
|
+
|
|
331
|
+
const parsedMin = toDateTimestamp(unsafeMin)
|
|
332
|
+
const parsedMax = toDateTimestamp(unsafeMax)
|
|
333
|
+
|
|
334
|
+
let min = Number.isNaN(parsedMin) ? -Infinity : parsedMin
|
|
335
|
+
let max = Number.isNaN(parsedMax) ? Infinity : parsedMax
|
|
336
|
+
|
|
337
|
+
if (min > max) {
|
|
338
|
+
const temp = min
|
|
339
|
+
min = max
|
|
340
|
+
max = temp
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
return [min, max] as const
|
|
344
|
+
},
|
|
345
|
+
resolveDataValue: (val: any) => toDateTimestamp(val),
|
|
346
|
+
autoRemove: (val: any) =>
|
|
347
|
+
testFalsy(val) ||
|
|
348
|
+
(Array.isArray(val) && testFalsy(val[0]) && testFalsy(val[1])),
|
|
349
|
+
})
|
|
335
350
|
|
|
336
351
|
// Array filters
|
|
337
352
|
|
|
338
353
|
/**
|
|
339
354
|
* Keeps rows whose scalar column value equals at least one filter value.
|
|
340
355
|
*/
|
|
341
|
-
export const filterFn_arrHas =
|
|
342
|
-
|
|
343
|
-
row: Row<TFeatures, TData>,
|
|
344
|
-
columnId: string,
|
|
345
|
-
filterValue: Array<unknown>,
|
|
346
|
-
) => {
|
|
347
|
-
const value = row.getValue<unknown>(columnId)
|
|
356
|
+
export const filterFn_arrHas = constructFilterFn({
|
|
357
|
+
filter: (dataValue, filterValue: Array<unknown>) => {
|
|
348
358
|
for (let i = 0; i < filterValue.length; i++) {
|
|
349
|
-
if (
|
|
359
|
+
if (dataValue === filterValue[i]) {
|
|
350
360
|
return true
|
|
351
361
|
}
|
|
352
362
|
}
|
|
353
363
|
return false
|
|
354
364
|
},
|
|
355
|
-
|
|
356
|
-
)
|
|
365
|
+
autoRemove: (val: any) => testFalsy(val) || !val?.length,
|
|
366
|
+
})
|
|
357
367
|
|
|
358
368
|
/**
|
|
359
369
|
* Keeps rows whose array or string column value includes at least one filter value.
|
|
360
370
|
*/
|
|
361
|
-
export const filterFn_arrIncludes =
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
columnId: string,
|
|
365
|
-
filterValue: Array<unknown>,
|
|
366
|
-
) => {
|
|
367
|
-
const value = row.getValue<unknown>(columnId)
|
|
368
|
-
if (typeof value !== 'string' && !Array.isArray(value)) {
|
|
371
|
+
export const filterFn_arrIncludes = constructFilterFn({
|
|
372
|
+
filter: (dataValue, filterValue: Array<unknown>) => {
|
|
373
|
+
if (typeof dataValue !== 'string' && !Array.isArray(dataValue)) {
|
|
369
374
|
return false
|
|
370
375
|
}
|
|
371
376
|
|
|
372
377
|
for (let i = 0; i < filterValue.length; i++) {
|
|
373
|
-
if (
|
|
378
|
+
if (dataValue.includes(filterValue[i] as any)) {
|
|
374
379
|
return true
|
|
375
380
|
}
|
|
376
381
|
}
|
|
377
382
|
return false
|
|
378
383
|
},
|
|
379
|
-
|
|
380
|
-
)
|
|
384
|
+
autoRemove: (val: any) => testFalsy(val) || !val?.length,
|
|
385
|
+
})
|
|
381
386
|
|
|
382
387
|
/**
|
|
383
388
|
* Keeps rows whose array column value includes every filter value.
|
|
384
389
|
*/
|
|
385
|
-
export const filterFn_arrIncludesAll =
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
columnId: string,
|
|
389
|
-
filterValue: Array<unknown>,
|
|
390
|
-
) => {
|
|
391
|
-
const value = row.getValue<Array<unknown>>(columnId)
|
|
392
|
-
if (!Array.isArray(value)) return false
|
|
390
|
+
export const filterFn_arrIncludesAll = constructFilterFn({
|
|
391
|
+
filter: (dataValue, filterValue: Array<unknown>) => {
|
|
392
|
+
if (!Array.isArray(dataValue)) return false
|
|
393
393
|
for (let i = 0; i < filterValue.length; i++) {
|
|
394
|
-
if (!
|
|
394
|
+
if (!dataValue.includes(filterValue[i])) {
|
|
395
395
|
return false
|
|
396
396
|
}
|
|
397
397
|
}
|
|
398
398
|
return true
|
|
399
399
|
},
|
|
400
|
-
|
|
401
|
-
)
|
|
400
|
+
autoRemove: (val: any) => testFalsy(val) || !val?.length,
|
|
401
|
+
})
|
|
402
402
|
|
|
403
403
|
/**
|
|
404
404
|
* Keeps rows whose array column value includes at least one filter value.
|
|
405
405
|
*/
|
|
406
|
-
export const filterFn_arrIncludesSome =
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
columnId: string,
|
|
410
|
-
filterValue: Array<unknown>,
|
|
411
|
-
) => {
|
|
412
|
-
const value = row.getValue<Array<unknown>>(columnId)
|
|
413
|
-
if (!Array.isArray(value)) return false
|
|
406
|
+
export const filterFn_arrIncludesSome = constructFilterFn({
|
|
407
|
+
filter: (dataValue, filterValue: Array<unknown>) => {
|
|
408
|
+
if (!Array.isArray(dataValue)) return false
|
|
414
409
|
for (let i = 0; i < filterValue.length; i++) {
|
|
415
|
-
if (
|
|
410
|
+
if (dataValue.includes(filterValue[i])) {
|
|
416
411
|
return true
|
|
417
412
|
}
|
|
418
413
|
}
|
|
419
414
|
return false
|
|
420
415
|
},
|
|
421
|
-
|
|
422
|
-
)
|
|
416
|
+
autoRemove: (val: any) => testFalsy(val) || !val?.length,
|
|
417
|
+
})
|
|
423
418
|
|
|
424
419
|
// Export
|
|
425
420
|
|
|
426
421
|
/**
|
|
427
422
|
* The built-in filter function registry.
|
|
428
423
|
*
|
|
429
|
-
*
|
|
424
|
+
* Registering this full object opts out of tree-shaking: every built-in
|
|
425
|
+
* filter function ends up in your bundle. Prefer importing the `filterFn_*`
|
|
426
|
+
* functions you actually use and registering just those in the `filterFns`
|
|
427
|
+
* slot, or passing them directly to the `filterFn` column option.
|
|
428
|
+
*
|
|
429
|
+
* @deprecated Import individual `filterFn_*` functions instead for a smaller
|
|
430
|
+
* bundle. This export still works and is not going away in v9, but built-in
|
|
431
|
+
* name resolution (including `filterFn: 'auto'`) only finds functions you
|
|
432
|
+
* register yourself.
|
|
430
433
|
*/
|
|
431
434
|
export const filterFns = {
|
|
432
435
|
arrIncludes: filterFn_arrIncludes,
|
|
@@ -435,11 +438,17 @@ export const filterFns = {
|
|
|
435
438
|
arrIncludesSome: filterFn_arrIncludesSome,
|
|
436
439
|
between: filterFn_between,
|
|
437
440
|
betweenInclusive: filterFn_betweenInclusive,
|
|
441
|
+
empty: filterFn_empty,
|
|
442
|
+
endsWith: filterFn_endsWith,
|
|
438
443
|
equals: filterFn_equals,
|
|
439
444
|
equalsString: filterFn_equalsString,
|
|
445
|
+
equalsStringSensitive: filterFn_equalsStringSensitive,
|
|
446
|
+
inDateRange: filterFn_inDateRange,
|
|
440
447
|
inNumberRange: filterFn_inNumberRange,
|
|
441
448
|
includesString: filterFn_includesString,
|
|
442
449
|
includesStringSensitive: filterFn_includesStringSensitive,
|
|
450
|
+
notEmpty: filterFn_notEmpty,
|
|
451
|
+
startsWith: filterFn_startsWith,
|
|
443
452
|
weakEquals: filterFn_weakEquals,
|
|
444
453
|
}
|
|
445
454
|
|
|
@@ -450,3 +459,75 @@ export type BuiltInFilterFn = keyof typeof filterFns
|
|
|
450
459
|
function testFalsy(val: any) {
|
|
451
460
|
return val === undefined || val === null || val === ''
|
|
452
461
|
}
|
|
462
|
+
|
|
463
|
+
function testValueEmpty(dataValue: any) {
|
|
464
|
+
return dataValue == null || String(dataValue).trim() === ''
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
function toDateTimestamp(value: any): number {
|
|
468
|
+
if (value instanceof Date) {
|
|
469
|
+
return value.getTime()
|
|
470
|
+
}
|
|
471
|
+
if (typeof value === 'number') {
|
|
472
|
+
return value
|
|
473
|
+
}
|
|
474
|
+
// Guard nullish and blank values explicitly: `new Date(null)` is the epoch,
|
|
475
|
+
// not an invalid date.
|
|
476
|
+
if (value == null || value === '') {
|
|
477
|
+
return NaN
|
|
478
|
+
}
|
|
479
|
+
return new Date(value).getTime()
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
function compareGreaterThan(dataValue: any, filterValue: any): boolean {
|
|
483
|
+
const numericDataValue = dataValue == null ? 0 : +dataValue
|
|
484
|
+
const numericFilterValue = Number(filterValue)
|
|
485
|
+
|
|
486
|
+
if (!isNaN(numericFilterValue) && !isNaN(numericDataValue)) {
|
|
487
|
+
return numericDataValue > numericFilterValue
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
const stringDataValue = String(dataValue ?? '')
|
|
491
|
+
.toLowerCase()
|
|
492
|
+
.trim()
|
|
493
|
+
const stringFilterValue = String(filterValue).toLowerCase().trim()
|
|
494
|
+
return stringDataValue > stringFilterValue
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
function compareGreaterThanOrEqualTo(dataValue: any, filterValue: any) {
|
|
498
|
+
return dataValue === filterValue || compareGreaterThan(dataValue, filterValue)
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
function compareBetween(
|
|
502
|
+
dataValue: any,
|
|
503
|
+
filterValues: [unknown, unknown],
|
|
504
|
+
inclusive: boolean,
|
|
505
|
+
): boolean {
|
|
506
|
+
const min = filterValues[0]
|
|
507
|
+
const hasMin = min !== '' && min !== undefined
|
|
508
|
+
if (hasMin) {
|
|
509
|
+
const passesMin = inclusive
|
|
510
|
+
? compareGreaterThanOrEqualTo(dataValue, min)
|
|
511
|
+
: compareGreaterThan(dataValue, min)
|
|
512
|
+
if (!passesMin) {
|
|
513
|
+
return false
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
const max = filterValues[1]
|
|
518
|
+
if (max === '' || max === undefined) {
|
|
519
|
+
return true
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
if (hasMin) {
|
|
523
|
+
const numericMin = Number(min)
|
|
524
|
+
const numericMax = Number(max)
|
|
525
|
+
if (!isNaN(numericMin) && !isNaN(numericMax) && numericMin > numericMax) {
|
|
526
|
+
return true
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
return inclusive
|
|
531
|
+
? !compareGreaterThan(dataValue, max)
|
|
532
|
+
: !compareGreaterThanOrEqualTo(dataValue, max)
|
|
533
|
+
}
|