@vuu-ui/vuu-filters 4.0.0-alpha.17 → 4.0.0-alpha.19
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/package.json +12 -12
- package/src/FilterAggregator.js +61 -42
- package/src/column-filter/ColumnFilter.css.js +4 -0
- package/src/column-filter/ColumnFilter.js +18 -7
- package/src/filter-clause/operator-picker/operator-utils.js +2 -2
- package/src/filter-clause/value-editors/FilterClauseValueEditor.js +8 -5
- package/src/filter-clause/value-editors/FilterClauseValueEditorDate.js +11 -43
- package/src/filter-clause/value-editors/FilterClauseValueEditorTime.js +16 -42
- package/src/filter-container/ExtendedSingleValueFilterClause.js +21 -7
- package/src/filter-container/FilterContainer.js +21 -8
- package/src/filter-context-menu/useFilterContextMenu.js +36 -9
- package/src/filter-pill/getFilterTooltipText.js +2 -8
- package/src/filter-provider/FilterContext.js +20 -1
- package/src/filter-provider/FilterProvider.js +20 -2
- package/src/filter-utils.js +5 -10
- package/src/index.js +1 -1
- package/src/quick-filters/useQuickFilters.js +6 -1
- package/src/toggle-filter/ToggleFilter.css.js +8 -0
- package/src/toggle-filter/ToggleFilter.js +21 -11
- package/src/toggle-filter/useToggleFilter.js +57 -33
- package/types/column-filter/ColumnFilter.d.ts +14 -1
- package/types/filter-clause/value-editors/FilterClauseValueEditorDate.d.ts +13 -2
- package/types/filter-clause/value-editors/FilterClauseValueEditorTime.d.ts +15 -4
- package/types/filter-container/ExtendedSingleValueFilterClause.d.ts +6 -0
- package/types/filter-container/FilterContainer.d.ts +8 -1
- package/types/filter-context-menu/useFilterContextMenu.d.ts +11 -1
- package/types/filter-provider/FilterContext.d.ts +34 -0
- package/types/filter-provider/FilterProvider.d.ts +19 -1
- package/types/index.d.ts +3 -3
- package/types/toggle-filter/ToggleFilter.d.ts +5 -0
- package/types/toggle-filter/useToggleFilter.d.ts +19 -1
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "4.0.0-alpha.
|
|
2
|
+
"version": "4.0.0-alpha.19",
|
|
3
3
|
"main": "./src/index.js",
|
|
4
4
|
"author": "heswell",
|
|
5
5
|
"license": "Apache-2.0",
|
|
@@ -10,19 +10,19 @@
|
|
|
10
10
|
"type-defs": "node ../../scripts/build-type-defs.mjs"
|
|
11
11
|
},
|
|
12
12
|
"devDependencies": {
|
|
13
|
-
"@vuu-ui/vuu-data-types": "4.0.0-alpha.
|
|
14
|
-
"@vuu-ui/vuu-protocol-types": "4.0.0-alpha.
|
|
15
|
-
"@vuu-ui/vuu-table-types": "4.0.0-alpha.
|
|
16
|
-
"@vuu-ui/vuu-filter-types": "4.0.0-alpha.
|
|
13
|
+
"@vuu-ui/vuu-data-types": "4.0.0-alpha.19",
|
|
14
|
+
"@vuu-ui/vuu-protocol-types": "4.0.0-alpha.19",
|
|
15
|
+
"@vuu-ui/vuu-table-types": "4.0.0-alpha.19",
|
|
16
|
+
"@vuu-ui/vuu-filter-types": "4.0.0-alpha.19"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@vuu-ui/vuu-context-menu": "4.0.0-alpha.
|
|
20
|
-
"@vuu-ui/vuu-data-react": "4.0.0-alpha.
|
|
21
|
-
"@vuu-ui/vuu-filter-parser": "4.0.0-alpha.
|
|
22
|
-
"@vuu-ui/vuu-popups": "4.0.0-alpha.
|
|
23
|
-
"@vuu-ui/vuu-ui-controls": "4.0.0-alpha.
|
|
24
|
-
"@vuu-ui/vuu-table": "4.0.0-alpha.
|
|
25
|
-
"@vuu-ui/vuu-utils": "4.0.0-alpha.
|
|
19
|
+
"@vuu-ui/vuu-context-menu": "4.0.0-alpha.19",
|
|
20
|
+
"@vuu-ui/vuu-data-react": "4.0.0-alpha.19",
|
|
21
|
+
"@vuu-ui/vuu-filter-parser": "4.0.0-alpha.19",
|
|
22
|
+
"@vuu-ui/vuu-popups": "4.0.0-alpha.19",
|
|
23
|
+
"@vuu-ui/vuu-ui-controls": "4.0.0-alpha.19",
|
|
24
|
+
"@vuu-ui/vuu-table": "4.0.0-alpha.19",
|
|
25
|
+
"@vuu-ui/vuu-utils": "4.0.0-alpha.19",
|
|
26
26
|
"@salt-ds/core": "1.67.0",
|
|
27
27
|
"@salt-ds/lab": "1.0.0-alpha.99",
|
|
28
28
|
"@salt-ds/styles": "0.2.1",
|
package/src/FilterAggregator.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { EpochTimestamp, getTemporalInfo, getTypedValueForDescriptor, isAndFilter, isBetweenFilter, isExtendedFilter, isMultiClauseFilter, isSingleValueFilter } from "@vuu-ui/vuu-utils";
|
|
2
2
|
import { ExtendedSingleValueFilterClause } from "./filter-container/ExtendedSingleValueFilterClause.js";
|
|
3
3
|
function installExtendedFilters(filter, throwIfUndefined = false) {
|
|
4
4
|
if (void 0 !== filter) if (isExtendedFilter(filter)) {
|
|
@@ -12,6 +12,47 @@ function installExtendedFilters(filter, throwIfUndefined = false) {
|
|
|
12
12
|
else return filter;
|
|
13
13
|
if (throwIfUndefined) throw Error("filter is undefined");
|
|
14
14
|
}
|
|
15
|
+
const getExtendedFilterOptions = (temporalInfo, extendedFilterOptions)=>{
|
|
16
|
+
if (temporalInfo?.kind === "time") return {
|
|
17
|
+
type: "TimeString",
|
|
18
|
+
date: "today",
|
|
19
|
+
encoding: temporalInfo.encoding,
|
|
20
|
+
timeZone: temporalInfo.timeZone,
|
|
21
|
+
...extendedFilterOptions
|
|
22
|
+
};
|
|
23
|
+
return extendedFilterOptions;
|
|
24
|
+
};
|
|
25
|
+
const getFilterValue = (column, value, temporalInfo, options, throwIfInvalid)=>{
|
|
26
|
+
if ("" === value) {
|
|
27
|
+
if (throwIfInvalid && !temporalInfo) return getTypedValueForDescriptor(value, column, true);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (options?.type === "TimeString") return asFilterTimeString(value, temporalInfo, throwIfInvalid);
|
|
31
|
+
return getTypedValueForDescriptor(value, column, throwIfInvalid);
|
|
32
|
+
};
|
|
33
|
+
const timeOfDayPattern = /^\d{2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?$/;
|
|
34
|
+
const asFilterTimeString = (value, temporalInfo, throwIfInvalid)=>{
|
|
35
|
+
if (timeOfDayPattern.test(value)) return 5 === value.length ? `${value}:00` : value;
|
|
36
|
+
const timestamp = EpochTimestamp.fromWire(value, temporalInfo?.encoding ?? "epochMillis");
|
|
37
|
+
if (timestamp && /^\d+$/.test(value)) {
|
|
38
|
+
const { hour, minute, second } = timestamp.getFields(temporalInfo?.timeZone);
|
|
39
|
+
const pad = (n)=>`${n}`.padStart(2, "0");
|
|
40
|
+
return `${pad(hour)}:${pad(minute)}:${pad(second)}`;
|
|
41
|
+
}
|
|
42
|
+
if (throwIfInvalid) throw Error(`[FilterAggregator] value ${value} is not a valid time`);
|
|
43
|
+
};
|
|
44
|
+
const singleValueOps = [
|
|
45
|
+
"=",
|
|
46
|
+
"!=",
|
|
47
|
+
">",
|
|
48
|
+
">=",
|
|
49
|
+
"<",
|
|
50
|
+
"<=",
|
|
51
|
+
"contains",
|
|
52
|
+
"starts",
|
|
53
|
+
"ends"
|
|
54
|
+
];
|
|
55
|
+
const asSingleValueOp = (op)=>singleValueOps.includes(op) ? op : "=";
|
|
15
56
|
class FilterAggregator {
|
|
16
57
|
#filters = new Map();
|
|
17
58
|
constructor(filter){
|
|
@@ -24,51 +65,29 @@ class FilterAggregator {
|
|
|
24
65
|
});
|
|
25
66
|
}
|
|
26
67
|
add(column, value, op = "=", extendedFilterOptions) {
|
|
27
|
-
const
|
|
68
|
+
const temporalInfo = getTemporalInfo(column);
|
|
69
|
+
const options = getExtendedFilterOptions(temporalInfo, extendedFilterOptions);
|
|
28
70
|
const isInclusive = "between-inclusive" === op;
|
|
29
|
-
const
|
|
30
|
-
if (Array.isArray(value)) {
|
|
31
|
-
const [value1, value2] = getTypedRange(value, dataType, extendedFilterOptions);
|
|
32
|
-
if (void 0 !== value1 && void 0 !== value2) {
|
|
33
|
-
const filter = extendedFilterOptions ? {
|
|
34
|
-
op: "and",
|
|
35
|
-
filters: [
|
|
36
|
-
new ExtendedSingleValueFilterClause(column.name, isInclusive ? ">=" : ">", value1, extendedFilterOptions),
|
|
37
|
-
new ExtendedSingleValueFilterClause(column.name, isInclusive ? "<=" : "<", value2, extendedFilterOptions)
|
|
38
|
-
]
|
|
39
|
-
} : {
|
|
40
|
-
op: "and",
|
|
41
|
-
filters: [
|
|
42
|
-
{
|
|
43
|
-
column: column.name,
|
|
44
|
-
op: ">",
|
|
45
|
-
value: value1
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
column: column.name,
|
|
49
|
-
op: "<",
|
|
50
|
-
value: value2
|
|
51
|
-
}
|
|
52
|
-
]
|
|
53
|
-
};
|
|
54
|
-
this.#filters.set(column.name, filter);
|
|
55
|
-
} else if (void 0 !== value1) this.#filters.set(column.name, {
|
|
71
|
+
const createClause = (op, value)=>options ? new ExtendedSingleValueFilterClause(column.name, op, value, options) : {
|
|
56
72
|
column: column.name,
|
|
57
|
-
op
|
|
58
|
-
value
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
73
|
+
op,
|
|
74
|
+
value
|
|
75
|
+
};
|
|
76
|
+
if (Array.isArray(value)) {
|
|
77
|
+
const [value1, value2] = value.map((v)=>getFilterValue(column, v, temporalInfo, options, false));
|
|
78
|
+
if (void 0 !== value1 && void 0 !== value2) this.#filters.set(column.name, {
|
|
79
|
+
op: "and",
|
|
80
|
+
filters: [
|
|
81
|
+
createClause(isInclusive ? ">=" : ">", value1),
|
|
82
|
+
createClause(isInclusive ? "<=" : "<", value2)
|
|
83
|
+
]
|
|
64
84
|
});
|
|
85
|
+
else if (void 0 !== value1) this.#filters.set(column.name, createClause("=", value1));
|
|
86
|
+
else if (void 0 !== value2) this.#filters.set(column.name, createClause(isInclusive ? "<=" : "<", value2));
|
|
65
87
|
} else {
|
|
66
|
-
const typedValue =
|
|
67
|
-
this.#filters.
|
|
68
|
-
|
|
69
|
-
op: "=",
|
|
70
|
-
value: typedValue
|
|
71
|
-
});
|
|
88
|
+
const typedValue = getFilterValue(column, value.toString(), temporalInfo, options, true);
|
|
89
|
+
if (void 0 === typedValue) this.#filters.delete(column.name);
|
|
90
|
+
else this.#filters.set(column.name, createClause(temporalInfo ? asSingleValueOp(op) : "=", typedValue));
|
|
72
91
|
}
|
|
73
92
|
}
|
|
74
93
|
has({ name }) {
|
|
@@ -2,23 +2,32 @@ import { jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { SegmentedButtonGroup } from "@salt-ds/core";
|
|
3
3
|
import { getDataItemEditControl } from "@vuu-ui/vuu-data-react";
|
|
4
4
|
import clsx from "clsx";
|
|
5
|
-
import { forwardRef as external_react_forwardRef } from "react";
|
|
5
|
+
import { forwardRef as external_react_forwardRef, useMemo } from "react";
|
|
6
6
|
import { useColumnFilter } from "./useColumnFilter.js";
|
|
7
7
|
import { useComponentCssInjection } from "@salt-ds/styles";
|
|
8
8
|
import { useWindow } from "@salt-ds/window";
|
|
9
9
|
import ColumnFilter_0 from "./ColumnFilter.css";
|
|
10
|
-
import { isBetweenOperator } from "@vuu-ui/vuu-utils";
|
|
10
|
+
import { isBetweenOperator, withDateTimePattern } from "@vuu-ui/vuu-utils";
|
|
11
11
|
const classBase = "vuuColumnFilter";
|
|
12
|
-
const ColumnFilter_ColumnFilter = /*#__PURE__*/ external_react_forwardRef(function({ InputProps: InputPropsProp, TypeaheadProps, className, column, defaultValue, extendedFilterOptions, onColumnFilterChange, onColumnRangeFilterChange, onCommit: onCommitProp, operator = "=", table, value: valueProp, values, variant, ...buttonGroupProps }, forwardRef) {
|
|
12
|
+
const ColumnFilter_ColumnFilter = /*#__PURE__*/ external_react_forwardRef(function({ InputProps: InputPropsProp, TypeaheadProps, className, column, defaultValue, extendedFilterOptions, labels, onColumnFilterChange, onColumnRangeFilterChange, onCommit: onCommitProp, operator = "=", pattern, table, value: valueProp, values, variant, ...buttonGroupProps }, forwardRef) {
|
|
13
13
|
const targetWindow = useWindow();
|
|
14
14
|
useComponentCssInjection({
|
|
15
15
|
testId: "vuu-filter-editor",
|
|
16
16
|
css: ColumnFilter_0,
|
|
17
17
|
window: targetWindow
|
|
18
18
|
});
|
|
19
|
+
const { date, time } = pattern ?? {};
|
|
20
|
+
const controlDescriptor = useMemo(()=>withDateTimePattern(column, date || time ? {
|
|
21
|
+
date,
|
|
22
|
+
time
|
|
23
|
+
} : void 0), [
|
|
24
|
+
column,
|
|
25
|
+
date,
|
|
26
|
+
time
|
|
27
|
+
]);
|
|
19
28
|
const { InputProps, InputPropsRange, isInvalid, onCommit, onCommitRange } = useColumnFilter({
|
|
20
29
|
InputProps: InputPropsProp,
|
|
21
|
-
column,
|
|
30
|
+
column: controlDescriptor,
|
|
22
31
|
defaultValue,
|
|
23
32
|
extendedFilterOptions,
|
|
24
33
|
onColumnFilterChange,
|
|
@@ -30,7 +39,8 @@ const ColumnFilter_ColumnFilter = /*#__PURE__*/ external_react_forwardRef(functi
|
|
|
30
39
|
return /*#__PURE__*/ jsxs(SegmentedButtonGroup, {
|
|
31
40
|
...buttonGroupProps,
|
|
32
41
|
className: clsx(classBase, className, {
|
|
33
|
-
[`${classBase}-invalid`]: isInvalid
|
|
42
|
+
[`${classBase}-invalid`]: isInvalid,
|
|
43
|
+
[`${classBase}-toggle`]: "toggle" === variant
|
|
34
44
|
}),
|
|
35
45
|
ref: forwardRef,
|
|
36
46
|
children: [
|
|
@@ -38,7 +48,8 @@ const ColumnFilter_ColumnFilter = /*#__PURE__*/ external_react_forwardRef(functi
|
|
|
38
48
|
InputProps,
|
|
39
49
|
TypeaheadProps,
|
|
40
50
|
commitWhenCleared: true,
|
|
41
|
-
dataDescriptor:
|
|
51
|
+
dataDescriptor: controlDescriptor,
|
|
52
|
+
labels,
|
|
42
53
|
onCommit,
|
|
43
54
|
table,
|
|
44
55
|
values,
|
|
@@ -49,7 +60,7 @@ const ColumnFilter_ColumnFilter = /*#__PURE__*/ external_react_forwardRef(functi
|
|
|
49
60
|
className: `${classBase}-rangeHigh`,
|
|
50
61
|
commitWhenCleared: true,
|
|
51
62
|
variant,
|
|
52
|
-
dataDescriptor:
|
|
63
|
+
dataDescriptor: controlDescriptor,
|
|
53
64
|
onCommit: onCommitRange,
|
|
54
65
|
table
|
|
55
66
|
}) : null
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isNumericColumn,
|
|
1
|
+
import { isNumericColumn, isTemporalColumn, isTextColumn } from "@vuu-ui/vuu-utils";
|
|
2
2
|
const textOperators = [
|
|
3
3
|
"=",
|
|
4
4
|
"in",
|
|
@@ -17,7 +17,7 @@ const numericOperators = [
|
|
|
17
17
|
];
|
|
18
18
|
const getOperators = (column)=>{
|
|
19
19
|
if (isTextColumn(column)) return textOperators;
|
|
20
|
-
if (isNumericColumn(column) ||
|
|
20
|
+
if (isNumericColumn(column) || isTemporalColumn(column)) return numericOperators;
|
|
21
21
|
throw Error("getOperators only supports text and numeric columns");
|
|
22
22
|
};
|
|
23
23
|
export { getOperators, numericOperators, textOperators };
|
|
@@ -2,26 +2,29 @@ import { jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import clsx from "clsx";
|
|
3
3
|
import { FilterClauseValueEditorNumber } from "./FilterClauseValueEditorNumber.js";
|
|
4
4
|
import { FilterClauseValueEditorText } from "./FilterClauseValueEditorText.js";
|
|
5
|
-
import {
|
|
5
|
+
import { getTemporalInfo } from "@vuu-ui/vuu-utils";
|
|
6
6
|
import { forwardRef } from "react";
|
|
7
7
|
import { FilterClauseValueEditorDate } from "./FilterClauseValueEditorDate.js";
|
|
8
8
|
import { FilterClauseValueEditorTime } from "./FilterClauseValueEditorTime.js";
|
|
9
9
|
const classBase = "vuuFilterClause";
|
|
10
10
|
const FilterClauseValueEditor_FilterClauseValueEditor = /*#__PURE__*/ forwardRef(function({ selectedColumn, operator, inputProps, onChangeValue, onDeselectValue, onOpenChange, table, value, dropdownOnAutofocus = true }, forwardedRef) {
|
|
11
11
|
if (void 0 === selectedColumn || void 0 === operator) return null;
|
|
12
|
-
|
|
12
|
+
const temporalInfo = getTemporalInfo(selectedColumn);
|
|
13
|
+
if (temporalInfo?.kind === "time") return /*#__PURE__*/ jsx(FilterClauseValueEditorTime, {
|
|
13
14
|
inputProps: inputProps,
|
|
14
15
|
className: clsx(`${classBase}Field`, `${classBase}Value`),
|
|
15
16
|
value: value,
|
|
16
17
|
operator: operator,
|
|
17
|
-
onChangeValue: onChangeValue
|
|
18
|
+
onChangeValue: onChangeValue,
|
|
19
|
+
temporalInfo: temporalInfo
|
|
18
20
|
});
|
|
19
|
-
if (
|
|
21
|
+
if (temporalInfo) return /*#__PURE__*/ jsx(FilterClauseValueEditorDate, {
|
|
20
22
|
inputProps: inputProps,
|
|
21
23
|
className: clsx(`${classBase}Field`, `${classBase}Value`),
|
|
22
24
|
value: value,
|
|
23
25
|
operator: operator,
|
|
24
|
-
onChangeValue: onChangeValue
|
|
26
|
+
onChangeValue: onChangeValue,
|
|
27
|
+
temporalInfo: temporalInfo
|
|
25
28
|
});
|
|
26
29
|
switch(selectedColumn.serverDataType){
|
|
27
30
|
case "string":
|
|
@@ -1,55 +1,23 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useCallback
|
|
3
|
-
import {
|
|
4
|
-
import { toCalendarDate } from "@vuu-ui/vuu-utils";
|
|
2
|
+
import { useCallback } from "react";
|
|
3
|
+
import { EpochTimestamp } from "@vuu-ui/vuu-utils";
|
|
5
4
|
import { VuuDatePicker } from "@vuu-ui/vuu-ui-controls";
|
|
6
|
-
const FilterClauseValueEditorDate = (
|
|
7
|
-
const {
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
console.log("change date");
|
|
12
|
-
if (selectedDateInputValue) {
|
|
13
|
-
const dateValue = toCalendarDate(new Date(selectedDateInputValue));
|
|
14
|
-
setDate(dateValue);
|
|
15
|
-
if (selectedDateInputValue) onChangeValue(toEpochMilliS(dateValue));
|
|
16
|
-
}
|
|
5
|
+
const FilterClauseValueEditorDate = ({ className, inputProps, onChangeValue, temporalInfo, value })=>{
|
|
6
|
+
const { encoding, timeZone } = temporalInfo;
|
|
7
|
+
const handleCommit = useCallback((_e, startOfDayMillis)=>{
|
|
8
|
+
const timestamp = EpochTimestamp.fromMillis(startOfDayMillis);
|
|
9
|
+
onChangeValue("epochNanos" === encoding ? timestamp.toWire("epochNanos") : timestamp.toWire("epochMillis"));
|
|
17
10
|
}, [
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
]);
|
|
21
|
-
const onBlur = useCallback(()=>{
|
|
22
|
-
date && onChangeValue(toEpochMilliS(date));
|
|
23
|
-
}, [
|
|
24
|
-
date,
|
|
25
|
-
onChangeValue,
|
|
26
|
-
toEpochMilliS
|
|
11
|
+
encoding,
|
|
12
|
+
onChangeValue
|
|
27
13
|
]);
|
|
28
14
|
return /*#__PURE__*/ jsx(VuuDatePicker, {
|
|
29
15
|
"data-field": "value",
|
|
30
16
|
inputProps: inputProps,
|
|
31
17
|
className: className,
|
|
32
|
-
onBlur: onBlur,
|
|
33
18
|
onCommit: handleCommit,
|
|
34
|
-
|
|
19
|
+
timeZone: timeZone,
|
|
20
|
+
value: EpochTimestamp.fromWire(value, encoding)
|
|
35
21
|
});
|
|
36
22
|
};
|
|
37
|
-
function getInitialState(value) {
|
|
38
|
-
return value ? toCalendarDate(new Date(value)) : void 0;
|
|
39
|
-
}
|
|
40
|
-
const getEpochMillisConverter = (op)=>(date, timezone = getLocalTimeZone())=>{
|
|
41
|
-
const d = date.toDate(timezone);
|
|
42
|
-
switch(op){
|
|
43
|
-
case ">":
|
|
44
|
-
case "<=":
|
|
45
|
-
d.setHours(23, 59, 59, 999);
|
|
46
|
-
return d.getTime();
|
|
47
|
-
case ">=":
|
|
48
|
-
case "<":
|
|
49
|
-
case "=":
|
|
50
|
-
case "!=":
|
|
51
|
-
d.setHours(0, 0, 0, 0);
|
|
52
|
-
return d.getTime();
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
23
|
export { FilterClauseValueEditorDate };
|
|
@@ -1,53 +1,27 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useCallback
|
|
3
|
-
import {
|
|
4
|
-
import { toCalendarDate } from "@vuu-ui/vuu-utils";
|
|
2
|
+
import { useCallback } from "react";
|
|
3
|
+
import { EpochTimestamp, isValidTimeString } from "@vuu-ui/vuu-utils";
|
|
5
4
|
import { VuuTimePicker } from "@vuu-ui/vuu-ui-controls";
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
onChangeValue,
|
|
19
|
-
toEpochMilliS
|
|
20
|
-
]);
|
|
21
|
-
const onBlur = useCallback(()=>{
|
|
22
|
-
date && onChangeValue(toEpochMilliS(date));
|
|
5
|
+
const pad = (n)=>`${n}`.padStart(2, "0");
|
|
6
|
+
const toTimeString = (value, { encoding, timeZone })=>{
|
|
7
|
+
if (isValidTimeString(value)) return value;
|
|
8
|
+
const timestamp = EpochTimestamp.fromWire(value, encoding);
|
|
9
|
+
if (timestamp) {
|
|
10
|
+
const { hour, minute, second } = timestamp.getFields(timeZone);
|
|
11
|
+
return `${pad(hour)}:${pad(minute)}:${pad(second)}`;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
const FilterClauseValueEditorTime = ({ className, onChangeValue, temporalInfo, value })=>{
|
|
15
|
+
const handleCommit = useCallback((_e, timeString)=>{
|
|
16
|
+
if (isValidTimeString(timeString)) onChangeValue(timeString);
|
|
23
17
|
}, [
|
|
24
|
-
|
|
25
|
-
onChangeValue,
|
|
26
|
-
toEpochMilliS
|
|
18
|
+
onChangeValue
|
|
27
19
|
]);
|
|
28
20
|
return /*#__PURE__*/ jsx(VuuTimePicker, {
|
|
29
21
|
"data-field": "value",
|
|
30
22
|
className: className,
|
|
31
|
-
|
|
23
|
+
defaultValue: toTimeString(value, temporalInfo),
|
|
32
24
|
onCommit: handleCommit
|
|
33
25
|
});
|
|
34
26
|
};
|
|
35
|
-
function getInitialState(value) {
|
|
36
|
-
return value ? toCalendarDate(new Date(value)) : void 0;
|
|
37
|
-
}
|
|
38
|
-
const getEpochMillisConverter = (op)=>(date, timezone = getLocalTimeZone())=>{
|
|
39
|
-
const d = date.toDate(timezone);
|
|
40
|
-
switch(op){
|
|
41
|
-
case ">":
|
|
42
|
-
case "<=":
|
|
43
|
-
d.setHours(23, 59, 59, 999);
|
|
44
|
-
return d.getTime();
|
|
45
|
-
case ">=":
|
|
46
|
-
case "<":
|
|
47
|
-
case "=":
|
|
48
|
-
case "!=":
|
|
49
|
-
d.setHours(0, 0, 0, 0);
|
|
50
|
-
return d.getTime();
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
27
|
export { FilterClauseValueEditorTime };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getDefaultTimeZone, isValidTimeString, temporalFilterAsQuery } from "@vuu-ui/vuu-utils";
|
|
2
2
|
const isTimeToday = (options)=>"TimeString" === options.type && "today" === options.date;
|
|
3
|
+
const timeStringWithFraction = /^\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?$/;
|
|
3
4
|
class ExtendedSingleValueFilterClause {
|
|
4
5
|
column;
|
|
5
6
|
op;
|
|
@@ -12,14 +13,27 @@ class ExtendedSingleValueFilterClause {
|
|
|
12
13
|
this.#options = extendedOptions;
|
|
13
14
|
}
|
|
14
15
|
name;
|
|
16
|
+
get extendedOptions() {
|
|
17
|
+
return this.#options;
|
|
18
|
+
}
|
|
15
19
|
asQuery() {
|
|
16
20
|
const { column, op, value } = this;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
const { date, encoding = "epochMillis", timeZone } = this.#options;
|
|
22
|
+
if ("TimeString" === this.#options.type) if ("string" == typeof value && (isValidTimeString(value) || timeStringWithFraction.test(value))) {
|
|
23
|
+
const temporalInfo = {
|
|
24
|
+
kind: "time",
|
|
25
|
+
encoding,
|
|
26
|
+
precision: "epochNanos" === encoding ? "ns" : "ms",
|
|
27
|
+
timeZone: timeZone ?? getDefaultTimeZone()
|
|
28
|
+
};
|
|
29
|
+
return temporalFilterAsQuery({
|
|
30
|
+
column,
|
|
31
|
+
op,
|
|
32
|
+
value
|
|
33
|
+
}, temporalInfo, {
|
|
34
|
+
date: date
|
|
35
|
+
});
|
|
36
|
+
} else throw Error(`[ExtendedSingleValueFilterClause] invalid TimeString ${value}`);
|
|
23
37
|
throw Error("[ExtendedSingleValueFilterClause] unhandled extended filter type");
|
|
24
38
|
}
|
|
25
39
|
toJSON() {
|
|
@@ -1,17 +1,33 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
2
|
import clsx from "clsx";
|
|
3
|
-
import { useCallback, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
4
4
|
import { ColumnFilterContext, useFilterContainer, useFilterContext } from "./useFilterContainer.js";
|
|
5
5
|
import { ColumnFilter } from "../column-filter/ColumnFilter.js";
|
|
6
|
-
import { filterDescriptorHasFilter, isNullFilter, useSavedFilters } from "../filter-provider/FilterContext.js";
|
|
7
|
-
import { filtersAreEqual, getColumnValueFromFilter, isBetweenOperator } from "@vuu-ui/vuu-utils";
|
|
6
|
+
import { filterDescriptorHasFilter, isNullFilter, useColumnFilterRegistry, useSavedFilters } from "../filter-provider/FilterContext.js";
|
|
7
|
+
import { filtersAreEqual, getColumnValueFromFilter, getDefaultTimeRange, isBetweenOperator, isTimeDataValue, withDateTimePattern } from "@vuu-ui/vuu-utils";
|
|
8
8
|
import { useComponentCssInjection } from "@salt-ds/styles";
|
|
9
9
|
import { useWindow } from "@salt-ds/window";
|
|
10
10
|
import FilterContainer from "./FilterContainer.css";
|
|
11
11
|
const classBase = "vuuFilterContainer";
|
|
12
12
|
const notEmpty = (value)=>Array.isArray(value) ? "" !== value[0] && "" !== value[1] : "" !== value;
|
|
13
|
-
const FilterContainerColumnFilter = ({ column, operator = "=", variant, ...props })=>{
|
|
13
|
+
const FilterContainerColumnFilter = ({ column: columnProp, operator = "=", pattern: patternProp, variant, ...props })=>{
|
|
14
|
+
const { getColumnFilterPattern } = useColumnFilterRegistry();
|
|
15
|
+
const pattern = patternProp ?? getColumnFilterPattern(columnProp);
|
|
16
|
+
const { date, time } = pattern ?? {};
|
|
17
|
+
const column = useMemo(()=>withDateTimePattern(columnProp, date || time ? {
|
|
18
|
+
date,
|
|
19
|
+
time
|
|
20
|
+
} : void 0), [
|
|
21
|
+
columnProp,
|
|
22
|
+
date,
|
|
23
|
+
time
|
|
24
|
+
]);
|
|
14
25
|
const { filterProviderKey, onChange: onFilterContextChange, onCommit: onFilterContextCommit, register } = useFilterContext(column, true);
|
|
26
|
+
const { registerColumnFilterColumn } = useColumnFilterRegistry(filterProviderKey);
|
|
27
|
+
useEffect(()=>registerColumnFilterColumn(column), [
|
|
28
|
+
column,
|
|
29
|
+
registerColumnFilterColumn
|
|
30
|
+
]);
|
|
15
31
|
const initialValue = useMemo(()=>register(column, operator), [
|
|
16
32
|
column,
|
|
17
33
|
operator,
|
|
@@ -25,10 +41,7 @@ const FilterContainerColumnFilter = ({ column, operator = "=", variant, ...props
|
|
|
25
41
|
if (currentFilterRef.current.id !== currentFilter.id || !filtersAreEqual(currentFilterRef.current.filter, currentFilter.filter)) {
|
|
26
42
|
currentFilterRef.current = currentFilter;
|
|
27
43
|
if (isNullFilter(currentFilter) && notEmpty(valueRef.current)) {
|
|
28
|
-
if (Array.isArray(valueRef.current)) if (
|
|
29
|
-
"00:00:00",
|
|
30
|
-
"23:59:59"
|
|
31
|
-
];
|
|
44
|
+
if (Array.isArray(valueRef.current)) if (isTimeDataValue(column)) valueRef.current = getDefaultTimeRange(column);
|
|
32
45
|
else valueRef.current = [
|
|
33
46
|
"",
|
|
34
47
|
""
|
|
@@ -1,14 +1,39 @@
|
|
|
1
|
-
import { filtersAreEqual } from "@vuu-ui/vuu-utils";
|
|
1
|
+
import { filtersAreEqual, getTemporalCellFilter, getTemporalInfo, withDateTimePattern } from "@vuu-ui/vuu-utils";
|
|
2
2
|
import { useCallback, useMemo, useRef } from "react";
|
|
3
|
-
import { useSavedFilters } from "../filter-provider/FilterContext.js";
|
|
3
|
+
import { useColumnFilterRegistry, useSavedFilters } from "../filter-provider/FilterContext.js";
|
|
4
4
|
import { FilterAggregator } from "../FilterAggregator.js";
|
|
5
5
|
const EmptyAggregator = new FilterAggregator();
|
|
6
6
|
const defaultProps = {
|
|
7
7
|
filterColumns: "*"
|
|
8
8
|
};
|
|
9
|
-
const useFilterContextMenu = ({ filterColumns = "*", filterProviderKey } = defaultProps)=>{
|
|
9
|
+
const useFilterContextMenu = ({ filterColumns = "*", filterPatterns, filterProviderKey } = defaultProps)=>{
|
|
10
10
|
const { currentFilter, clearCurrentFilter, setCurrentFilter } = useSavedFilters(filterProviderKey);
|
|
11
|
+
const { getColumnFilterColumn, getColumnFilterPattern } = useColumnFilterRegistry(filterProviderKey);
|
|
11
12
|
const filterAggregatorRef = useRef(EmptyAggregator);
|
|
13
|
+
const getCellFilter = useCallback((column, dataRow)=>{
|
|
14
|
+
const value = dataRow[column.name];
|
|
15
|
+
if (getTemporalInfo(column)) {
|
|
16
|
+
const pattern = filterPatterns?.[column.name];
|
|
17
|
+
const columnFilterPattern = getColumnFilterPattern(column);
|
|
18
|
+
const columnFilterColumn = pattern ? void 0 : getColumnFilterColumn(column.name) ?? (columnFilterPattern ? withDateTimePattern(column, columnFilterPattern) : void 0);
|
|
19
|
+
const filterColumn = pattern ? withDateTimePattern(column, pattern) : columnFilterColumn;
|
|
20
|
+
const temporalCellFilter = getTemporalCellFilter(filterColumn ?? column, value, void 0 !== columnFilterColumn);
|
|
21
|
+
if (temporalCellFilter) return {
|
|
22
|
+
column: filterColumn ?? column,
|
|
23
|
+
...temporalCellFilter
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
column,
|
|
28
|
+
label: `${value}`,
|
|
29
|
+
op: "=",
|
|
30
|
+
value
|
|
31
|
+
};
|
|
32
|
+
}, [
|
|
33
|
+
filterPatterns,
|
|
34
|
+
getColumnFilterColumn,
|
|
35
|
+
getColumnFilterPattern
|
|
36
|
+
]);
|
|
12
37
|
useMemo(()=>{
|
|
13
38
|
if (!filtersAreEqual(currentFilter.filter, filterAggregatorRef.current.filter)) filterAggregatorRef.current = currentFilter.filter ? new FilterAggregator(currentFilter.filter) : new FilterAggregator();
|
|
14
39
|
}, [
|
|
@@ -18,13 +43,13 @@ const useFilterContextMenu = ({ filterColumns = "*", filterProviderKey } = defau
|
|
|
18
43
|
const { column, dataRow } = options;
|
|
19
44
|
const { current: fag } = filterAggregatorRef;
|
|
20
45
|
const { name, label = name } = column;
|
|
21
|
-
const value = dataRow[column.name];
|
|
22
46
|
const ClearFilter = {
|
|
23
47
|
id: "filter-clear",
|
|
24
48
|
label: "Clear filter",
|
|
25
49
|
options
|
|
26
50
|
};
|
|
27
51
|
if ("*" === filterColumns || filterColumns.includes(column.name)) {
|
|
52
|
+
const { label: value } = getCellFilter(column, dataRow);
|
|
28
53
|
const SetFilter = {
|
|
29
54
|
id: "filter-set",
|
|
30
55
|
label: `Set filter ${label} '${value}'`,
|
|
@@ -60,7 +85,8 @@ const useFilterContextMenu = ({ filterColumns = "*", filterProviderKey } = defau
|
|
|
60
85
|
];
|
|
61
86
|
return [];
|
|
62
87
|
}, [
|
|
63
|
-
filterColumns
|
|
88
|
+
filterColumns,
|
|
89
|
+
getCellFilter
|
|
64
90
|
]);
|
|
65
91
|
const menuActionHandler = useCallback((menuItemId, options)=>{
|
|
66
92
|
if (!options) return false;
|
|
@@ -73,8 +99,8 @@ const useFilterContextMenu = ({ filterColumns = "*", filterProviderKey } = defau
|
|
|
73
99
|
break;
|
|
74
100
|
case "filter-add":
|
|
75
101
|
{
|
|
76
|
-
const
|
|
77
|
-
fag.add(column, value,
|
|
102
|
+
const cellFilter = getCellFilter(column, dataRow);
|
|
103
|
+
fag.add(cellFilter.column, cellFilter.value, cellFilter.op);
|
|
78
104
|
if (fag.filter) setCurrentFilter(fag.filter);
|
|
79
105
|
}
|
|
80
106
|
break;
|
|
@@ -84,9 +110,9 @@ const useFilterContextMenu = ({ filterColumns = "*", filterProviderKey } = defau
|
|
|
84
110
|
break;
|
|
85
111
|
case "filter-set":
|
|
86
112
|
{
|
|
87
|
-
const
|
|
113
|
+
const cellFilter = getCellFilter(column, dataRow);
|
|
88
114
|
fag.clear();
|
|
89
|
-
fag.add(column, value,
|
|
115
|
+
fag.add(cellFilter.column, cellFilter.value, cellFilter.op);
|
|
90
116
|
if (fag.filter) setCurrentFilter(fag.filter);
|
|
91
117
|
}
|
|
92
118
|
break;
|
|
@@ -96,6 +122,7 @@ const useFilterContextMenu = ({ filterColumns = "*", filterProviderKey } = defau
|
|
|
96
122
|
}
|
|
97
123
|
}, [
|
|
98
124
|
clearCurrentFilter,
|
|
125
|
+
getCellFilter,
|
|
99
126
|
setCurrentFilter
|
|
100
127
|
]);
|
|
101
128
|
return {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { filterAsQuery, formatTemporalFilterValue, isMultiClauseFilter, isTemporal } from "@vuu-ui/vuu-utils";
|
|
2
2
|
import { filterClauses } from "../filter-utils.js";
|
|
3
3
|
function applyFormatter(filter, formatter) {
|
|
4
4
|
if ("value" in filter) return {
|
|
@@ -12,13 +12,7 @@ function applyFormatter(filter, formatter) {
|
|
|
12
12
|
}
|
|
13
13
|
function formatFilterValue(filter, columnsByName) {
|
|
14
14
|
const column = columnsByName?.[filter.column];
|
|
15
|
-
if (column &&
|
|
16
|
-
const pattern = dateTimePattern(column.type);
|
|
17
|
-
const formatter = (n)=>formatDate({
|
|
18
|
-
date: pattern.date ?? defaultPatternsByType.date
|
|
19
|
-
})(new Date(n));
|
|
20
|
-
return applyFormatter(filter, formatter);
|
|
21
|
-
}
|
|
15
|
+
if (column && isTemporal(column)) return applyFormatter(filter, (value)=>formatTemporalFilterValue(value, column));
|
|
22
16
|
return filter;
|
|
23
17
|
}
|
|
24
18
|
const getFilterAsFormattedText = (columnsByName)=>(filter)=>{
|