@vuu-ui/vuu-filters 3.4.1 → 3.5.0-beta.2

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 (96) hide show
  1. package/package.json +12 -12
  2. package/src/FilterAggregator.js +61 -42
  3. package/src/column-filter/ColumnFilter.js +15 -6
  4. package/src/filter-clause/operator-picker/operator-utils.js +2 -2
  5. package/src/filter-clause/value-editors/FilterClauseValueEditor.js +8 -5
  6. package/src/filter-clause/value-editors/FilterClauseValueEditorDate.js +11 -43
  7. package/src/filter-clause/value-editors/FilterClauseValueEditorTime.js +16 -42
  8. package/src/filter-container/ExtendedSingleValueFilterClause.js +21 -7
  9. package/src/filter-container/FilterContainer.js +19 -8
  10. package/src/filter-context-menu/useFilterContextMenu.js +34 -9
  11. package/src/filter-pill/getFilterTooltipText.js +2 -8
  12. package/src/filter-provider/FilterContext.js +16 -1
  13. package/src/filter-provider/FilterProvider.js +14 -1
  14. package/src/filter-utils.js +5 -10
  15. package/src/index.js +1 -1
  16. package/src/quick-filters/useQuickFilters.js +6 -1
  17. package/types/column-filter/ColumnFilter.d.ts +13 -0
  18. package/types/filter-clause/value-editors/FilterClauseValueEditorDate.d.ts +13 -2
  19. package/types/filter-clause/value-editors/FilterClauseValueEditorTime.d.ts +15 -4
  20. package/types/filter-container/ExtendedSingleValueFilterClause.d.ts +6 -0
  21. package/types/filter-container/FilterContainer.d.ts +1 -1
  22. package/types/filter-context-menu/useFilterContextMenu.d.ts +11 -1
  23. package/types/filter-provider/FilterContext.d.ts +19 -0
  24. package/types/index.d.ts +2 -2
  25. package/types/src/FilterAggregator.d.ts +0 -28
  26. package/types/src/FilterModel.d.ts +0 -44
  27. package/types/src/column-filter/ColumnFilter.d.ts +0 -6
  28. package/types/src/column-filter/useColumnFilter.d.ts +0 -43
  29. package/types/src/column-filter/utils.d.ts +0 -4
  30. package/types/src/custom-filters/CustomFilters.d.ts +0 -7
  31. package/types/src/custom-filters/filterBarFocusManagement.d.ts +0 -1
  32. package/types/src/custom-filters/index.d.ts +0 -1
  33. package/types/src/custom-filters/useCustomFilters.d.ts +0 -33
  34. package/types/src/custom-filters/useFilterState.d.ts +0 -17
  35. package/types/src/filter-bar/FilterBar.d.ts +0 -33
  36. package/types/src/filter-bar/FilterBarMenu.d.ts +0 -1
  37. package/types/src/filter-bar/index.d.ts +0 -2
  38. package/types/src/filter-bar/useFilterBar.d.ts +0 -7
  39. package/types/src/filter-clause/ExpandoCombobox.d.ts +0 -10
  40. package/types/src/filter-clause/FilterClause.d.ts +0 -16
  41. package/types/src/filter-clause/column-picker/ColumnPicker.d.ts +0 -13
  42. package/types/src/filter-clause/column-picker/useColumnPicker.d.ts +0 -11
  43. package/types/src/filter-clause/filterClauseFocusManagement.d.ts +0 -15
  44. package/types/src/filter-clause/filterClauseTypes.d.ts +0 -12
  45. package/types/src/filter-clause/index.d.ts +0 -5
  46. package/types/src/filter-clause/operator-picker/OperatorPicker.d.ts +0 -14
  47. package/types/src/filter-clause/operator-picker/operator-utils.d.ts +0 -5
  48. package/types/src/filter-clause/operator-picker/useOperatorPicker.d.ts +0 -12
  49. package/types/src/filter-clause/useFilterClause.d.ts +0 -26
  50. package/types/src/filter-clause/value-editors/FilterClauseValueEditor.d.ts +0 -26
  51. package/types/src/filter-clause/value-editors/FilterClauseValueEditorDate.d.ts +0 -9
  52. package/types/src/filter-clause/value-editors/FilterClauseValueEditorNumber.d.ts +0 -9
  53. package/types/src/filter-clause/value-editors/FilterClauseValueEditorText.d.ts +0 -9
  54. package/types/src/filter-clause/value-editors/FilterClauseValueEditorTime.d.ts +0 -9
  55. package/types/src/filter-container/ExtendedSingleValueFilterClause.d.ts +0 -21
  56. package/types/src/filter-container/FilterContainer.d.ts +0 -23
  57. package/types/src/filter-container/useFilterContainer.d.ts +0 -22
  58. package/types/src/filter-context-menu/useFilterContextMenu.d.ts +0 -6
  59. package/types/src/filter-display/FilterDisplay.d.ts +0 -10
  60. package/types/src/filter-editor/FilterClauseCombinator.d.ts +0 -9
  61. package/types/src/filter-editor/FilterEditor.d.ts +0 -14
  62. package/types/src/filter-editor/index.d.ts +0 -1
  63. package/types/src/filter-editor/useFilterEditor.d.ts +0 -32
  64. package/types/src/filter-panel/FilterPanel.d.ts +0 -9
  65. package/types/src/filter-panel/useFilterPanel.d.ts +0 -14
  66. package/types/src/filter-pill/FilterMenu.d.ts +0 -24
  67. package/types/src/filter-pill/FilterPill.d.ts +0 -16
  68. package/types/src/filter-pill/FilterPillNext.d.ts +0 -15
  69. package/types/src/filter-pill/filterAsReactNode.d.ts +0 -3
  70. package/types/src/filter-pill/getFilterLabel.d.ts +0 -2
  71. package/types/src/filter-pill/getFilterTooltipText.d.ts +0 -2
  72. package/types/src/filter-pill/index.d.ts +0 -1
  73. package/types/src/filter-pill/useFilterPillNext.d.ts +0 -25
  74. package/types/src/filter-pill-menu/FilterPillMenu.d.ts +0 -17
  75. package/types/src/filter-pill-menu/FilterPillMenuOptions.d.ts +0 -12
  76. package/types/src/filter-pill-menu/index.d.ts +0 -1
  77. package/types/src/filter-provider/FilterContext.d.ts +0 -39
  78. package/types/src/filter-provider/FilterProvider.d.ts +0 -10
  79. package/types/src/filter-provider/filter-descriptor-utils.d.ts +0 -7
  80. package/types/src/filter-toggle-button/FilterToggleButton.d.ts +0 -9
  81. package/types/src/filter-utils.d.ts +0 -60
  82. package/types/src/index.d.ts +0 -24
  83. package/types/src/inline-filter/InlineFilter.d.ts +0 -12
  84. package/types/src/inline-filter/index.d.ts +0 -1
  85. package/types/src/inline-filter/useInlineFilter.d.ts +0 -5
  86. package/types/src/quick-filters/QuickFilters.d.ts +0 -17
  87. package/types/src/quick-filters/index.d.ts +0 -1
  88. package/types/src/quick-filters/useQuickFilters.d.ts +0 -12
  89. package/types/src/saved-filters/DeleteFilterPrompt.d.ts +0 -9
  90. package/types/src/saved-filters/FilterNamePrompt.d.ts +0 -6
  91. package/types/src/saved-filters/SavedFilterPanel.d.ts +0 -14
  92. package/types/src/saved-filters/useFilterNamePrompt.d.ts +0 -28
  93. package/types/src/saved-filters/useSavedFilterPanel.d.ts +0 -13
  94. package/types/src/tabbed-filter-container/TabbedFilterContainer.d.ts +0 -10
  95. package/types/src/toggle-filter/ToggleFilter.d.ts +0 -17
  96. 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.2",
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.2",
14
+ "@vuu-ui/vuu-protocol-types": "3.5.0-beta.2",
15
+ "@vuu-ui/vuu-table-types": "3.5.0-beta.2",
16
+ "@vuu-ui/vuu-filter-types": "3.5.0-beta.2"
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.2",
20
+ "@vuu-ui/vuu-data-react": "3.5.0-beta.2",
21
+ "@vuu-ui/vuu-filter-parser": "3.5.0-beta.2",
22
+ "@vuu-ui/vuu-popups": "3.5.0-beta.2",
23
+ "@vuu-ui/vuu-ui-controls": "3.5.0-beta.2",
24
+ "@vuu-ui/vuu-table": "3.5.0-beta.2",
25
+ "@vuu-ui/vuu-utils": "3.5.0-beta.2",
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 }) {
@@ -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, 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,
@@ -38,7 +47,7 @@ const ColumnFilter_ColumnFilter = /*#__PURE__*/ external_react_forwardRef(functi
38
47
  InputProps,
39
48
  TypeaheadProps,
40
49
  commitWhenCleared: true,
41
- dataDescriptor: column,
50
+ dataDescriptor: controlDescriptor,
42
51
  onCommit,
43
52
  table,
44
53
  values,
@@ -49,7 +58,7 @@ const ColumnFilter_ColumnFilter = /*#__PURE__*/ external_react_forwardRef(functi
49
58
  className: `${classBase}-rangeHigh`,
50
59
  commitWhenCleared: true,
51
60
  variant,
52
- dataDescriptor: column,
61
+ dataDescriptor: controlDescriptor,
53
62
  onCommit: onCommitRange,
54
63
  table
55
64
  }) : null
@@ -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() {
@@ -1,17 +1,31 @@
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, variant, ...props })=>{
14
+ const { date, time } = pattern ?? {};
15
+ const column = useMemo(()=>withDateTimePattern(columnProp, date || time ? {
16
+ date,
17
+ time
18
+ } : void 0), [
19
+ columnProp,
20
+ date,
21
+ time
22
+ ]);
14
23
  const { filterProviderKey, onChange: onFilterContextChange, onCommit: onFilterContextCommit, register } = useFilterContext(column, true);
24
+ const { registerColumnFilterColumn } = useColumnFilterRegistry(filterProviderKey);
25
+ useEffect(()=>registerColumnFilterColumn(column), [
26
+ column,
27
+ registerColumnFilterColumn
28
+ ]);
15
29
  const initialValue = useMemo(()=>register(column, operator), [
16
30
  column,
17
31
  operator,
@@ -25,10 +39,7 @@ const FilterContainerColumnFilter = ({ column, operator = "=", variant, ...props
25
39
  if (currentFilterRef.current.id !== currentFilter.id || !filtersAreEqual(currentFilterRef.current.filter, currentFilter.filter)) {
26
40
  currentFilterRef.current = currentFilter;
27
41
  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
- ];
42
+ if (Array.isArray(valueRef.current)) if (isTimeDataValue(column)) valueRef.current = getDefaultTimeRange(column);
32
43
  else valueRef.current = [
33
44
  "",
34
45
  ""
@@ -1,14 +1,37 @@
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 } = 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 columnFilterColumn = pattern ? void 0 : getColumnFilterColumn(column.name);
18
+ const filterColumn = pattern ? withDateTimePattern(column, pattern) : columnFilterColumn;
19
+ const temporalCellFilter = getTemporalCellFilter(filterColumn ?? column, value, void 0 !== columnFilterColumn);
20
+ if (temporalCellFilter) return {
21
+ column: filterColumn ?? column,
22
+ ...temporalCellFilter
23
+ };
24
+ }
25
+ return {
26
+ column,
27
+ label: `${value}`,
28
+ op: "=",
29
+ value
30
+ };
31
+ }, [
32
+ filterPatterns,
33
+ getColumnFilterColumn
34
+ ]);
12
35
  useMemo(()=>{
13
36
  if (!filtersAreEqual(currentFilter.filter, filterAggregatorRef.current.filter)) filterAggregatorRef.current = currentFilter.filter ? new FilterAggregator(currentFilter.filter) : new FilterAggregator();
14
37
  }, [
@@ -18,13 +41,13 @@ const useFilterContextMenu = ({ filterColumns = "*", filterProviderKey } = defau
18
41
  const { column, dataRow } = options;
19
42
  const { current: fag } = filterAggregatorRef;
20
43
  const { name, label = name } = column;
21
- const value = dataRow[column.name];
22
44
  const ClearFilter = {
23
45
  id: "filter-clear",
24
46
  label: "Clear filter",
25
47
  options
26
48
  };
27
49
  if ("*" === filterColumns || filterColumns.includes(column.name)) {
50
+ const { label: value } = getCellFilter(column, dataRow);
28
51
  const SetFilter = {
29
52
  id: "filter-set",
30
53
  label: `Set filter ${label} '${value}'`,
@@ -60,7 +83,8 @@ const useFilterContextMenu = ({ filterColumns = "*", filterProviderKey } = defau
60
83
  ];
61
84
  return [];
62
85
  }, [
63
- filterColumns
86
+ filterColumns,
87
+ getCellFilter
64
88
  ]);
65
89
  const menuActionHandler = useCallback((menuItemId, options)=>{
66
90
  if (!options) return false;
@@ -73,8 +97,8 @@ const useFilterContextMenu = ({ filterColumns = "*", filterProviderKey } = defau
73
97
  break;
74
98
  case "filter-add":
75
99
  {
76
- const value = dataRow[column.name];
77
- fag.add(column, value, "=");
100
+ const cellFilter = getCellFilter(column, dataRow);
101
+ fag.add(cellFilter.column, cellFilter.value, cellFilter.op);
78
102
  if (fag.filter) setCurrentFilter(fag.filter);
79
103
  }
80
104
  break;
@@ -84,9 +108,9 @@ const useFilterContextMenu = ({ filterColumns = "*", filterProviderKey } = defau
84
108
  break;
85
109
  case "filter-set":
86
110
  {
87
- const value = dataRow[column.name];
111
+ const cellFilter = getCellFilter(column, dataRow);
88
112
  fag.clear();
89
- fag.add(column, value, "=");
113
+ fag.add(cellFilter.column, cellFilter.value, cellFilter.op);
90
114
  if (fag.filter) setCurrentFilter(fag.filter);
91
115
  }
92
116
  break;
@@ -96,6 +120,7 @@ const useFilterContextMenu = ({ filterColumns = "*", filterProviderKey } = defau
96
120
  }
97
121
  }, [
98
122
  clearCurrentFilter,
123
+ getCellFilter,
99
124
  setCurrentFilter
100
125
  ]);
101
126
  return {
@@ -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)=>{
@@ -31,6 +31,21 @@ const FilterContext = createContext({
31
31
  clearCurrentFilter: ()=>console.warn("[FilterContext] clearFilter, no FilterProvider has been configured"),
32
32
  setCurrentFilter: ()=>console.warn("[FilterContext] setCurrentFilter, no FilterProvider has been configured")
33
33
  });
34
+ function useColumnFilterRegistry(key = "GLOBAL") {
35
+ const { getColumnFilterColumn, registerColumnFilterColumn } = useContext(FilterContext);
36
+ const getColumn = useCallback((columnName)=>getColumnFilterColumn?.(key, columnName), [
37
+ getColumnFilterColumn,
38
+ key
39
+ ]);
40
+ const register = useCallback((column)=>registerColumnFilterColumn?.(key, column), [
41
+ key,
42
+ registerColumnFilterColumn
43
+ ]);
44
+ return {
45
+ getColumnFilterColumn: getColumn,
46
+ registerColumnFilterColumn: register
47
+ };
48
+ }
34
49
  function useSavedFilters(key = "GLOBAL", props) {
35
50
  const { onFilterMenuAction, filterDescriptors, saveFilter, setCurrentFilter, filterNameMaxLength } = useContext(FilterContext);
36
51
  const handleFilterMenuAction = useCallback((filterId, filterAction)=>{
@@ -68,4 +83,4 @@ function useSavedFilters(key = "GLOBAL", props) {
68
83
  filterNameMaxLength
69
84
  };
70
85
  }
71
- export { EMPTY_FILTER, EmptyFilterDescriptor, FilterContext, NULL_FILTER, NullFilterDescriptor, UNSAVED_FILTER, filterDescriptorHasFilter, isEmptyFilter, isNullFilter, useSavedFilters };
86
+ export { EMPTY_FILTER, EmptyFilterDescriptor, FilterContext, NULL_FILTER, NullFilterDescriptor, UNSAVED_FILTER, filterDescriptorHasFilter, isEmptyFilter, isNullFilter, useColumnFilterRegistry, useSavedFilters };