@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/useMenuFlow/useMenuFlow.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { MenuFlowDeps } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Aggregates the segment-specific flow hooks (`useFieldFlow`,
|
|
4
|
+
* `useOperatorFlow`, `useValueFlow`) and adds the cross-cutting
|
|
5
|
+
* `handleMenuClose` handler. Sub-hooks share a single fresh-conditions ref so
|
|
6
|
+
* none of them re-create callbacks on every keystroke that mutates conditions.
|
|
7
|
+
*/
|
|
8
|
+
export declare const useMenuFlow: (deps: MenuFlowDeps) => {
|
|
9
|
+
handleMenuClose: () => void;
|
|
10
|
+
handleFieldSelect: (field: import("../../..").FieldMetadata) => void;
|
|
11
|
+
handleOperatorSelect: (operator: import("../../..").FilterOperator) => void;
|
|
12
|
+
handleValueSelect: (val: string | number | boolean) => void;
|
|
13
|
+
handleMultiCommit: (values: Array<string | number | boolean>) => void;
|
|
14
|
+
handleBuildingValueChange: (preview: string | undefined) => void;
|
|
15
|
+
handleMultiSelectToggle: () => void;
|
|
16
|
+
handleRangeSelect: (from: string, to: string) => void;
|
|
17
|
+
handleCustomValueCommit: (customText: string) => void;
|
|
18
|
+
handleCustomOperatorCommit: (customText: string) => void;
|
|
19
|
+
handleCustomAttributeCommit: (customText: string) => void;
|
|
20
|
+
};
|
package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/useMenuFlow.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { useCallback, useLayoutEffect, useRef } from "react";
|
|
2
|
+
import { useFieldFlow } from "./useFieldFlow.js";
|
|
3
|
+
import { useOperatorFlow } from "./useOperatorFlow.js";
|
|
4
|
+
import { useValueFlow } from "./useValueFlow.js";
|
|
5
|
+
const useMenuFlow = (deps)=>{
|
|
6
|
+
const { editing, selectedField, inputRef, conditions, resetState, commitBuildingOnBlur, setMenuState } = deps;
|
|
7
|
+
const conditionsRef = useRef(conditions);
|
|
8
|
+
useLayoutEffect(()=>{
|
|
9
|
+
conditionsRef.current = conditions;
|
|
10
|
+
});
|
|
11
|
+
const internalDeps = {
|
|
12
|
+
...deps,
|
|
13
|
+
conditionsRef
|
|
14
|
+
};
|
|
15
|
+
const { handleFieldSelect, handleCustomAttributeCommit } = useFieldFlow(internalDeps);
|
|
16
|
+
const { handleOperatorSelect, handleCustomOperatorCommit } = useOperatorFlow(internalDeps);
|
|
17
|
+
const { handleValueSelect, handleMultiCommit, handleBuildingValueChange, handleMultiSelectToggle, handleRangeSelect, handleCustomValueCommit } = useValueFlow(internalDeps);
|
|
18
|
+
const handleMenuClose = useCallback(()=>{
|
|
19
|
+
if (document.activeElement === inputRef.current) return;
|
|
20
|
+
if (document.activeElement?.closest?.('[data-slot^="segment-"]')) return;
|
|
21
|
+
if (commitBuildingOnBlur()) return;
|
|
22
|
+
const hasIncompleteBuilding = null !== selectedField && !editing.editingChipId;
|
|
23
|
+
if (hasIncompleteBuilding) return void setMenuState('closed');
|
|
24
|
+
resetState();
|
|
25
|
+
}, [
|
|
26
|
+
commitBuildingOnBlur,
|
|
27
|
+
resetState,
|
|
28
|
+
inputRef,
|
|
29
|
+
selectedField,
|
|
30
|
+
editing.editingChipId,
|
|
31
|
+
setMenuState
|
|
32
|
+
]);
|
|
33
|
+
return {
|
|
34
|
+
handleMenuClose,
|
|
35
|
+
handleFieldSelect,
|
|
36
|
+
handleOperatorSelect,
|
|
37
|
+
handleValueSelect,
|
|
38
|
+
handleMultiCommit,
|
|
39
|
+
handleBuildingValueChange,
|
|
40
|
+
handleMultiSelectToggle,
|
|
41
|
+
handleRangeSelect,
|
|
42
|
+
handleCustomValueCommit,
|
|
43
|
+
handleCustomOperatorCommit,
|
|
44
|
+
handleCustomAttributeCommit
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
export { useMenuFlow };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { FilterOperator } from '../../../types';
|
|
2
|
+
import type { MenuFlowInternalDeps } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Operator-segment handlers. `handleOperatorSelect` is the menu-click path,
|
|
5
|
+
* `handleCustomOperatorCommit` is the keyboard-typed path — it resolves the
|
|
6
|
+
* typed text to a known operator (label / raw key / symbol) and delegates.
|
|
7
|
+
*/
|
|
8
|
+
export declare const useOperatorFlow: ({ editing, selectedField, selectedOperator, insertIndex, upsertCondition, conditionsRef, resetState, setSelectedOperator, setMenuState, setBuildingMultiValue, setInputText, }: MenuFlowInternalDeps) => {
|
|
9
|
+
handleOperatorSelect: (operator: FilterOperator) => void;
|
|
10
|
+
handleCustomOperatorCommit: (customText: string) => void;
|
|
11
|
+
};
|
package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/useOperatorFlow.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
import { SEGMENT_VARIANT } from "../../../FilterInputField/FilterInputChip/index.js";
|
|
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;
|
|
6
|
+
const handleOperatorSelect = useCallback((operator)=>{
|
|
7
|
+
if (!selectedField) return;
|
|
8
|
+
if (isBuildingEdit && editingSegment === SEGMENT_VARIANT.operator) {
|
|
9
|
+
const shapeCompatible = isValueShapeCompatible(selectedOperator, operator);
|
|
10
|
+
if (!shapeCompatible) setBuildingMultiValue(void 0);
|
|
11
|
+
setSelectedOperator(operator);
|
|
12
|
+
clearEditing();
|
|
13
|
+
if (isNoValueOperator(operator)) {
|
|
14
|
+
upsertCondition(selectedField, operator, null, null, insertIndex);
|
|
15
|
+
resetState(true);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
setMenuState('value');
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (isNoValueOperator(operator)) {
|
|
22
|
+
const isEditing = !!editingChipId;
|
|
23
|
+
upsertCondition(selectedField, operator, null, editingChipId, isEditing ? void 0 : insertIndex);
|
|
24
|
+
resetState(!isEditing);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (editingChipId && editingSegment === SEGMENT_VARIANT.operator) {
|
|
28
|
+
const idx = chipIdToConditionIndex(editingChipId);
|
|
29
|
+
const condition = null !== idx ? conditionsRef.current[idx] : null;
|
|
30
|
+
if (condition) {
|
|
31
|
+
const hasValue = null !== condition.value && '' !== condition.value;
|
|
32
|
+
if (hasValue) {
|
|
33
|
+
upsertCondition(selectedField, operator, condition.value, editingChipId, void 0, void 0, condition.dateOrigin);
|
|
34
|
+
resetState();
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
upsertCondition(selectedField, operator, null, editingChipId);
|
|
38
|
+
setEditingSegment(SEGMENT_VARIANT.value);
|
|
39
|
+
setSegmentFilterText('');
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
setSelectedOperator(operator);
|
|
43
|
+
setInputText('');
|
|
44
|
+
setMenuState('value');
|
|
45
|
+
}, [
|
|
46
|
+
editingChipId,
|
|
47
|
+
editingSegment,
|
|
48
|
+
isBuildingEdit,
|
|
49
|
+
setEditingSegment,
|
|
50
|
+
setSegmentFilterText,
|
|
51
|
+
clearEditing,
|
|
52
|
+
selectedField,
|
|
53
|
+
selectedOperator,
|
|
54
|
+
insertIndex,
|
|
55
|
+
conditionsRef,
|
|
56
|
+
upsertCondition,
|
|
57
|
+
resetState,
|
|
58
|
+
setSelectedOperator,
|
|
59
|
+
setMenuState,
|
|
60
|
+
setBuildingMultiValue,
|
|
61
|
+
setInputText
|
|
62
|
+
]);
|
|
63
|
+
const handleCustomOperatorCommit = useCallback((customText)=>{
|
|
64
|
+
if (!selectedField || !customText.trim()) return;
|
|
65
|
+
const trimmed = customText.trim();
|
|
66
|
+
const allowed = getFieldOperators(selectedField);
|
|
67
|
+
let matched = getOperatorFromLabel(trimmed, selectedField.type);
|
|
68
|
+
if (!matched) {
|
|
69
|
+
const symbolMatch = allowed.find((op)=>OPERATOR_SYMBOLS[op].toLowerCase() === trimmed.toLowerCase());
|
|
70
|
+
if (symbolMatch) matched = symbolMatch;
|
|
71
|
+
}
|
|
72
|
+
if (!matched) {
|
|
73
|
+
const rawMatch = allowed.find((op)=>op.toLowerCase() === trimmed.toLowerCase());
|
|
74
|
+
if (rawMatch) matched = rawMatch;
|
|
75
|
+
}
|
|
76
|
+
if (!matched || !isOperatorAllowedForField(selectedField, matched)) return;
|
|
77
|
+
handleOperatorSelect(matched);
|
|
78
|
+
}, [
|
|
79
|
+
selectedField,
|
|
80
|
+
handleOperatorSelect
|
|
81
|
+
]);
|
|
82
|
+
return {
|
|
83
|
+
handleOperatorSelect,
|
|
84
|
+
handleCustomOperatorCommit
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
export { useOperatorFlow };
|
package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/useValueFlow.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { MenuFlowInternalDeps } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Value-segment handlers: single/multi/range select from the menu and the
|
|
4
|
+
* keyboard-typed custom-value commit (which routes through the field-type
|
|
5
|
+
* specific resolver). Also handles multi-select preview/toggle plumbing.
|
|
6
|
+
*/
|
|
7
|
+
export declare const useValueFlow: ({ editing, selectedField, selectedOperator, insertIndex, upsertCondition, conditionsRef, resetState, dateRange, setBuildingMultiValue, setInputText, }: MenuFlowInternalDeps) => {
|
|
8
|
+
handleValueSelect: (val: string | number | boolean) => void;
|
|
9
|
+
handleMultiCommit: (values: Array<string | number | boolean>) => void;
|
|
10
|
+
handleBuildingValueChange: (preview: string | undefined) => void;
|
|
11
|
+
handleMultiSelectToggle: () => void;
|
|
12
|
+
handleRangeSelect: (from: string, to: string) => void;
|
|
13
|
+
handleCustomValueCommit: (customText: string) => void;
|
|
14
|
+
};
|
package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow/useValueFlow.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
import { SEGMENT_VARIANT } from "../../../FilterInputField/FilterInputChip/index.js";
|
|
3
|
+
import { isBetweenOperator, isMultiSelectOperator } from "../../../lib/index.js";
|
|
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;
|
|
7
|
+
const handleValueSelect = useCallback((val)=>{
|
|
8
|
+
if (!selectedField || !selectedOperator) return;
|
|
9
|
+
if (isBetweenOperator(selectedOperator) && 'date' === selectedField.type) {
|
|
10
|
+
const result = dateRange.selectValue(String(val));
|
|
11
|
+
if (!result) return;
|
|
12
|
+
upsertCondition(selectedField, selectedOperator, result, editingChipId);
|
|
13
|
+
resetState(!editingChipId);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const isEditing = !!editingChipId;
|
|
17
|
+
upsertCondition(selectedField, selectedOperator, val, editingChipId, isEditing ? void 0 : insertIndex);
|
|
18
|
+
resetState(!isEditing);
|
|
19
|
+
}, [
|
|
20
|
+
selectedField,
|
|
21
|
+
selectedOperator,
|
|
22
|
+
editingChipId,
|
|
23
|
+
dateRange,
|
|
24
|
+
insertIndex,
|
|
25
|
+
upsertCondition,
|
|
26
|
+
resetState
|
|
27
|
+
]);
|
|
28
|
+
const handleMultiCommit = useCallback((values)=>{
|
|
29
|
+
if (!selectedField || !selectedOperator || 0 === values.length) return;
|
|
30
|
+
const isEditing = !!editingChipId;
|
|
31
|
+
upsertCondition(selectedField, selectedOperator, values, editingChipId, isEditing ? void 0 : insertIndex);
|
|
32
|
+
resetState(!isEditing);
|
|
33
|
+
}, [
|
|
34
|
+
selectedField,
|
|
35
|
+
selectedOperator,
|
|
36
|
+
editingChipId,
|
|
37
|
+
insertIndex,
|
|
38
|
+
upsertCondition,
|
|
39
|
+
resetState
|
|
40
|
+
]);
|
|
41
|
+
const handleBuildingValueChange = useCallback((preview)=>{
|
|
42
|
+
setBuildingMultiValue(preview);
|
|
43
|
+
}, [
|
|
44
|
+
setBuildingMultiValue
|
|
45
|
+
]);
|
|
46
|
+
const handleMultiSelectToggle = useCallback(()=>{
|
|
47
|
+
if (editingSegment === SEGMENT_VARIANT.value) resetSegmentTyping();
|
|
48
|
+
else setInputText('');
|
|
49
|
+
}, [
|
|
50
|
+
editingSegment,
|
|
51
|
+
resetSegmentTyping,
|
|
52
|
+
setInputText
|
|
53
|
+
]);
|
|
54
|
+
const handleRangeSelect = useCallback((from, to)=>{
|
|
55
|
+
if (!selectedField || !selectedOperator) return;
|
|
56
|
+
const isEditing = !!editingChipId;
|
|
57
|
+
upsertCondition(selectedField, selectedOperator, [
|
|
58
|
+
from,
|
|
59
|
+
to
|
|
60
|
+
], editingChipId, isEditing ? void 0 : insertIndex);
|
|
61
|
+
resetState(!isEditing);
|
|
62
|
+
}, [
|
|
63
|
+
selectedField,
|
|
64
|
+
selectedOperator,
|
|
65
|
+
editingChipId,
|
|
66
|
+
insertIndex,
|
|
67
|
+
upsertCondition,
|
|
68
|
+
resetState
|
|
69
|
+
]);
|
|
70
|
+
const handleCustomValueCommit = useCallback((customText)=>{
|
|
71
|
+
if (!selectedField || !selectedOperator || !customText.trim()) return;
|
|
72
|
+
const trimmed = customText.trim();
|
|
73
|
+
const isEditing = !!editingChipId;
|
|
74
|
+
if (isMultiSelectOperator(selectedOperator)) {
|
|
75
|
+
const { resolved, error } = resolveMultiValues(selectedField, trimmed);
|
|
76
|
+
upsertCondition(selectedField, selectedOperator, resolved, editingChipId, isEditing ? void 0 : insertIndex, error ? SEGMENT_VARIANT.value : void 0);
|
|
77
|
+
} else if ('date' === selectedField.type) if (isBetweenOperator(selectedOperator)) {
|
|
78
|
+
const rangeValue = resolveDateRangeValue(trimmed);
|
|
79
|
+
upsertCondition(selectedField, selectedOperator, rangeValue ?? trimmed, editingChipId, isEditing ? void 0 : insertIndex, rangeValue ? void 0 : SEGMENT_VARIANT.value, 'absolute');
|
|
80
|
+
} else {
|
|
81
|
+
const { error, dateOrigin } = resolveDateValue(trimmed, editingChipId, conditionsRef.current);
|
|
82
|
+
upsertCondition(selectedField, selectedOperator, trimmed, editingChipId, isEditing ? void 0 : insertIndex, error ? SEGMENT_VARIANT.value : void 0, dateOrigin);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
const { resolved, error } = resolveSingleValue(selectedField, trimmed);
|
|
86
|
+
upsertCondition(selectedField, selectedOperator, resolved, editingChipId, isEditing ? void 0 : insertIndex, error ? SEGMENT_VARIANT.value : void 0);
|
|
87
|
+
}
|
|
88
|
+
resetState(!isEditing);
|
|
89
|
+
}, [
|
|
90
|
+
selectedField,
|
|
91
|
+
selectedOperator,
|
|
92
|
+
editingChipId,
|
|
93
|
+
insertIndex,
|
|
94
|
+
conditionsRef,
|
|
95
|
+
upsertCondition,
|
|
96
|
+
resetState
|
|
97
|
+
]);
|
|
98
|
+
return {
|
|
99
|
+
handleValueSelect,
|
|
100
|
+
handleMultiCommit,
|
|
101
|
+
handleBuildingValueChange,
|
|
102
|
+
handleMultiSelectToggle,
|
|
103
|
+
handleRangeSelect,
|
|
104
|
+
handleCustomValueCommit
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
export { useValueFlow };
|
package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuPositioning.d.ts
CHANGED
|
@@ -5,15 +5,32 @@ interface UseMenuPositioningOptions {
|
|
|
5
5
|
inputRef?: RefObject<HTMLElement | null>;
|
|
6
6
|
isBuilding: boolean;
|
|
7
7
|
insertIndex: number;
|
|
8
|
+
/** Committed chip count — recompute trigger for sibling reflow (ResizeObserver
|
|
9
|
+
* misses it; without this the dropdown sticks at the old position). */
|
|
10
|
+
chipsCount: number;
|
|
11
|
+
/** Menu is currently visible — gates the synthetic resize so a chip add/remove
|
|
12
|
+
* while the menu is closed doesn't wake page-wide resize listeners. */
|
|
13
|
+
isMenuOpen: boolean;
|
|
8
14
|
}
|
|
9
15
|
/**
|
|
10
|
-
* Manages dropdown menu positioning relative to the active chip
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
16
|
+
* Manages dropdown menu positioning relative to the active chip / segment / input.
|
|
17
|
+
*
|
|
18
|
+
* Anchor resolution (highest priority first):
|
|
19
|
+
* 1. Explicit element passed via `setMenuAnchor` — set when a chip segment is
|
|
20
|
+
* clicked, including the Backspace cascade.
|
|
21
|
+
* 2. Building chip ref (for operator/value menus while a chip is being built).
|
|
22
|
+
* 3. Input ref — only when chips are already committed (the input has shifted
|
|
23
|
+
* past them, so the dropdown follows the input's live position).
|
|
24
|
+
* 4. Container rect — the empty/initial state, flush with the field's border.
|
|
25
|
+
*
|
|
26
|
+
* Three signal sources keep Ark UI in sync with the live anchor:
|
|
27
|
+
* - `useResizeTracker` — width changes inside a segment as the user types.
|
|
28
|
+
* - `useFloatingRecomputeOn(chipsCount)` — sibling reflow when chips are
|
|
29
|
+
* added/removed (no element resized, so the resize observer is silent).
|
|
30
|
+
* - `useAutoCleanupDetachedElement` — drops the captured anchor as soon as
|
|
31
|
+
* its DOM node leaves the tree (e.g. controlled-value prop removed it).
|
|
15
32
|
*/
|
|
16
|
-
export declare const useMenuPositioning: ({ containerRef, buildingChipRef, inputRef, isBuilding, insertIndex, }: UseMenuPositioningOptions) => {
|
|
33
|
+
export declare const useMenuPositioning: ({ containerRef, buildingChipRef, inputRef, isBuilding, insertIndex, chipsCount, isMenuOpen, }: UseMenuPositioningOptions) => {
|
|
17
34
|
menuPositioning: {
|
|
18
35
|
placement: "bottom-start";
|
|
19
36
|
gutter: number;
|
|
@@ -28,7 +45,7 @@ export declare const useMenuPositioning: ({ containerRef, buildingChipRef, input
|
|
|
28
45
|
right: number;
|
|
29
46
|
} | null;
|
|
30
47
|
};
|
|
31
|
-
|
|
32
|
-
|
|
48
|
+
setMenuAnchor: import("react").Dispatch<import("react").SetStateAction<HTMLElement | null>>;
|
|
49
|
+
resetMenuAnchor: () => void;
|
|
33
50
|
};
|
|
34
51
|
export {};
|
|
@@ -1,36 +1,52 @@
|
|
|
1
|
-
import { useCallback, useRef } from "react";
|
|
1
|
+
import { useCallback, useRef, useState } from "react";
|
|
2
|
+
import { MENU_BASE_GUTTER, MENU_CHIP_GUTTER_OFFSET, QUERY_BAR_SELECTOR, toAnchorBounds } from "../../lib/index.js";
|
|
3
|
+
import { useAutoCleanupDetachedElement } from "../useAutoCleanupDetachedElement.js";
|
|
2
4
|
import { useFilterInputPositioning } from "../useFilterInputPositioning.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
return containerRect.left;
|
|
19
|
-
}, [
|
|
5
|
+
import { useFloatingRecomputeOn } from "../useFloatingRecomputeOn.js";
|
|
6
|
+
import { useResizeTracker } from "../useResizeTracker.js";
|
|
7
|
+
const useMenuPositioning = ({ containerRef, buildingChipRef, inputRef, isBuilding, insertIndex, chipsCount, isMenuOpen })=>{
|
|
8
|
+
const [editingEl, setMenuAnchor] = useState(null);
|
|
9
|
+
const resetMenuAnchor = useCallback(()=>setMenuAnchor(null), []);
|
|
10
|
+
useAutoCleanupDetachedElement(editingEl, resetMenuAnchor);
|
|
11
|
+
useFloatingRecomputeOn(chipsCount, isMenuOpen);
|
|
12
|
+
const tick = useResizeTracker(editingEl, buildingChipRef.current, containerRef.current);
|
|
13
|
+
const anchorStateRef = useRef({
|
|
14
|
+
editingEl,
|
|
15
|
+
isBuilding,
|
|
16
|
+
chipsCount
|
|
17
|
+
});
|
|
18
|
+
anchorStateRef.current = {
|
|
19
|
+
editingEl,
|
|
20
20
|
isBuilding,
|
|
21
|
+
chipsCount
|
|
22
|
+
};
|
|
23
|
+
const getAnchorBounds = useCallback((containerRect)=>{
|
|
24
|
+
const { editingEl: el, isBuilding: building, chipsCount: count } = anchorStateRef.current;
|
|
25
|
+
const chipAnchor = el?.isConnected ? toAnchorBounds(el.getBoundingClientRect()) : building && buildingChipRef.current ? toAnchorBounds(buildingChipRef.current.getBoundingClientRect()) : count > 0 && inputRef?.current ? toAnchorBounds(inputRef.current.getBoundingClientRect()) : null;
|
|
26
|
+
if (chipAnchor) return {
|
|
27
|
+
...chipAnchor,
|
|
28
|
+
bottom: chipAnchor.bottom + MENU_CHIP_GUTTER_OFFSET
|
|
29
|
+
};
|
|
30
|
+
const fieldEl = containerRef.current?.querySelector(QUERY_BAR_SELECTOR) ?? null;
|
|
31
|
+
return toAnchorBounds(fieldEl?.getBoundingClientRect() ?? containerRect);
|
|
32
|
+
}, [
|
|
33
|
+
containerRef,
|
|
21
34
|
buildingChipRef,
|
|
22
35
|
inputRef
|
|
23
36
|
]);
|
|
24
37
|
const menuPositioning = useFilterInputPositioning({
|
|
25
38
|
containerRef,
|
|
26
|
-
|
|
39
|
+
getAnchorBounds,
|
|
40
|
+
gutter: MENU_BASE_GUTTER
|
|
27
41
|
}, [
|
|
28
|
-
insertIndex
|
|
42
|
+
insertIndex,
|
|
43
|
+
chipsCount,
|
|
44
|
+
tick
|
|
29
45
|
]);
|
|
30
46
|
return {
|
|
31
47
|
menuPositioning,
|
|
32
|
-
|
|
33
|
-
|
|
48
|
+
setMenuAnchor,
|
|
49
|
+
resetMenuAnchor
|
|
34
50
|
};
|
|
35
51
|
};
|
|
36
52
|
export { useMenuPositioning };
|
|
@@ -9,7 +9,7 @@ interface UseResetStateDeps {
|
|
|
9
9
|
};
|
|
10
10
|
containerRef: RefObject<HTMLElement | null>;
|
|
11
11
|
inputRef: RefObject<HTMLInputElement | null>;
|
|
12
|
-
|
|
12
|
+
resetMenuAnchor: () => void;
|
|
13
13
|
setInputText: Dispatch<SetStateAction<string>>;
|
|
14
14
|
setSelectedField: Dispatch<SetStateAction<FieldMetadata | null>>;
|
|
15
15
|
setSelectedOperator: Dispatch<SetStateAction<FilterOperator | null>>;
|
|
@@ -19,24 +19,15 @@ interface UseResetStateDeps {
|
|
|
19
19
|
setMenuState: Dispatch<SetStateAction<MenuState>>;
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
22
|
-
* Resets all autocomplete state and
|
|
22
|
+
* Resets all autocomplete state and refocuses the input.
|
|
23
23
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* The opposite policy lives in `useFocusManagement.ts`'s rAF effect: there,
|
|
33
|
-
* body-focus means "user clicked outside" (e.g. tenant switcher) and we MUST
|
|
34
|
-
* NOT recapture. The two policies coexist because the triggers are different
|
|
35
|
-
* (state-driven re-render vs. genuine outside click).
|
|
36
|
-
*
|
|
37
|
-
* If you find yourself unifying these — make sure the "DOM dropped focus on
|
|
38
|
-
* re-render" case still refocuses, otherwise resetState during commit chains
|
|
39
|
-
* breaks. AS-882.
|
|
24
|
+
* Body-focus policy: here, activeElement===body is treated as "stayed inside"
|
|
25
|
+
* (refocus the input) because doReset()'s state mutations can synchronously
|
|
26
|
+
* unmount the element that had focus, and the browser drops to body. The
|
|
27
|
+
* opposite policy in useFocusManagement treats body-focus as "user clicked
|
|
28
|
+
* outside" — both coexist because the triggers differ (state-driven re-render
|
|
29
|
+
* vs. genuine outside click). If unifying these, preserve the re-render
|
|
30
|
+
* refocus case or commit chains break. AS-882.
|
|
40
31
|
*/
|
|
41
|
-
export declare const useResetState: ({ editing, dateRange, containerRef, inputRef,
|
|
32
|
+
export declare const useResetState: ({ editing, dateRange, containerRef, inputRef, resetMenuAnchor, setInputText, setSelectedField, setSelectedOperator, setBuildingMultiValue, setInsertIndex, setInsertAfterConnector, setMenuState, }: UseResetStateDeps) => (continueBuilding?: boolean) => void;
|
|
42
33
|
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,
|
|
4
|
+
const useResetState = ({ editing, dateRange, containerRef, inputRef, resetMenuAnchor, setInputText, setSelectedField, setSelectedOperator, setBuildingMultiValue, setInsertIndex, setInsertAfterConnector, setMenuState })=>{
|
|
5
5
|
const resetState = useCallback((continueBuilding = false)=>{
|
|
6
6
|
const doReset = ()=>{
|
|
7
7
|
setInputText('');
|
|
@@ -12,7 +12,7 @@ const useResetState = ({ editing, dateRange, containerRef, inputRef, resetMenuOf
|
|
|
12
12
|
setBuildingMultiValue(void 0);
|
|
13
13
|
setInsertIndex(null);
|
|
14
14
|
setInsertAfterConnector(false);
|
|
15
|
-
|
|
15
|
+
resetMenuAnchor();
|
|
16
16
|
setMenuState('closed');
|
|
17
17
|
};
|
|
18
18
|
if (continueBuilding) {
|
|
@@ -27,7 +27,7 @@ const useResetState = ({ editing, dateRange, containerRef, inputRef, resetMenuOf
|
|
|
27
27
|
dateRange,
|
|
28
28
|
inputRef,
|
|
29
29
|
containerRef,
|
|
30
|
-
|
|
30
|
+
resetMenuAnchor,
|
|
31
31
|
setInputText,
|
|
32
32
|
setSelectedField,
|
|
33
33
|
setSelectedOperator,
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type ChipSegment } from '../../FilterInputField/FilterInputChip';
|
|
2
|
+
import type { FieldMetadata, FilterOperator, MenuState } from '../../types';
|
|
3
|
+
interface UseSegmentEditFlowOptions {
|
|
4
|
+
editing: {
|
|
5
|
+
editingChipId: string | null;
|
|
6
|
+
editingSegment: ChipSegment | null;
|
|
7
|
+
isBuildingEdit: boolean;
|
|
8
|
+
setSegmentFilterText: (text: string) => void;
|
|
9
|
+
startBuildingEdit: (segment: ChipSegment, currentText: string) => void;
|
|
10
|
+
clearEditing: () => void;
|
|
11
|
+
};
|
|
12
|
+
selectedField: FieldMetadata | null;
|
|
13
|
+
selectedOperator: FilterOperator | null;
|
|
14
|
+
buildingMultiValue: string | undefined;
|
|
15
|
+
resetState: (continueBuilding?: boolean) => void;
|
|
16
|
+
setSelectedField: (f: FieldMetadata | null) => void;
|
|
17
|
+
setSelectedOperator: (op: FilterOperator | null) => void;
|
|
18
|
+
setInputText: (text: string) => void;
|
|
19
|
+
setMenuState: (state: MenuState) => void;
|
|
20
|
+
setMenuAnchor: (el: HTMLElement | null) => void;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Cross-cutting inline-segment handlers that don't fit any single segment flow:
|
|
24
|
+
* - `handleSegmentFilterChange` — apply field-level acceptChar to value-segment text
|
|
25
|
+
* - `cancelSegmentEdit` — full state reset on blur-cancel of a committed-chip edit;
|
|
26
|
+
* preserves selectedField/Operator when bailing out of a building-chip edit (AS-929)
|
|
27
|
+
* - `handleMenuDiscard` — Escape from a menu (preserve building state, reset otherwise)
|
|
28
|
+
* - `handleBuildingChipClick` — enter inline-edit on a building-chip segment
|
|
29
|
+
*/
|
|
30
|
+
export declare const useSegmentEditFlow: ({ editing, selectedField, selectedOperator, buildingMultiValue, resetState, setSelectedField, setSelectedOperator, setInputText, setMenuState, setMenuAnchor, }: UseSegmentEditFlowOptions) => {
|
|
31
|
+
handleSegmentFilterChange: (text: string) => void;
|
|
32
|
+
cancelSegmentEdit: () => void;
|
|
33
|
+
handleMenuDiscard: () => void;
|
|
34
|
+
handleBuildingChipClick: (segment: ChipSegment, anchorEl: HTMLElement) => void;
|
|
35
|
+
};
|
|
36
|
+
export {};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
import { SEGMENT_VARIANT } from "../../FilterInputField/FilterInputChip/index.js";
|
|
3
|
+
import { SEGMENT_TO_MENU, applyAcceptChar } from "../../lib/index.js";
|
|
4
|
+
import { getInitialSegmentText } from "./lib/index.js";
|
|
5
|
+
const useSegmentEditFlow = ({ editing, selectedField, selectedOperator, buildingMultiValue, resetState, setSelectedField, setSelectedOperator, setInputText, setMenuState, setMenuAnchor })=>{
|
|
6
|
+
const { editingSegment, isBuildingEdit, setSegmentFilterText, startBuildingEdit, clearEditing } = editing;
|
|
7
|
+
const handleSegmentFilterChange = useCallback((text)=>{
|
|
8
|
+
const accept = selectedField?.acceptChar;
|
|
9
|
+
const next = editingSegment === SEGMENT_VARIANT.value && accept ? applyAcceptChar(text, accept) : text;
|
|
10
|
+
setSegmentFilterText(next);
|
|
11
|
+
}, [
|
|
12
|
+
selectedField,
|
|
13
|
+
editingSegment,
|
|
14
|
+
setSegmentFilterText
|
|
15
|
+
]);
|
|
16
|
+
const cancelSegmentEdit = useCallback(()=>{
|
|
17
|
+
if (isBuildingEdit) {
|
|
18
|
+
clearEditing();
|
|
19
|
+
setMenuState('closed');
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
setSelectedField(null);
|
|
23
|
+
setSelectedOperator(null);
|
|
24
|
+
clearEditing();
|
|
25
|
+
setMenuState('closed');
|
|
26
|
+
}, [
|
|
27
|
+
isBuildingEdit,
|
|
28
|
+
clearEditing,
|
|
29
|
+
setSelectedField,
|
|
30
|
+
setSelectedOperator,
|
|
31
|
+
setMenuState
|
|
32
|
+
]);
|
|
33
|
+
const handleMenuDiscard = useCallback(()=>{
|
|
34
|
+
if (isBuildingEdit) {
|
|
35
|
+
clearEditing();
|
|
36
|
+
setMenuState('closed');
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
resetState();
|
|
40
|
+
}, [
|
|
41
|
+
isBuildingEdit,
|
|
42
|
+
clearEditing,
|
|
43
|
+
resetState,
|
|
44
|
+
setMenuState
|
|
45
|
+
]);
|
|
46
|
+
const handleBuildingChipClick = useCallback((segment, anchorEl)=>{
|
|
47
|
+
if (!selectedField) return;
|
|
48
|
+
setMenuAnchor(anchorEl);
|
|
49
|
+
const initialText = getInitialSegmentText(segment, selectedField, selectedOperator, buildingMultiValue);
|
|
50
|
+
startBuildingEdit(segment, initialText);
|
|
51
|
+
setInputText('');
|
|
52
|
+
setMenuState(SEGMENT_TO_MENU[segment]);
|
|
53
|
+
}, [
|
|
54
|
+
selectedField,
|
|
55
|
+
selectedOperator,
|
|
56
|
+
buildingMultiValue,
|
|
57
|
+
setMenuAnchor,
|
|
58
|
+
startBuildingEdit,
|
|
59
|
+
setInputText,
|
|
60
|
+
setMenuState
|
|
61
|
+
]);
|
|
62
|
+
return {
|
|
63
|
+
handleSegmentFilterChange,
|
|
64
|
+
cancelSegmentEdit,
|
|
65
|
+
handleMenuDiscard,
|
|
66
|
+
handleBuildingChipClick
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
export { useSegmentEditFlow };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { SEGMENT_VARIANT } from "../../FilterInputField/FilterInputChip/index.js";
|
|
2
|
-
import { NO_VALUE_PLACEHOLDER, findOptionByValue, getDateDisplayLabel, getOperatorLabel, isNoValueOperator } from "../../lib/index.js";
|
|
3
|
-
import { getInvalidValueIndices } from "../useFilterInputAutocomplete/valueCommitHelpers.js";
|
|
2
|
+
import { NO_VALUE_PLACEHOLDER, findOptionByValue, getDateDisplayLabel, getInvalidValueIndices, getOperatorLabel, isNoValueOperator } from "../../lib/index.js";
|
|
4
3
|
const INVALID_DATE = 'Invalid Date';
|
|
5
4
|
const DATE_RANGE_SEPARATOR = ' – ';
|
|
6
5
|
const MULTI_VALUE_SEPARATOR = ', ';
|
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
import type { RefObject } from 'react';
|
|
2
|
+
import { type AnchorBounds } from '../lib';
|
|
2
3
|
interface UseFilterInputPositioningProps {
|
|
3
4
|
/** Ref to the trigger element (used for X anchor and container lookup via closest) */
|
|
4
5
|
anchorRef?: RefObject<HTMLElement | null>;
|
|
5
6
|
/** Explicit container ref (skips closest lookup) */
|
|
6
7
|
containerRef?: RefObject<HTMLElement | null>;
|
|
7
|
-
/** Override
|
|
8
|
-
|
|
8
|
+
/** Override anchor calculation (receives containerRect, returns vertical/left bounds) */
|
|
9
|
+
getAnchorBounds?: (containerRect: DOMRect) => AnchorBounds;
|
|
10
|
+
/** Vertical gap below the anchor. Defaults to 12. */
|
|
11
|
+
gutter?: number;
|
|
9
12
|
}
|
|
10
13
|
/**
|
|
11
14
|
* Shared positioning hook for all FilterInput dropdowns.
|
|
12
|
-
* Anchors
|
|
15
|
+
* Anchors the dropdown to the active element (chip segment, building chip, input
|
|
16
|
+
* or trigger) so it sits flush below the element being interacted with. Falls
|
|
17
|
+
* back to the trigger element's rect, then to the query bar container.
|
|
13
18
|
* @param deps - extra values that force positioning recalculation (e.g. insertIndex)
|
|
14
19
|
*/
|
|
15
|
-
export declare const useFilterInputPositioning: ({ anchorRef, containerRef,
|
|
20
|
+
export declare const useFilterInputPositioning: ({ anchorRef, containerRef, getAnchorBounds, gutter }: UseFilterInputPositioningProps, deps?: unknown[]) => {
|
|
16
21
|
placement: "bottom-start";
|
|
17
22
|
gutter: number;
|
|
18
23
|
getAnchorRect: () => {
|