@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.
Files changed (86) hide show
  1. package/package.json +12 -12
  2. package/src/FilterAggregator.js +61 -42
  3. package/src/filter-clause/operator-picker/operator-utils.js +2 -2
  4. package/src/filter-clause/value-editors/FilterClauseValueEditor.js +8 -5
  5. package/src/filter-clause/value-editors/FilterClauseValueEditorDate.js +11 -43
  6. package/src/filter-clause/value-editors/FilterClauseValueEditorTime.js +16 -42
  7. package/src/filter-container/ExtendedSingleValueFilterClause.js +21 -7
  8. package/src/filter-container/FilterContainer.js +2 -5
  9. package/src/filter-pill/getFilterTooltipText.js +2 -8
  10. package/src/filter-utils.js +5 -10
  11. package/src/quick-filters/useQuickFilters.js +6 -1
  12. package/types/filter-clause/value-editors/FilterClauseValueEditorDate.d.ts +13 -2
  13. package/types/filter-clause/value-editors/FilterClauseValueEditorTime.d.ts +15 -4
  14. package/types/filter-container/ExtendedSingleValueFilterClause.d.ts +6 -0
  15. package/types/src/FilterAggregator.d.ts +0 -28
  16. package/types/src/FilterModel.d.ts +0 -44
  17. package/types/src/column-filter/ColumnFilter.d.ts +0 -6
  18. package/types/src/column-filter/useColumnFilter.d.ts +0 -43
  19. package/types/src/column-filter/utils.d.ts +0 -4
  20. package/types/src/custom-filters/CustomFilters.d.ts +0 -7
  21. package/types/src/custom-filters/filterBarFocusManagement.d.ts +0 -1
  22. package/types/src/custom-filters/index.d.ts +0 -1
  23. package/types/src/custom-filters/useCustomFilters.d.ts +0 -33
  24. package/types/src/custom-filters/useFilterState.d.ts +0 -17
  25. package/types/src/filter-bar/FilterBar.d.ts +0 -33
  26. package/types/src/filter-bar/FilterBarMenu.d.ts +0 -1
  27. package/types/src/filter-bar/index.d.ts +0 -2
  28. package/types/src/filter-bar/useFilterBar.d.ts +0 -7
  29. package/types/src/filter-clause/ExpandoCombobox.d.ts +0 -10
  30. package/types/src/filter-clause/FilterClause.d.ts +0 -16
  31. package/types/src/filter-clause/column-picker/ColumnPicker.d.ts +0 -13
  32. package/types/src/filter-clause/column-picker/useColumnPicker.d.ts +0 -11
  33. package/types/src/filter-clause/filterClauseFocusManagement.d.ts +0 -15
  34. package/types/src/filter-clause/filterClauseTypes.d.ts +0 -12
  35. package/types/src/filter-clause/index.d.ts +0 -5
  36. package/types/src/filter-clause/operator-picker/OperatorPicker.d.ts +0 -14
  37. package/types/src/filter-clause/operator-picker/operator-utils.d.ts +0 -5
  38. package/types/src/filter-clause/operator-picker/useOperatorPicker.d.ts +0 -12
  39. package/types/src/filter-clause/useFilterClause.d.ts +0 -26
  40. package/types/src/filter-clause/value-editors/FilterClauseValueEditor.d.ts +0 -26
  41. package/types/src/filter-clause/value-editors/FilterClauseValueEditorDate.d.ts +0 -9
  42. package/types/src/filter-clause/value-editors/FilterClauseValueEditorNumber.d.ts +0 -9
  43. package/types/src/filter-clause/value-editors/FilterClauseValueEditorText.d.ts +0 -9
  44. package/types/src/filter-clause/value-editors/FilterClauseValueEditorTime.d.ts +0 -9
  45. package/types/src/filter-container/ExtendedSingleValueFilterClause.d.ts +0 -21
  46. package/types/src/filter-container/FilterContainer.d.ts +0 -23
  47. package/types/src/filter-container/useFilterContainer.d.ts +0 -22
  48. package/types/src/filter-context-menu/useFilterContextMenu.d.ts +0 -6
  49. package/types/src/filter-display/FilterDisplay.d.ts +0 -10
  50. package/types/src/filter-editor/FilterClauseCombinator.d.ts +0 -9
  51. package/types/src/filter-editor/FilterEditor.d.ts +0 -14
  52. package/types/src/filter-editor/index.d.ts +0 -1
  53. package/types/src/filter-editor/useFilterEditor.d.ts +0 -32
  54. package/types/src/filter-panel/FilterPanel.d.ts +0 -9
  55. package/types/src/filter-panel/useFilterPanel.d.ts +0 -14
  56. package/types/src/filter-pill/FilterMenu.d.ts +0 -24
  57. package/types/src/filter-pill/FilterPill.d.ts +0 -16
  58. package/types/src/filter-pill/FilterPillNext.d.ts +0 -15
  59. package/types/src/filter-pill/filterAsReactNode.d.ts +0 -3
  60. package/types/src/filter-pill/getFilterLabel.d.ts +0 -2
  61. package/types/src/filter-pill/getFilterTooltipText.d.ts +0 -2
  62. package/types/src/filter-pill/index.d.ts +0 -1
  63. package/types/src/filter-pill/useFilterPillNext.d.ts +0 -25
  64. package/types/src/filter-pill-menu/FilterPillMenu.d.ts +0 -17
  65. package/types/src/filter-pill-menu/FilterPillMenuOptions.d.ts +0 -12
  66. package/types/src/filter-pill-menu/index.d.ts +0 -1
  67. package/types/src/filter-provider/FilterContext.d.ts +0 -39
  68. package/types/src/filter-provider/FilterProvider.d.ts +0 -10
  69. package/types/src/filter-provider/filter-descriptor-utils.d.ts +0 -7
  70. package/types/src/filter-toggle-button/FilterToggleButton.d.ts +0 -9
  71. package/types/src/filter-utils.d.ts +0 -60
  72. package/types/src/index.d.ts +0 -24
  73. package/types/src/inline-filter/InlineFilter.d.ts +0 -12
  74. package/types/src/inline-filter/index.d.ts +0 -1
  75. package/types/src/inline-filter/useInlineFilter.d.ts +0 -5
  76. package/types/src/quick-filters/QuickFilters.d.ts +0 -17
  77. package/types/src/quick-filters/index.d.ts +0 -1
  78. package/types/src/quick-filters/useQuickFilters.d.ts +0 -12
  79. package/types/src/saved-filters/DeleteFilterPrompt.d.ts +0 -9
  80. package/types/src/saved-filters/FilterNamePrompt.d.ts +0 -6
  81. package/types/src/saved-filters/SavedFilterPanel.d.ts +0 -14
  82. package/types/src/saved-filters/useFilterNamePrompt.d.ts +0 -28
  83. package/types/src/saved-filters/useSavedFilterPanel.d.ts +0 -13
  84. package/types/src/tabbed-filter-container/TabbedFilterContainer.d.ts +0 -10
  85. package/types/src/toggle-filter/ToggleFilter.d.ts +0 -17
  86. package/types/src/toggle-filter/useToggleFilter.d.ts +0 -11
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "3.4.1",
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.4.1",
14
- "@vuu-ui/vuu-protocol-types": "3.4.1",
15
- "@vuu-ui/vuu-table-types": "3.4.1",
16
- "@vuu-ui/vuu-filter-types": "3.4.1"
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.4.1",
20
- "@vuu-ui/vuu-data-react": "3.4.1",
21
- "@vuu-ui/vuu-filter-parser": "3.4.1",
22
- "@vuu-ui/vuu-popups": "3.4.1",
23
- "@vuu-ui/vuu-ui-controls": "3.4.1",
24
- "@vuu-ui/vuu-table": "3.4.1",
25
- "@vuu-ui/vuu-utils": "3.4.1",
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",
@@ -1,4 +1,4 @@
1
- import { getTypedRange, getTypedValue, isAndFilter, isBetweenFilter, isExtendedFilter, isMultiClauseFilter, isSingleValueFilter, isTypeDescriptor } from "@vuu-ui/vuu-utils";
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 { serverDataType = "string", type } = column;
68
+ const temporalInfo = getTemporalInfo(column);
69
+ const options = getExtendedFilterOptions(temporalInfo, extendedFilterOptions);
28
70
  const isInclusive = "between-inclusive" === op;
29
- const dataType = isTypeDescriptor(type) ? type.name : type ?? serverDataType;
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: value1
59
- });
60
- else if (void 0 !== value2) this.#filters.set(column.name, {
61
- column: column.name,
62
- op: "<",
63
- value: value2
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 = getTypedValue(value.toString(), dataType, true);
67
- this.#filters.set(column.name, {
68
- column: column.name,
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, isTextColumn, isTimestampColumn } from "@vuu-ui/vuu-utils";
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) || isTimestampColumn(column)) return numericOperators;
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 { isDateTimeDataValue, isTimeDataValue } from "@vuu-ui/vuu-utils";
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
- if (isDateTimeDataValue(selectedColumn)) return /*#__PURE__*/ jsx(FilterClauseValueEditorDate, {
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 (isTimeDataValue(selectedColumn)) return /*#__PURE__*/ jsx(FilterClauseValueEditorTime, {
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, useState } from "react";
3
- import { getLocalTimeZone } from "@internationalized/date";
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 = (props)=>{
7
- const { inputProps, className, onChangeValue, operator, value } = props;
8
- const toEpochMilliS = getEpochMillisConverter(operator);
9
- const [date, setDate] = useState(()=>getInitialState(value));
10
- const handleCommit = useCallback((e, selectedDateInputValue)=>{
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
- onChangeValue,
19
- toEpochMilliS
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
- selectedDate: date
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, useState } from "react";
3
- import { getLocalTimeZone } from "@internationalized/date";
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 FilterClauseValueEditorTime = (props)=>{
7
- const { className, onChangeValue, operator, value } = props;
8
- const toEpochMilliS = getEpochMillisConverter(operator);
9
- const [date, setDate] = useState(()=>getInitialState(value));
10
- const handleCommit = useCallback((e, selectedDateInputValue)=>{
11
- console.log("change time");
12
- if (selectedDateInputValue) {
13
- const dateValue = toCalendarDate(new Date(selectedDateInputValue));
14
- setDate(dateValue);
15
- if (selectedDateInputValue) onChangeValue(toEpochMilliS(dateValue));
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
- date,
25
- onChangeValue,
26
- toEpochMilliS
18
+ onChangeValue
27
19
  ]);
28
20
  return /*#__PURE__*/ jsx(VuuTimePicker, {
29
21
  "data-field": "value",
30
22
  className: className,
31
- onBlur: onBlur,
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 { Time, isValidTimeString } from "@vuu-ui/vuu-utils";
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
- if (isTimeToday(this.#options)) if (isValidTimeString(value)) {
18
- const timeValue = +Time(value).asDate();
19
- if (">=" === op) return `${column} > ${timeValue - 1}`;
20
- if ("<=" === op) return `${column} < ${timeValue + 1}`;
21
- return `${column} ${op} ${timeValue}`;
22
- } else throw Error(`[ExtendedSingleValueFilterClause] invalid TimeStrong ${value}`);
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 ("time" === column.type) valueRef.current = [
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 { dateTimePattern, defaultPatternsByType, filterAsQuery, formatDate, isDateTimeDataValue, isMultiClauseFilter } from "@vuu-ui/vuu-utils";
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 && isDateTimeDataValue(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)=>{
@@ -1,4 +1,4 @@
1
- import { Time, extractFilterForColumn, filtersAreEqual, isAndFilter, isBetweenFilter, isInFilter, isMultiClauseFilter, isMultiValueFilter, isOrFilter, isSingleValueFilter, isTimeDataValue, partition } from "@vuu-ui/vuu-utils";
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.toString()
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 (isTimeDataValue(column) && "number" == typeof f1.value && "number" == typeof f2.value) {
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
- export declare const FilterClauseValueEditorDate: (props: FilterClauseValueEditorDateProps) => import("react/jsx-runtime").JSX.Element;
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 { NumericFilterClauseOp } from "@vuu-ui/vuu-filter-types";
2
- import { FilterClauseValueEditor } from "../filterClauseTypes";
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
- export declare const FilterClauseValueEditorTime: (props: FilterClauseValueEditorTimeProps) => import("react/jsx-runtime").JSX.Element;
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
- }