@wallarm-org/design-system 0.66.3 → 0.67.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/FilterInput/FilterInputContext/types.d.ts +6 -0
- package/dist/components/FilterInput/FilterInputContext/useFilterInputContextValue.d.ts +2 -0
- package/dist/components/FilterInput/FilterInputContext/useFilterInputContextValue.js +4 -0
- package/dist/components/FilterInput/FilterInputErrors/parseFilterInputErrors.js +11 -2
- package/dist/components/FilterInput/FilterInputField/ChipsWithGaps.js +4 -2
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/FilterInputChip.d.ts +7 -1
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/FilterInputChip.js +44 -6
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/PairSeparator.d.ts +3 -0
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/PairSeparator.js +16 -0
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/context/EditingContext.d.ts +2 -0
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/index.d.ts +3 -2
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/index.js +3 -2
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/segmentVariant.d.ts +2 -0
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/segmentVariant.js +2 -1
- package/dist/components/FilterInput/FilterInputField/FilterInputField.js +3 -1
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/lib/deriveAutocompleteValues.d.ts +8 -1
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/lib/deriveAutocompleteValues.js +22 -3
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useAutocompleteState.d.ts +10 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useAutocompleteState.js +6 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useChipEditing.d.ts +2 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useChipEditing.js +31 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useFilterInputAutocomplete.d.ts +2 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useFilterInputAutocomplete.js +14 -3
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/types.d.ts +9 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/useOperatorFlow.d.ts +1 -1
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/useOperatorFlow.js +22 -7
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/useValueFlow.d.ts +1 -1
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/useValueFlow.js +77 -13
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useResetState.d.ts +3 -1
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useResetState.js +5 -1
- package/dist/components/FilterInput/hooks/useFilterInputExpression/buildChips.js +26 -1
- package/dist/components/FilterInput/hooks/useFilterInputExpression/expression.d.ts +9 -3
- package/dist/components/FilterInput/hooks/useFilterInputExpression/expression.js +75 -9
- package/dist/components/FilterInput/hooks/useFilterInputExpression/useFilterInputExpression.d.ts +1 -1
- package/dist/components/FilterInput/hooks/useFilterInputExpression/useFilterInputExpression.js +99 -71
- package/dist/components/FilterInput/lib/constants.js +2 -2
- package/dist/components/FilterInput/types.d.ts +25 -1
- package/dist/metadata/components.json +8 -2
- package/package.json +1 -1
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import type { RefObject } from 'react';
|
|
2
2
|
import type { ChipSegment } from '../../../FilterInputField/FilterInputChip';
|
|
3
3
|
import type { Condition, FieldMetadata, FilterOperator, MenuState, UpsertCondition } from '../../../types';
|
|
4
|
+
import type { BuildingBase } from '../useAutocompleteState';
|
|
4
5
|
export interface MenuFlowDeps {
|
|
5
6
|
editing: {
|
|
6
7
|
editingChipId: string | null;
|
|
7
8
|
editingSegment: ChipSegment | null;
|
|
9
|
+
/** Which triplet is being edited: 0 = base, 1 = paired second. */
|
|
10
|
+
editingSide: 0 | 1;
|
|
8
11
|
/** Pre-derived chipId === null && segment !== null marker — single source
|
|
9
12
|
* of truth, avoids re-deriving in every consumer. */
|
|
10
13
|
isBuildingEdit: boolean;
|
|
@@ -36,6 +39,12 @@ export interface MenuFlowDeps {
|
|
|
36
39
|
setInputText: (text: string) => void;
|
|
37
40
|
setMenuState: (state: MenuState) => void;
|
|
38
41
|
setBuildingMultiValue: (val: string | undefined) => void;
|
|
42
|
+
/** Which triplet is being built: 0 = base, 1 = paired second. */
|
|
43
|
+
buildingSide: 0 | 1;
|
|
44
|
+
setBuildingSide: (side: 0 | 1) => void;
|
|
45
|
+
/** Base triplet stashed while building a paired chip's second value. */
|
|
46
|
+
buildingBase: BuildingBase | null;
|
|
47
|
+
setBuildingBase: (base: BuildingBase | null) => void;
|
|
39
48
|
}
|
|
40
49
|
/** Shared shape: each sub-hook receives the same deps plus a ref to `conditions`
|
|
41
50
|
* that stays fresh without forcing callbacks to recreate on every keystroke. */
|
|
@@ -5,7 +5,7 @@ import type { MenuFlowInternalDeps } from './types';
|
|
|
5
5
|
* `handleCustomOperatorCommit` is the keyboard-typed path — it resolves the
|
|
6
6
|
* typed text to a known operator (label / raw key / symbol) and delegates.
|
|
7
7
|
*/
|
|
8
|
-
export declare const useOperatorFlow: ({ editing, selectedField, selectedOperator, insertIndex, upsertCondition, conditionsRef, resetState, setSelectedOperator, setMenuState, setBuildingMultiValue, setInputText, }: MenuFlowInternalDeps) => {
|
|
8
|
+
export declare const useOperatorFlow: ({ editing, selectedField, selectedOperator, insertIndex, upsertCondition, conditionsRef, resetState, setSelectedOperator, setMenuState, setBuildingMultiValue, setInputText, buildingSide, buildingBase, setBuildingBase, setBuildingSide, }: MenuFlowInternalDeps) => {
|
|
9
9
|
handleOperatorSelect: (operator: FilterOperator) => void;
|
|
10
10
|
handleCustomOperatorCommit: (customText: string) => void;
|
|
11
11
|
};
|
package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/useOperatorFlow.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { useCallback } from "react";
|
|
2
2
|
import { SEGMENT_VARIANT } from "../../../FilterInputField/FilterInputChip/index.js";
|
|
3
3
|
import { OPERATOR_SYMBOLS, chipIdToConditionIndex, getFieldOperators, getOperatorFromLabel, isNoValueOperator, isOperatorAllowedForField, isValueShapeCompatible } from "../../../lib/index.js";
|
|
4
|
-
const useOperatorFlow = ({ editing, selectedField, selectedOperator, insertIndex, upsertCondition, conditionsRef, resetState, setSelectedOperator, setMenuState, setBuildingMultiValue, setInputText })=>{
|
|
5
|
-
const { editingChipId, editingSegment, isBuildingEdit, setEditingSegment, setSegmentFilterText, clearEditing } = editing;
|
|
4
|
+
const useOperatorFlow = ({ editing, selectedField, selectedOperator, insertIndex, upsertCondition, conditionsRef, resetState, setSelectedOperator, setMenuState, setBuildingMultiValue, setInputText, buildingSide, buildingBase, setBuildingBase, setBuildingSide })=>{
|
|
5
|
+
const { editingChipId, editingSegment, editingSide, isBuildingEdit, setEditingSegment, setSegmentFilterText, clearEditing } = editing;
|
|
6
6
|
const handleOperatorSelect = useCallback((operator)=>{
|
|
7
7
|
if (!selectedField) return;
|
|
8
8
|
if (isBuildingEdit && editingSegment === SEGMENT_VARIANT.operator) {
|
|
@@ -19,8 +19,16 @@ const useOperatorFlow = ({ editing, selectedField, selectedOperator, insertIndex
|
|
|
19
19
|
return;
|
|
20
20
|
}
|
|
21
21
|
if (isNoValueOperator(operator)) {
|
|
22
|
+
if (1 === buildingSide && buildingBase && !editingChipId) {
|
|
23
|
+
upsertCondition(buildingBase.field, buildingBase.operator, buildingBase.value, null, insertIndex);
|
|
24
|
+
upsertCondition(selectedField, operator, null, null, void 0, void 0, void 0, 1);
|
|
25
|
+
setBuildingBase(null);
|
|
26
|
+
setBuildingSide(0);
|
|
27
|
+
resetState(true);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
22
30
|
const isEditing = !!editingChipId;
|
|
23
|
-
upsertCondition(selectedField, operator, null, editingChipId, isEditing ? void 0 : insertIndex);
|
|
31
|
+
upsertCondition(selectedField, operator, null, editingChipId, isEditing ? void 0 : insertIndex, void 0, void 0, editingSide);
|
|
24
32
|
resetState(!isEditing);
|
|
25
33
|
return;
|
|
26
34
|
}
|
|
@@ -28,13 +36,15 @@ const useOperatorFlow = ({ editing, selectedField, selectedOperator, insertIndex
|
|
|
28
36
|
const idx = chipIdToConditionIndex(editingChipId);
|
|
29
37
|
const condition = null !== idx ? conditionsRef.current[idx] : null;
|
|
30
38
|
if (condition) {
|
|
31
|
-
const
|
|
39
|
+
const currentValue = 1 === editingSide ? condition.pair?.value : condition.value;
|
|
40
|
+
const currentDateOrigin = 1 === editingSide ? condition.pair?.dateOrigin : condition.dateOrigin;
|
|
41
|
+
const hasValue = null !== currentValue && '' !== currentValue && void 0 !== currentValue;
|
|
32
42
|
if (hasValue) {
|
|
33
|
-
upsertCondition(selectedField, operator,
|
|
43
|
+
upsertCondition(selectedField, operator, currentValue, editingChipId, void 0, void 0, currentDateOrigin, editingSide);
|
|
34
44
|
resetState();
|
|
35
45
|
return;
|
|
36
46
|
}
|
|
37
|
-
upsertCondition(selectedField, operator, null, editingChipId);
|
|
47
|
+
upsertCondition(selectedField, operator, 1 === editingSide ? '' : null, editingChipId, void 0, void 0, void 0, editingSide);
|
|
38
48
|
setEditingSegment(SEGMENT_VARIANT.value);
|
|
39
49
|
setSegmentFilterText('');
|
|
40
50
|
}
|
|
@@ -45,6 +55,7 @@ const useOperatorFlow = ({ editing, selectedField, selectedOperator, insertIndex
|
|
|
45
55
|
}, [
|
|
46
56
|
editingChipId,
|
|
47
57
|
editingSegment,
|
|
58
|
+
editingSide,
|
|
48
59
|
isBuildingEdit,
|
|
49
60
|
setEditingSegment,
|
|
50
61
|
setSegmentFilterText,
|
|
@@ -58,7 +69,11 @@ const useOperatorFlow = ({ editing, selectedField, selectedOperator, insertIndex
|
|
|
58
69
|
setSelectedOperator,
|
|
59
70
|
setMenuState,
|
|
60
71
|
setBuildingMultiValue,
|
|
61
|
-
setInputText
|
|
72
|
+
setInputText,
|
|
73
|
+
buildingSide,
|
|
74
|
+
buildingBase,
|
|
75
|
+
setBuildingBase,
|
|
76
|
+
setBuildingSide
|
|
62
77
|
]);
|
|
63
78
|
const handleCustomOperatorCommit = useCallback((customText)=>{
|
|
64
79
|
if (!selectedField || !customText.trim()) return;
|
package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/useValueFlow.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type { MenuFlowInternalDeps } from './types';
|
|
|
4
4
|
* keyboard-typed custom-value commit (which routes through the field-type
|
|
5
5
|
* specific resolver). Also handles multi-select preview/toggle plumbing.
|
|
6
6
|
*/
|
|
7
|
-
export declare const useValueFlow: ({ editing, selectedField, selectedOperator, insertIndex, upsertCondition, conditionsRef, resetState, dateRange, setBuildingMultiValue, setInputText, }: MenuFlowInternalDeps) => {
|
|
7
|
+
export declare const useValueFlow: ({ editing, selectedField, selectedOperator, insertIndex, upsertCondition, conditionsRef, resetState, dateRange, buildingSide, setBuildingSide, buildingBase, setBuildingBase, setSelectedField, setSelectedOperator, setMenuState, setBuildingMultiValue, setInputText, }: MenuFlowInternalDeps) => {
|
|
8
8
|
handleValueSelect: (val: string | number | boolean) => void;
|
|
9
9
|
handleMultiCommit: (values: Array<string | number | boolean>) => void;
|
|
10
10
|
handleBuildingValueChange: (preview: string | undefined) => void;
|
package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/useValueFlow.js
CHANGED
|
@@ -2,28 +2,77 @@ import { useCallback } from "react";
|
|
|
2
2
|
import { SEGMENT_VARIANT } from "../../../FilterInputField/FilterInputChip/index.js";
|
|
3
3
|
import { isBetweenOperator, isMultiSelectOperator } from "../../../lib/index.js";
|
|
4
4
|
import { resolveDateRangeValue, resolveDateValue, resolveMultiValues, resolveSingleValue } from "../lib/index.js";
|
|
5
|
-
const useValueFlow = ({ editing, selectedField, selectedOperator, insertIndex, upsertCondition, conditionsRef, resetState, dateRange, setBuildingMultiValue, setInputText })=>{
|
|
6
|
-
const { editingChipId, editingSegment, resetSegmentTyping } = editing;
|
|
5
|
+
const useValueFlow = ({ editing, selectedField, selectedOperator, insertIndex, upsertCondition, conditionsRef, resetState, dateRange, buildingSide, setBuildingSide, buildingBase, setBuildingBase, setSelectedField, setSelectedOperator, setMenuState, setBuildingMultiValue, setInputText })=>{
|
|
6
|
+
const { editingChipId, editingSegment, editingSide, resetSegmentTyping } = editing;
|
|
7
|
+
const stashAndAdvance = useCallback((value)=>{
|
|
8
|
+
const pairedField = selectedField?.pairedField;
|
|
9
|
+
if (0 !== buildingSide || !pairedField || editingChipId || !selectedField || !selectedOperator) return false;
|
|
10
|
+
setBuildingBase({
|
|
11
|
+
field: selectedField,
|
|
12
|
+
operator: selectedOperator,
|
|
13
|
+
value
|
|
14
|
+
});
|
|
15
|
+
setSelectedField(pairedField);
|
|
16
|
+
setSelectedOperator(null);
|
|
17
|
+
setBuildingSide(1);
|
|
18
|
+
setInputText('');
|
|
19
|
+
setMenuState('operator');
|
|
20
|
+
return true;
|
|
21
|
+
}, [
|
|
22
|
+
selectedField,
|
|
23
|
+
selectedOperator,
|
|
24
|
+
buildingSide,
|
|
25
|
+
editingChipId,
|
|
26
|
+
setBuildingBase,
|
|
27
|
+
setSelectedField,
|
|
28
|
+
setSelectedOperator,
|
|
29
|
+
setBuildingSide,
|
|
30
|
+
setInputText,
|
|
31
|
+
setMenuState
|
|
32
|
+
]);
|
|
33
|
+
const commitPairedSecond = useCallback((value, error, dateOrigin)=>{
|
|
34
|
+
if (1 !== buildingSide || !buildingBase || !selectedField || !selectedOperator) return false;
|
|
35
|
+
upsertCondition(buildingBase.field, buildingBase.operator, buildingBase.value, null, insertIndex);
|
|
36
|
+
upsertCondition(selectedField, selectedOperator, value, null, void 0, error, dateOrigin, 1);
|
|
37
|
+
setBuildingBase(null);
|
|
38
|
+
setBuildingSide(0);
|
|
39
|
+
resetState(true);
|
|
40
|
+
return true;
|
|
41
|
+
}, [
|
|
42
|
+
buildingSide,
|
|
43
|
+
buildingBase,
|
|
44
|
+
selectedField,
|
|
45
|
+
selectedOperator,
|
|
46
|
+
insertIndex,
|
|
47
|
+
upsertCondition,
|
|
48
|
+
setBuildingBase,
|
|
49
|
+
setBuildingSide,
|
|
50
|
+
resetState
|
|
51
|
+
]);
|
|
7
52
|
const handleValueSelect = useCallback((val)=>{
|
|
8
53
|
if (!selectedField || !selectedOperator) return;
|
|
54
|
+
let committedValue = val;
|
|
9
55
|
if (isBetweenOperator(selectedOperator) && 'date' === selectedField.type) {
|
|
10
56
|
const result = dateRange.selectValue(String(val));
|
|
11
57
|
if (!result) return;
|
|
12
|
-
|
|
13
|
-
resetState(!editingChipId);
|
|
14
|
-
return;
|
|
58
|
+
committedValue = result;
|
|
15
59
|
}
|
|
60
|
+
if (stashAndAdvance(committedValue)) return;
|
|
61
|
+
if (commitPairedSecond(committedValue)) return;
|
|
16
62
|
const isEditing = !!editingChipId;
|
|
17
|
-
upsertCondition(selectedField, selectedOperator,
|
|
63
|
+
upsertCondition(selectedField, selectedOperator, committedValue, editingChipId, isEditing ? void 0 : insertIndex, void 0, void 0, editingSide);
|
|
18
64
|
resetState(!isEditing);
|
|
19
65
|
}, [
|
|
20
66
|
selectedField,
|
|
21
67
|
selectedOperator,
|
|
22
68
|
editingChipId,
|
|
69
|
+
editingSide,
|
|
23
70
|
dateRange,
|
|
24
71
|
insertIndex,
|
|
25
72
|
upsertCondition,
|
|
26
|
-
resetState
|
|
73
|
+
resetState,
|
|
74
|
+
stashAndAdvance,
|
|
75
|
+
commitPairedSecond
|
|
27
76
|
]);
|
|
28
77
|
const handleMultiCommit = useCallback((values)=>{
|
|
29
78
|
if (!selectedField || !selectedOperator || 0 === values.length) return;
|
|
@@ -71,29 +120,44 @@ const useValueFlow = ({ editing, selectedField, selectedOperator, insertIndex, u
|
|
|
71
120
|
if (!selectedField || !selectedOperator || !customText.trim()) return;
|
|
72
121
|
const trimmed = customText.trim();
|
|
73
122
|
const isEditing = !!editingChipId;
|
|
123
|
+
let resolvedValue = trimmed;
|
|
124
|
+
let valueError;
|
|
125
|
+
let dateOrigin;
|
|
74
126
|
if (isMultiSelectOperator(selectedOperator)) {
|
|
75
127
|
const { resolved, error } = resolveMultiValues(selectedField, trimmed);
|
|
76
|
-
|
|
128
|
+
resolvedValue = resolved;
|
|
129
|
+
valueError = error ? SEGMENT_VARIANT.value : void 0;
|
|
77
130
|
} else if ('date' === selectedField.type) if (isBetweenOperator(selectedOperator)) {
|
|
78
131
|
const rangeValue = resolveDateRangeValue(trimmed);
|
|
79
|
-
|
|
132
|
+
resolvedValue = rangeValue ?? trimmed;
|
|
133
|
+
valueError = rangeValue ? void 0 : SEGMENT_VARIANT.value;
|
|
134
|
+
dateOrigin = 'absolute';
|
|
80
135
|
} else {
|
|
81
|
-
const
|
|
82
|
-
|
|
136
|
+
const resolved = resolveDateValue(trimmed, editingChipId, conditionsRef.current);
|
|
137
|
+
resolvedValue = trimmed;
|
|
138
|
+
valueError = resolved.error ? SEGMENT_VARIANT.value : void 0;
|
|
139
|
+
dateOrigin = resolved.dateOrigin;
|
|
83
140
|
}
|
|
84
141
|
else {
|
|
85
142
|
const { resolved, error } = resolveSingleValue(selectedField, trimmed);
|
|
86
|
-
|
|
143
|
+
resolvedValue = resolved;
|
|
144
|
+
valueError = error ? SEGMENT_VARIANT.value : void 0;
|
|
87
145
|
}
|
|
146
|
+
if (stashAndAdvance(resolvedValue)) return;
|
|
147
|
+
if (commitPairedSecond(resolvedValue, valueError, dateOrigin)) return;
|
|
148
|
+
upsertCondition(selectedField, selectedOperator, resolvedValue, editingChipId, isEditing ? void 0 : insertIndex, valueError, dateOrigin, editingSide);
|
|
88
149
|
resetState(!isEditing);
|
|
89
150
|
}, [
|
|
90
151
|
selectedField,
|
|
91
152
|
selectedOperator,
|
|
92
153
|
editingChipId,
|
|
154
|
+
editingSide,
|
|
93
155
|
insertIndex,
|
|
94
156
|
conditionsRef,
|
|
95
157
|
upsertCondition,
|
|
96
|
-
resetState
|
|
158
|
+
resetState,
|
|
159
|
+
stashAndAdvance,
|
|
160
|
+
commitPairedSecond
|
|
97
161
|
]);
|
|
98
162
|
return {
|
|
99
163
|
handleValueSelect,
|
|
@@ -14,6 +14,8 @@ interface UseResetStateDeps {
|
|
|
14
14
|
setSelectedField: Dispatch<SetStateAction<FieldMetadata | null>>;
|
|
15
15
|
setSelectedOperator: Dispatch<SetStateAction<FilterOperator | null>>;
|
|
16
16
|
setBuildingMultiValue: Dispatch<SetStateAction<string | undefined>>;
|
|
17
|
+
setBuildingSide: (side: 0 | 1) => void;
|
|
18
|
+
setBuildingBase: (base: null) => void;
|
|
17
19
|
setInsertIndex: Dispatch<SetStateAction<number | null>>;
|
|
18
20
|
setInsertAfterConnector: Dispatch<SetStateAction<boolean>>;
|
|
19
21
|
setMenuState: Dispatch<SetStateAction<MenuState>>;
|
|
@@ -29,5 +31,5 @@ interface UseResetStateDeps {
|
|
|
29
31
|
* vs. genuine outside click). If unifying these, preserve the re-render
|
|
30
32
|
* refocus case or commit chains break. AS-882.
|
|
31
33
|
*/
|
|
32
|
-
export declare const useResetState: ({ editing, dateRange, containerRef, inputRef, resetMenuAnchor, setInputText, setSelectedField, setSelectedOperator, setBuildingMultiValue, setInsertIndex, setInsertAfterConnector, setMenuState, }: UseResetStateDeps) => (continueBuilding?: boolean) => void;
|
|
34
|
+
export declare const useResetState: ({ editing, dateRange, containerRef, inputRef, resetMenuAnchor, setInputText, setSelectedField, setSelectedOperator, setBuildingMultiValue, setBuildingSide, setBuildingBase, setInsertIndex, setInsertAfterConnector, setMenuState, }: UseResetStateDeps) => (continueBuilding?: boolean) => void;
|
|
33
35
|
export {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useCallback } from "react";
|
|
2
2
|
import { flushSync } from "react-dom";
|
|
3
3
|
import { isMenuRelated } from "../../lib/index.js";
|
|
4
|
-
const useResetState = ({ editing, dateRange, containerRef, inputRef, resetMenuAnchor, setInputText, setSelectedField, setSelectedOperator, setBuildingMultiValue, setInsertIndex, setInsertAfterConnector, setMenuState })=>{
|
|
4
|
+
const useResetState = ({ editing, dateRange, containerRef, inputRef, resetMenuAnchor, setInputText, setSelectedField, setSelectedOperator, setBuildingMultiValue, setBuildingSide, setBuildingBase, setInsertIndex, setInsertAfterConnector, setMenuState })=>{
|
|
5
5
|
const resetState = useCallback((continueBuilding = false)=>{
|
|
6
6
|
const doReset = ()=>{
|
|
7
7
|
setInputText('');
|
|
@@ -10,6 +10,8 @@ const useResetState = ({ editing, dateRange, containerRef, inputRef, resetMenuAn
|
|
|
10
10
|
editing.clearEditing();
|
|
11
11
|
dateRange.reset();
|
|
12
12
|
setBuildingMultiValue(void 0);
|
|
13
|
+
setBuildingSide(0);
|
|
14
|
+
setBuildingBase(null);
|
|
13
15
|
setInsertIndex(null);
|
|
14
16
|
setInsertAfterConnector(false);
|
|
15
17
|
resetMenuAnchor();
|
|
@@ -32,6 +34,8 @@ const useResetState = ({ editing, dateRange, containerRef, inputRef, resetMenuAn
|
|
|
32
34
|
setSelectedField,
|
|
33
35
|
setSelectedOperator,
|
|
34
36
|
setBuildingMultiValue,
|
|
37
|
+
setBuildingSide,
|
|
38
|
+
setBuildingBase,
|
|
35
39
|
setInsertIndex,
|
|
36
40
|
setInsertAfterConnector,
|
|
37
41
|
setMenuState
|
|
@@ -82,7 +82,21 @@ const buildMultiValueChip = (baseChip, condition, field, fields, chipError)=>{
|
|
|
82
82
|
}
|
|
83
83
|
};
|
|
84
84
|
};
|
|
85
|
-
const
|
|
85
|
+
const buildPairChip = (condition, field, fields)=>{
|
|
86
|
+
if (!condition.pair || !field?.pairedField) return;
|
|
87
|
+
const pf = field.pairedField;
|
|
88
|
+
const { operator, value, error } = condition.pair;
|
|
89
|
+
const displayValue = operator && isNoValueOperator(operator) ? NO_VALUE_PLACEHOLDER : resolveValueLabel(value, pf, fields) ?? String(value ?? '');
|
|
90
|
+
return {
|
|
91
|
+
attribute: pf.label || pf.name,
|
|
92
|
+
operator: operator ? getOperatorLabel(operator, pf.type || DEFAULT_FIELD_TYPE) : void 0,
|
|
93
|
+
value: displayValue,
|
|
94
|
+
...error && {
|
|
95
|
+
error
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
const makeConditionChipBase = (i, conditions, fields, error)=>{
|
|
86
100
|
const condition = conditions[i];
|
|
87
101
|
if (!condition) return makeEmptyChip(i, error);
|
|
88
102
|
const chipError = condition.error || (error ? true : void 0);
|
|
@@ -101,6 +115,17 @@ const makeConditionChip = (i, conditions, fields, error)=>{
|
|
|
101
115
|
error: chipError
|
|
102
116
|
};
|
|
103
117
|
};
|
|
118
|
+
const makeConditionChip = (i, conditions, fields, error)=>{
|
|
119
|
+
const base = makeConditionChipBase(i, conditions, fields, error);
|
|
120
|
+
const condition = conditions[i];
|
|
121
|
+
if (!condition) return base;
|
|
122
|
+
const field = fields.find((f)=>f.name === condition.field);
|
|
123
|
+
const pair = buildPairChip(condition, field, fields);
|
|
124
|
+
return pair ? {
|
|
125
|
+
...base,
|
|
126
|
+
pair
|
|
127
|
+
} : base;
|
|
128
|
+
};
|
|
104
129
|
const buildChips = (conditions, connectors, fields, error)=>{
|
|
105
130
|
if (0 === conditions.length) return [];
|
|
106
131
|
const hasMixed = connectors.includes('and') && connectors.includes('or');
|
|
@@ -1,15 +1,21 @@
|
|
|
1
|
-
import type { Condition, ExprNode } from '../../types';
|
|
1
|
+
import type { Condition, ExprNode, FieldMetadata } from '../../types';
|
|
2
2
|
/**
|
|
3
3
|
* Build an ExprNode from flat conditions + per-gap connectors.
|
|
4
4
|
* AND has higher precedence than OR: conditions connected by AND
|
|
5
5
|
* are grouped together, then those groups are joined by OR.
|
|
6
|
+
*
|
|
7
|
+
* Pass `fields` to expand paired conditions into two AND-joined conditions
|
|
8
|
+
* (the serialization contract). Omit it to keep paired conditions intact.
|
|
6
9
|
*/
|
|
7
|
-
export declare const buildExpression: (
|
|
10
|
+
export declare const buildExpression: (rawConditions: Condition[], rawConnectors: Array<"and" | "or">, fields?: FieldMetadata[]) => ExprNode | null;
|
|
8
11
|
/**
|
|
9
12
|
* Flatten a (possibly nested) ExprNode back to flat conditions + connectors.
|
|
10
13
|
* Walks the tree depth-first; between siblings inserts the parent's operator.
|
|
14
|
+
*
|
|
15
|
+
* Pass `fields` to re-pair AND-adjacent conditions into paired conditions (the
|
|
16
|
+
* inverse of `buildExpression`'s expansion). Omit it to keep them separate.
|
|
11
17
|
*/
|
|
12
|
-
export declare const expressionToConditions: (expr: ExprNode | null) => {
|
|
18
|
+
export declare const expressionToConditions: (expr: ExprNode | null, fields?: FieldMetadata[]) => {
|
|
13
19
|
conditions: Condition[];
|
|
14
20
|
connectors: Array<"and" | "or">;
|
|
15
21
|
};
|
|
@@ -1,4 +1,39 @@
|
|
|
1
|
-
const
|
|
1
|
+
const expandPairs = (conditions, connectors, fields)=>{
|
|
2
|
+
const outConditions = [];
|
|
3
|
+
const outConnectors = [];
|
|
4
|
+
conditions.forEach((condition, i)=>{
|
|
5
|
+
if (i > 0) outConnectors.push(connectors[i - 1] ?? 'and');
|
|
6
|
+
const field = fields.find((f)=>f.name === condition.field);
|
|
7
|
+
if (condition.pair && field?.pairedField) {
|
|
8
|
+
const { pair, ...base } = condition;
|
|
9
|
+
outConditions.push(base);
|
|
10
|
+
outConnectors.push('and');
|
|
11
|
+
outConditions.push({
|
|
12
|
+
type: 'condition',
|
|
13
|
+
field: field.pairedField.name,
|
|
14
|
+
...pair.operator && {
|
|
15
|
+
operator: pair.operator
|
|
16
|
+
},
|
|
17
|
+
value: pair.value,
|
|
18
|
+
...pair.error && {
|
|
19
|
+
error: pair.error
|
|
20
|
+
},
|
|
21
|
+
...pair.dateOrigin && {
|
|
22
|
+
dateOrigin: pair.dateOrigin
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
} else outConditions.push(condition);
|
|
26
|
+
});
|
|
27
|
+
return {
|
|
28
|
+
conditions: outConditions,
|
|
29
|
+
connectors: outConnectors
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
const buildExpression = (rawConditions, rawConnectors, fields)=>{
|
|
33
|
+
const { conditions, connectors } = fields ? expandPairs(rawConditions, rawConnectors, fields) : {
|
|
34
|
+
conditions: rawConditions,
|
|
35
|
+
connectors: rawConnectors
|
|
36
|
+
};
|
|
2
37
|
if (0 === conditions.length) return null;
|
|
3
38
|
if (1 === conditions.length) return conditions[0] ?? null;
|
|
4
39
|
const first = conditions[0];
|
|
@@ -27,17 +62,48 @@ const buildExpression = (conditions, connectors)=>{
|
|
|
27
62
|
children: andNodes
|
|
28
63
|
};
|
|
29
64
|
};
|
|
30
|
-
const
|
|
65
|
+
const repairPairs = (conditions, connectors, fields)=>{
|
|
66
|
+
const outConditions = [];
|
|
67
|
+
const outConnectors = [];
|
|
68
|
+
let i = 0;
|
|
69
|
+
while(i < conditions.length){
|
|
70
|
+
const condition = conditions[i];
|
|
71
|
+
if (i > 0) outConnectors.push(connectors[i - 1] ?? 'and');
|
|
72
|
+
const field = fields.find((f)=>f.name === condition.field);
|
|
73
|
+
const next = conditions[i + 1];
|
|
74
|
+
const canMerge = field?.pairedField && !condition.pair && 'and' === connectors[i] && next && next.field === field.pairedField.name;
|
|
75
|
+
if (canMerge && next) {
|
|
76
|
+
outConditions.push({
|
|
77
|
+
...condition,
|
|
78
|
+
pair: {
|
|
79
|
+
...next.operator && {
|
|
80
|
+
operator: next.operator
|
|
81
|
+
},
|
|
82
|
+
value: next.value,
|
|
83
|
+
...next.error && {
|
|
84
|
+
error: next.error
|
|
85
|
+
},
|
|
86
|
+
...next.dateOrigin && {
|
|
87
|
+
dateOrigin: next.dateOrigin
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
i += 2;
|
|
92
|
+
} else {
|
|
93
|
+
outConditions.push(condition);
|
|
94
|
+
i += 1;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
conditions: outConditions,
|
|
99
|
+
connectors: outConnectors
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
const expressionToConditions = (expr, fields)=>{
|
|
31
103
|
if (!expr) return {
|
|
32
104
|
conditions: [],
|
|
33
105
|
connectors: []
|
|
34
106
|
};
|
|
35
|
-
if ('condition' === expr.type) return {
|
|
36
|
-
conditions: [
|
|
37
|
-
expr
|
|
38
|
-
],
|
|
39
|
-
connectors: []
|
|
40
|
-
};
|
|
41
107
|
const conditions = [];
|
|
42
108
|
const connectors = [];
|
|
43
109
|
const walk = (node)=>{
|
|
@@ -48,7 +114,7 @@ const expressionToConditions = (expr)=>{
|
|
|
48
114
|
});
|
|
49
115
|
};
|
|
50
116
|
walk(expr);
|
|
51
|
-
return {
|
|
117
|
+
return fields ? repairPairs(conditions, connectors, fields) : {
|
|
52
118
|
conditions,
|
|
53
119
|
connectors
|
|
54
120
|
};
|
package/dist/components/FilterInput/hooks/useFilterInputExpression/useFilterInputExpression.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export declare const useFilterInputExpression: ({ fields, value, onChange, error
|
|
|
10
10
|
conditions: Condition[];
|
|
11
11
|
connectors: ("and" | "or")[];
|
|
12
12
|
chips: import("../..").FilterInputChipData[];
|
|
13
|
-
upsertCondition: (field: FieldMetadata, operator: FilterOperator | undefined, val: string | number | boolean | null | Array<string | number | boolean>, editingChipId?: string | null, atIndex?: number, error?: ChipErrorSegment, dateOrigin?: "relative" | "absolute") => void;
|
|
13
|
+
upsertCondition: (field: FieldMetadata, operator: FilterOperator | undefined, val: string | number | boolean | null | Array<string | number | boolean>, editingChipId?: string | null, atIndex?: number, error?: ChipErrorSegment, dateOrigin?: "relative" | "absolute", side?: 0 | 1) => void;
|
|
14
14
|
removeCondition: (chipId: string) => void;
|
|
15
15
|
removeConditionAtIndex: (idx: number) => void;
|
|
16
16
|
clearAll: () => void;
|