@vuu-ui/vuu-filters 2.1.19-beta.1 → 2.1.19-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 (91) hide show
  1. package/package.json +14 -15
  2. package/src/FilterAggregator.js +107 -0
  3. package/src/FilterModel.js +161 -0
  4. package/src/column-filter/ColumnFilter.css.js +52 -0
  5. package/src/column-filter/ColumnFilter.js +59 -0
  6. package/src/column-filter/useColumnFilter.js +130 -0
  7. package/src/column-filter/utils.js +35 -0
  8. package/src/custom-filters/CustomFilters.js +74 -0
  9. package/src/custom-filters/filterBarFocusManagement.js +25 -0
  10. package/src/custom-filters/index.js +1 -0
  11. package/src/custom-filters/useCustomFilters.js +237 -0
  12. package/src/custom-filters/useFilterState.js +123 -0
  13. package/src/filter-bar/FilterBar.css.js +81 -0
  14. package/src/filter-bar/FilterBar.js +97 -0
  15. package/src/filter-bar/FilterBarMenu.js +14 -0
  16. package/src/filter-bar/index.js +1 -0
  17. package/src/filter-bar/useFilterBar.js +28 -0
  18. package/src/filter-clause/ExpandoCombobox.css.js +54 -0
  19. package/src/filter-clause/ExpandoCombobox.js +86 -0
  20. package/src/filter-clause/FilterClause.css.js +101 -0
  21. package/src/filter-clause/FilterClause.js +69 -0
  22. package/src/filter-clause/column-picker/ColumnPicker.js +25 -0
  23. package/src/filter-clause/column-picker/useColumnPicker.js +42 -0
  24. package/src/filter-clause/filterClauseFocusManagement.js +156 -0
  25. package/src/filter-clause/filterClauseTypes.js +0 -0
  26. package/src/filter-clause/index.js +5 -0
  27. package/src/filter-clause/operator-picker/OperatorPicker.js +25 -0
  28. package/src/filter-clause/operator-picker/operator-utils.js +23 -0
  29. package/src/filter-clause/operator-picker/useOperatorPicker.js +42 -0
  30. package/src/filter-clause/useFilterClause.js +135 -0
  31. package/src/filter-clause/value-editors/FilterClauseValueEditor.js +59 -0
  32. package/src/filter-clause/value-editors/FilterClauseValueEditorDate.js +55 -0
  33. package/src/filter-clause/value-editors/FilterClauseValueEditorNumber.js +32 -0
  34. package/src/filter-clause/value-editors/FilterClauseValueEditorText.js +182 -0
  35. package/src/filter-clause/value-editors/FilterClauseValueEditorTime.js +53 -0
  36. package/src/filter-container/ExtendedSingleValueFilterClause.js +35 -0
  37. package/src/filter-container/FilterContainer.css.js +13 -0
  38. package/src/filter-container/FilterContainer.js +121 -0
  39. package/src/filter-container/useFilterContainer.js +82 -0
  40. package/src/filter-context-menu/useFilterContextMenu.js +106 -0
  41. package/src/filter-display/FilterDisplay.css.js +31 -0
  42. package/src/filter-display/FilterDisplay.js +59 -0
  43. package/src/filter-editor/FilterClauseCombinator.css.js +15 -0
  44. package/src/filter-editor/FilterClauseCombinator.js +32 -0
  45. package/src/filter-editor/FilterEditor.css.js +13 -0
  46. package/src/filter-editor/FilterEditor.js +65 -0
  47. package/src/filter-editor/index.js +1 -0
  48. package/src/filter-editor/useFilterEditor.js +193 -0
  49. package/src/filter-panel/FilterPanel.css.js +22 -0
  50. package/src/filter-panel/FilterPanel.js +59 -0
  51. package/src/filter-panel/useFilterPanel.js +58 -0
  52. package/src/filter-pill/FilterMenu.js +48 -0
  53. package/src/filter-pill/FilterPill.css.js +15 -0
  54. package/src/filter-pill/FilterPill.js +114 -0
  55. package/src/filter-pill/FilterPillNext.css.js +12 -0
  56. package/src/filter-pill/FilterPillNext.js +65 -0
  57. package/src/filter-pill/filterAsReactNode.js +28 -0
  58. package/src/filter-pill/getFilterLabel.js +11 -0
  59. package/src/filter-pill/getFilterTooltipText.js +32 -0
  60. package/src/filter-pill/index.js +1 -0
  61. package/src/filter-pill/useFilterPillNext.js +53 -0
  62. package/src/filter-pill-menu/FilterPillMenu.css.js +8 -0
  63. package/src/filter-pill-menu/FilterPillMenu.js +46 -0
  64. package/src/filter-pill-menu/FilterPillMenuOptions.js +26 -0
  65. package/src/filter-pill-menu/index.js +1 -0
  66. package/src/filter-provider/FilterContext.js +71 -0
  67. package/src/filter-provider/FilterProvider.js +188 -0
  68. package/src/filter-provider/filter-descriptor-utils.js +41 -0
  69. package/src/filter-toggle-button/FilterToggleButton.css.js +22 -0
  70. package/src/filter-toggle-button/FilterToggleButton.js +103 -0
  71. package/src/filter-utils.js +300 -0
  72. package/src/index.js +21 -0
  73. package/src/inline-filter/InlineFilter.css.js +89 -0
  74. package/src/inline-filter/InlineFilter.js +56 -0
  75. package/src/inline-filter/index.js +1 -0
  76. package/src/inline-filter/useInlineFilter.js +11 -0
  77. package/src/quick-filters/QuickFilters.css.js +39 -0
  78. package/src/quick-filters/QuickFilters.js +81 -0
  79. package/src/quick-filters/index.js +1 -0
  80. package/src/quick-filters/useQuickFilters.js +108 -0
  81. package/src/saved-filters/DeleteFilterPrompt.js +23 -0
  82. package/src/saved-filters/FilterNamePrompt.css.js +19 -0
  83. package/src/saved-filters/FilterNamePrompt.js +74 -0
  84. package/src/saved-filters/SavedFilterPanel.css.js +21 -0
  85. package/src/saved-filters/SavedFilterPanel.js +53 -0
  86. package/src/saved-filters/useFilterNamePrompt.js +74 -0
  87. package/src/saved-filters/useSavedFilterPanel.js +19 -0
  88. package/src/tabbed-filter-container/TabbedFilterContainer.js +61 -0
  89. package/src/toggle-filter/ToggleFilter.css.js +10 -0
  90. package/src/toggle-filter/ToggleFilter.js +68 -0
  91. package/src/toggle-filter/useToggleFilter.js +57 -0
package/package.json CHANGED
@@ -1,22 +1,22 @@
1
1
  {
2
- "version": "2.1.19-beta.1",
2
+ "version": "2.1.19-beta.2",
3
3
  "author": "heswell",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "devDependencies": {
7
- "@vuu-ui/vuu-data-types": "2.1.19-beta.1",
8
- "@vuu-ui/vuu-protocol-types": "2.1.19-beta.1",
9
- "@vuu-ui/vuu-table-types": "2.1.19-beta.1",
10
- "@vuu-ui/vuu-filter-types": "2.1.19-beta.1"
7
+ "@vuu-ui/vuu-data-types": "2.1.19-beta.2",
8
+ "@vuu-ui/vuu-protocol-types": "2.1.19-beta.2",
9
+ "@vuu-ui/vuu-table-types": "2.1.19-beta.2",
10
+ "@vuu-ui/vuu-filter-types": "2.1.19-beta.2"
11
11
  },
12
12
  "dependencies": {
13
- "@vuu-ui/vuu-context-menu": "2.1.19-beta.1",
14
- "@vuu-ui/vuu-data-react": "2.1.19-beta.1",
15
- "@vuu-ui/vuu-filter-parser": "2.1.19-beta.1",
16
- "@vuu-ui/vuu-popups": "2.1.19-beta.1",
17
- "@vuu-ui/vuu-ui-controls": "2.1.19-beta.1",
18
- "@vuu-ui/vuu-table": "2.1.19-beta.1",
19
- "@vuu-ui/vuu-utils": "2.1.19-beta.1",
13
+ "@vuu-ui/vuu-context-menu": "2.1.19-beta.2",
14
+ "@vuu-ui/vuu-data-react": "2.1.19-beta.2",
15
+ "@vuu-ui/vuu-filter-parser": "2.1.19-beta.2",
16
+ "@vuu-ui/vuu-popups": "2.1.19-beta.2",
17
+ "@vuu-ui/vuu-ui-controls": "2.1.19-beta.2",
18
+ "@vuu-ui/vuu-table": "2.1.19-beta.2",
19
+ "@vuu-ui/vuu-utils": "2.1.19-beta.2",
20
20
  "@salt-ds/core": "1.54.1",
21
21
  "@salt-ds/lab": "1.0.0-alpha.83",
22
22
  "@salt-ds/styles": "0.2.1",
@@ -30,13 +30,12 @@
30
30
  },
31
31
  "files": [
32
32
  "README.md",
33
- "esm",
34
- "cjs",
33
+ "src",
35
34
  "/types"
36
35
  ],
37
36
  "exports": {
38
37
  ".": {
39
- "import": "./index.js",
38
+ "import": "./src/index.js",
40
39
  "types": "./types/index.d.ts"
41
40
  }
42
41
  },
@@ -0,0 +1,107 @@
1
+ import { getTypedRange, getTypedValue, isAndFilter, isBetweenFilter, isExtendedFilter, isMultiClauseFilter, isSingleValueFilter, isTypeDescriptor } from "@vuu-ui/vuu-utils";
2
+ import { ExtendedSingleValueFilterClause } from "./filter-container/ExtendedSingleValueFilterClause.js";
3
+ function installExtendedFilters(filter, throwIfUndefined = false) {
4
+ if (void 0 !== filter) if (isExtendedFilter(filter)) {
5
+ const { column, op, value, extendedOptions } = filter;
6
+ return new ExtendedSingleValueFilterClause(column, op, value, extendedOptions);
7
+ } else if (!isMultiClauseFilter(filter)) return filter;
8
+ else if (filter.filters.some(isExtendedFilter)) return {
9
+ op: filter.op,
10
+ filters: filter.filters.map((f)=>installExtendedFilters(f, true))
11
+ };
12
+ else return filter;
13
+ if (throwIfUndefined) throw Error("filter is undefined");
14
+ }
15
+ class FilterAggregator {
16
+ #filters = new Map();
17
+ constructor(filter){
18
+ const runtimeFilter = installExtendedFilters(filter);
19
+ if (isSingleValueFilter(runtimeFilter)) this.#filters.set(runtimeFilter.column, runtimeFilter);
20
+ else if (isBetweenFilter(runtimeFilter)) this.#filters.set(runtimeFilter.filters[0].column, runtimeFilter);
21
+ else if (isAndFilter(runtimeFilter)) runtimeFilter.filters.forEach((f)=>{
22
+ if (isBetweenFilter(f)) this.#filters.set(f.filters[0].column, f);
23
+ else this.#filters.set(f.column, f);
24
+ });
25
+ }
26
+ add(column, value, op, extendedFilterOptions) {
27
+ const { serverDataType = "string", type } = column;
28
+ 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, {
56
+ 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
64
+ });
65
+ } 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
+ });
72
+ }
73
+ }
74
+ has({ name }) {
75
+ return this.#filters.has(name);
76
+ }
77
+ get({ name }) {
78
+ return this.#filters.get(name);
79
+ }
80
+ clear() {
81
+ this.#filters.clear();
82
+ }
83
+ remove(column) {
84
+ if (!this.#filters.has(column.name)) return false;
85
+ this.#filters.delete(column.name);
86
+ return true;
87
+ }
88
+ get isEmpty() {
89
+ return 0 === this.#filters.size;
90
+ }
91
+ get count() {
92
+ return this.#filters.size;
93
+ }
94
+ get filter() {
95
+ const { size } = this.#filters;
96
+ if (0 === size) return;
97
+ if (1 !== size) return {
98
+ op: "and",
99
+ filters: Array.from(this.#filters.values())
100
+ };
101
+ {
102
+ const [filter] = this.#filters.values();
103
+ return filter;
104
+ }
105
+ }
106
+ }
107
+ export { FilterAggregator };
@@ -0,0 +1,161 @@
1
+ import { EventEmitter, isMultiClauseFilter, isMultiValueFilter, isSingleValueFilter } from "@vuu-ui/vuu-utils";
2
+ const hasValues = ({ values })=>Array.isArray(values) && values.length > 0;
3
+ const isValidFilterClause = (filterClause)=>{
4
+ if (void 0 === filterClause.op || void 0 === filterClause.column) return false;
5
+ if (isMultiValueFilter(filterClause)) return hasValues(filterClause);
6
+ if (isSingleValueFilter(filterClause)) return void 0 !== filterClause.value && "" !== filterClause.value;
7
+ throw Error("isValidFilterClause should never reach this far");
8
+ };
9
+ const isValidFilter = (filter)=>{
10
+ if (void 0 === filter) return false;
11
+ if (isMultiClauseFilter(filter)) return filter.filters.every(isValidFilter);
12
+ return isValidFilterClause(filter);
13
+ };
14
+ const isValidFilterModel = (filterModel)=>{
15
+ if (filterModel.isMultiClauseFilter) return filterModel.filterClauses.every((f)=>f.isValid);
16
+ return 1 === filterModel.filterClauses.length && filterModel.filterClauses[0].isValid;
17
+ };
18
+ class FilterClauseModel extends EventEmitter {
19
+ #filterClause;
20
+ #isValid;
21
+ constructor(filterClause){
22
+ super();
23
+ this.#filterClause = filterClause;
24
+ this.#isValid = isValidFilterClause(filterClause);
25
+ }
26
+ get isValid() {
27
+ return this.#isValid;
28
+ }
29
+ setIsValid(isValid) {
30
+ this.#isValid = isValid;
31
+ this.emit("isValid", isValid);
32
+ }
33
+ get column() {
34
+ return this.#filterClause.column;
35
+ }
36
+ set column(column) {
37
+ this.#filterClause = {
38
+ column
39
+ };
40
+ const isValid = isValidFilterClause(this.#filterClause);
41
+ this.emit("filterClause", this.#filterClause, isValid);
42
+ if (isValid !== this.#isValid) this.setIsValid(isValid);
43
+ }
44
+ get op() {
45
+ return this.#filterClause.op;
46
+ }
47
+ setOp(op) {
48
+ this.#filterClause = {
49
+ ...this.#filterClause,
50
+ op
51
+ };
52
+ const isValid = isValidFilterClause(this.#filterClause);
53
+ this.emit("filterClause", this.#filterClause, isValid);
54
+ if (isValid !== this.#isValid) this.setIsValid(isValid);
55
+ }
56
+ setValue(value, isFinal = true) {
57
+ if (isSingleValueFilter(this.#filterClause)) this.#filterClause = {
58
+ ...this.#filterClause,
59
+ value
60
+ };
61
+ else if (Array.isArray(value)) this.#filterClause = {
62
+ ...this.#filterClause,
63
+ values: value
64
+ };
65
+ const isValid = isValidFilterClause(this.#filterClause);
66
+ if (isValid !== this.#isValid) this.setIsValid(isValid);
67
+ if (isFinal) this.emit("filterClause", this.#filterClause, isValid);
68
+ }
69
+ asFilter(throwIfInvalid = true) {
70
+ if (throwIfInvalid && !this.#isValid) throw Error("Invalid filter model cannot be returned as Filter");
71
+ return this.#filterClause;
72
+ }
73
+ commit() {
74
+ this.emit("filterClause", this.#filterClause, true);
75
+ }
76
+ }
77
+ class FilterModel extends EventEmitter {
78
+ #children = [];
79
+ #isValid;
80
+ #op;
81
+ constructor(filter){
82
+ super();
83
+ if (isMultiClauseFilter(filter)) {
84
+ this.#op = filter.op;
85
+ filter.filters.forEach((f)=>this.addFilterClause(f));
86
+ } else if (filter) this.addFilterClause(filter);
87
+ else this.addNewFilterClause();
88
+ this.#isValid = isValidFilter(filter);
89
+ }
90
+ get isValid() {
91
+ return this.#isValid;
92
+ }
93
+ setIsValid(isValid) {
94
+ this.#isValid = isValid;
95
+ this.emit("isValid", isValid);
96
+ }
97
+ addNewFilterClause(operator) {
98
+ const count = this.#children.length;
99
+ if (operator || this.#op || 1 !== count) {
100
+ if (operator && !this.#op && 1 === count) this.#op = operator;
101
+ else if (operator && this.#op && operator !== this.#op) throw Error("FilterModel: use setOp to change the Filter combinator operator");
102
+ } else this.#op = "and";
103
+ const filterClauseModel = new FilterClauseModel({});
104
+ filterClauseModel.on("isValid", this.onFilterClauseStatusChange);
105
+ filterClauseModel.on("filterClause", this.onFilterClauseChange);
106
+ this.#children.push(filterClauseModel);
107
+ this.setIsValid(false);
108
+ }
109
+ addFilterClause(filterClause = {}) {
110
+ const filterClauseModel = new FilterClauseModel(filterClause);
111
+ filterClauseModel.on("isValid", this.onFilterClauseStatusChange);
112
+ filterClauseModel.on("filterClause", this.onFilterClauseChange);
113
+ this.#children.push(filterClauseModel);
114
+ }
115
+ removeFilterClause(filterClause) {
116
+ if (this.isMultiClauseFilter) {
117
+ const doomedFilter = this.filterClauses.indexOf(filterClause);
118
+ if (-1 !== doomedFilter) {
119
+ this.filterClauses.splice(doomedFilter, 1);
120
+ if (1 === this.filterClauses.length) this.#op = void 0;
121
+ this.checkValidStatus();
122
+ }
123
+ }
124
+ }
125
+ checkValidStatus() {
126
+ const nowValid = isValidFilterModel(this);
127
+ if (nowValid !== this.#isValid) this.setIsValid(nowValid);
128
+ }
129
+ onFilterClauseChange = ()=>{
130
+ this.emit("filter", this.asFilter(false), this.#isValid);
131
+ };
132
+ onFilterClauseStatusChange = (isValid)=>{
133
+ if (!isValid && this.#isValid) this.setIsValid(false);
134
+ else this.checkValidStatus();
135
+ };
136
+ getFilterClause(index) {
137
+ return this.#children[index];
138
+ }
139
+ get op() {
140
+ return this.#op;
141
+ }
142
+ setOp(op) {
143
+ this.#op = op;
144
+ this.emit("filter", this.asFilter(false), this.#isValid);
145
+ }
146
+ get filterClauses() {
147
+ return this.#children;
148
+ }
149
+ get isMultiClauseFilter() {
150
+ return "and" === this.#op || "or" === this.#op;
151
+ }
152
+ asFilter(throwIfInvalid = true) {
153
+ if (throwIfInvalid && !this.#isValid) throw Error("Invalid filter model cannot be returned as Filter");
154
+ if ("and" === this.#op || "or" === this.#op) return {
155
+ op: this.#op,
156
+ filters: this.#children.map((f)=>f.asFilter(throwIfInvalid))
157
+ };
158
+ return this.#children[0].asFilter(throwIfInvalid);
159
+ }
160
+ }
161
+ export { FilterClauseModel, FilterModel };
@@ -0,0 +1,52 @@
1
+ const css = `
2
+ .vuuColumnFilter {
3
+ --vuuTimeInput-outline: none;
4
+ border: solid 1px var(--salt-editable-borderColor);
5
+ max-width: 200px;
6
+ width: var(--vuuColumnFilter-width, fit-content);
7
+ padding-left: 4px;
8
+ padding-right: 4px;
9
+
10
+ & .vuuColumnFilter-rangeHigh {
11
+ position: relative;
12
+ }
13
+
14
+ & .vuuColumnFilter-rangeHigh:before {
15
+ content: "";
16
+ background: var(--salt-separable-primary-borderColor);
17
+ width: 1px;
18
+ left: calc(-1 * var(--salt-spacing-100));
19
+ position: absolute;
20
+ top: -1px;
21
+ bottom: -1px;
22
+ }
23
+
24
+ & .vuuTimePicker {
25
+ border-radius: 6px;
26
+ }
27
+
28
+ & .vuuTimePicker + .vuuTimePicker {
29
+ margin-left: var(--salt-spacing-100);
30
+ }
31
+
32
+ & .vuuTimeInput {
33
+ border: none;
34
+ }
35
+
36
+ & .vuuTimeInput:focus-visible {
37
+ outline: none;
38
+ }
39
+ }
40
+
41
+ .vuuColumnFilter-trigger {
42
+ --saltButton-borderRadius: 4px 0px 0px 4px;
43
+ --saltButton-padding: var(--salt-spacing-200);
44
+ }
45
+
46
+ .vuuColumnFilter-invalid {
47
+ border-color: red;
48
+ }
49
+
50
+
51
+ `;
52
+ export default css;
@@ -0,0 +1,59 @@
1
+ import { jsxs } from "react/jsx-runtime";
2
+ import { SegmentedButtonGroup } from "@salt-ds/core";
3
+ import { getDataItemEditControl } from "@vuu-ui/vuu-data-react";
4
+ import clsx from "clsx";
5
+ import { forwardRef as external_react_forwardRef } from "react";
6
+ import { useColumnFilter } from "./useColumnFilter.js";
7
+ import { useComponentCssInjection } from "@salt-ds/styles";
8
+ import { useWindow } from "@salt-ds/window";
9
+ import ColumnFilter_0 from "./ColumnFilter.css";
10
+ import { isBetweenOperator } from "@vuu-ui/vuu-utils";
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) {
13
+ const targetWindow = useWindow();
14
+ useComponentCssInjection({
15
+ testId: "vuu-filter-editor",
16
+ css: ColumnFilter_0,
17
+ window: targetWindow
18
+ });
19
+ const { InputProps, InputPropsRange, isInvalid, onCommit, onCommitRange } = useColumnFilter({
20
+ InputProps: InputPropsProp,
21
+ column,
22
+ defaultValue,
23
+ extendedFilterOptions,
24
+ onColumnFilterChange,
25
+ onColumnRangeFilterChange,
26
+ onCommit: onCommitProp,
27
+ operator,
28
+ value: valueProp
29
+ });
30
+ return /*#__PURE__*/ jsxs(SegmentedButtonGroup, {
31
+ ...buttonGroupProps,
32
+ className: clsx(classBase, className, {
33
+ [`${classBase}-invalid`]: isInvalid
34
+ }),
35
+ ref: forwardRef,
36
+ children: [
37
+ getDataItemEditControl({
38
+ InputProps,
39
+ TypeaheadProps,
40
+ commitWhenCleared: true,
41
+ dataDescriptor: column,
42
+ onCommit,
43
+ table,
44
+ values,
45
+ variant
46
+ }),
47
+ isBetweenOperator(operator) ? getDataItemEditControl({
48
+ InputProps: InputPropsRange,
49
+ className: `${classBase}-rangeHigh`,
50
+ commitWhenCleared: true,
51
+ variant,
52
+ dataDescriptor: column,
53
+ onCommit: onCommitRange,
54
+ table
55
+ }) : null
56
+ ]
57
+ });
58
+ });
59
+ export { ColumnFilter_ColumnFilter as ColumnFilter };
@@ -0,0 +1,130 @@
1
+ import { useControlled } from "@salt-ds/core";
2
+ import { useCallback, useMemo, useState } from "react";
3
+ import { isBetweenOperator, isValidRange } from "@vuu-ui/vuu-utils";
4
+ const injectInputProps = (InputProps, inputProps)=>{
5
+ if (void 0 === InputProps) return {
6
+ inputProps
7
+ };
8
+ return {
9
+ ...InputProps,
10
+ inputProps: {
11
+ ...InputProps.inputProps,
12
+ ...inputProps
13
+ }
14
+ };
15
+ };
16
+ const useColumnFilter = ({ InputProps: InputPropsProp, column, defaultValue, extendedFilterOptions, onColumnFilterChange, onColumnRangeFilterChange, onCommit, operator = "=", value: valueProp })=>{
17
+ const [isInvalid, setIsInvalid] = useState(false);
18
+ const [value, setValue] = useControlled({
19
+ controlled: valueProp,
20
+ default: defaultValue,
21
+ name: "ColumnFilterNext",
22
+ state: "value"
23
+ });
24
+ const handleCommit = useCallback((_e, newValue = "")=>{
25
+ if (Array.isArray(value)) if (isBetweenOperator(operator)) {
26
+ setValue([
27
+ `${newValue}`,
28
+ value[1]
29
+ ]);
30
+ onCommit?.(column, operator, [
31
+ `${newValue}`,
32
+ value[1]
33
+ ], extendedFilterOptions);
34
+ } else throw Error("[useColumnFilterNext] value has been initialised incorrectly for non-range filter");
35
+ else {
36
+ setValue(newValue);
37
+ onCommit?.(column, operator, `${newValue}`, extendedFilterOptions);
38
+ }
39
+ }, [
40
+ value,
41
+ operator,
42
+ setValue,
43
+ onCommit,
44
+ column,
45
+ extendedFilterOptions
46
+ ]);
47
+ const handleRangeCommit = useCallback((_e, newValue = "")=>{
48
+ if (Array.isArray(value)) {
49
+ const [firstValue] = value;
50
+ const newRange = [
51
+ value[0],
52
+ `${newValue}`
53
+ ];
54
+ setValue(newRange);
55
+ const validRange = isValidRange(newRange);
56
+ setIsInvalid(!validRange);
57
+ if (validRange) onCommit?.(column, operator, [
58
+ firstValue,
59
+ `${newValue}`
60
+ ], extendedFilterOptions);
61
+ } else if ("" !== value) {
62
+ const currentValue = `${value}`;
63
+ setValue([
64
+ currentValue,
65
+ `${newValue}`
66
+ ]);
67
+ onCommit?.(column, operator, [
68
+ currentValue,
69
+ `${newValue}`
70
+ ], extendedFilterOptions);
71
+ } else throw Error("[useColumnFilterNext] value has been initialised incorrectly for range filter");
72
+ }, [
73
+ value,
74
+ setValue,
75
+ onCommit,
76
+ column,
77
+ operator,
78
+ extendedFilterOptions
79
+ ]);
80
+ const onChange = useCallback((e)=>{
81
+ const { value = "" } = e.target;
82
+ setValue((v)=>Array.isArray(v) ? [
83
+ value,
84
+ v[1]
85
+ ] : value);
86
+ onColumnFilterChange?.(e.target.value, column, operator);
87
+ }, [
88
+ column,
89
+ onColumnFilterChange,
90
+ operator,
91
+ setValue
92
+ ]);
93
+ const onRangeInputChange = useCallback((e)=>{
94
+ const { value = "" } = e.target;
95
+ setValue((v)=>Array.isArray(v) ? [
96
+ v[0],
97
+ value
98
+ ] : value);
99
+ onColumnRangeFilterChange?.(value, column, operator);
100
+ }, [
101
+ setValue,
102
+ onColumnRangeFilterChange,
103
+ column,
104
+ operator
105
+ ]);
106
+ const InputProps = useMemo(()=>injectInputProps(InputPropsProp, {
107
+ onChange,
108
+ value: Array.isArray(value) ? value[0] : value
109
+ }), [
110
+ InputPropsProp,
111
+ onChange,
112
+ value
113
+ ]);
114
+ const InputPropsRange = useMemo(()=>injectInputProps(InputPropsProp, Array.isArray(value) ? {
115
+ onChange: onRangeInputChange,
116
+ value: value[1]
117
+ } : void 0), [
118
+ InputPropsProp,
119
+ onRangeInputChange,
120
+ value
121
+ ]);
122
+ return {
123
+ InputProps,
124
+ InputPropsRange,
125
+ isInvalid,
126
+ onCommit: handleCommit,
127
+ onCommitRange: handleRangeCommit
128
+ };
129
+ };
130
+ export { useColumnFilter };
@@ -0,0 +1,35 @@
1
+ import { addFilter } from "../filter-utils.js";
2
+ const isStartsWithValue = (value)=>/\.\.\.$/.test(value);
3
+ const getTypeaheadFilter = (column, filterValues, isStartsWithFilter)=>{
4
+ if (0 === filterValues.length) return;
5
+ if (isStartsWithFilter) {
6
+ const startsWith = filterValues[0].substring(0, filterValues[0].length - 3);
7
+ return {
8
+ column,
9
+ op: "starts",
10
+ value: `"${startsWith}"`
11
+ };
12
+ }
13
+ return {
14
+ column,
15
+ op: "in",
16
+ values: filterValues.map((value)=>`"${value}"`)
17
+ };
18
+ };
19
+ const getRangeFilter = (column, startValue, endValue)=>{
20
+ const startFilter = void 0 === startValue ? void 0 : {
21
+ column,
22
+ op: ">",
23
+ value: startValue - 1
24
+ };
25
+ const endFilter = void 0 === endValue ? void 0 : {
26
+ column,
27
+ op: "<",
28
+ value: endValue + 1
29
+ };
30
+ if (void 0 === endFilter) return startFilter;
31
+ return addFilter(startFilter, endFilter, {
32
+ combineWith: "and"
33
+ });
34
+ };
35
+ export { getRangeFilter, getTypeaheadFilter, isStartsWithValue };
@@ -0,0 +1,74 @@
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import { createElement, useRef } from "react";
3
+ import { IconButton, Prompt } from "@vuu-ui/vuu-ui-controls";
4
+ import { FilterEditor } from "../filter-editor/index.js";
5
+ import { FilterPill } from "../filter-pill/index.js";
6
+ import { FilterModel } from "../FilterModel.js";
7
+ import { isEditFilterState, useCustomFilters } from "./useCustomFilters.js";
8
+ const classBase = "vuuCustomFilters";
9
+ const CustomFilters = ({ columnDescriptors, defaultFilterState, filterState, onApplyFilter, onClearFilter, onFilterDeleted, onFilterRenamed, onFilterStateChanged, vuuTable })=>{
10
+ const rootRef = useRef(null);
11
+ const { activeFilterIndex, addButtonProps, columnsByName, filters, interactedFilterState, onCancelEdit, onSave, FilterPillProps, promptProps } = useCustomFilters({
12
+ containerRef: rootRef,
13
+ columnDescriptors,
14
+ defaultFilterState,
15
+ filterState,
16
+ onApplyFilter,
17
+ onClearFilter,
18
+ onFilterStateChanged,
19
+ onFilterDeleted,
20
+ onFilterRenamed
21
+ });
22
+ const indexOfFilterBeingRenamed = interactedFilterState?.state === "rename" ? interactedFilterState.index : -1;
23
+ const filterModel = isEditFilterState(interactedFilterState?.state) ? new FilterModel(interactedFilterState.filter) : void 0;
24
+ const getFilterPills = ()=>{
25
+ const items = [];
26
+ filters.forEach((filter, i)=>{
27
+ items.push(/*#__PURE__*/ createElement(FilterPill, {
28
+ ...FilterPillProps,
29
+ editing: indexOfFilterBeingRenamed === i,
30
+ columnsByName: columnsByName,
31
+ "data-index": i,
32
+ filter: filter,
33
+ key: `filter-${i}`,
34
+ selected: activeFilterIndex.includes(i)
35
+ }));
36
+ });
37
+ return items;
38
+ };
39
+ return /*#__PURE__*/ jsxs(Fragment, {
40
+ children: [
41
+ /*#__PURE__*/ jsxs("div", {
42
+ className: "vuuCustomFilters",
43
+ ref: rootRef,
44
+ children: [
45
+ /*#__PURE__*/ jsx("div", {
46
+ className: `${classBase}-filters`,
47
+ children: getFilterPills()
48
+ }),
49
+ /*#__PURE__*/ createElement(IconButton, {
50
+ ...addButtonProps,
51
+ "aria-label": "Add filter",
52
+ "data-selectable": false,
53
+ icon: "plus",
54
+ key: "filter-add",
55
+ tabIndex: 0,
56
+ variant: "secondary"
57
+ }),
58
+ promptProps ? /*#__PURE__*/ jsx(Prompt, {
59
+ ...promptProps,
60
+ open: true
61
+ }) : null
62
+ ]
63
+ }),
64
+ filterModel && vuuTable && /*#__PURE__*/ jsx(FilterEditor, {
65
+ columnDescriptors: columnDescriptors,
66
+ onCancel: onCancelEdit,
67
+ onSave: onSave,
68
+ filter: interactedFilterState?.filter,
69
+ vuuTable: vuuTable
70
+ }, "filter-editor")
71
+ ]
72
+ });
73
+ };
74
+ export { CustomFilters };