@vuu-ui/vuu-filters 4.0.0-alpha.9 → 4.0.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 (123) hide show
  1. package/package.json +16 -16
  2. package/src/FilterAggregator.js +61 -42
  3. package/src/column-filter/ColumnFilter.css.js +4 -0
  4. package/src/column-filter/ColumnFilter.js +18 -7
  5. package/src/filter-clause/FilterClause.css.js +1 -1
  6. package/src/filter-clause/operator-picker/operator-utils.js +2 -2
  7. package/src/filter-clause/value-editors/FilterClauseValueEditor.js +8 -5
  8. package/src/filter-clause/value-editors/FilterClauseValueEditorDate.js +11 -43
  9. package/src/filter-clause/value-editors/FilterClauseValueEditorTime.js +16 -42
  10. package/src/filter-container/ExtendedSingleValueFilterClause.js +21 -7
  11. package/src/filter-container/FilterContainer.js +22 -10
  12. package/src/filter-context-menu/useFilterContextMenu.js +36 -9
  13. package/src/filter-pill/getFilterTooltipText.js +2 -8
  14. package/src/filter-provider/FilterContext.js +20 -1
  15. package/src/filter-provider/FilterProvider.js +20 -2
  16. package/src/filter-utils.js +5 -10
  17. package/src/index.js +1 -1
  18. package/src/quick-filters/useQuickFilters.js +6 -1
  19. package/src/tabbed-filter-container/TabbedFilterContainer.js +19 -17
  20. package/src/toggle-filter/ToggleFilter.css.js +8 -0
  21. package/src/toggle-filter/ToggleFilter.js +34 -15
  22. package/src/toggle-filter/useToggleFilter.js +57 -33
  23. package/types/column-filter/ColumnFilter.d.ts +16 -3
  24. package/types/custom-filters/CustomFilters.d.ts +1 -1
  25. package/types/filter-bar/FilterBar.d.ts +1 -1
  26. package/types/filter-bar/FilterBarMenu.d.ts +1 -1
  27. package/types/filter-clause/FilterClause.d.ts +1 -1
  28. package/types/filter-clause/value-editors/FilterClauseValueEditorDate.d.ts +13 -2
  29. package/types/filter-clause/value-editors/FilterClauseValueEditorText.d.ts +2 -2
  30. package/types/filter-clause/value-editors/FilterClauseValueEditorTime.d.ts +15 -4
  31. package/types/filter-container/ExtendedSingleValueFilterClause.d.ts +6 -0
  32. package/types/filter-container/FilterContainer.d.ts +11 -4
  33. package/types/filter-context-menu/useFilterContextMenu.d.ts +11 -1
  34. package/types/filter-editor/FilterClauseCombinator.d.ts +1 -1
  35. package/types/filter-editor/FilterEditor.d.ts +1 -1
  36. package/types/filter-pill/FilterMenu.d.ts +1 -1
  37. package/types/filter-pill/FilterPill.d.ts +1 -1
  38. package/types/filter-pill/FilterPillNext.d.ts +1 -1
  39. package/types/filter-pill-menu/FilterPillMenu.d.ts +1 -1
  40. package/types/filter-provider/FilterContext.d.ts +34 -0
  41. package/types/filter-provider/FilterProvider.d.ts +19 -1
  42. package/types/filter-toggle-button/FilterToggleButton.d.ts +1 -1
  43. package/types/index.d.ts +4 -4
  44. package/types/inline-filter/InlineFilter.d.ts +1 -1
  45. package/types/quick-filters/QuickFilters.d.ts +1 -1
  46. package/types/saved-filters/DeleteFilterPrompt.d.ts +1 -1
  47. package/types/saved-filters/FilterNamePrompt.d.ts +1 -1
  48. package/types/saved-filters/SavedFilterPanel.d.ts +1 -1
  49. package/types/tabbed-filter-container/TabbedFilterContainer.d.ts +3 -3
  50. package/types/toggle-filter/ToggleFilter.d.ts +16 -1
  51. package/types/toggle-filter/useToggleFilter.d.ts +19 -1
  52. package/types/src/FilterAggregator.d.ts +0 -28
  53. package/types/src/FilterModel.d.ts +0 -44
  54. package/types/src/column-filter/ColumnFilter.d.ts +0 -6
  55. package/types/src/column-filter/useColumnFilter.d.ts +0 -43
  56. package/types/src/column-filter/utils.d.ts +0 -4
  57. package/types/src/custom-filters/CustomFilters.d.ts +0 -7
  58. package/types/src/custom-filters/filterBarFocusManagement.d.ts +0 -1
  59. package/types/src/custom-filters/index.d.ts +0 -1
  60. package/types/src/custom-filters/useCustomFilters.d.ts +0 -33
  61. package/types/src/custom-filters/useFilterState.d.ts +0 -17
  62. package/types/src/filter-bar/FilterBar.d.ts +0 -33
  63. package/types/src/filter-bar/FilterBarMenu.d.ts +0 -1
  64. package/types/src/filter-bar/index.d.ts +0 -2
  65. package/types/src/filter-bar/useFilterBar.d.ts +0 -7
  66. package/types/src/filter-clause/ExpandoCombobox.d.ts +0 -10
  67. package/types/src/filter-clause/FilterClause.d.ts +0 -16
  68. package/types/src/filter-clause/column-picker/ColumnPicker.d.ts +0 -13
  69. package/types/src/filter-clause/column-picker/useColumnPicker.d.ts +0 -11
  70. package/types/src/filter-clause/filterClauseFocusManagement.d.ts +0 -15
  71. package/types/src/filter-clause/filterClauseTypes.d.ts +0 -12
  72. package/types/src/filter-clause/index.d.ts +0 -5
  73. package/types/src/filter-clause/operator-picker/OperatorPicker.d.ts +0 -14
  74. package/types/src/filter-clause/operator-picker/operator-utils.d.ts +0 -5
  75. package/types/src/filter-clause/operator-picker/useOperatorPicker.d.ts +0 -12
  76. package/types/src/filter-clause/useFilterClause.d.ts +0 -26
  77. package/types/src/filter-clause/value-editors/FilterClauseValueEditor.d.ts +0 -26
  78. package/types/src/filter-clause/value-editors/FilterClauseValueEditorDate.d.ts +0 -9
  79. package/types/src/filter-clause/value-editors/FilterClauseValueEditorNumber.d.ts +0 -9
  80. package/types/src/filter-clause/value-editors/FilterClauseValueEditorText.d.ts +0 -9
  81. package/types/src/filter-clause/value-editors/FilterClauseValueEditorTime.d.ts +0 -9
  82. package/types/src/filter-container/ExtendedSingleValueFilterClause.d.ts +0 -21
  83. package/types/src/filter-container/FilterContainer.d.ts +0 -23
  84. package/types/src/filter-container/useFilterContainer.d.ts +0 -22
  85. package/types/src/filter-context-menu/useFilterContextMenu.d.ts +0 -6
  86. package/types/src/filter-display/FilterDisplay.d.ts +0 -10
  87. package/types/src/filter-editor/FilterClauseCombinator.d.ts +0 -9
  88. package/types/src/filter-editor/FilterEditor.d.ts +0 -14
  89. package/types/src/filter-editor/index.d.ts +0 -1
  90. package/types/src/filter-editor/useFilterEditor.d.ts +0 -32
  91. package/types/src/filter-panel/FilterPanel.d.ts +0 -9
  92. package/types/src/filter-panel/useFilterPanel.d.ts +0 -14
  93. package/types/src/filter-pill/FilterMenu.d.ts +0 -24
  94. package/types/src/filter-pill/FilterPill.d.ts +0 -16
  95. package/types/src/filter-pill/FilterPillNext.d.ts +0 -15
  96. package/types/src/filter-pill/filterAsReactNode.d.ts +0 -3
  97. package/types/src/filter-pill/getFilterLabel.d.ts +0 -2
  98. package/types/src/filter-pill/getFilterTooltipText.d.ts +0 -2
  99. package/types/src/filter-pill/index.d.ts +0 -1
  100. package/types/src/filter-pill/useFilterPillNext.d.ts +0 -25
  101. package/types/src/filter-pill-menu/FilterPillMenu.d.ts +0 -17
  102. package/types/src/filter-pill-menu/FilterPillMenuOptions.d.ts +0 -12
  103. package/types/src/filter-pill-menu/index.d.ts +0 -1
  104. package/types/src/filter-provider/FilterContext.d.ts +0 -39
  105. package/types/src/filter-provider/FilterProvider.d.ts +0 -10
  106. package/types/src/filter-provider/filter-descriptor-utils.d.ts +0 -7
  107. package/types/src/filter-toggle-button/FilterToggleButton.d.ts +0 -9
  108. package/types/src/filter-utils.d.ts +0 -60
  109. package/types/src/index.d.ts +0 -24
  110. package/types/src/inline-filter/InlineFilter.d.ts +0 -12
  111. package/types/src/inline-filter/index.d.ts +0 -1
  112. package/types/src/inline-filter/useInlineFilter.d.ts +0 -5
  113. package/types/src/quick-filters/QuickFilters.d.ts +0 -17
  114. package/types/src/quick-filters/index.d.ts +0 -1
  115. package/types/src/quick-filters/useQuickFilters.d.ts +0 -12
  116. package/types/src/saved-filters/DeleteFilterPrompt.d.ts +0 -9
  117. package/types/src/saved-filters/FilterNamePrompt.d.ts +0 -6
  118. package/types/src/saved-filters/SavedFilterPanel.d.ts +0 -14
  119. package/types/src/saved-filters/useFilterNamePrompt.d.ts +0 -28
  120. package/types/src/saved-filters/useSavedFilterPanel.d.ts +0 -13
  121. package/types/src/tabbed-filter-container/TabbedFilterContainer.d.ts +0 -10
  122. package/types/src/toggle-filter/ToggleFilter.d.ts +0 -17
  123. package/types/src/toggle-filter/useToggleFilter.d.ts +0 -11
@@ -1,14 +1,39 @@
1
- import { filtersAreEqual } from "@vuu-ui/vuu-utils";
1
+ import { filtersAreEqual, getTemporalCellFilter, getTemporalInfo, withDateTimePattern } from "@vuu-ui/vuu-utils";
2
2
  import { useCallback, useMemo, useRef } from "react";
3
- import { useSavedFilters } from "../filter-provider/FilterContext.js";
3
+ import { useColumnFilterRegistry, useSavedFilters } from "../filter-provider/FilterContext.js";
4
4
  import { FilterAggregator } from "../FilterAggregator.js";
5
5
  const EmptyAggregator = new FilterAggregator();
6
6
  const defaultProps = {
7
7
  filterColumns: "*"
8
8
  };
9
- const useFilterContextMenu = ({ filterColumns = "*", filterProviderKey } = defaultProps)=>{
9
+ const useFilterContextMenu = ({ filterColumns = "*", filterPatterns, filterProviderKey } = defaultProps)=>{
10
10
  const { currentFilter, clearCurrentFilter, setCurrentFilter } = useSavedFilters(filterProviderKey);
11
+ const { getColumnFilterColumn, getColumnFilterPattern } = useColumnFilterRegistry(filterProviderKey);
11
12
  const filterAggregatorRef = useRef(EmptyAggregator);
13
+ const getCellFilter = useCallback((column, dataRow)=>{
14
+ const value = dataRow[column.name];
15
+ if (getTemporalInfo(column)) {
16
+ const pattern = filterPatterns?.[column.name];
17
+ const columnFilterPattern = getColumnFilterPattern(column);
18
+ const columnFilterColumn = pattern ? void 0 : getColumnFilterColumn(column.name) ?? (columnFilterPattern ? withDateTimePattern(column, columnFilterPattern) : void 0);
19
+ const filterColumn = pattern ? withDateTimePattern(column, pattern) : columnFilterColumn;
20
+ const temporalCellFilter = getTemporalCellFilter(filterColumn ?? column, value, void 0 !== columnFilterColumn);
21
+ if (temporalCellFilter) return {
22
+ column: filterColumn ?? column,
23
+ ...temporalCellFilter
24
+ };
25
+ }
26
+ return {
27
+ column,
28
+ label: `${value}`,
29
+ op: "=",
30
+ value
31
+ };
32
+ }, [
33
+ filterPatterns,
34
+ getColumnFilterColumn,
35
+ getColumnFilterPattern
36
+ ]);
12
37
  useMemo(()=>{
13
38
  if (!filtersAreEqual(currentFilter.filter, filterAggregatorRef.current.filter)) filterAggregatorRef.current = currentFilter.filter ? new FilterAggregator(currentFilter.filter) : new FilterAggregator();
14
39
  }, [
@@ -18,13 +43,13 @@ const useFilterContextMenu = ({ filterColumns = "*", filterProviderKey } = defau
18
43
  const { column, dataRow } = options;
19
44
  const { current: fag } = filterAggregatorRef;
20
45
  const { name, label = name } = column;
21
- const value = dataRow[column.name];
22
46
  const ClearFilter = {
23
47
  id: "filter-clear",
24
48
  label: "Clear filter",
25
49
  options
26
50
  };
27
51
  if ("*" === filterColumns || filterColumns.includes(column.name)) {
52
+ const { label: value } = getCellFilter(column, dataRow);
28
53
  const SetFilter = {
29
54
  id: "filter-set",
30
55
  label: `Set filter ${label} '${value}'`,
@@ -60,7 +85,8 @@ const useFilterContextMenu = ({ filterColumns = "*", filterProviderKey } = defau
60
85
  ];
61
86
  return [];
62
87
  }, [
63
- filterColumns
88
+ filterColumns,
89
+ getCellFilter
64
90
  ]);
65
91
  const menuActionHandler = useCallback((menuItemId, options)=>{
66
92
  if (!options) return false;
@@ -73,8 +99,8 @@ const useFilterContextMenu = ({ filterColumns = "*", filterProviderKey } = defau
73
99
  break;
74
100
  case "filter-add":
75
101
  {
76
- const value = dataRow[column.name];
77
- fag.add(column, value, "=");
102
+ const cellFilter = getCellFilter(column, dataRow);
103
+ fag.add(cellFilter.column, cellFilter.value, cellFilter.op);
78
104
  if (fag.filter) setCurrentFilter(fag.filter);
79
105
  }
80
106
  break;
@@ -84,9 +110,9 @@ const useFilterContextMenu = ({ filterColumns = "*", filterProviderKey } = defau
84
110
  break;
85
111
  case "filter-set":
86
112
  {
87
- const value = dataRow[column.name];
113
+ const cellFilter = getCellFilter(column, dataRow);
88
114
  fag.clear();
89
- fag.add(column, value, "=");
115
+ fag.add(cellFilter.column, cellFilter.value, cellFilter.op);
90
116
  if (fag.filter) setCurrentFilter(fag.filter);
91
117
  }
92
118
  break;
@@ -96,6 +122,7 @@ const useFilterContextMenu = ({ filterColumns = "*", filterProviderKey } = defau
96
122
  }
97
123
  }, [
98
124
  clearCurrentFilter,
125
+ getCellFilter,
99
126
  setCurrentFilter
100
127
  ]);
101
128
  return {
@@ -1,4 +1,4 @@
1
- import { 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,25 @@ 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, getColumnFilterPattern, 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
+ const getPattern = useCallback((column)=>getColumnFilterPattern?.(column), [
45
+ getColumnFilterPattern
46
+ ]);
47
+ return {
48
+ getColumnFilterColumn: getColumn,
49
+ getColumnFilterPattern: getPattern,
50
+ registerColumnFilterColumn: register
51
+ };
52
+ }
34
53
  function useSavedFilters(key = "GLOBAL", props) {
35
54
  const { onFilterMenuAction, filterDescriptors, saveFilter, setCurrentFilter, filterNameMaxLength } = useContext(FilterContext);
36
55
  const handleFilterMenuAction = useCallback((filterId, filterAction)=>{
@@ -68,4 +87,4 @@ function useSavedFilters(key = "GLOBAL", props) {
68
87
  filterNameMaxLength
69
88
  };
70
89
  }
71
- export { EMPTY_FILTER, EmptyFilterDescriptor, FilterContext, NULL_FILTER, NullFilterDescriptor, UNSAVED_FILTER, filterDescriptorHasFilter, isEmptyFilter, isNullFilter, useSavedFilters };
90
+ export { EMPTY_FILTER, EmptyFilterDescriptor, FilterContext, NULL_FILTER, NullFilterDescriptor, UNSAVED_FILTER, filterDescriptorHasFilter, isEmptyFilter, isNullFilter, useColumnFilterRegistry, useSavedFilters };
@@ -1,5 +1,5 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
- import { useCallback, useMemo, useState } from "react";
2
+ import { useCallback, useMemo, useRef, useState } from "react";
3
3
  import { FilterNamePrompt } from "../saved-filters/FilterNamePrompt.js";
4
4
  import { DeleteFilterPrompt } from "../saved-filters/DeleteFilterPrompt.js";
5
5
  import { activateFilter, findFilter, insertOrReplaceFilter, renameFilter } from "./filter-descriptor-utils.js";
@@ -14,12 +14,27 @@ const mapToRecord = (savedFilters)=>{
14
14
  });
15
15
  return record;
16
16
  };
17
- const FilterProvider = ({ children, onFiltersSaved, savedFilters: savedFiltersProp, filterNameMaxLength })=>{
17
+ const FilterProvider = ({ children, columnFilterPatterns, columnTypeFilterPatterns, onFiltersSaved, savedFilters: savedFiltersProp, filterNameMaxLength })=>{
18
18
  const [, forceRefresh] = useState({});
19
19
  const savedFilters = useMemo(()=>savedFiltersProp ? new Map(Object.entries(savedFiltersProp)) : new Map(), [
20
20
  savedFiltersProp
21
21
  ]);
22
22
  const [dialog, setDialog] = useState(null);
23
+ const columnFilterColumnsRef = useRef(new Map());
24
+ const registerColumnFilterColumn = useCallback((key, column)=>{
25
+ const { current: columnsByKey } = columnFilterColumnsRef;
26
+ let columns = columnsByKey.get(key);
27
+ if (void 0 === columns) columnsByKey.set(key, columns = new Map());
28
+ columns.set(column.name, column);
29
+ return ()=>{
30
+ if (columns.get(column.name) === column) columns.delete(column.name);
31
+ };
32
+ }, []);
33
+ const getColumnFilterPattern = useCallback(({ name, serverDataType })=>columnFilterPatterns?.[name] ?? (serverDataType ? columnTypeFilterPatterns?.[serverDataType] : void 0), [
34
+ columnFilterPatterns,
35
+ columnTypeFilterPatterns
36
+ ]);
37
+ const getColumnFilterColumn = useCallback((key, columnName)=>columnFilterColumnsRef.current.get(key)?.get(columnName), []);
23
38
  const deleteFilter = useCallback((key, filterId)=>{
24
39
  const filterDescriptors = savedFilters.get(key);
25
40
  if (void 0 === filterDescriptors) throw Error(`[FilterProvider] deleteFilter, key ${key} not found`);
@@ -171,6 +186,9 @@ const FilterProvider = ({ children, onFiltersSaved, savedFilters: savedFiltersPr
171
186
  ]);
172
187
  return /*#__PURE__*/ jsxs(FilterContext.Provider, {
173
188
  value: {
189
+ getColumnFilterColumn,
190
+ getColumnFilterPattern,
191
+ registerColumnFilterColumn,
174
192
  onFilterMenuAction: handleFilterMenuAction,
175
193
  deleteFilter,
176
194
  saveFilter: handleSaveFilter,
@@ -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,
package/src/index.js CHANGED
@@ -13,7 +13,7 @@ export { useFilterContextMenu } from "./filter-context-menu/useFilterContextMenu
13
13
  export { FilterDisplay } from "./filter-display/FilterDisplay.js";
14
14
  export { FilterPanel } from "./filter-panel/FilterPanel.js";
15
15
  export { FilterPillNext } from "./filter-pill/FilterPillNext.js";
16
- export { NULL_FILTER, useSavedFilters } from "./filter-provider/FilterContext.js";
16
+ export { NULL_FILTER, useColumnFilterRegistry, useSavedFilters } from "./filter-provider/FilterContext.js";
17
17
  export { FilterProvider } from "./filter-provider/FilterProvider.js";
18
18
  export { FilterNamePrompt as SaveFilterConfirmPrompt } from "./saved-filters/FilterNamePrompt.js";
19
19
  export { SavedFilterPanel } from "./saved-filters/SavedFilterPanel.js";
@@ -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,5 +1,5 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
- import { Tab, TabList, TabPanel, TabTrigger, Tabs } from "@salt-ds/core";
2
+ import { Tab, TabBar, TabList, TabPanel, TabTrigger, Tabs } from "@salt-ds/core";
3
3
  import clsx from "clsx";
4
4
  import { FilterPanel } from "../filter-panel/FilterPanel.js";
5
5
  import { SavedFilterPanel } from "../saved-filters/SavedFilterPanel.js";
@@ -10,22 +10,24 @@ const TabbedFilterContainer = ({ SavedFilterPanelProps, children, className, fil
10
10
  children: /*#__PURE__*/ jsxs(Tabs, {
11
11
  defaultValue: "ad-hoc-filter",
12
12
  children: [
13
- /*#__PURE__*/ jsxs(TabList, {
14
- appearance: "transparent",
15
- children: [
16
- /*#__PURE__*/ jsx(Tab, {
17
- value: "ad-hoc-filter",
18
- children: /*#__PURE__*/ jsx(TabTrigger, {
19
- children: filterTabLabel
20
- })
21
- }, "ad-hoc-filter"),
22
- /*#__PURE__*/ jsx(Tab, {
23
- value: "saved-filters",
24
- children: /*#__PURE__*/ jsx(TabTrigger, {
25
- children: savedFilterLabel
26
- })
27
- }, "saved-filters")
28
- ]
13
+ /*#__PURE__*/ jsx(TabBar, {
14
+ children: /*#__PURE__*/ jsxs(TabList, {
15
+ appearance: "transparent",
16
+ children: [
17
+ /*#__PURE__*/ jsx(Tab, {
18
+ value: "ad-hoc-filter",
19
+ children: /*#__PURE__*/ jsx(TabTrigger, {
20
+ children: filterTabLabel
21
+ })
22
+ }, "ad-hoc-filter"),
23
+ /*#__PURE__*/ jsx(Tab, {
24
+ value: "saved-filters",
25
+ children: /*#__PURE__*/ jsx(TabTrigger, {
26
+ children: savedFilterLabel
27
+ })
28
+ }, "saved-filters")
29
+ ]
30
+ })
29
31
  }),
30
32
  /*#__PURE__*/ jsx(TabPanel, {
31
33
  value: "ad-hoc-filter",
@@ -3,6 +3,14 @@ const css = `
3
3
  & .vuuToggleFilter-onlyAvailableValue:not([aria-checked="true"], :hover) {
4
4
  background-color: var(--vuuToggleFilter-implicitActive-bg, var(--salt-actionable-bold-background));
5
5
  }
6
+
7
+ & .vuuToggleFilter-unavailableValue:not([aria-checked="true"]) {
8
+ background: var(--toggleButton-background);
9
+ border-color: var(--toggleButton-borderColor);
10
+ color: var(--toggleButton-text-color);
11
+ cursor: var(--vuuToggleFilter-unavailableValue-cursor, var(--salt-cursor-disabled));
12
+ opacity: var(--vuuToggleFilter-unavailableValue-opacity, .4);
13
+ }
6
14
  }
7
15
 
8
16
 
@@ -1,12 +1,15 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
- import { ToggleButton, ToggleButtonGroup } from "@salt-ds/core";
2
+ import { ToggleButton, ToggleButtonGroup, Tooltip } from "@salt-ds/core";
3
3
  import { useComponentCssInjection } from "@salt-ds/styles";
4
4
  import { useWindow } from "@salt-ds/window";
5
5
  import clsx from "clsx";
6
6
  import { forwardRef, useCallback } from "react";
7
7
  import { useToggleFilter } from "./useToggleFilter.js";
8
8
  import ToggleFilter_0 from "./ToggleFilter.css";
9
- const getValues = (defaultValue, value)=>{
9
+ const toToggleButtonValue = (value)=>"boolean" == typeof value ? `${value}` : value;
10
+ const getValues = (defaultValueProp, valueProp)=>{
11
+ const defaultValue = toToggleButtonValue(defaultValueProp);
12
+ const value = toToggleButtonValue(valueProp);
10
13
  if (void 0 === defaultValue && void 0 === value) return [
11
14
  "all"
12
15
  ];
@@ -24,24 +27,32 @@ const getValues = (defaultValue, value)=>{
24
27
  ];
25
28
  };
26
29
  const classBase = "vuuToggleFilter";
27
- const ToggleFilter_ToggleFilter = /*#__PURE__*/ forwardRef(function({ className, column, defaultValue: defaultValueProp, onCommit, table, value: valueProp, values, ...ToggleButtonGroupProps }, forwardedRef) {
30
+ const NO_MATCHING_DATA = "No matching data";
31
+ const ToggleFilter_ToggleFilter = /*#__PURE__*/ forwardRef(function({ className, column, defaultValue: defaultValueProp, labels, onCommit, table, value: valueProp, values, ...ToggleButtonGroupProps }, forwardedRef) {
28
32
  const targetWindow = useWindow();
29
33
  useComponentCssInjection({
30
34
  testId: "vuu-toggle-filter",
31
35
  css: ToggleFilter_0,
32
36
  window: targetWindow
33
37
  });
38
+ if (void 0 !== labels && labels.length !== values.length) throw Error("[ToggleFilter] labels must have the same number of items as values");
34
39
  const [defaultValue, value] = getValues(defaultValueProp, valueProp);
35
- const onlyAvailableValue = useToggleFilter({
40
+ const stringValues = values.map(String);
41
+ const { onlyAvailableValue, unavailableValues } = useToggleFilter({
36
42
  column,
37
43
  table,
38
- values
44
+ values: stringValues
39
45
  });
40
46
  const handleChange = useCallback((e)=>{
41
47
  const value = e.currentTarget.value;
42
- "all" === value ? onCommit(e, "") : onCommit(e, value);
48
+ if ("all" === value) onCommit(e, "");
49
+ else {
50
+ const toggleValue = values.find((v)=>`${v}` === value);
51
+ onCommit(e, "boolean" == typeof toggleValue ? toggleValue : value);
52
+ }
43
53
  }, [
44
- onCommit
54
+ onCommit,
55
+ values
45
56
  ]);
46
57
  return /*#__PURE__*/ jsxs(ToggleButtonGroup, {
47
58
  ...ToggleButtonGroupProps,
@@ -53,15 +64,23 @@ const ToggleFilter_ToggleFilter = /*#__PURE__*/ forwardRef(function({ className,
53
64
  children: [
54
65
  /*#__PURE__*/ jsx(ToggleButton, {
55
66
  value: "all",
56
- children: "ALL"
67
+ children: "All"
57
68
  }, "all"),
58
- values.map((toggleValue)=>/*#__PURE__*/ jsx(ToggleButton, {
59
- className: clsx({
60
- [`${classBase}-onlyAvailableValue`]: onlyAvailableValue === toggleValue
61
- }),
62
- value: toggleValue,
63
- children: toggleValue
64
- }, toggleValue))
69
+ stringValues.map((toggleValue, i)=>{
70
+ const unavailable = unavailableValues.includes(toggleValue);
71
+ return /*#__PURE__*/ jsx(Tooltip, {
72
+ content: NO_MATCHING_DATA,
73
+ disabled: !unavailable,
74
+ children: /*#__PURE__*/ jsx(ToggleButton, {
75
+ className: clsx({
76
+ [`${classBase}-onlyAvailableValue`]: onlyAvailableValue === toggleValue,
77
+ [`${classBase}-unavailableValue`]: unavailable
78
+ }),
79
+ value: toggleValue,
80
+ children: labels?.[i] ?? toggleValue
81
+ })
82
+ }, toggleValue);
83
+ })
65
84
  ]
66
85
  });
67
86
  });
@@ -1,57 +1,81 @@
1
- import { useSavedFilters } from "../filter-provider/FilterContext.js";
2
1
  import { useTypeaheadSuggestions } from "@vuu-ui/vuu-data-react";
3
- import { useCallback, useMemo, useRef, useState } from "react";
4
- import { filtersAreEqual, getVuuTable } from "@vuu-ui/vuu-utils";
2
+ import { parseFilter } from "@vuu-ui/vuu-filter-parser";
3
+ import { extractFilterForColumn, getVuuTable, useDataSource } from "@vuu-ui/vuu-utils";
4
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
5
5
  const assertValid = (values, actualValues)=>{
6
6
  if (actualValues.some((val)=>-1 === values.indexOf(val))) console.warn(`[useToggleFilter] ToggleFilter is configured with values which do not include all values from data source
7
7
  [${values.join()}]
8
8
  [${actualValues.join()}]`);
9
9
  };
10
- const typeaheadRefreshRequired = (f1, f2)=>{
11
- if (null === f1 && null === f2) return false;
12
- if (null === f1 || null === f2) return true;
13
- if (filtersAreEqual(f1, f2)) return false;
14
- return true;
10
+ const hasFilterOnColumn = (filterSpec, column)=>{
11
+ if (void 0 === filterSpec || "" === filterSpec.filter) return false;
12
+ const filter = filterSpec.filterStruct ?? parseFilter(filterSpec.filter);
13
+ return void 0 !== extractFilterForColumn(filter, column);
15
14
  };
16
15
  const useToggleFilter = ({ column, table, values })=>{
17
- const { currentFilter } = useSavedFilters();
18
- const currentFilterRef = useRef(currentFilter.filter);
19
- const [typeaheadValues, setTypeaheadValues] = useState([]);
16
+ const dataSource = useDataSource(false);
17
+ const [typeaheadValues, setTypeaheadValues] = useState(void 0);
20
18
  const getSuggestions = useTypeaheadSuggestions();
21
- const refreshSuggestions = useCallback((table)=>{
22
- const vuuTable = getVuuTable(table);
19
+ const requestIdRef = useRef(0);
20
+ const valuesRef = useRef(values);
21
+ valuesRef.current = values;
22
+ const valuesKey = values.join("\u0000");
23
+ const vuuTable = table ? getVuuTable(table) : void 0;
24
+ const tableModule = vuuTable?.module;
25
+ const tableName = vuuTable?.table;
26
+ const checkAvailability = useCallback(()=>{
27
+ if (void 0 === tableModule || void 0 === tableName || void 0 === dataSource) return;
28
+ if (hasFilterOnColumn(dataSource.filter, column)) return;
29
+ const requestId = ++requestIdRef.current;
23
30
  const params = [
24
- vuuTable,
31
+ {
32
+ module: tableModule,
33
+ table: tableName
34
+ },
25
35
  column
26
36
  ];
27
37
  getSuggestions(params).then((suggestions)=>{
28
- if (false === suggestions) setTypeaheadValues([]);
29
- else {
30
- assertValid(values, suggestions);
31
- setTypeaheadValues(suggestions);
32
- }
38
+ if (requestId !== requestIdRef.current || false === suggestions) return;
39
+ assertValid(valuesRef.current, suggestions);
40
+ setTypeaheadValues(suggestions);
33
41
  });
34
42
  }, [
35
43
  column,
44
+ dataSource,
36
45
  getSuggestions,
37
- values
46
+ tableModule,
47
+ tableName
38
48
  ]);
39
- useMemo(()=>{
40
- if (table) refreshSuggestions(table);
49
+ useEffect(()=>{
50
+ checkAvailability();
41
51
  }, [
42
- refreshSuggestions,
43
- table
52
+ checkAvailability
44
53
  ]);
45
- useMemo(()=>{
46
- const { current: filter } = currentFilterRef;
47
- if (table && typeaheadRefreshRequired(filter, currentFilter.filter)) refreshSuggestions(table);
48
- currentFilterRef.current = currentFilter.filter;
54
+ useEffect(()=>{
55
+ if (dataSource) {
56
+ const handleConfigChange = (_config, _range, _confirmed, configChanges)=>{
57
+ if (configChanges?.filterChanged) checkAvailability();
58
+ };
59
+ dataSource.on("config", handleConfigChange);
60
+ return ()=>{
61
+ dataSource.removeListener("config", handleConfigChange);
62
+ };
63
+ }
49
64
  }, [
50
- currentFilter.filter,
51
- refreshSuggestions,
52
- table
65
+ checkAvailability,
66
+ dataSource
67
+ ]);
68
+ return useMemo(()=>{
69
+ if (void 0 === typeaheadValues) return {
70
+ unavailableValues: []
71
+ };
72
+ return {
73
+ onlyAvailableValue: 1 === typeaheadValues.length ? typeaheadValues[0] : void 0,
74
+ unavailableValues: valuesRef.current.filter((value)=>!typeaheadValues.includes(value))
75
+ };
76
+ }, [
77
+ typeaheadValues,
78
+ valuesKey
53
79
  ]);
54
- const [firstValue] = typeaheadValues;
55
- return 1 === typeaheadValues.length ? firstValue : void 0;
56
80
  };
57
81
  export { useToggleFilter };
@@ -1,6 +1,19 @@
1
1
  import { type SegmentedButtonGroupProps } from "@salt-ds/core";
2
- import { type DataItemEditControlProps } from "@vuu-ui/vuu-data-react";
3
- import { type ColumnFilterHookProps } from "./useColumnFilter";
4
- export interface ColumnFilterProps extends ColumnFilterHookProps, Omit<SegmentedButtonGroupProps, "defaultValue">, Pick<DataItemEditControlProps, "TypeaheadProps" | "table" | "values" | "variant"> {
2
+ import { DataItemEditControlProps } from "@vuu-ui/vuu-data-react";
3
+ import { ColumnFilterHookProps } from "./useColumnFilter";
4
+ import { type DateTimePattern } from "@vuu-ui/vuu-utils";
5
+ export interface ColumnFilterProps extends ColumnFilterHookProps, Omit<SegmentedButtonGroupProps, "defaultValue">, Pick<DataItemEditControlProps, "TypeaheadProps" | "labels" | "table" | "values" | "variant"> {
6
+ /**
7
+ * Temporal columns only. The date/time pattern used by the filter control,
8
+ * overriding any pattern configured on the column type. The pattern also
9
+ * determines the control rendered: a time picker for a time only pattern
10
+ * (with milliseconds if time is 'hh:mm:ss.ms'), otherwise a date picker.
11
+ * This allows a filter to use different formatting to the Table, e.g.
12
+ * the Table might display nanoseconds whilst the filter accepts a time
13
+ * of day with milliseconds. The column passed to change and commit
14
+ * handlers is the column with this pattern applied (see withDateTimePattern),
15
+ * so that filter values are interpreted consistently with the control.
16
+ */
17
+ pattern?: DateTimePattern;
5
18
  }
6
19
  export declare const ColumnFilter: import("react").ForwardRefExoticComponent<ColumnFilterProps & import("react").RefAttributes<HTMLDivElement>>;
@@ -4,4 +4,4 @@ import { type FilterBarProps } from "../filter-bar";
4
4
  export interface CustomFilterProps extends HTMLAttributes<HTMLDivElement>, Pick<FilterBarProps, "defaultFilterState" | "filterState" | "onApplyFilter" | "onClearFilter" | "onFilterDeleted" | "onFilterRenamed" | "onFilterStateChanged" | "vuuTable"> {
5
5
  columnDescriptors: readonly ColumnDescriptor[];
6
6
  }
7
- export declare const CustomFilters: ({ columnDescriptors, defaultFilterState, filterState, onApplyFilter, onClearFilter, onFilterDeleted, onFilterRenamed, onFilterStateChanged, vuuTable, }: CustomFilterProps) => import("react").JSX.Element;
7
+ export declare const CustomFilters: ({ columnDescriptors, defaultFilterState, filterState, onApplyFilter, onClearFilter, onFilterDeleted, onFilterRenamed, onFilterStateChanged, vuuTable, }: CustomFilterProps) => import("react/jsx-runtime").JSX.Element;
@@ -30,4 +30,4 @@ export interface FilterBarProps extends HTMLAttributes<HTMLDivElement> {
30
30
  vuuTable?: VuuTable;
31
31
  variant?: FilterBarVariant;
32
32
  }
33
- export declare const FilterBar: ({ QuickFilterProps, className: classNameProp, columnDescriptors, defaultFilterMode, defaultFilterState, filterMode: filterModeProp, filterState, onApplyFilter, onChangeFilterMode: onChangeFilterModeProp, onClearFilter, onFilterDeleted, onFilterRenamed, onFilterStateChanged, vuuTable, variant, ...htmlAttributes }: FilterBarProps) => import("react").JSX.Element;
33
+ export declare const FilterBar: ({ QuickFilterProps, className: classNameProp, columnDescriptors, defaultFilterMode, defaultFilterState, filterMode: filterModeProp, filterState, onApplyFilter, onChangeFilterMode: onChangeFilterModeProp, onClearFilter, onFilterDeleted, onFilterRenamed, onFilterStateChanged, vuuTable, variant, ...htmlAttributes }: FilterBarProps) => import("react/jsx-runtime").JSX.Element;
@@ -1 +1 @@
1
- export declare const FilterBarMenu: () => import("react").JSX.Element;
1
+ export declare const FilterBarMenu: () => import("react/jsx-runtime").JSX.Element;
@@ -13,4 +13,4 @@ export interface FilterClauseProps extends Omit<HTMLAttributes<HTMLDivElement>,
13
13
  dropdownOnAutofocus?: boolean;
14
14
  vuuTable: VuuTable;
15
15
  }
16
- export declare const FilterClause: ({ className, columnsByName, onCancel, onDropdownOpen, onFocusSave, filterClauseModel, vuuTable, dropdownOnAutofocus, ...htmlAttributes }: FilterClauseProps) => import("react").JSX.Element;
16
+ export declare const FilterClause: ({ className, columnsByName, onCancel, onDropdownOpen, onFocusSave, filterClauseModel, vuuTable, dropdownOnAutofocus, ...htmlAttributes }: FilterClauseProps) => import("react/jsx-runtime").JSX.Element;
@@ -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.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,5 +1,5 @@
1
- import { type HTMLAttributes } from "react";
2
- import type { FilterClauseValueEditor } from "../filterClauseTypes";
1
+ import { HTMLAttributes } from "react";
2
+ import { FilterClauseValueEditor } from "../filterClauseTypes";
3
3
  export interface FilterClauseTextValueEditorProps extends FilterClauseValueEditor, HTMLAttributes<HTMLDivElement> {
4
4
  "data-field"?: string;
5
5
  operator: string;