@wallarm-org/design-system 0.41.0 → 0.42.0
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/FilterInput.d.ts +7 -8
- package/dist/components/FilterInput/FilterInputContext/types.d.ts +12 -9
- package/dist/components/FilterInput/FilterInputContext/useFilterInputContextValue.d.ts +4 -2
- package/dist/components/FilterInput/FilterInputContext/useFilterInputContextValue.js +4 -0
- package/dist/components/FilterInput/FilterInputErrors/parseFilterInputErrors.js +1 -2
- package/dist/components/FilterInput/FilterInputField/ChipsWithGaps.d.ts +1 -1
- package/dist/components/FilterInput/FilterInputField/ChipsWithGaps.js +1 -1
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/FilterInputChip.d.ts +1 -1
- package/dist/components/FilterInput/FilterInputField/FilterInputChip/FilterInputChip.js +2 -2
- package/dist/components/FilterInput/FilterInputField/FilterInputField.js +11 -40
- package/dist/components/FilterInput/FilterInputField/hooks/useSegmentEditKeyboard.d.ts +37 -0
- package/dist/components/FilterInput/FilterInputField/hooks/useSegmentEditKeyboard.js +78 -0
- package/dist/components/FilterInput/FilterInputMenu/FilterInputDateValueMenu/constants.d.ts +2 -2
- package/dist/components/FilterInput/FilterInputMenu/FilterInputFieldMenu/FilterInputFieldMenu.d.ts +1 -1
- package/dist/components/FilterInput/FilterInputMenu/FilterInputMenu.d.ts +1 -1
- package/dist/components/FilterInput/FilterInputMenu/FilterInputValueMenu/FilterInputValueMenu.d.ts +5 -6
- package/dist/components/FilterInput/FilterInputMenu/FilterInputValueMenu/useValueMenuDisplayValues.d.ts +4 -14
- package/dist/components/FilterInput/FilterInputMenu/FilterInputValueMenu/useValueMenuState.d.ts +3 -5
- package/dist/components/FilterInput/FilterInputMenu/hooks/useKeyboardNav.d.ts +1 -1
- package/dist/components/FilterInput/FilterInputMenu/hooks/useKeyboardNav.js +5 -2
- package/dist/components/FilterInput/hooks/useAutoCleanupDetachedElement.d.ts +13 -0
- package/dist/components/FilterInput/hooks/useAutoCleanupDetachedElement.js +22 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/{deriveAutocompleteValues.d.ts → lib/deriveAutocompleteValues.d.ts} +3 -3
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/{deriveAutocompleteValues.js → lib/deriveAutocompleteValues.js} +1 -1
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/lib/getInitialSegmentText.d.ts +9 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/lib/getInitialSegmentText.js +8 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/lib/index.d.ts +3 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/lib/index.js +4 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/{valueCommitHelpers.d.ts → lib/valueResolution.d.ts} +1 -7
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/{valueCommitHelpers.js → lib/valueResolution.js} +3 -25
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useAutocompleteState.d.ts +36 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useAutocompleteState.js +53 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useBlurCommit.d.ts +5 -16
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useBlurCommit.js +12 -5
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useChipActions.d.ts +2 -2
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useChipActions.js +5 -5
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useChipCascade.d.ts +44 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useChipCascade.js +99 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useChipEditing.d.ts +7 -8
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useChipEditing.js +21 -16
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useFilterInputAutocomplete.d.ts +9 -10
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useFilterInputAutocomplete.js +50 -83
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useFocusManagement.d.ts +9 -11
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useFocusManagement.js +47 -20
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useInputHandlers.d.ts +6 -4
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useInputHandlers.js +32 -15
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/index.d.ts +1 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/index.js +2 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/{useMenuFlow.d.ts → useMenuFlow/types.d.ts} +15 -18
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/types.js +0 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/useFieldFlow.d.ts +11 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/useFieldFlow.js +95 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/useMenuFlow.d.ts +20 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/useMenuFlow.js +47 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/useOperatorFlow.d.ts +11 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/useOperatorFlow.js +87 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/useValueFlow.d.ts +14 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/useValueFlow.js +107 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuPositioning.d.ts +25 -8
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuPositioning.js +38 -22
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useResetState.d.ts +10 -19
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useResetState.js +3 -3
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useSegmentEditFlow.d.ts +36 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useSegmentEditFlow.js +69 -0
- package/dist/components/FilterInput/hooks/useFilterInputExpression/buildChips.js +1 -2
- package/dist/components/FilterInput/hooks/useFilterInputPositioning.d.ts +9 -4
- package/dist/components/FilterInput/hooks/useFilterInputPositioning.js +7 -6
- package/dist/components/FilterInput/hooks/useFilterInputSelection/lib/dom.d.ts +4 -4
- package/dist/components/FilterInput/hooks/useFilterInputSelection/useFilterInputSelection.d.ts +2 -2
- package/dist/components/FilterInput/hooks/useFilterInputSelection/useSelectionClipboard.d.ts +2 -5
- package/dist/components/FilterInput/hooks/useFloatingRecomputeOn.d.ts +14 -0
- package/dist/components/FilterInput/hooks/useFloatingRecomputeOn.js +18 -0
- package/dist/components/FilterInput/hooks/useResizeTracker.d.ts +10 -0
- package/dist/components/FilterInput/hooks/useResizeTracker.js +21 -0
- package/dist/components/FilterInput/lib/applyAcceptChar.d.ts +3 -4
- package/dist/components/FilterInput/lib/applyKnownFieldHelpers.d.ts +13 -25
- package/dist/components/FilterInput/lib/constants.d.ts +16 -34
- package/dist/components/FilterInput/lib/constants.js +3 -1
- package/dist/components/FilterInput/lib/dom.d.ts +15 -11
- package/dist/components/FilterInput/lib/dom.js +14 -9
- package/dist/components/FilterInput/lib/fields.d.ts +10 -19
- package/dist/components/FilterInput/lib/index.d.ts +4 -2
- package/dist/components/FilterInput/lib/index.js +5 -3
- package/dist/components/FilterInput/lib/menuFilterText.d.ts +8 -18
- package/dist/components/FilterInput/lib/operators.d.ts +11 -21
- package/dist/components/FilterInput/lib/segmentMenu.d.ts +4 -0
- package/dist/components/FilterInput/lib/segmentMenu.js +7 -0
- package/dist/components/FilterInput/lib/serializeExpression.d.ts +5 -12
- package/dist/components/FilterInput/lib/validation.d.ts +9 -0
- package/dist/components/FilterInput/lib/validation.js +24 -0
- package/dist/metadata/components.json +8 -8
- package/package.json +1 -1
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow.js +0 -260
package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useFilterInputAutocomplete.js
CHANGED
|
@@ -1,50 +1,36 @@
|
|
|
1
|
-
import { useCallback, useRef, useState } from "react";
|
|
2
1
|
import { SEGMENT_VARIANT } from "../../FilterInputField/FilterInputChip/index.js";
|
|
3
2
|
import { useDateRange } from "../../FilterInputMenu/FilterInputDateValueMenu/hooks.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { deriveAutocompleteValues } from "./lib/index.js";
|
|
4
|
+
import { useAutocompleteState } from "./useAutocompleteState.js";
|
|
6
5
|
import { useBlurCommit } from "./useBlurCommit.js";
|
|
7
6
|
import { useChipActions } from "./useChipActions.js";
|
|
7
|
+
import { useChipCascade } from "./useChipCascade.js";
|
|
8
8
|
import { useChipEditing } from "./useChipEditing.js";
|
|
9
9
|
import { useFocusManagement } from "./useFocusManagement.js";
|
|
10
10
|
import { useInputHandlers } from "./useInputHandlers.js";
|
|
11
|
-
import { useMenuFlow } from "./useMenuFlow.js";
|
|
11
|
+
import { useMenuFlow } from "./useMenuFlow/index.js";
|
|
12
12
|
import { useMenuPositioning } from "./useMenuPositioning.js";
|
|
13
13
|
import { useResetState } from "./useResetState.js";
|
|
14
|
+
import { useSegmentEditFlow } from "./useSegmentEditFlow.js";
|
|
14
15
|
const useFilterInputAutocomplete = ({ fields, conditions, chips, upsertCondition, removeCondition, removeConditionAtIndex, clearAll, setConnectorValue, containerRef, buildingChipRef, inputRef })=>{
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
const [buildingMultiValue, setBuildingMultiValue] = useState(void 0);
|
|
21
|
-
const [insertIndex, setInsertIndex] = useState(null);
|
|
22
|
-
const [insertAfterConnector, setInsertAfterConnector] = useState(false);
|
|
23
|
-
const effectiveInsertIndex = insertIndex ?? conditions.length;
|
|
24
|
-
const effectiveInsertIndexRef = useRef(effectiveInsertIndex);
|
|
25
|
-
effectiveInsertIndexRef.current = effectiveInsertIndex;
|
|
26
|
-
const conditionsRef = useRef(conditions);
|
|
27
|
-
conditionsRef.current = conditions;
|
|
28
|
-
const conditionsLengthRef = useRef(conditions.length);
|
|
29
|
-
conditionsLengthRef.current = conditions.length;
|
|
30
|
-
const blurCommitRef = useRef(null);
|
|
31
|
-
const segmentAttributeInputRef = useRef(null);
|
|
32
|
-
const segmentOperatorInputRef = useRef(null);
|
|
33
|
-
const segmentValueInputRef = useRef(null);
|
|
34
|
-
const commitBuildingOnBlurRef = useRef(()=>false);
|
|
35
|
-
const { menuPositioning, setMenuOffset, resetMenuOffset } = useMenuPositioning({
|
|
16
|
+
const state = useAutocompleteState({
|
|
17
|
+
conditions
|
|
18
|
+
});
|
|
19
|
+
const { inputText, setInputText, menuState, setMenuState, selectedField, setSelectedField, selectedOperator, setSelectedOperator, isFocused, setIsFocused, buildingMultiValue, setBuildingMultiValue, insertIndex, setInsertIndex, insertAfterConnector, setInsertAfterConnector, effectiveInsertIndex, effectiveInsertIndexRef, conditionsRef, conditionsLengthRef, blurCommitRef, segmentAttributeInputRef, segmentOperatorInputRef, segmentValueInputRef, commitBuildingOnBlurRef } = state;
|
|
20
|
+
const { menuPositioning, setMenuAnchor, resetMenuAnchor } = useMenuPositioning({
|
|
36
21
|
containerRef,
|
|
37
22
|
buildingChipRef,
|
|
38
23
|
inputRef,
|
|
39
24
|
isBuilding: null !== selectedField,
|
|
40
|
-
insertIndex: effectiveInsertIndex
|
|
25
|
+
insertIndex: effectiveInsertIndex,
|
|
26
|
+
chipsCount: chips.length,
|
|
27
|
+
isMenuOpen: 'closed' !== menuState
|
|
41
28
|
});
|
|
42
29
|
const editing = useChipEditing({
|
|
43
30
|
conditions,
|
|
44
31
|
chips,
|
|
45
32
|
fields,
|
|
46
|
-
|
|
47
|
-
setMenuOffset,
|
|
33
|
+
setMenuAnchor,
|
|
48
34
|
setSelectedField,
|
|
49
35
|
setSelectedOperator,
|
|
50
36
|
setMenuState,
|
|
@@ -56,7 +42,7 @@ const useFilterInputAutocomplete = ({ fields, conditions, chips, upsertCondition
|
|
|
56
42
|
dateRange,
|
|
57
43
|
containerRef,
|
|
58
44
|
inputRef,
|
|
59
|
-
|
|
45
|
+
resetMenuAnchor,
|
|
60
46
|
setInputText,
|
|
61
47
|
setSelectedField,
|
|
62
48
|
setSelectedOperator,
|
|
@@ -83,6 +69,22 @@ const useFilterInputAutocomplete = ({ fields, conditions, chips, upsertCondition
|
|
|
83
69
|
setMenuState,
|
|
84
70
|
setBuildingMultiValue
|
|
85
71
|
});
|
|
72
|
+
const { switchEditSegment, removeEditingChip, stepBackBuildingMenu } = useChipCascade({
|
|
73
|
+
editing,
|
|
74
|
+
chips,
|
|
75
|
+
fields,
|
|
76
|
+
conditionsRef,
|
|
77
|
+
selectedField,
|
|
78
|
+
selectedOperator,
|
|
79
|
+
buildingMultiValue,
|
|
80
|
+
upsertCondition,
|
|
81
|
+
removeCondition,
|
|
82
|
+
resetState,
|
|
83
|
+
setInputText,
|
|
84
|
+
setMenuState,
|
|
85
|
+
setSelectedOperator,
|
|
86
|
+
setBuildingMultiValue
|
|
87
|
+
});
|
|
86
88
|
const { handleInputChange, handleInputClick, handleKeyDown, menuRef } = useInputHandlers({
|
|
87
89
|
inputText,
|
|
88
90
|
menuState,
|
|
@@ -97,11 +99,12 @@ const useFilterInputAutocomplete = ({ fields, conditions, chips, upsertCondition
|
|
|
97
99
|
setInputText,
|
|
98
100
|
setMenuState,
|
|
99
101
|
setInsertIndex,
|
|
100
|
-
|
|
102
|
+
resetMenuAnchor,
|
|
101
103
|
removeConditionAtIndex,
|
|
102
104
|
handleFieldSelect,
|
|
103
105
|
handleOperatorSelect,
|
|
104
|
-
handleCustomValueCommit
|
|
106
|
+
handleCustomValueCommit,
|
|
107
|
+
stepBackBuildingMenu
|
|
105
108
|
});
|
|
106
109
|
const { commitBuildingOnBlur, hasIncompleteBuilding } = useBlurCommit({
|
|
107
110
|
selectedField,
|
|
@@ -132,7 +135,7 @@ const useFilterInputAutocomplete = ({ fields, conditions, chips, upsertCondition
|
|
|
132
135
|
hasIncompleteBuilding,
|
|
133
136
|
setIsFocused,
|
|
134
137
|
setMenuState,
|
|
135
|
-
|
|
138
|
+
resetMenuAnchor,
|
|
136
139
|
resetState
|
|
137
140
|
});
|
|
138
141
|
const { handleChipRemove, handleClear, handleGapClick, closeAutocompleteMenu } = useChipActions({
|
|
@@ -140,12 +143,24 @@ const useFilterInputAutocomplete = ({ fields, conditions, chips, upsertCondition
|
|
|
140
143
|
inputRef,
|
|
141
144
|
removeCondition,
|
|
142
145
|
clearAll,
|
|
143
|
-
|
|
146
|
+
resetMenuAnchor,
|
|
144
147
|
resetState,
|
|
145
148
|
setInsertIndex,
|
|
146
149
|
setInsertAfterConnector,
|
|
147
150
|
setMenuState
|
|
148
151
|
});
|
|
152
|
+
const { handleSegmentFilterChange, cancelSegmentEdit, handleMenuDiscard, handleBuildingChipClick } = useSegmentEditFlow({
|
|
153
|
+
editing,
|
|
154
|
+
selectedField,
|
|
155
|
+
selectedOperator,
|
|
156
|
+
buildingMultiValue,
|
|
157
|
+
resetState,
|
|
158
|
+
setSelectedField,
|
|
159
|
+
setSelectedOperator,
|
|
160
|
+
setInputText,
|
|
161
|
+
setMenuState,
|
|
162
|
+
setMenuAnchor
|
|
163
|
+
});
|
|
149
164
|
const { isBuilding, buildingChipData, editingMultiValues, editingSingleValue, editingDateRange } = deriveAutocompleteValues({
|
|
150
165
|
editingChipId: editing.editingChipId,
|
|
151
166
|
selectedField,
|
|
@@ -155,56 +170,6 @@ const useFilterInputAutocomplete = ({ fields, conditions, chips, upsertCondition
|
|
|
155
170
|
dateRangeFromValue: dateRange.fromValue,
|
|
156
171
|
segmentFilterText: editing.editingSegment === SEGMENT_VARIANT.value ? editing.segmentMenuFilterText : void 0
|
|
157
172
|
});
|
|
158
|
-
const handleSegmentFilterChange = useCallback((text)=>{
|
|
159
|
-
const accept = selectedField?.acceptChar;
|
|
160
|
-
const next = editing.editingSegment === SEGMENT_VARIANT.value && accept ? applyAcceptChar(text, accept) : text;
|
|
161
|
-
editing.setSegmentFilterText(next);
|
|
162
|
-
}, [
|
|
163
|
-
selectedField,
|
|
164
|
-
editing
|
|
165
|
-
]);
|
|
166
|
-
const cancelSegmentEdit = useCallback(()=>{
|
|
167
|
-
const isBuildingEdit = !editing.editingChipId && null !== editing.editingSegment;
|
|
168
|
-
if (isBuildingEdit) {
|
|
169
|
-
editing.clearEditing();
|
|
170
|
-
setMenuState('closed');
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
setSelectedField(null);
|
|
174
|
-
setSelectedOperator(null);
|
|
175
|
-
editing.clearEditing();
|
|
176
|
-
setMenuState('closed');
|
|
177
|
-
}, [
|
|
178
|
-
editing
|
|
179
|
-
]);
|
|
180
|
-
const handleMenuDiscard = useCallback(()=>{
|
|
181
|
-
const isBuildingEdit = !editing.editingChipId && null !== editing.editingSegment;
|
|
182
|
-
if (isBuildingEdit) {
|
|
183
|
-
editing.clearEditing();
|
|
184
|
-
setMenuState('closed');
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
|
-
resetState();
|
|
188
|
-
}, [
|
|
189
|
-
editing,
|
|
190
|
-
resetState
|
|
191
|
-
]);
|
|
192
|
-
const handleBuildingChipClick = useCallback((segment, anchorRect)=>{
|
|
193
|
-
if (!selectedField) return;
|
|
194
|
-
const containerRect = containerRef.current?.getBoundingClientRect();
|
|
195
|
-
setMenuOffset(containerRect ? anchorRect.left - containerRect.left : 0);
|
|
196
|
-
const initialText = segment === SEGMENT_VARIANT.attribute ? selectedField.label : segment === SEGMENT_VARIANT.operator ? selectedOperator ? getOperatorLabel(selectedOperator, selectedField.type) : '' : buildingMultiValue ?? '';
|
|
197
|
-
editing.startBuildingEdit(segment, initialText);
|
|
198
|
-
setInputText('');
|
|
199
|
-
setMenuState(segment === SEGMENT_VARIANT.attribute ? 'field' : segment === SEGMENT_VARIANT.operator ? 'operator' : 'value');
|
|
200
|
-
}, [
|
|
201
|
-
selectedField,
|
|
202
|
-
selectedOperator,
|
|
203
|
-
buildingMultiValue,
|
|
204
|
-
containerRef,
|
|
205
|
-
setMenuOffset,
|
|
206
|
-
editing
|
|
207
|
-
]);
|
|
208
173
|
return {
|
|
209
174
|
inputText,
|
|
210
175
|
menuState,
|
|
@@ -229,6 +194,8 @@ const useFilterInputAutocomplete = ({ fields, conditions, chips, upsertCondition
|
|
|
229
194
|
resetAutocompleteState: resetState,
|
|
230
195
|
handleChipClick: editing.handleChipClick,
|
|
231
196
|
handleBuildingChipClick,
|
|
197
|
+
switchEditSegment,
|
|
198
|
+
removeEditingChip,
|
|
232
199
|
handleConnectorChange: setConnectorValue,
|
|
233
200
|
handleChipRemove,
|
|
234
201
|
handleClear,
|
package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useFocusManagement.d.ts
CHANGED
|
@@ -6,32 +6,30 @@ interface UseFocusManagementDeps {
|
|
|
6
6
|
isFocused: boolean;
|
|
7
7
|
conditionsLength: number;
|
|
8
8
|
inputText: string;
|
|
9
|
-
/** In-progress building-chip state —
|
|
10
|
-
* missing segment instead of (incorrectly) reopening the field menu. */
|
|
9
|
+
/** In-progress building-chip state — refocus resumes at next missing segment. */
|
|
11
10
|
selectedField: FieldMetadata | null;
|
|
12
11
|
selectedOperator: FilterOperator | null;
|
|
13
12
|
containerRef: RefObject<HTMLElement | null>;
|
|
14
13
|
inputRef: RefObject<HTMLInputElement | null>;
|
|
15
14
|
editingSegment: ChipSegment | null;
|
|
16
|
-
/** Direct ref to the attribute segment
|
|
15
|
+
/** Direct ref to the attribute segment input when editing. */
|
|
17
16
|
segmentAttributeInputRef: RefObject<HTMLInputElement | null>;
|
|
18
|
-
/** Direct ref to the operator segment
|
|
17
|
+
/** Direct ref to the operator segment input when editing. */
|
|
19
18
|
segmentOperatorInputRef: RefObject<HTMLInputElement | null>;
|
|
20
|
-
/** Direct ref to the value segment
|
|
19
|
+
/** Direct ref to the value segment input when editing. */
|
|
21
20
|
segmentValueInputRef: RefObject<HTMLInputElement | null>;
|
|
22
|
-
/**
|
|
21
|
+
/** Set by FilterInputValueMenu — commits multi-select checked values on blur. */
|
|
23
22
|
blurCommitRef: RefObject<(() => boolean) | null>;
|
|
24
|
-
/**
|
|
23
|
+
/** Commits a building chip's freeform value on blur. Returns true if committed. */
|
|
25
24
|
commitBuildingOnBlur: () => boolean;
|
|
26
|
-
/**
|
|
27
|
-
* must preserve (skip resetState). */
|
|
25
|
+
/** In-progress building chip exists — blur/close must preserve it (skip resetState). */
|
|
28
26
|
hasIncompleteBuilding: () => boolean;
|
|
29
27
|
setIsFocused: (focused: boolean) => void;
|
|
30
28
|
setMenuState: (state: MenuState) => void;
|
|
31
|
-
|
|
29
|
+
resetMenuAnchor: () => void;
|
|
32
30
|
resetState: (continueBuilding?: boolean) => void;
|
|
33
31
|
}
|
|
34
|
-
export declare const useFocusManagement: ({ menuState, isFocused, conditionsLength, inputText, selectedField, selectedOperator, containerRef, inputRef, editingSegment, segmentAttributeInputRef, segmentOperatorInputRef, segmentValueInputRef, blurCommitRef, commitBuildingOnBlur, hasIncompleteBuilding, setIsFocused, setMenuState,
|
|
32
|
+
export declare const useFocusManagement: ({ menuState, isFocused, conditionsLength, inputText, selectedField, selectedOperator, containerRef, inputRef, editingSegment, segmentAttributeInputRef, segmentOperatorInputRef, segmentValueInputRef, blurCommitRef, commitBuildingOnBlur, hasIncompleteBuilding, setIsFocused, setMenuState, resetMenuAnchor, resetState, }: UseFocusManagementDeps) => {
|
|
35
33
|
handleFocus: (e: FocusEvent) => void;
|
|
36
34
|
handleBlur: (e: FocusEvent) => void;
|
|
37
35
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { useCallback, useEffect, useRef } from "react";
|
|
1
|
+
import { useCallback, useEffect, useMemo, useRef } from "react";
|
|
2
2
|
import { SEGMENT_VARIANT } from "../../FilterInputField/FilterInputChip/index.js";
|
|
3
3
|
import { isMenuRelated, nextBuildingMenu } from "../../lib/index.js";
|
|
4
|
-
const useFocusManagement = ({ menuState, isFocused, conditionsLength, inputText, selectedField, selectedOperator, containerRef, inputRef, editingSegment, segmentAttributeInputRef, segmentOperatorInputRef, segmentValueInputRef, blurCommitRef, commitBuildingOnBlur, hasIncompleteBuilding, setIsFocused, setMenuState,
|
|
4
|
+
const useFocusManagement = ({ menuState, isFocused, conditionsLength, inputText, selectedField, selectedOperator, containerRef, inputRef, editingSegment, segmentAttributeInputRef, segmentOperatorInputRef, segmentValueInputRef, blurCommitRef, commitBuildingOnBlur, hasIncompleteBuilding, setIsFocused, setMenuState, resetMenuAnchor, resetState })=>{
|
|
5
5
|
const handleFocus = useCallback((e)=>{
|
|
6
6
|
if (e.target?.closest?.('[data-slot="filter-input-connector-chip"]')) return;
|
|
7
7
|
setIsFocused(true);
|
|
@@ -46,10 +46,10 @@ const useFocusManagement = ({ menuState, isFocused, conditionsLength, inputText,
|
|
|
46
46
|
return;
|
|
47
47
|
}
|
|
48
48
|
if (selectedField) {
|
|
49
|
-
|
|
49
|
+
resetMenuAnchor();
|
|
50
50
|
setMenuState(nextBuildingMenu(selectedField, selectedOperator));
|
|
51
51
|
} else if (0 === conditionsLength && '' === inputText) {
|
|
52
|
-
|
|
52
|
+
resetMenuAnchor();
|
|
53
53
|
setMenuState('field');
|
|
54
54
|
}
|
|
55
55
|
prevFocusedRef.current = isFocused;
|
|
@@ -60,9 +60,18 @@ const useFocusManagement = ({ menuState, isFocused, conditionsLength, inputText,
|
|
|
60
60
|
selectedField,
|
|
61
61
|
selectedOperator,
|
|
62
62
|
editingSegment,
|
|
63
|
-
|
|
63
|
+
resetMenuAnchor,
|
|
64
64
|
setMenuState
|
|
65
65
|
]);
|
|
66
|
+
const segmentInputRefsMap = useMemo(()=>({
|
|
67
|
+
[SEGMENT_VARIANT.attribute]: segmentAttributeInputRef,
|
|
68
|
+
[SEGMENT_VARIANT.operator]: segmentOperatorInputRef,
|
|
69
|
+
[SEGMENT_VARIANT.value]: segmentValueInputRef
|
|
70
|
+
}), [
|
|
71
|
+
segmentAttributeInputRef,
|
|
72
|
+
segmentOperatorInputRef,
|
|
73
|
+
segmentValueInputRef
|
|
74
|
+
]);
|
|
66
75
|
useEffect(()=>{
|
|
67
76
|
if ('closed' === menuState) return;
|
|
68
77
|
if (!isFocused) return;
|
|
@@ -74,18 +83,11 @@ const useFocusManagement = ({ menuState, isFocused, conditionsLength, inputText,
|
|
|
74
83
|
if (!container) return;
|
|
75
84
|
const active = document.activeElement;
|
|
76
85
|
if (active && !container.contains(active) && !isMenuRelated(active)) return;
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
};
|
|
83
|
-
const segmentInput = segmentInputRefs[editingSegment]?.current ?? null;
|
|
84
|
-
if (segmentInput && document.activeElement !== segmentInput) {
|
|
85
|
-
segmentInput.focus();
|
|
86
|
-
segmentInput.select();
|
|
87
|
-
}
|
|
88
|
-
} else if (document.activeElement !== inputRef.current) inputRef.current?.focus();
|
|
86
|
+
const dest = editingSegment ? segmentInputRefsMap[editingSegment]?.current ?? null : inputRef.current;
|
|
87
|
+
if (!dest) return;
|
|
88
|
+
if (document.activeElement === dest) return;
|
|
89
|
+
dest.focus();
|
|
90
|
+
if (editingSegment) dest.select();
|
|
89
91
|
});
|
|
90
92
|
});
|
|
91
93
|
return ()=>{
|
|
@@ -98,9 +100,34 @@ const useFocusManagement = ({ menuState, isFocused, conditionsLength, inputText,
|
|
|
98
100
|
inputRef,
|
|
99
101
|
editingSegment,
|
|
100
102
|
containerRef,
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
103
|
+
segmentInputRefsMap
|
|
104
|
+
]);
|
|
105
|
+
useEffect(()=>{
|
|
106
|
+
if ('closed' === menuState) return;
|
|
107
|
+
if (!isFocused) return;
|
|
108
|
+
const handleFocusIn = (e)=>{
|
|
109
|
+
const targetEl = e.target;
|
|
110
|
+
if (!targetEl) return;
|
|
111
|
+
const menu = targetEl.closest('[data-filter-input-menu]');
|
|
112
|
+
if (!menu) return;
|
|
113
|
+
if (menu.querySelector('[data-scope="date-picker"]')) return;
|
|
114
|
+
const tag = targetEl.tagName;
|
|
115
|
+
if ('BUTTON' === tag || 'INPUT' === tag || 'TEXTAREA' === tag || 'SELECT' === tag) return;
|
|
116
|
+
const dest = editingSegment ? segmentInputRefsMap[editingSegment]?.current : inputRef.current;
|
|
117
|
+
if (!dest) return;
|
|
118
|
+
if (document.activeElement === dest) return;
|
|
119
|
+
dest.focus({
|
|
120
|
+
preventScroll: true
|
|
121
|
+
});
|
|
122
|
+
};
|
|
123
|
+
document.addEventListener('focusin', handleFocusIn);
|
|
124
|
+
return ()=>document.removeEventListener('focusin', handleFocusIn);
|
|
125
|
+
}, [
|
|
126
|
+
menuState,
|
|
127
|
+
isFocused,
|
|
128
|
+
editingSegment,
|
|
129
|
+
inputRef,
|
|
130
|
+
segmentInputRefsMap
|
|
104
131
|
]);
|
|
105
132
|
return {
|
|
106
133
|
handleFocus,
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { ChangeEvent, KeyboardEvent, MutableRefObject, RefObject } from 'react';
|
|
2
2
|
import type { Condition, FieldMetadata, FilterOperator, MenuState } from '../../types';
|
|
3
|
+
type BuildingStep = 'field' | 'operator' | 'value';
|
|
3
4
|
interface UseInputHandlersDeps {
|
|
4
5
|
inputText: string;
|
|
5
6
|
menuState: MenuState;
|
|
6
7
|
selectedField: FieldMetadata | null;
|
|
7
|
-
/**
|
|
8
|
-
* resumes at the next missing segment instead of doing nothing. */
|
|
8
|
+
/** Lets click resume at the next missing segment of a live building chip. */
|
|
9
9
|
selectedOperator: FilterOperator | null;
|
|
10
10
|
isFocused: boolean;
|
|
11
11
|
fields: FieldMetadata[];
|
|
@@ -16,13 +16,15 @@ interface UseInputHandlersDeps {
|
|
|
16
16
|
setInputText: (text: string) => void;
|
|
17
17
|
setMenuState: (state: MenuState) => void;
|
|
18
18
|
setInsertIndex: (fn: (prev: number | null) => number) => void;
|
|
19
|
-
|
|
19
|
+
resetMenuAnchor: () => void;
|
|
20
20
|
removeConditionAtIndex: (index: number) => void;
|
|
21
21
|
handleFieldSelect: (field: FieldMetadata) => void;
|
|
22
22
|
handleOperatorSelect: (operator: FilterOperator) => void;
|
|
23
23
|
handleCustomValueCommit: (text: string) => void;
|
|
24
|
+
/** Step building chip back one segment; tears it down at the field step. */
|
|
25
|
+
stepBackBuildingMenu: (current: BuildingStep) => void;
|
|
24
26
|
}
|
|
25
|
-
export declare const useInputHandlers: ({ inputText, menuState, selectedField, selectedOperator, isFocused, fields, inputRef, conditionsRef, conditionsLengthRef, effectiveInsertIndexRef, setInputText, setMenuState, setInsertIndex,
|
|
27
|
+
export declare const useInputHandlers: ({ inputText, menuState, selectedField, selectedOperator, isFocused, fields, inputRef, conditionsRef, conditionsLengthRef, effectiveInsertIndexRef, setInputText, setMenuState, setInsertIndex, resetMenuAnchor, removeConditionAtIndex, handleFieldSelect, handleOperatorSelect, handleCustomValueCommit, stepBackBuildingMenu, }: UseInputHandlersDeps) => {
|
|
26
28
|
handleInputChange: (e: ChangeEvent<HTMLInputElement>) => void;
|
|
27
29
|
handleInputClick: () => void;
|
|
28
30
|
handleKeyDown: (e: KeyboardEvent<HTMLInputElement>) => void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useCallback, useRef } from "react";
|
|
2
2
|
import { OPERATOR_SYMBOLS, applyAcceptChar, getOperatorFromLabel, hasFieldValues, nextBuildingMenu } from "../../lib/index.js";
|
|
3
|
-
const useInputHandlers = ({ inputText, menuState, selectedField, selectedOperator, isFocused, fields, inputRef, conditionsRef, conditionsLengthRef, effectiveInsertIndexRef, setInputText, setMenuState, setInsertIndex,
|
|
3
|
+
const useInputHandlers = ({ inputText, menuState, selectedField, selectedOperator, isFocused, fields, inputRef, conditionsRef, conditionsLengthRef, effectiveInsertIndexRef, setInputText, setMenuState, setInsertIndex, resetMenuAnchor, removeConditionAtIndex, handleFieldSelect, handleOperatorSelect, handleCustomValueCommit, stepBackBuildingMenu })=>{
|
|
4
4
|
const menuRef = useRef(null);
|
|
5
5
|
const handleInputChange = useCallback((e)=>{
|
|
6
6
|
let text = e.target.value;
|
|
@@ -22,18 +22,24 @@ const useInputHandlers = ({ inputText, menuState, selectedField, selectedOperato
|
|
|
22
22
|
const handleInputClick = useCallback(()=>{
|
|
23
23
|
inputRef.current?.focus();
|
|
24
24
|
if ('closed' !== menuState) return;
|
|
25
|
-
|
|
25
|
+
resetMenuAnchor();
|
|
26
26
|
setMenuState(nextBuildingMenu(selectedField, selectedOperator));
|
|
27
27
|
}, [
|
|
28
28
|
menuState,
|
|
29
29
|
selectedField,
|
|
30
30
|
selectedOperator,
|
|
31
|
-
|
|
31
|
+
resetMenuAnchor,
|
|
32
32
|
inputRef,
|
|
33
33
|
setMenuState
|
|
34
34
|
]);
|
|
35
35
|
const handleKeyDown = useCallback((e)=>{
|
|
36
|
-
if ('ArrowDown' === e.key
|
|
36
|
+
if ('ArrowDown' === e.key) {
|
|
37
|
+
if ('closed' === menuState) {
|
|
38
|
+
e.preventDefault();
|
|
39
|
+
resetMenuAnchor();
|
|
40
|
+
setMenuState(nextBuildingMenu(selectedField, selectedOperator));
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
37
43
|
e.preventDefault();
|
|
38
44
|
menuRef.current?.focus();
|
|
39
45
|
return;
|
|
@@ -72,23 +78,32 @@ const useInputHandlers = ({ inputText, menuState, selectedField, selectedOperato
|
|
|
72
78
|
handleCustomValueCommit(inputText);
|
|
73
79
|
return;
|
|
74
80
|
}
|
|
75
|
-
if ('Backspace' === e.key && !e.repeat && '' === inputText
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
81
|
+
if ('Backspace' === e.key && !e.repeat && '' === inputText) {
|
|
82
|
+
if (selectedField && ('value' === menuState || 'operator' === menuState || 'field' === menuState)) {
|
|
83
|
+
e.preventDefault();
|
|
84
|
+
stepBackBuildingMenu(menuState);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (conditionsLengthRef.current > 0) {
|
|
88
|
+
e.preventDefault();
|
|
89
|
+
const removeIdx = effectiveInsertIndexRef.current - 1;
|
|
90
|
+
if (removeIdx >= 0 && !conditionsRef.current[removeIdx]?.disabled) {
|
|
91
|
+
removeConditionAtIndex(removeIdx);
|
|
92
|
+
setInsertIndex((prev)=>{
|
|
93
|
+
const eff = prev ?? conditionsLengthRef.current;
|
|
94
|
+
return eff > 0 ? eff - 1 : 0;
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
resetMenuAnchor();
|
|
98
|
+
setMenuState('field');
|
|
84
99
|
}
|
|
85
|
-
setMenuState('closed');
|
|
86
100
|
}
|
|
87
101
|
}, [
|
|
88
102
|
inputText,
|
|
89
103
|
removeConditionAtIndex,
|
|
90
104
|
menuState,
|
|
91
105
|
selectedField,
|
|
106
|
+
selectedOperator,
|
|
92
107
|
fields,
|
|
93
108
|
handleFieldSelect,
|
|
94
109
|
handleOperatorSelect,
|
|
@@ -98,7 +113,9 @@ const useInputHandlers = ({ inputText, menuState, selectedField, selectedOperato
|
|
|
98
113
|
setInsertIndex,
|
|
99
114
|
conditionsRef,
|
|
100
115
|
conditionsLengthRef,
|
|
101
|
-
effectiveInsertIndexRef
|
|
116
|
+
effectiveInsertIndexRef,
|
|
117
|
+
stepBackBuildingMenu,
|
|
118
|
+
resetMenuAnchor
|
|
102
119
|
]);
|
|
103
120
|
return {
|
|
104
121
|
handleInputChange,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useMenuFlow } from './useMenuFlow';
|
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import type { RefObject } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
import type { Condition, FieldMetadata, FilterOperator, MenuState, UpsertCondition } from '
|
|
4
|
-
interface MenuFlowDeps {
|
|
2
|
+
import type { ChipSegment } from '../../../FilterInputField/FilterInputChip';
|
|
3
|
+
import type { Condition, FieldMetadata, FilterOperator, MenuState, UpsertCondition } from '../../../types';
|
|
4
|
+
export interface MenuFlowDeps {
|
|
5
5
|
editing: {
|
|
6
6
|
editingChipId: string | null;
|
|
7
|
-
editingSegment:
|
|
7
|
+
editingSegment: ChipSegment | null;
|
|
8
|
+
/** Pre-derived chipId === null && segment !== null marker — single source
|
|
9
|
+
* of truth, avoids re-deriving in every consumer. */
|
|
10
|
+
isBuildingEdit: boolean;
|
|
8
11
|
setEditingSegment: (segment: ChipSegment | null) => void;
|
|
9
12
|
setSegmentFilterText: (text: string) => void;
|
|
10
13
|
resetSegmentTyping: () => void;
|
|
14
|
+
/** Switch inline-edit to another segment of the same committed chip,
|
|
15
|
+
* loading its text and resetting the user-typed flag in one step. */
|
|
16
|
+
switchEditSegment: (segment: ChipSegment, currentText: string) => void;
|
|
11
17
|
/** Exit inline-edit and the building-edit marker. Called when switching
|
|
12
18
|
* filter/operator in the building chip lands on the next menu. */
|
|
13
19
|
clearEditing: () => void;
|
|
@@ -31,17 +37,8 @@ interface MenuFlowDeps {
|
|
|
31
37
|
setMenuState: (state: MenuState) => void;
|
|
32
38
|
setBuildingMultiValue: (val: string | undefined) => void;
|
|
33
39
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
handleMultiCommit: (values: Array<string | number | boolean>) => void;
|
|
40
|
-
handleBuildingValueChange: (preview: string | undefined) => void;
|
|
41
|
-
handleMultiSelectToggle: () => void;
|
|
42
|
-
handleRangeSelect: (from: string, to: string) => void;
|
|
43
|
-
handleCustomValueCommit: (customText: string) => void;
|
|
44
|
-
handleCustomOperatorCommit: (customText: string) => void;
|
|
45
|
-
handleCustomAttributeCommit: (customText: string) => void;
|
|
46
|
-
};
|
|
47
|
-
export {};
|
|
40
|
+
/** Shared shape: each sub-hook receives the same deps plus a ref to `conditions`
|
|
41
|
+
* that stays fresh without forcing callbacks to recreate on every keystroke. */
|
|
42
|
+
export interface MenuFlowInternalDeps extends MenuFlowDeps {
|
|
43
|
+
conditionsRef: RefObject<Condition[]>;
|
|
44
|
+
}
|
|
File without changes
|
package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/useFieldFlow.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { FieldMetadata } from '../../../types';
|
|
2
|
+
import type { MenuFlowInternalDeps } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Attribute-segment handlers. `handleFieldSelect` is the menu-click path,
|
|
5
|
+
* `handleCustomAttributeCommit` is the keyboard-typed path — they share
|
|
6
|
+
* post-cascade and shape-preservation logic via mutual delegation.
|
|
7
|
+
*/
|
|
8
|
+
export declare const useFieldFlow: ({ editing, selectedOperator, fields, upsertCondition, conditionsRef, resetState, setSelectedField, setSelectedOperator, setInputText, setMenuState, }: MenuFlowInternalDeps) => {
|
|
9
|
+
handleFieldSelect: (field: FieldMetadata) => void;
|
|
10
|
+
handleCustomAttributeCommit: (customText: string) => void;
|
|
11
|
+
};
|
package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/useFieldFlow.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
import { SEGMENT_VARIANT } from "../../../FilterInputField/FilterInputChip/index.js";
|
|
3
|
+
import { chipIdToConditionIndex, isOperatorAllowedForField, validateValueForField } from "../../../lib/index.js";
|
|
4
|
+
const useFieldFlow = ({ editing, selectedOperator, fields, upsertCondition, conditionsRef, resetState, setSelectedField, setSelectedOperator, setInputText, setMenuState })=>{
|
|
5
|
+
const { editingChipId, editingSegment, isBuildingEdit, switchEditSegment, clearEditing } = editing;
|
|
6
|
+
const handleFieldSelect = useCallback((field)=>{
|
|
7
|
+
if (editingChipId && editingSegment === SEGMENT_VARIANT.attribute) {
|
|
8
|
+
const idx = chipIdToConditionIndex(editingChipId);
|
|
9
|
+
const condition = null !== idx ? conditionsRef.current[idx] : null;
|
|
10
|
+
if (condition) {
|
|
11
|
+
if (!condition.operator) {
|
|
12
|
+
upsertCondition(field, void 0, condition.value, editingChipId, void 0, void 0, condition.dateOrigin);
|
|
13
|
+
setSelectedField(field);
|
|
14
|
+
setSelectedOperator(null);
|
|
15
|
+
switchEditSegment(SEGMENT_VARIANT.operator, '');
|
|
16
|
+
setMenuState('operator');
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const hasValueError = validateValueForField(field, condition.value);
|
|
20
|
+
upsertCondition(field, condition.operator, condition.value, editingChipId, void 0, hasValueError ? SEGMENT_VARIANT.value : void 0, condition.dateOrigin);
|
|
21
|
+
}
|
|
22
|
+
resetState();
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (isBuildingEdit && editingSegment === SEGMENT_VARIANT.attribute) {
|
|
26
|
+
setSelectedField(field);
|
|
27
|
+
const keepOperator = selectedOperator ? isOperatorAllowedForField(field, selectedOperator) : false;
|
|
28
|
+
if (!keepOperator) setSelectedOperator(null);
|
|
29
|
+
clearEditing();
|
|
30
|
+
setMenuState(keepOperator ? 'value' : 'operator');
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
setSelectedField(field);
|
|
34
|
+
setInputText('');
|
|
35
|
+
setMenuState('operator');
|
|
36
|
+
}, [
|
|
37
|
+
editingChipId,
|
|
38
|
+
editingSegment,
|
|
39
|
+
isBuildingEdit,
|
|
40
|
+
switchEditSegment,
|
|
41
|
+
clearEditing,
|
|
42
|
+
selectedOperator,
|
|
43
|
+
conditionsRef,
|
|
44
|
+
upsertCondition,
|
|
45
|
+
resetState,
|
|
46
|
+
setSelectedField,
|
|
47
|
+
setSelectedOperator,
|
|
48
|
+
setInputText,
|
|
49
|
+
setMenuState
|
|
50
|
+
]);
|
|
51
|
+
const handleCustomAttributeCommit = useCallback((customText)=>{
|
|
52
|
+
if (!customText.trim()) return;
|
|
53
|
+
const trimmed = customText.trim();
|
|
54
|
+
const matchedField = fields.find((f)=>f.label.toLowerCase() === trimmed.toLowerCase() || f.name.toLowerCase() === trimmed.toLowerCase());
|
|
55
|
+
if (!editingChipId) {
|
|
56
|
+
if (matchedField) handleFieldSelect(matchedField);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const idx = chipIdToConditionIndex(editingChipId);
|
|
60
|
+
const condition = null !== idx ? conditionsRef.current[idx] : null;
|
|
61
|
+
if (!condition) return;
|
|
62
|
+
if (matchedField) return void handleFieldSelect(matchedField);
|
|
63
|
+
const syntheticField = {
|
|
64
|
+
name: trimmed,
|
|
65
|
+
label: trimmed,
|
|
66
|
+
type: 'string'
|
|
67
|
+
};
|
|
68
|
+
if (!condition.operator) {
|
|
69
|
+
upsertCondition(syntheticField, void 0, condition.value, editingChipId, void 0, SEGMENT_VARIANT.attribute, condition.dateOrigin);
|
|
70
|
+
setSelectedField(syntheticField);
|
|
71
|
+
setSelectedOperator(null);
|
|
72
|
+
switchEditSegment(SEGMENT_VARIANT.operator, '');
|
|
73
|
+
setMenuState('operator');
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
upsertCondition(syntheticField, condition.operator, condition.value, editingChipId, void 0, SEGMENT_VARIANT.attribute, condition.dateOrigin);
|
|
77
|
+
resetState();
|
|
78
|
+
}, [
|
|
79
|
+
editingChipId,
|
|
80
|
+
switchEditSegment,
|
|
81
|
+
fields,
|
|
82
|
+
conditionsRef,
|
|
83
|
+
upsertCondition,
|
|
84
|
+
resetState,
|
|
85
|
+
handleFieldSelect,
|
|
86
|
+
setSelectedField,
|
|
87
|
+
setSelectedOperator,
|
|
88
|
+
setMenuState
|
|
89
|
+
]);
|
|
90
|
+
return {
|
|
91
|
+
handleFieldSelect,
|
|
92
|
+
handleCustomAttributeCommit
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
export { useFieldFlow };
|