@vuu-ui/vuu-filters 3.4.1 → 3.5.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +12 -12
- package/src/FilterAggregator.js +61 -42
- 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 +2 -5
- package/src/filter-pill/getFilterTooltipText.js +2 -8
- package/src/filter-utils.js +5 -10
- package/src/quick-filters/useQuickFilters.js +6 -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/src/FilterAggregator.d.ts +0 -28
- package/types/src/FilterModel.d.ts +0 -44
- package/types/src/column-filter/ColumnFilter.d.ts +0 -6
- package/types/src/column-filter/useColumnFilter.d.ts +0 -43
- package/types/src/column-filter/utils.d.ts +0 -4
- package/types/src/custom-filters/CustomFilters.d.ts +0 -7
- package/types/src/custom-filters/filterBarFocusManagement.d.ts +0 -1
- package/types/src/custom-filters/index.d.ts +0 -1
- package/types/src/custom-filters/useCustomFilters.d.ts +0 -33
- package/types/src/custom-filters/useFilterState.d.ts +0 -17
- package/types/src/filter-bar/FilterBar.d.ts +0 -33
- package/types/src/filter-bar/FilterBarMenu.d.ts +0 -1
- package/types/src/filter-bar/index.d.ts +0 -2
- package/types/src/filter-bar/useFilterBar.d.ts +0 -7
- package/types/src/filter-clause/ExpandoCombobox.d.ts +0 -10
- package/types/src/filter-clause/FilterClause.d.ts +0 -16
- package/types/src/filter-clause/column-picker/ColumnPicker.d.ts +0 -13
- package/types/src/filter-clause/column-picker/useColumnPicker.d.ts +0 -11
- package/types/src/filter-clause/filterClauseFocusManagement.d.ts +0 -15
- package/types/src/filter-clause/filterClauseTypes.d.ts +0 -12
- package/types/src/filter-clause/index.d.ts +0 -5
- package/types/src/filter-clause/operator-picker/OperatorPicker.d.ts +0 -14
- package/types/src/filter-clause/operator-picker/operator-utils.d.ts +0 -5
- package/types/src/filter-clause/operator-picker/useOperatorPicker.d.ts +0 -12
- package/types/src/filter-clause/useFilterClause.d.ts +0 -26
- package/types/src/filter-clause/value-editors/FilterClauseValueEditor.d.ts +0 -26
- package/types/src/filter-clause/value-editors/FilterClauseValueEditorDate.d.ts +0 -9
- package/types/src/filter-clause/value-editors/FilterClauseValueEditorNumber.d.ts +0 -9
- package/types/src/filter-clause/value-editors/FilterClauseValueEditorText.d.ts +0 -9
- package/types/src/filter-clause/value-editors/FilterClauseValueEditorTime.d.ts +0 -9
- package/types/src/filter-container/ExtendedSingleValueFilterClause.d.ts +0 -21
- package/types/src/filter-container/FilterContainer.d.ts +0 -23
- package/types/src/filter-container/useFilterContainer.d.ts +0 -22
- package/types/src/filter-context-menu/useFilterContextMenu.d.ts +0 -6
- package/types/src/filter-display/FilterDisplay.d.ts +0 -10
- package/types/src/filter-editor/FilterClauseCombinator.d.ts +0 -9
- package/types/src/filter-editor/FilterEditor.d.ts +0 -14
- package/types/src/filter-editor/index.d.ts +0 -1
- package/types/src/filter-editor/useFilterEditor.d.ts +0 -32
- package/types/src/filter-panel/FilterPanel.d.ts +0 -9
- package/types/src/filter-panel/useFilterPanel.d.ts +0 -14
- package/types/src/filter-pill/FilterMenu.d.ts +0 -24
- package/types/src/filter-pill/FilterPill.d.ts +0 -16
- package/types/src/filter-pill/FilterPillNext.d.ts +0 -15
- package/types/src/filter-pill/filterAsReactNode.d.ts +0 -3
- package/types/src/filter-pill/getFilterLabel.d.ts +0 -2
- package/types/src/filter-pill/getFilterTooltipText.d.ts +0 -2
- package/types/src/filter-pill/index.d.ts +0 -1
- package/types/src/filter-pill/useFilterPillNext.d.ts +0 -25
- package/types/src/filter-pill-menu/FilterPillMenu.d.ts +0 -17
- package/types/src/filter-pill-menu/FilterPillMenuOptions.d.ts +0 -12
- package/types/src/filter-pill-menu/index.d.ts +0 -1
- package/types/src/filter-provider/FilterContext.d.ts +0 -39
- package/types/src/filter-provider/FilterProvider.d.ts +0 -10
- package/types/src/filter-provider/filter-descriptor-utils.d.ts +0 -7
- package/types/src/filter-toggle-button/FilterToggleButton.d.ts +0 -9
- package/types/src/filter-utils.d.ts +0 -60
- package/types/src/index.d.ts +0 -24
- package/types/src/inline-filter/InlineFilter.d.ts +0 -12
- package/types/src/inline-filter/index.d.ts +0 -1
- package/types/src/inline-filter/useInlineFilter.d.ts +0 -5
- package/types/src/quick-filters/QuickFilters.d.ts +0 -17
- package/types/src/quick-filters/index.d.ts +0 -1
- package/types/src/quick-filters/useQuickFilters.d.ts +0 -12
- package/types/src/saved-filters/DeleteFilterPrompt.d.ts +0 -9
- package/types/src/saved-filters/FilterNamePrompt.d.ts +0 -6
- package/types/src/saved-filters/SavedFilterPanel.d.ts +0 -14
- package/types/src/saved-filters/useFilterNamePrompt.d.ts +0 -28
- package/types/src/saved-filters/useSavedFilterPanel.d.ts +0 -13
- package/types/src/tabbed-filter-container/TabbedFilterContainer.d.ts +0 -10
- package/types/src/toggle-filter/ToggleFilter.d.ts +0 -17
- package/types/src/toggle-filter/useToggleFilter.d.ts +0 -11
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "3.
|
|
2
|
+
"version": "3.5.0-beta.1",
|
|
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": "3.
|
|
14
|
-
"@vuu-ui/vuu-protocol-types": "3.
|
|
15
|
-
"@vuu-ui/vuu-table-types": "3.
|
|
16
|
-
"@vuu-ui/vuu-filter-types": "3.
|
|
13
|
+
"@vuu-ui/vuu-data-types": "3.5.0-beta.1",
|
|
14
|
+
"@vuu-ui/vuu-protocol-types": "3.5.0-beta.1",
|
|
15
|
+
"@vuu-ui/vuu-table-types": "3.5.0-beta.1",
|
|
16
|
+
"@vuu-ui/vuu-filter-types": "3.5.0-beta.1"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@vuu-ui/vuu-context-menu": "3.
|
|
20
|
-
"@vuu-ui/vuu-data-react": "3.
|
|
21
|
-
"@vuu-ui/vuu-filter-parser": "3.
|
|
22
|
-
"@vuu-ui/vuu-popups": "3.
|
|
23
|
-
"@vuu-ui/vuu-ui-controls": "3.
|
|
24
|
-
"@vuu-ui/vuu-table": "3.
|
|
25
|
-
"@vuu-ui/vuu-utils": "3.
|
|
19
|
+
"@vuu-ui/vuu-context-menu": "3.5.0-beta.1",
|
|
20
|
+
"@vuu-ui/vuu-data-react": "3.5.0-beta.1",
|
|
21
|
+
"@vuu-ui/vuu-filter-parser": "3.5.0-beta.1",
|
|
22
|
+
"@vuu-ui/vuu-popups": "3.5.0-beta.1",
|
|
23
|
+
"@vuu-ui/vuu-ui-controls": "3.5.0-beta.1",
|
|
24
|
+
"@vuu-ui/vuu-table": "3.5.0-beta.1",
|
|
25
|
+
"@vuu-ui/vuu-utils": "3.5.0-beta.1",
|
|
26
26
|
"@salt-ds/core": "1.54.1",
|
|
27
27
|
"@salt-ds/lab": "1.0.0-alpha.83",
|
|
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 }) {
|
|
@@ -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() {
|
|
@@ -4,7 +4,7 @@ import { useCallback, useMemo, useRef, useState } from "react";
|
|
|
4
4
|
import { ColumnFilterContext, useFilterContainer, useFilterContext } from "./useFilterContainer.js";
|
|
5
5
|
import { ColumnFilter } from "../column-filter/ColumnFilter.js";
|
|
6
6
|
import { filterDescriptorHasFilter, isNullFilter, useSavedFilters } from "../filter-provider/FilterContext.js";
|
|
7
|
-
import { filtersAreEqual, getColumnValueFromFilter, isBetweenOperator } from "@vuu-ui/vuu-utils";
|
|
7
|
+
import { filtersAreEqual, getColumnValueFromFilter, getDefaultTimeRange, isBetweenOperator, isTimeDataValue } 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";
|
|
@@ -25,10 +25,7 @@ const FilterContainerColumnFilter = ({ column, operator = "=", variant, ...props
|
|
|
25
25
|
if (currentFilterRef.current.id !== currentFilter.id || !filtersAreEqual(currentFilterRef.current.filter, currentFilter.filter)) {
|
|
26
26
|
currentFilterRef.current = currentFilter;
|
|
27
27
|
if (isNullFilter(currentFilter) && notEmpty(valueRef.current)) {
|
|
28
|
-
if (Array.isArray(valueRef.current)) if (
|
|
29
|
-
"00:00:00",
|
|
30
|
-
"23:59:59"
|
|
31
|
-
];
|
|
28
|
+
if (Array.isArray(valueRef.current)) if (isTimeDataValue(column)) valueRef.current = getDefaultTimeRange(column);
|
|
32
29
|
else valueRef.current = [
|
|
33
30
|
"",
|
|
34
31
|
""
|
|
@@ -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)=>{
|
package/src/filter-utils.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { extractFilterForColumn, filtersAreEqual, formatTemporalFilterValue, isAndFilter, isBetweenFilter, isInFilter, isMultiClauseFilter, isMultiValueFilter, isOrFilter, isSingleValueFilter, isTemporal, partition } from "@vuu-ui/vuu-utils";
|
|
2
2
|
const AND = "and";
|
|
3
3
|
const EQUALS = "=";
|
|
4
4
|
const GREATER_THAN = ">";
|
|
@@ -260,6 +260,7 @@ const getNumericFilter = (column, op, value)=>{
|
|
|
260
260
|
value
|
|
261
261
|
};
|
|
262
262
|
};
|
|
263
|
+
const formatValueForDisplay = (value, column)=>isTemporal(column) ? formatTemporalFilterValue(value, column) : value.toString();
|
|
263
264
|
const getFilterClausesForDisplay = (filter, columns = [], clauses = [])=>{
|
|
264
265
|
if (void 0 === filter) ;
|
|
265
266
|
else if (isSingleValueFilter(filter)) {
|
|
@@ -268,7 +269,7 @@ const getFilterClausesForDisplay = (filter, columns = [], clauses = [])=>{
|
|
|
268
269
|
const { name, label = name } = column;
|
|
269
270
|
clauses.push([
|
|
270
271
|
label,
|
|
271
|
-
filter.value
|
|
272
|
+
formatValueForDisplay(filter.value, column)
|
|
272
273
|
]);
|
|
273
274
|
} else clauses.push([
|
|
274
275
|
filter.column,
|
|
@@ -277,17 +278,11 @@ const getFilterClausesForDisplay = (filter, columns = [], clauses = [])=>{
|
|
|
277
278
|
} else if (isBetweenFilter(filter)) {
|
|
278
279
|
const [f1, f2] = filter.filters;
|
|
279
280
|
const column = columns.find((c)=>c.name === f1.column);
|
|
280
|
-
if (
|
|
281
|
-
const { name, label = name } = column;
|
|
282
|
-
clauses.push([
|
|
283
|
-
label,
|
|
284
|
-
`${Time.millisToTimeString(f1.value)} - ${Time.millisToTimeString(f2.value)}`
|
|
285
|
-
]);
|
|
286
|
-
} else if (column) {
|
|
281
|
+
if (column) {
|
|
287
282
|
const { name, label = name } = column;
|
|
288
283
|
clauses.push([
|
|
289
284
|
label,
|
|
290
|
-
`${f1.value} - ${f2.value}`
|
|
285
|
+
`${formatValueForDisplay(f1.value, column)} - ${formatValueForDisplay(f2.value, column)}`
|
|
291
286
|
]);
|
|
292
287
|
} else clauses.push([
|
|
293
288
|
f1.column,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isNumericColumn, queryClosest } from "@vuu-ui/vuu-utils";
|
|
1
|
+
import { getTypedValueForDescriptor, isNumericColumn, isTemporalColumn, queryClosest } from "@vuu-ui/vuu-utils";
|
|
2
2
|
import { useCallback, useMemo, useRef } from "react";
|
|
3
3
|
const findColumn = (columns, name)=>{
|
|
4
4
|
const column = columns?.find((col)=>col.name === name);
|
|
@@ -38,6 +38,11 @@ const createFilterClause = ([identifier, value], availableColumns)=>{
|
|
|
38
38
|
} else throw Error("columns must be provided for find operation");
|
|
39
39
|
{
|
|
40
40
|
const column = findColumn(availableColumns, identifier);
|
|
41
|
+
if (isTemporalColumn(column)) return {
|
|
42
|
+
column: identifier,
|
|
43
|
+
op: "=",
|
|
44
|
+
value: getTypedValueForDescriptor(value, column, true)
|
|
45
|
+
};
|
|
41
46
|
if (!isNumericColumn(column)) return {
|
|
42
47
|
column: identifier,
|
|
43
48
|
op: "contains",
|
|
@@ -1,9 +1,20 @@
|
|
|
1
|
+
import { type TemporalInfo } from "@vuu-ui/vuu-utils";
|
|
1
2
|
import type { NumericFilterClauseOp } from "@vuu-ui/vuu-filter-types";
|
|
2
3
|
import type { FilterClauseValueEditor } from "../filterClauseTypes";
|
|
3
4
|
interface FilterClauseValueEditorDateProps extends Pick<FilterClauseValueEditor, "onChangeValue" | "inputProps"> {
|
|
4
5
|
className?: string;
|
|
5
|
-
value: number | undefined;
|
|
6
6
|
operator: NumericFilterClauseOp;
|
|
7
|
+
temporalInfo: TemporalInfo;
|
|
8
|
+
/**
|
|
9
|
+
* epoch value in the column encoding (millis number or nanos string)
|
|
10
|
+
*/
|
|
11
|
+
value: number | string | undefined;
|
|
7
12
|
}
|
|
8
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Date filter value editor. The value emitted is the start of the selected day
|
|
15
|
+
* (in the column time zone), in the column encoding. When the filter query is
|
|
16
|
+
* created, the clause is applied to the whole day e.g. '=' selects all values
|
|
17
|
+
* on that day, '>' selects all values after that day (see temporalFilterAsQuery).
|
|
18
|
+
*/
|
|
19
|
+
export declare const FilterClauseValueEditorDate: ({ className, inputProps, onChangeValue, temporalInfo, value, }: FilterClauseValueEditorDateProps) => import("react/jsx-runtime").JSX.Element;
|
|
9
20
|
export {};
|
|
@@ -1,9 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { type TemporalInfo } from "@vuu-ui/vuu-utils";
|
|
2
|
+
import type { NumericFilterClauseOp } from "@vuu-ui/vuu-filter-types";
|
|
3
|
+
import type { FilterClauseValueEditor } from "../filterClauseTypes";
|
|
3
4
|
interface FilterClauseValueEditorTimeProps extends Pick<FilterClauseValueEditor, "onChangeValue" | "inputProps"> {
|
|
4
5
|
className?: string;
|
|
5
|
-
value: number | undefined;
|
|
6
6
|
operator: NumericFilterClauseOp;
|
|
7
|
+
temporalInfo: TemporalInfo;
|
|
8
|
+
/**
|
|
9
|
+
* A TimeString (hh:mm:ss) or, for filters created by an earlier version,
|
|
10
|
+
* an epoch value.
|
|
11
|
+
*/
|
|
12
|
+
value: number | string | undefined;
|
|
7
13
|
}
|
|
8
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Time filter value editor. Emits a TimeString value. When the filter query
|
|
16
|
+
* is created, the time is resolved against today's date (in the column time
|
|
17
|
+
* zone), so a saved filter always applies to the current day.
|
|
18
|
+
*/
|
|
19
|
+
export declare const FilterClauseValueEditorTime: ({ className, onChangeValue, temporalInfo, value, }: FilterClauseValueEditorTimeProps) => import("react/jsx-runtime").JSX.Element;
|
|
9
20
|
export {};
|
|
@@ -4,6 +4,11 @@ export declare const isTimeToday: (options: ExtendedFilterOptions) => options is
|
|
|
4
4
|
export interface SerializableFilter {
|
|
5
5
|
asQuery: () => string;
|
|
6
6
|
}
|
|
7
|
+
/**
|
|
8
|
+
* A filter clause on a time of day value (TimeString). The time is resolved
|
|
9
|
+
* against a date ('today' by default) only when the query is created, so a
|
|
10
|
+
* persisted filter remains relative.
|
|
11
|
+
*/
|
|
7
12
|
export declare class ExtendedSingleValueFilterClause implements SerializableSingleValueFilterClause {
|
|
8
13
|
#private;
|
|
9
14
|
column: string;
|
|
@@ -11,6 +16,7 @@ export declare class ExtendedSingleValueFilterClause implements SerializableSing
|
|
|
11
16
|
value: VuuRowDataItemType;
|
|
12
17
|
constructor(column: string, op: SingleValueFilterClauseOp, value: VuuRowDataItemType, extendedOptions: ExtendedFilterOptions);
|
|
13
18
|
name?: string | undefined;
|
|
19
|
+
get extendedOptions(): ExtendedFilterOptions;
|
|
14
20
|
asQuery(): string;
|
|
15
21
|
toJSON(): {
|
|
16
22
|
column: string;
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { ColumnFilterValue, ExtendedFilterOptions, FilterClauseOp, FilterContainerFilter } from "@vuu-ui/vuu-filter-types";
|
|
2
|
-
import { ColumnDescriptor } from "@vuu-ui/vuu-table-types";
|
|
3
|
-
/**
|
|
4
|
-
* Manages a filter that can be updated one clause at a time.
|
|
5
|
-
* Works with FilterContainer to aggregate multiple filter
|
|
6
|
-
* clauses edited via individual controls. It is just a wrapper
|
|
7
|
-
* around a Map, does not support switching filters - create a
|
|
8
|
-
* new FilterAggregator for a new filter.
|
|
9
|
-
*
|
|
10
|
-
*/
|
|
11
|
-
export declare class FilterAggregator {
|
|
12
|
-
#private;
|
|
13
|
-
constructor(filter?: FilterContainerFilter);
|
|
14
|
-
add(column: ColumnDescriptor, value: ColumnFilterValue, op?: FilterClauseOp | "between" | "between-inclusive", extendedFilterOptions?: ExtendedFilterOptions): void;
|
|
15
|
-
has({ name }: ColumnDescriptor): boolean;
|
|
16
|
-
get({ name }: ColumnDescriptor): FilterContainerFilter | undefined;
|
|
17
|
-
clear(): void;
|
|
18
|
-
/**
|
|
19
|
-
* Remove filter for this column. Return false if no filter found, otw true
|
|
20
|
-
*/
|
|
21
|
-
remove(column: ColumnDescriptor): boolean;
|
|
22
|
-
get isEmpty(): boolean;
|
|
23
|
-
/**
|
|
24
|
-
* Count of the number of columns for which filters are stored
|
|
25
|
-
*/
|
|
26
|
-
get count(): number;
|
|
27
|
-
get filter(): FilterContainerFilter | undefined;
|
|
28
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { Filter, FilterClause, FilterClauseOp, FilterCombinatorOp } from "@vuu-ui/vuu-filter-types";
|
|
2
|
-
import { EventEmitter } from "@vuu-ui/vuu-utils";
|
|
3
|
-
export type FilterStatusChangeHandler = (isValid: boolean) => void;
|
|
4
|
-
export type FilterChangeHandler = (filter: Partial<Filter>, isValid: boolean) => void;
|
|
5
|
-
export type FilterClauseChangeHandler = (filter: Partial<FilterClause>, isValid: boolean) => void;
|
|
6
|
-
export type FilterClauseModelEvents = {
|
|
7
|
-
filterClause: FilterClauseChangeHandler;
|
|
8
|
-
isValid: FilterStatusChangeHandler;
|
|
9
|
-
};
|
|
10
|
-
export type FilterModelEvents = {
|
|
11
|
-
filter: FilterChangeHandler;
|
|
12
|
-
isValid: FilterStatusChangeHandler;
|
|
13
|
-
};
|
|
14
|
-
export declare class FilterClauseModel extends EventEmitter<FilterClauseModelEvents> {
|
|
15
|
-
#private;
|
|
16
|
-
constructor(filterClause: Partial<FilterClause>);
|
|
17
|
-
get isValid(): boolean;
|
|
18
|
-
private setIsValid;
|
|
19
|
-
get column(): undefined | string;
|
|
20
|
-
set column(column: undefined | string);
|
|
21
|
-
get op(): "in" | import("@vuu-ui/vuu-filter-types").SingleValueFilterClauseOp | undefined;
|
|
22
|
-
setOp(op: undefined | FilterClauseOp): void;
|
|
23
|
-
setValue(value: undefined | string | string[] | number | number[] | boolean | boolean[], isFinal?: boolean): void;
|
|
24
|
-
asFilter(throwIfInvalid?: boolean): FilterClause;
|
|
25
|
-
commit(): void;
|
|
26
|
-
}
|
|
27
|
-
export declare class FilterModel extends EventEmitter<FilterModelEvents> {
|
|
28
|
-
#private;
|
|
29
|
-
constructor(filter?: Filter);
|
|
30
|
-
get isValid(): boolean;
|
|
31
|
-
private setIsValid;
|
|
32
|
-
addNewFilterClause(operator?: FilterCombinatorOp): void;
|
|
33
|
-
addFilterClause(filterClause?: Partial<FilterClause>): void;
|
|
34
|
-
removeFilterClause(filterClause: FilterClauseModel): void;
|
|
35
|
-
checkValidStatus(): void;
|
|
36
|
-
onFilterClauseChange: FilterChangeHandler;
|
|
37
|
-
onFilterClauseStatusChange: (isValid: boolean) => void;
|
|
38
|
-
getFilterClause(index: number): FilterClauseModel;
|
|
39
|
-
get op(): FilterCombinatorOp | undefined;
|
|
40
|
-
setOp(op: FilterCombinatorOp): void;
|
|
41
|
-
get filterClauses(): FilterClauseModel[];
|
|
42
|
-
get isMultiClauseFilter(): boolean;
|
|
43
|
-
asFilter(throwIfInvalid?: boolean): Filter;
|
|
44
|
-
}
|