@wallarm-org/design-system 0.39.1 → 0.40.0-rc-feature-AS-982.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/FilterInputField/FilterInputField.js +11 -40
- package/dist/components/FilterInput/FilterInputField/hooks/useSegmentEditKeyboard.d.ts +36 -0
- package/dist/components/FilterInput/FilterInputField/hooks/useSegmentEditKeyboard.js +77 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/getInitialSegmentText.d.ts +9 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/getInitialSegmentText.js +8 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useChipCascade.d.ts +45 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useChipCascade.js +103 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useChipEditing.d.ts +1 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useChipEditing.js +8 -6
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useFilterInputAutocomplete.d.ts +2 -0
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useFilterInputAutocomplete.js +27 -4
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useInputHandlers.d.ts +7 -1
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useInputHandlers.js +20 -12
- package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useMenuFlow.js +16 -11
- package/dist/components/FilterInput/lib/index.d.ts +1 -0
- package/dist/components/FilterInput/lib/index.js +2 -1
- package/dist/components/FilterInput/lib/segmentMenu.d.ts +9 -0
- package/dist/components/FilterInput/lib/segmentMenu.js +7 -0
- package/dist/components/SimpleCharts/LineChart/LineChartLine.js +6 -2
- package/dist/components/SimpleCharts/LineChart/LineChartTooltip.js +3 -3
- package/dist/components/SimpleCharts/LineChart/LineChartXAxis.js +3 -12
- package/dist/components/SimpleCharts/LineChart/LineChartYAxis.js +15 -6
- package/dist/components/SimpleCharts/LineChart/LineChartZoomBrush.js +8 -19
- package/dist/components/SimpleCharts/LineChart/classes.js +1 -1
- package/dist/components/SimpleCharts/LineChart/constants.d.ts +1 -1
- package/dist/components/SimpleCharts/LineChart/constants.js +2 -2
- package/dist/components/SimpleCharts/LineChart/lib/formatRange.d.ts +9 -5
- package/dist/components/SimpleCharts/LineChart/lib/formatRange.js +6 -1
- package/dist/components/SimpleCharts/LineChart/lib/sampleData.js +3 -3
- package/dist/components/SimpleCharts/hooks/useChartTimeFormatters.d.ts +5 -0
- package/dist/components/SimpleCharts/hooks/useChartTimeFormatters.js +2 -1
- package/dist/metadata/components.json +1 -1
- package/package.json +1 -1
- package/dist/components/SimpleCharts/LineChart/lib/renderAxisTick.d.ts +0 -13
- package/dist/components/SimpleCharts/LineChart/lib/renderAxisTick.js +0 -20
|
@@ -26,6 +26,12 @@ export interface FilterInputContextValue {
|
|
|
26
26
|
/** Click on a segment of the *building* (in-progress) chip — re-opens the
|
|
27
27
|
* corresponding menu and enters inline-edit without committing the chip. */
|
|
28
28
|
onBuildingChipClick: (segment: ChipSegment, anchorRect: DOMRect) => void;
|
|
29
|
+
/** Switch the inline-edit to a different segment within the chip currently
|
|
30
|
+
* being edited — used by Backspace-on-empty to walk back through segments. */
|
|
31
|
+
onSwitchEditSegment: (targetSegment: ChipSegment) => boolean;
|
|
32
|
+
/** Remove the chip currently being edited inline — used by Backspace on an
|
|
33
|
+
* empty attribute segment when operator/value are absent. */
|
|
34
|
+
onRemoveEditingChip: () => void;
|
|
29
35
|
onConnectorChange: (chipId: string, value: 'and' | 'or') => void;
|
|
30
36
|
onChipRemove: (chipId: string) => void;
|
|
31
37
|
onClear: () => void;
|
|
@@ -11,6 +11,8 @@ interface AutocompleteForContext {
|
|
|
11
11
|
handleInputClick: () => void;
|
|
12
12
|
handleChipClick: (chipId: string, segment: ChipSegment, anchorRect: DOMRect) => void;
|
|
13
13
|
handleBuildingChipClick: (segment: ChipSegment, anchorRect: DOMRect) => void;
|
|
14
|
+
switchEditSegment: (targetSegment: ChipSegment) => boolean;
|
|
15
|
+
removeEditingChip: () => void;
|
|
14
16
|
handleConnectorChange: (chipId: string, value: 'and' | 'or') => void;
|
|
15
17
|
handleChipRemove: (chipId: string) => void;
|
|
16
18
|
handleClear: () => void;
|
|
@@ -17,6 +17,8 @@ const useFilterInputContextValue = ({ chips, autocomplete, buildingChipRef, inpu
|
|
|
17
17
|
onInputClick: autocomplete.handleInputClick,
|
|
18
18
|
onChipClick: autocomplete.handleChipClick,
|
|
19
19
|
onBuildingChipClick: autocomplete.handleBuildingChipClick,
|
|
20
|
+
onSwitchEditSegment: autocomplete.switchEditSegment,
|
|
21
|
+
onRemoveEditingChip: autocomplete.removeEditingChip,
|
|
20
22
|
onConnectorChange: autocomplete.handleConnectorChange,
|
|
21
23
|
onChipRemove: autocomplete.handleChipRemove,
|
|
22
24
|
onClear: autocomplete.handleClear,
|
|
@@ -47,6 +49,8 @@ const useFilterInputContextValue = ({ chips, autocomplete, buildingChipRef, inpu
|
|
|
47
49
|
autocomplete.handleInputClick,
|
|
48
50
|
autocomplete.handleChipClick,
|
|
49
51
|
autocomplete.handleBuildingChipClick,
|
|
52
|
+
autocomplete.switchEditSegment,
|
|
53
|
+
autocomplete.removeEditingChip,
|
|
50
54
|
autocomplete.handleConnectorChange,
|
|
51
55
|
autocomplete.handleChipRemove,
|
|
52
56
|
autocomplete.handleClear,
|
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useCallback } from "react";
|
|
3
2
|
import { cn } from "../../../utils/cn.js";
|
|
4
3
|
import { inputVariants } from "../../Input/classes.js";
|
|
5
4
|
import { ScrollArea, ScrollAreaScrollbar, ScrollAreaViewport } from "../../ScrollArea/index.js";
|
|
6
5
|
import { useFilterInputContext } from "../FilterInputContext/index.js";
|
|
7
|
-
import { isMenuRelated } from "../lib/index.js";
|
|
8
6
|
import { ChipsWithGaps, TrailingGap } from "./ChipsWithGaps.js";
|
|
9
7
|
import { ACTIONS_PADDING, COLLAPSED_MAX_HEIGHT, filterInputContainerVariants, filterInputInnerVariants } from "./classes.js";
|
|
10
8
|
import { EditingProvider } from "./FilterInputChip/context/EditingContext.js";
|
|
11
9
|
import { FilterInputChip } from "./FilterInputChip/FilterInputChip.js";
|
|
12
|
-
import { SEGMENT_VARIANT } from "./FilterInputChip/segmentVariant.js";
|
|
13
10
|
import { FilterInputFieldActions } from "./FilterInputFieldActions.js";
|
|
14
11
|
import { FilterInputSearch } from "./FilterInputSearch.js";
|
|
15
12
|
import { useChipsSplitting } from "./hooks/useChipsSplitting.js";
|
|
16
13
|
import { useExpandCollapse } from "./hooks/useExpandCollapse.js";
|
|
14
|
+
import { useSegmentEditKeyboard } from "./hooks/useSegmentEditKeyboard.js";
|
|
17
15
|
const FilterInputField = ({ className, ...props })=>{
|
|
18
|
-
const { chips, buildingChipData, buildingChipRef, insertIndex, insertAfterConnector, error, onInputClick, onGapClick, onChipClick, onBuildingChipClick, onConnectorChange, onChipRemove, editingChipId, editingSegment, segmentFilterText, onSegmentFilterChange, onCancelSegmentEdit, onCustomValueCommit, onCustomAttributeCommit, onCustomOperatorCommit, menuRef } = useFilterInputContext();
|
|
16
|
+
const { chips, buildingChipData, buildingChipRef, insertIndex, insertAfterConnector, error, onInputClick, onGapClick, onChipClick, onBuildingChipClick, onConnectorChange, onChipRemove, editingChipId, editingSegment, segmentFilterText, onSegmentFilterChange, onCancelSegmentEdit, onCustomValueCommit, onCustomAttributeCommit, onCustomOperatorCommit, onSwitchEditSegment, onRemoveEditingChip, menuRef } = useFilterInputContext();
|
|
19
17
|
const hasContent = chips.length > 0 || null != buildingChipData;
|
|
20
18
|
const { isExpanded, isOverflowing, innerRef, toggleExpand, multiRow } = useExpandCollapse();
|
|
21
19
|
const { chipsBefore, chipsAfter, hideTrailingGap, hideLeadingGap } = useChipsSplitting(chips, insertIndex, insertAfterConnector);
|
|
@@ -25,47 +23,20 @@ const FilterInputField = ({ className, ...props })=>{
|
|
|
25
23
|
onChipRemove,
|
|
26
24
|
onGapClick
|
|
27
25
|
};
|
|
28
|
-
const handleSegmentEditKeyDown =
|
|
29
|
-
|
|
30
|
-
e.preventDefault();
|
|
31
|
-
onCancelSegmentEdit();
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
if ('Enter' === e.key && !e.defaultPrevented) {
|
|
35
|
-
if (editingSegment === SEGMENT_VARIANT.value) {
|
|
36
|
-
e.preventDefault();
|
|
37
|
-
onCustomValueCommit(segmentFilterText);
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
if (editingSegment === SEGMENT_VARIANT.attribute) {
|
|
41
|
-
e.preventDefault();
|
|
42
|
-
onCustomAttributeCommit(segmentFilterText);
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
if (editingSegment === SEGMENT_VARIANT.operator) {
|
|
46
|
-
e.preventDefault();
|
|
47
|
-
onCustomOperatorCommit(segmentFilterText);
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
if ('ArrowDown' === e.key) {
|
|
52
|
-
e.preventDefault();
|
|
53
|
-
menuRef.current?.focus();
|
|
54
|
-
}
|
|
55
|
-
}, [
|
|
56
|
-
onCancelSegmentEdit,
|
|
26
|
+
const { handleSegmentEditKeyDown, handleSegmentEditBlur } = useSegmentEditKeyboard({
|
|
27
|
+
editingChipId,
|
|
57
28
|
editingSegment,
|
|
58
29
|
segmentFilterText,
|
|
30
|
+
chips,
|
|
31
|
+
buildingChipData,
|
|
32
|
+
menuRef,
|
|
33
|
+
onCancelSegmentEdit,
|
|
59
34
|
onCustomValueCommit,
|
|
60
35
|
onCustomAttributeCommit,
|
|
61
36
|
onCustomOperatorCommit,
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (!isMenuRelated(e.relatedTarget)) onCancelSegmentEdit();
|
|
66
|
-
}, [
|
|
67
|
-
onCancelSegmentEdit
|
|
68
|
-
]);
|
|
37
|
+
onSwitchEditSegment,
|
|
38
|
+
onRemoveEditingChip
|
|
39
|
+
});
|
|
69
40
|
return /*#__PURE__*/ jsx(EditingProvider, {
|
|
70
41
|
editingChipId: editingChipId,
|
|
71
42
|
editingSegment: editingSegment,
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { FocusEvent, KeyboardEvent, RefObject } from 'react';
|
|
2
|
+
import type { BuildingChipData } from '../../FilterInputContext/types';
|
|
3
|
+
import type { FilterInputChipData } from '../../types';
|
|
4
|
+
import { type ChipSegment } from '../FilterInputChip';
|
|
5
|
+
interface UseSegmentEditKeyboardOptions {
|
|
6
|
+
editingChipId: string | null;
|
|
7
|
+
editingSegment: ChipSegment | null;
|
|
8
|
+
segmentFilterText: string;
|
|
9
|
+
chips: FilterInputChipData[];
|
|
10
|
+
buildingChipData: BuildingChipData | null;
|
|
11
|
+
menuRef: RefObject<HTMLDivElement | null>;
|
|
12
|
+
onCancelSegmentEdit: () => void;
|
|
13
|
+
onCustomValueCommit: (text: string) => void;
|
|
14
|
+
onCustomAttributeCommit: (text: string) => void;
|
|
15
|
+
onCustomOperatorCommit: (text: string) => void;
|
|
16
|
+
onSwitchEditSegment: (target: ChipSegment) => boolean;
|
|
17
|
+
onRemoveEditingChip: () => void;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Keyboard handlers for a chip segment's inline-edit input:
|
|
21
|
+
*
|
|
22
|
+
* - Escape cancels.
|
|
23
|
+
* - Backspace on an empty input walks the cascade (value → operator →
|
|
24
|
+
* attribute) and, if the attribute is empty with no operator/value left,
|
|
25
|
+
* removes the chip.
|
|
26
|
+
* - Enter commits the typed text via the segment-specific commit callback.
|
|
27
|
+
* - ArrowDown moves focus into the open menu.
|
|
28
|
+
*
|
|
29
|
+
* Returns the keydown + blur handlers fed to `EditingProvider` so every
|
|
30
|
+
* segment input dispatches through the same logic.
|
|
31
|
+
*/
|
|
32
|
+
export declare const useSegmentEditKeyboard: ({ editingChipId, editingSegment, segmentFilterText, chips, buildingChipData, menuRef, onCancelSegmentEdit, onCustomValueCommit, onCustomAttributeCommit, onCustomOperatorCommit, onSwitchEditSegment, onRemoveEditingChip, }: UseSegmentEditKeyboardOptions) => {
|
|
33
|
+
handleSegmentEditKeyDown: (e: KeyboardEvent<HTMLInputElement>) => void;
|
|
34
|
+
handleSegmentEditBlur: (e: FocusEvent<HTMLInputElement>) => void;
|
|
35
|
+
};
|
|
36
|
+
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
import { isMenuRelated } from "../../lib/index.js";
|
|
3
|
+
import { SEGMENT_VARIANT } from "../FilterInputChip/index.js";
|
|
4
|
+
const useSegmentEditKeyboard = ({ editingChipId, editingSegment, segmentFilterText, chips, buildingChipData, menuRef, onCancelSegmentEdit, onCustomValueCommit, onCustomAttributeCommit, onCustomOperatorCommit, onSwitchEditSegment, onRemoveEditingChip })=>{
|
|
5
|
+
const handleSegmentEditKeyDown = useCallback((e)=>{
|
|
6
|
+
if ('Escape' === e.key) {
|
|
7
|
+
e.preventDefault();
|
|
8
|
+
onCancelSegmentEdit();
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
if ('Backspace' === e.key && '' === segmentFilterText) {
|
|
12
|
+
if (editingSegment === SEGMENT_VARIANT.attribute) {
|
|
13
|
+
const chipForEdit = editingChipId ? chips.find((c)=>c.id === editingChipId && 'chip' === c.variant) : null;
|
|
14
|
+
const operator = chipForEdit?.operator ?? buildingChipData?.operator ?? '';
|
|
15
|
+
if (!operator) {
|
|
16
|
+
e.preventDefault();
|
|
17
|
+
onRemoveEditingChip();
|
|
18
|
+
}
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (editingSegment === SEGMENT_VARIANT.operator) {
|
|
22
|
+
e.preventDefault();
|
|
23
|
+
onSwitchEditSegment(SEGMENT_VARIANT.attribute);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (editingSegment === SEGMENT_VARIANT.value) {
|
|
27
|
+
e.preventDefault();
|
|
28
|
+
onSwitchEditSegment(SEGMENT_VARIANT.operator);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if ('Enter' === e.key && !e.defaultPrevented) {
|
|
33
|
+
if (editingSegment === SEGMENT_VARIANT.value) {
|
|
34
|
+
e.preventDefault();
|
|
35
|
+
onCustomValueCommit(segmentFilterText);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (editingSegment === SEGMENT_VARIANT.attribute) {
|
|
39
|
+
e.preventDefault();
|
|
40
|
+
onCustomAttributeCommit(segmentFilterText);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (editingSegment === SEGMENT_VARIANT.operator) {
|
|
44
|
+
e.preventDefault();
|
|
45
|
+
onCustomOperatorCommit(segmentFilterText);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if ('ArrowDown' === e.key) {
|
|
50
|
+
e.preventDefault();
|
|
51
|
+
menuRef.current?.focus();
|
|
52
|
+
}
|
|
53
|
+
}, [
|
|
54
|
+
onCancelSegmentEdit,
|
|
55
|
+
editingSegment,
|
|
56
|
+
segmentFilterText,
|
|
57
|
+
onCustomValueCommit,
|
|
58
|
+
onCustomAttributeCommit,
|
|
59
|
+
onCustomOperatorCommit,
|
|
60
|
+
onSwitchEditSegment,
|
|
61
|
+
onRemoveEditingChip,
|
|
62
|
+
editingChipId,
|
|
63
|
+
chips,
|
|
64
|
+
buildingChipData,
|
|
65
|
+
menuRef
|
|
66
|
+
]);
|
|
67
|
+
const handleSegmentEditBlur = useCallback((e)=>{
|
|
68
|
+
if (!isMenuRelated(e.relatedTarget)) onCancelSegmentEdit();
|
|
69
|
+
}, [
|
|
70
|
+
onCancelSegmentEdit
|
|
71
|
+
]);
|
|
72
|
+
return {
|
|
73
|
+
handleSegmentEditKeyDown,
|
|
74
|
+
handleSegmentEditBlur
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
export { useSegmentEditKeyboard };
|
package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/getInitialSegmentText.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type ChipSegment } from '../../FilterInputField/FilterInputChip';
|
|
2
|
+
import type { FieldMetadata, FilterOperator } from '../../types';
|
|
3
|
+
/**
|
|
4
|
+
* Initial text shown in a building chip's inline-edit input for the given
|
|
5
|
+
* segment. Mirrors what the chip is currently displaying so the user can
|
|
6
|
+
* backspace through it (segment-click reopen + Backspace cascade share this
|
|
7
|
+
* derivation, hence the standalone helper).
|
|
8
|
+
*/
|
|
9
|
+
export declare const getInitialSegmentText: (segment: ChipSegment, selectedField: FieldMetadata, selectedOperator: FilterOperator | null, buildingMultiValue: string | undefined) => string;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { SEGMENT_VARIANT } from "../../FilterInputField/FilterInputChip/index.js";
|
|
2
|
+
import { getOperatorLabel } from "../../lib/index.js";
|
|
3
|
+
const getInitialSegmentText = (segment, selectedField, selectedOperator, buildingMultiValue)=>{
|
|
4
|
+
if (segment === SEGMENT_VARIANT.attribute) return selectedField.label;
|
|
5
|
+
if (segment === SEGMENT_VARIANT.operator) return selectedOperator ? getOperatorLabel(selectedOperator, selectedField.type) : '';
|
|
6
|
+
return buildingMultiValue ?? '';
|
|
7
|
+
};
|
|
8
|
+
export { getInitialSegmentText };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { MutableRefObject, RefObject } from 'react';
|
|
2
|
+
import { type ChipSegment } from '../../FilterInputField/FilterInputChip';
|
|
3
|
+
import type { Condition, FieldMetadata, FilterInputChipData, FilterOperator, MenuState, UpsertCondition } from '../../types';
|
|
4
|
+
interface UseChipCascadeOptions {
|
|
5
|
+
editing: {
|
|
6
|
+
editingChipId: string | null;
|
|
7
|
+
editingSegment: ChipSegment | null;
|
|
8
|
+
startBuildingEdit: (segment: ChipSegment, currentText: string) => void;
|
|
9
|
+
switchEditSegment: (segment: ChipSegment, currentText: string) => void;
|
|
10
|
+
};
|
|
11
|
+
chips: FilterInputChipData[];
|
|
12
|
+
fields: FieldMetadata[];
|
|
13
|
+
conditionsRef: MutableRefObject<Condition[]>;
|
|
14
|
+
effectiveInsertIndexRef: RefObject<number>;
|
|
15
|
+
selectedField: FieldMetadata | null;
|
|
16
|
+
selectedOperator: FilterOperator | null;
|
|
17
|
+
buildingMultiValue: string | undefined;
|
|
18
|
+
upsertCondition: UpsertCondition;
|
|
19
|
+
removeCondition: (chipId: string) => void;
|
|
20
|
+
resetState: (continueBuilding?: boolean) => void;
|
|
21
|
+
setInputText: (text: string) => void;
|
|
22
|
+
setMenuState: (state: MenuState) => void;
|
|
23
|
+
setSelectedOperator: (op: FilterOperator | null) => void;
|
|
24
|
+
setBuildingMultiValue: (val: string | undefined) => void;
|
|
25
|
+
setInsertIndex: (fn: (prev: number | null) => number | null) => void;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Backspace-driven cascade for chip segments. Bundles three closely related
|
|
29
|
+
* actions so consumers don't have to wire them piecemeal:
|
|
30
|
+
*
|
|
31
|
+
* - `switchEditSegment` — walks inline-edit one segment to the left (value
|
|
32
|
+
* → operator → attribute). The source segment's chip data is cleared so
|
|
33
|
+
* the just-emptied text does not re-render after the switch.
|
|
34
|
+
* - `removeEditingChip` — drops the chip currently being edited (building
|
|
35
|
+
* reset or committed `removeCondition` + insertIndex bookkeeping).
|
|
36
|
+
* - `stepBackBuildingMenu` — from the main input during building, enters
|
|
37
|
+
* inline-edit on the previous segment with its text pre-selected, mirroring
|
|
38
|
+
* the segment-click + cascade UX.
|
|
39
|
+
*/
|
|
40
|
+
export declare const useChipCascade: ({ editing, chips, fields, conditionsRef, effectiveInsertIndexRef, selectedField, selectedOperator, buildingMultiValue, upsertCondition, removeCondition, resetState, setInputText, setMenuState, setSelectedOperator, setBuildingMultiValue, setInsertIndex, }: UseChipCascadeOptions) => {
|
|
41
|
+
switchEditSegment: (targetSegment: ChipSegment) => boolean;
|
|
42
|
+
removeEditingChip: () => void;
|
|
43
|
+
stepBackBuildingMenu: (current: "field" | "operator" | "value") => void;
|
|
44
|
+
};
|
|
45
|
+
export {};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
import { SEGMENT_VARIANT } from "../../FilterInputField/FilterInputChip/index.js";
|
|
3
|
+
import { SEGMENT_TO_MENU, chipIdToConditionIndex } from "../../lib/index.js";
|
|
4
|
+
import { getInitialSegmentText } from "./getInitialSegmentText.js";
|
|
5
|
+
const useChipCascade = ({ editing, chips, fields, conditionsRef, effectiveInsertIndexRef, selectedField, selectedOperator, buildingMultiValue, upsertCondition, removeCondition, resetState, setInputText, setMenuState, setSelectedOperator, setBuildingMultiValue, setInsertIndex })=>{
|
|
6
|
+
const switchEditSegment = useCallback((targetSegment)=>{
|
|
7
|
+
const sourceSegment = editing.editingSegment;
|
|
8
|
+
if (null === sourceSegment) return false;
|
|
9
|
+
const isBuildingEdit = !editing.editingChipId;
|
|
10
|
+
if (isBuildingEdit) {
|
|
11
|
+
if (!selectedField) return false;
|
|
12
|
+
if (sourceSegment === SEGMENT_VARIANT.value) setBuildingMultiValue(void 0);
|
|
13
|
+
else if (sourceSegment === SEGMENT_VARIANT.operator) {
|
|
14
|
+
setSelectedOperator(null);
|
|
15
|
+
setBuildingMultiValue(void 0);
|
|
16
|
+
}
|
|
17
|
+
const initialText = getInitialSegmentText(targetSegment, selectedField, selectedOperator, buildingMultiValue);
|
|
18
|
+
editing.startBuildingEdit(targetSegment, initialText);
|
|
19
|
+
setInputText('');
|
|
20
|
+
setMenuState(SEGMENT_TO_MENU[targetSegment]);
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
const editingId = editing.editingChipId;
|
|
24
|
+
if (!editingId) return false;
|
|
25
|
+
const chip = chips.find((c)=>c.id === editingId);
|
|
26
|
+
if (!chip || 'chip' !== chip.variant) return false;
|
|
27
|
+
const idx = chipIdToConditionIndex(editingId);
|
|
28
|
+
const condition = null !== idx ? conditionsRef.current[idx] : null;
|
|
29
|
+
const field = condition ? fields.find((f)=>f.name === condition.field) : null;
|
|
30
|
+
if (condition && field) {
|
|
31
|
+
if (sourceSegment === SEGMENT_VARIANT.value) upsertCondition(field, condition.operator, null, editingId, void 0, void 0, condition.dateOrigin);
|
|
32
|
+
else if (sourceSegment === SEGMENT_VARIANT.operator) upsertCondition(field, void 0, condition.value, editingId, void 0, void 0, condition.dateOrigin);
|
|
33
|
+
}
|
|
34
|
+
const targetText = targetSegment === SEGMENT_VARIANT.attribute ? chip.attribute ?? '' : targetSegment === SEGMENT_VARIANT.operator ? chip.operator ?? '' : '';
|
|
35
|
+
editing.switchEditSegment(targetSegment, targetText);
|
|
36
|
+
setMenuState(SEGMENT_TO_MENU[targetSegment]);
|
|
37
|
+
return true;
|
|
38
|
+
}, [
|
|
39
|
+
editing,
|
|
40
|
+
selectedField,
|
|
41
|
+
selectedOperator,
|
|
42
|
+
buildingMultiValue,
|
|
43
|
+
chips,
|
|
44
|
+
fields,
|
|
45
|
+
conditionsRef,
|
|
46
|
+
upsertCondition,
|
|
47
|
+
setBuildingMultiValue,
|
|
48
|
+
setSelectedOperator,
|
|
49
|
+
setInputText,
|
|
50
|
+
setMenuState
|
|
51
|
+
]);
|
|
52
|
+
const removeEditingChip = useCallback(()=>{
|
|
53
|
+
if (null === editing.editingSegment) return;
|
|
54
|
+
const editingId = editing.editingChipId;
|
|
55
|
+
if (!editingId) return void resetState();
|
|
56
|
+
const chipCondIdx = chipIdToConditionIndex(editingId);
|
|
57
|
+
if (null !== chipCondIdx && chipCondIdx < effectiveInsertIndexRef.current) setInsertIndex((prev)=>null != prev ? prev - 1 : prev);
|
|
58
|
+
removeCondition(editingId);
|
|
59
|
+
resetState();
|
|
60
|
+
}, [
|
|
61
|
+
editing,
|
|
62
|
+
removeCondition,
|
|
63
|
+
resetState,
|
|
64
|
+
effectiveInsertIndexRef,
|
|
65
|
+
setInsertIndex
|
|
66
|
+
]);
|
|
67
|
+
const stepBackBuildingMenu = useCallback((current)=>{
|
|
68
|
+
if (!selectedField) return void resetState();
|
|
69
|
+
if ('value' === current) {
|
|
70
|
+
setBuildingMultiValue(void 0);
|
|
71
|
+
setInputText('');
|
|
72
|
+
const operatorText = getInitialSegmentText(SEGMENT_VARIANT.operator, selectedField, selectedOperator, buildingMultiValue);
|
|
73
|
+
editing.startBuildingEdit(SEGMENT_VARIANT.operator, operatorText);
|
|
74
|
+
setMenuState('operator');
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if ('operator' === current) {
|
|
78
|
+
setSelectedOperator(null);
|
|
79
|
+
setBuildingMultiValue(void 0);
|
|
80
|
+
setInputText('');
|
|
81
|
+
editing.startBuildingEdit(SEGMENT_VARIANT.attribute, selectedField.label);
|
|
82
|
+
setMenuState('field');
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
resetState();
|
|
86
|
+
}, [
|
|
87
|
+
selectedField,
|
|
88
|
+
selectedOperator,
|
|
89
|
+
buildingMultiValue,
|
|
90
|
+
editing,
|
|
91
|
+
resetState,
|
|
92
|
+
setBuildingMultiValue,
|
|
93
|
+
setInputText,
|
|
94
|
+
setMenuState,
|
|
95
|
+
setSelectedOperator
|
|
96
|
+
]);
|
|
97
|
+
return {
|
|
98
|
+
switchEditSegment,
|
|
99
|
+
removeEditingChip,
|
|
100
|
+
stepBackBuildingMenu
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
export { useChipCascade };
|
|
@@ -27,6 +27,7 @@ export declare const useChipEditing: ({ conditions, chips, fields, containerRef,
|
|
|
27
27
|
resetSegmentTyping: () => void;
|
|
28
28
|
handleChipClick: (chipId: string, segment: ChipSegment, anchorRect: DOMRect) => void;
|
|
29
29
|
startBuildingEdit: (segment: ChipSegment, currentText: string) => void;
|
|
30
|
+
switchEditSegment: (segment: ChipSegment, currentText: string) => void;
|
|
30
31
|
clearEditing: () => void;
|
|
31
32
|
};
|
|
32
33
|
export {};
|
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
import { useCallback, useMemo, useRef, useState } from "react";
|
|
2
2
|
import { SEGMENT_VARIANT } from "../../FilterInputField/FilterInputChip/index.js";
|
|
3
|
-
import { chipIdToConditionIndex, getOperatorFromLabel, isNoValueOperator } from "../../lib/index.js";
|
|
3
|
+
import { SEGMENT_TO_MENU, chipIdToConditionIndex, getOperatorFromLabel, isNoValueOperator } from "../../lib/index.js";
|
|
4
4
|
const getConditionByChipId = (chipId, conditions)=>{
|
|
5
5
|
const idx = chipIdToConditionIndex(chipId);
|
|
6
6
|
return null !== idx ? conditions[idx] ?? null : null;
|
|
7
7
|
};
|
|
8
|
-
const SEGMENT_TO_MENU = {
|
|
9
|
-
attribute: 'field',
|
|
10
|
-
operator: 'operator',
|
|
11
|
-
value: 'value'
|
|
12
|
-
};
|
|
13
8
|
const getFirstIncompleteSegment = (condition, fields)=>{
|
|
14
9
|
const field = fields.find((f)=>f.name === condition.field);
|
|
15
10
|
if (!field || condition.error === SEGMENT_VARIANT.attribute) return SEGMENT_VARIANT.attribute;
|
|
@@ -74,6 +69,11 @@ const useChipEditing = ({ conditions, chips, fields, containerRef, setMenuOffset
|
|
|
74
69
|
setSegmentFilterText(currentText);
|
|
75
70
|
setUserHasTyped(false);
|
|
76
71
|
}, []);
|
|
72
|
+
const switchEditSegment = useCallback((segment, currentText)=>{
|
|
73
|
+
setEditingSegment(segment);
|
|
74
|
+
setSegmentFilterText(currentText);
|
|
75
|
+
setUserHasTyped(false);
|
|
76
|
+
}, []);
|
|
77
77
|
const handleSegmentFilterChange = useCallback((text)=>{
|
|
78
78
|
setSegmentFilterText(text);
|
|
79
79
|
setUserHasTyped(true);
|
|
@@ -93,6 +93,7 @@ const useChipEditing = ({ conditions, chips, fields, containerRef, setMenuOffset
|
|
|
93
93
|
resetSegmentTyping,
|
|
94
94
|
handleChipClick,
|
|
95
95
|
startBuildingEdit,
|
|
96
|
+
switchEditSegment,
|
|
96
97
|
clearEditing
|
|
97
98
|
}), [
|
|
98
99
|
editingChipId,
|
|
@@ -103,6 +104,7 @@ const useChipEditing = ({ conditions, chips, fields, containerRef, setMenuOffset
|
|
|
103
104
|
resetSegmentTyping,
|
|
104
105
|
handleChipClick,
|
|
105
106
|
startBuildingEdit,
|
|
107
|
+
switchEditSegment,
|
|
106
108
|
clearEditing
|
|
107
109
|
]);
|
|
108
110
|
};
|
package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useFilterInputAutocomplete.d.ts
CHANGED
|
@@ -54,6 +54,8 @@ export declare const useFilterInputAutocomplete: ({ fields, conditions, chips, u
|
|
|
54
54
|
resetAutocompleteState: (continueBuilding?: boolean) => void;
|
|
55
55
|
handleChipClick: (chipId: string, segment: ChipSegment, anchorRect: DOMRect) => void;
|
|
56
56
|
handleBuildingChipClick: (segment: ChipSegment, anchorRect: DOMRect) => void;
|
|
57
|
+
switchEditSegment: (targetSegment: ChipSegment) => boolean;
|
|
58
|
+
removeEditingChip: () => void;
|
|
57
59
|
handleConnectorChange: (connectorId: string, value: "and" | "or") => void;
|
|
58
60
|
handleChipRemove: (chipId: string) => void;
|
|
59
61
|
handleClear: () => void;
|
package/dist/components/FilterInput/hooks/useFilterInputAutocomplete/useFilterInputAutocomplete.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { useCallback, useRef, useState } from "react";
|
|
2
2
|
import { SEGMENT_VARIANT } from "../../FilterInputField/FilterInputChip/index.js";
|
|
3
3
|
import { useDateRange } from "../../FilterInputMenu/FilterInputDateValueMenu/hooks.js";
|
|
4
|
-
import {
|
|
4
|
+
import { SEGMENT_TO_MENU, applyAcceptChar } from "../../lib/index.js";
|
|
5
5
|
import { deriveAutocompleteValues } from "./deriveAutocompleteValues.js";
|
|
6
|
+
import { getInitialSegmentText } from "./getInitialSegmentText.js";
|
|
6
7
|
import { useBlurCommit } from "./useBlurCommit.js";
|
|
7
8
|
import { useChipActions } from "./useChipActions.js";
|
|
9
|
+
import { useChipCascade } from "./useChipCascade.js";
|
|
8
10
|
import { useChipEditing } from "./useChipEditing.js";
|
|
9
11
|
import { useFocusManagement } from "./useFocusManagement.js";
|
|
10
12
|
import { useInputHandlers } from "./useInputHandlers.js";
|
|
@@ -83,6 +85,24 @@ const useFilterInputAutocomplete = ({ fields, conditions, chips, upsertCondition
|
|
|
83
85
|
setMenuState,
|
|
84
86
|
setBuildingMultiValue
|
|
85
87
|
});
|
|
88
|
+
const { switchEditSegment, removeEditingChip, stepBackBuildingMenu } = useChipCascade({
|
|
89
|
+
editing,
|
|
90
|
+
chips,
|
|
91
|
+
fields,
|
|
92
|
+
conditionsRef,
|
|
93
|
+
effectiveInsertIndexRef,
|
|
94
|
+
selectedField,
|
|
95
|
+
selectedOperator,
|
|
96
|
+
buildingMultiValue,
|
|
97
|
+
upsertCondition,
|
|
98
|
+
removeCondition,
|
|
99
|
+
resetState,
|
|
100
|
+
setInputText,
|
|
101
|
+
setMenuState,
|
|
102
|
+
setSelectedOperator,
|
|
103
|
+
setBuildingMultiValue,
|
|
104
|
+
setInsertIndex
|
|
105
|
+
});
|
|
86
106
|
const { handleInputChange, handleInputClick, handleKeyDown, menuRef } = useInputHandlers({
|
|
87
107
|
inputText,
|
|
88
108
|
menuState,
|
|
@@ -101,7 +121,8 @@ const useFilterInputAutocomplete = ({ fields, conditions, chips, upsertCondition
|
|
|
101
121
|
removeConditionAtIndex,
|
|
102
122
|
handleFieldSelect,
|
|
103
123
|
handleOperatorSelect,
|
|
104
|
-
handleCustomValueCommit
|
|
124
|
+
handleCustomValueCommit,
|
|
125
|
+
stepBackBuildingMenu
|
|
105
126
|
});
|
|
106
127
|
const { commitBuildingOnBlur, hasIncompleteBuilding } = useBlurCommit({
|
|
107
128
|
selectedField,
|
|
@@ -193,10 +214,10 @@ const useFilterInputAutocomplete = ({ fields, conditions, chips, upsertCondition
|
|
|
193
214
|
if (!selectedField) return;
|
|
194
215
|
const containerRect = containerRef.current?.getBoundingClientRect();
|
|
195
216
|
setMenuOffset(containerRect ? anchorRect.left - containerRect.left : 0);
|
|
196
|
-
const initialText = segment
|
|
217
|
+
const initialText = getInitialSegmentText(segment, selectedField, selectedOperator, buildingMultiValue);
|
|
197
218
|
editing.startBuildingEdit(segment, initialText);
|
|
198
219
|
setInputText('');
|
|
199
|
-
setMenuState(segment
|
|
220
|
+
setMenuState(SEGMENT_TO_MENU[segment]);
|
|
200
221
|
}, [
|
|
201
222
|
selectedField,
|
|
202
223
|
selectedOperator,
|
|
@@ -229,6 +250,8 @@ const useFilterInputAutocomplete = ({ fields, conditions, chips, upsertCondition
|
|
|
229
250
|
resetAutocompleteState: resetState,
|
|
230
251
|
handleChipClick: editing.handleChipClick,
|
|
231
252
|
handleBuildingChipClick,
|
|
253
|
+
switchEditSegment,
|
|
254
|
+
removeEditingChip,
|
|
232
255
|
handleConnectorChange: setConnectorValue,
|
|
233
256
|
handleChipRemove,
|
|
234
257
|
handleClear,
|
|
@@ -1,5 +1,6 @@
|
|
|
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;
|
|
@@ -21,8 +22,13 @@ interface UseInputHandlersDeps {
|
|
|
21
22
|
handleFieldSelect: (field: FieldMetadata) => void;
|
|
22
23
|
handleOperatorSelect: (operator: FilterOperator) => void;
|
|
23
24
|
handleCustomValueCommit: (text: string) => void;
|
|
25
|
+
/** Step the building chip one segment back (value → operator → field).
|
|
26
|
+
* Called on Backspace from an empty main input while a building chip is
|
|
27
|
+
* alive. At the field step (only attribute remains) it tears the chip
|
|
28
|
+
* down entirely. */
|
|
29
|
+
stepBackBuildingMenu: (current: BuildingStep) => void;
|
|
24
30
|
}
|
|
25
|
-
export declare const useInputHandlers: ({ inputText, menuState, selectedField, selectedOperator, isFocused, fields, inputRef, conditionsRef, conditionsLengthRef, effectiveInsertIndexRef, setInputText, setMenuState, setInsertIndex, resetMenuOffset, removeConditionAtIndex, handleFieldSelect, handleOperatorSelect, handleCustomValueCommit, }: UseInputHandlersDeps) => {
|
|
31
|
+
export declare const useInputHandlers: ({ inputText, menuState, selectedField, selectedOperator, isFocused, fields, inputRef, conditionsRef, conditionsLengthRef, effectiveInsertIndexRef, setInputText, setMenuState, setInsertIndex, resetMenuOffset, removeConditionAtIndex, handleFieldSelect, handleOperatorSelect, handleCustomValueCommit, stepBackBuildingMenu, }: UseInputHandlersDeps) => {
|
|
26
32
|
handleInputChange: (e: ChangeEvent<HTMLInputElement>) => void;
|
|
27
33
|
handleInputClick: () => void;
|
|
28
34
|
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, resetMenuOffset, removeConditionAtIndex, handleFieldSelect, handleOperatorSelect, handleCustomValueCommit })=>{
|
|
3
|
+
const useInputHandlers = ({ inputText, menuState, selectedField, selectedOperator, isFocused, fields, inputRef, conditionsRef, conditionsLengthRef, effectiveInsertIndexRef, setInputText, setMenuState, setInsertIndex, resetMenuOffset, removeConditionAtIndex, handleFieldSelect, handleOperatorSelect, handleCustomValueCommit, stepBackBuildingMenu })=>{
|
|
4
4
|
const menuRef = useRef(null);
|
|
5
5
|
const handleInputChange = useCallback((e)=>{
|
|
6
6
|
let text = e.target.value;
|
|
@@ -72,17 +72,24 @@ const useInputHandlers = ({ inputText, menuState, selectedField, selectedOperato
|
|
|
72
72
|
handleCustomValueCommit(inputText);
|
|
73
73
|
return;
|
|
74
74
|
}
|
|
75
|
-
if ('Backspace' === e.key && !e.repeat && '' === inputText
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
75
|
+
if ('Backspace' === e.key && !e.repeat && '' === inputText) {
|
|
76
|
+
if (selectedField && ('value' === menuState || 'operator' === menuState || 'field' === menuState)) {
|
|
77
|
+
e.preventDefault();
|
|
78
|
+
stepBackBuildingMenu(menuState);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (conditionsLengthRef.current > 0) {
|
|
82
|
+
e.preventDefault();
|
|
83
|
+
const removeIdx = effectiveInsertIndexRef.current - 1;
|
|
84
|
+
if (removeIdx >= 0 && !conditionsRef.current[removeIdx]?.disabled) {
|
|
85
|
+
removeConditionAtIndex(removeIdx);
|
|
86
|
+
setInsertIndex((prev)=>{
|
|
87
|
+
const eff = prev ?? conditionsLengthRef.current;
|
|
88
|
+
return eff > 0 ? eff - 1 : 0;
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
setMenuState('closed');
|
|
84
92
|
}
|
|
85
|
-
setMenuState('closed');
|
|
86
93
|
}
|
|
87
94
|
}, [
|
|
88
95
|
inputText,
|
|
@@ -98,7 +105,8 @@ const useInputHandlers = ({ inputText, menuState, selectedField, selectedOperato
|
|
|
98
105
|
setInsertIndex,
|
|
99
106
|
conditionsRef,
|
|
100
107
|
conditionsLengthRef,
|
|
101
|
-
effectiveInsertIndexRef
|
|
108
|
+
effectiveInsertIndexRef,
|
|
109
|
+
stepBackBuildingMenu
|
|
102
110
|
]);
|
|
103
111
|
return {
|
|
104
112
|
handleInputChange,
|
|
@@ -25,6 +25,15 @@ const useMenuFlow = ({ editing, selectedField, selectedOperator, fields, inputRe
|
|
|
25
25
|
const idx = chipIdToConditionIndex(editing.editingChipId);
|
|
26
26
|
const condition = null !== idx ? conditionsRef.current[idx] : null;
|
|
27
27
|
if (condition) {
|
|
28
|
+
if (!condition.operator) {
|
|
29
|
+
upsertCondition(field, void 0, condition.value, editing.editingChipId, void 0, void 0, condition.dateOrigin);
|
|
30
|
+
setSelectedField(field);
|
|
31
|
+
setSelectedOperator(null);
|
|
32
|
+
editing.setEditingSegment(SEGMENT_VARIANT.operator);
|
|
33
|
+
editing.setSegmentFilterText('');
|
|
34
|
+
setMenuState('operator');
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
28
37
|
const hasValueError = validateValueForField(field, condition.value);
|
|
29
38
|
upsertCondition(field, condition.operator, condition.value, editing.editingChipId, void 0, hasValueError ? SEGMENT_VARIANT.value : void 0, condition.dateOrigin);
|
|
30
39
|
}
|
|
@@ -205,17 +214,13 @@ const useMenuFlow = ({ editing, selectedField, selectedOperator, fields, inputRe
|
|
|
205
214
|
const idx = chipIdToConditionIndex(editing.editingChipId);
|
|
206
215
|
const condition = null !== idx ? conditionsRef.current[idx] : null;
|
|
207
216
|
if (!condition) return;
|
|
208
|
-
if (matchedField)
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
type: 'string'
|
|
216
|
-
};
|
|
217
|
-
upsertCondition(syntheticField, condition.operator, condition.value, editing.editingChipId, void 0, SEGMENT_VARIANT.attribute, condition.dateOrigin);
|
|
218
|
-
}
|
|
217
|
+
if (matchedField) return void handleFieldSelect(matchedField);
|
|
218
|
+
const syntheticField = {
|
|
219
|
+
name: trimmed,
|
|
220
|
+
label: trimmed,
|
|
221
|
+
type: 'string'
|
|
222
|
+
};
|
|
223
|
+
upsertCondition(syntheticField, condition.operator, condition.value, editing.editingChipId, void 0, SEGMENT_VARIANT.attribute, condition.dateOrigin);
|
|
219
224
|
resetState();
|
|
220
225
|
}, [
|
|
221
226
|
editing,
|
|
@@ -11,5 +11,6 @@ export { filterAndSort } from './filterSort';
|
|
|
11
11
|
export { getCurrentValueTokenText, getValueFilterText } from './menuFilterText';
|
|
12
12
|
export { getFieldOperators, getOperatorFromLabel, getOperatorLabel, isBetweenOperator, isBuildingComplete, isMultiSelectOperator, isNoValueOperator, isOperatorAllowedForField, isValueShapeCompatible, NO_VALUE_PLACEHOLDER, nextBuildingMenu, } from './operators';
|
|
13
13
|
export { type FilterParseError, isFilterParseError, parseExpression } from './parseExpression';
|
|
14
|
+
export { SEGMENT_TO_MENU } from './segmentMenu';
|
|
14
15
|
export { serializeExpression } from './serializeExpression';
|
|
15
16
|
export { createStatusCodeInputFilter, createStatusCodeNormalizer, createStatusCodeSerializer, createStatusCodeSuggestions, createStatusCodeValidator, } from './statusCode';
|
|
@@ -10,6 +10,7 @@ import { filterAndSort } from "./filterSort.js";
|
|
|
10
10
|
import { getCurrentValueTokenText, getValueFilterText } from "./menuFilterText.js";
|
|
11
11
|
import { NO_VALUE_PLACEHOLDER, getFieldOperators, getOperatorFromLabel, getOperatorLabel, isBetweenOperator, isBuildingComplete, isMultiSelectOperator, isNoValueOperator, isOperatorAllowedForField, isValueShapeCompatible, nextBuildingMenu } from "./operators.js";
|
|
12
12
|
import { isFilterParseError, parseExpression } from "./parseExpression/index.js";
|
|
13
|
+
import { SEGMENT_TO_MENU } from "./segmentMenu.js";
|
|
13
14
|
import { serializeExpression } from "./serializeExpression.js";
|
|
14
15
|
import { createStatusCodeInputFilter, createStatusCodeNormalizer, createStatusCodeSerializer, createStatusCodeSuggestions, createStatusCodeValidator } from "./statusCode/index.js";
|
|
15
|
-
export { CONNECTOR_ID_PATTERN, DATE_PRESETS, NO_VALUE_OPERATORS, NO_VALUE_PLACEHOLDER, OPERATORS_BY_TYPE, OPERATOR_LABELS, OPERATOR_LABELS_BY_TYPE, OPERATOR_SYMBOLS, QUERY_BAR_SELECTOR, VARIANT_LABELS, applyAcceptChar, applyFieldValueTransforms, applyKnownFieldHelpers, buildContainerAnchoredRect, chipIdToConditionIndex, createStatusCodeInputFilter, createStatusCodeNormalizer, createStatusCodeSerializer, createStatusCodeSuggestions, createStatusCodeValidator, filterAndSort, findChipSplitIndex, findOptionByValue, formatDateForChip, getCurrentValueTokenText, getDateDisplayLabel, getFieldOperators, getFieldValues, getKnownFieldSerializer, getOperatorFromLabel, getOperatorLabel, getValueFilterText, hasFieldValues, hasStaticAllowlist, isBetweenOperator, isBuildingComplete, isDatePreset, isFilterParseError, isMenuRelated, isMultiSelectOperator, isNoValueOperator, isOperatorAllowedForField, isValueShapeCompatible, nextBuildingMenu, parseExpression, serializeExpression };
|
|
16
|
+
export { CONNECTOR_ID_PATTERN, DATE_PRESETS, NO_VALUE_OPERATORS, NO_VALUE_PLACEHOLDER, OPERATORS_BY_TYPE, OPERATOR_LABELS, OPERATOR_LABELS_BY_TYPE, OPERATOR_SYMBOLS, QUERY_BAR_SELECTOR, SEGMENT_TO_MENU, VARIANT_LABELS, applyAcceptChar, applyFieldValueTransforms, applyKnownFieldHelpers, buildContainerAnchoredRect, chipIdToConditionIndex, createStatusCodeInputFilter, createStatusCodeNormalizer, createStatusCodeSerializer, createStatusCodeSuggestions, createStatusCodeValidator, filterAndSort, findChipSplitIndex, findOptionByValue, formatDateForChip, getCurrentValueTokenText, getDateDisplayLabel, getFieldOperators, getFieldValues, getKnownFieldSerializer, getOperatorFromLabel, getOperatorLabel, getValueFilterText, hasFieldValues, hasStaticAllowlist, isBetweenOperator, isBuildingComplete, isDatePreset, isFilterParseError, isMenuRelated, isMultiSelectOperator, isNoValueOperator, isOperatorAllowedForField, isValueShapeCompatible, nextBuildingMenu, parseExpression, serializeExpression };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type SegmentVariant } from '../FilterInputField/FilterInputChip/segmentVariant';
|
|
2
|
+
import type { MenuState } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* Open-menu state that corresponds to each chip segment. Used wherever an
|
|
5
|
+
* inline-edit transition needs to (re)open the matching menu — segment
|
|
6
|
+
* click, Backspace cascade, and the building-flow step-back from the main
|
|
7
|
+
* input. Kept here so the mapping is defined once.
|
|
8
|
+
*/
|
|
9
|
+
export declare const SEGMENT_TO_MENU: Record<SegmentVariant, MenuState>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { SEGMENT_VARIANT } from "../FilterInputField/FilterInputChip/segmentVariant.js";
|
|
2
|
+
const SEGMENT_TO_MENU = {
|
|
3
|
+
[SEGMENT_VARIANT.attribute]: 'field',
|
|
4
|
+
[SEGMENT_VARIANT.operator]: 'operator',
|
|
5
|
+
[SEGMENT_VARIANT.value]: 'value'
|
|
6
|
+
};
|
|
7
|
+
export { SEGMENT_TO_MENU };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useContext, useEffect } from "react";
|
|
3
3
|
import { Line } from "recharts";
|
|
4
|
-
import { LINE_ANIMATION_BEGIN, LINE_ANIMATION_DURATION, LINE_DASH_DASHARRAY, LINE_INACTIVE_OPACITY, LINE_STROKE_FILL, LINE_STROKE_WIDTH, resolveSeriesColor } from "./constants.js";
|
|
4
|
+
import { LINE_ACTIVE_DOT_RADIUS, LINE_ANIMATION_BEGIN, LINE_ANIMATION_DURATION, LINE_DASH_DASHARRAY, LINE_INACTIVE_OPACITY, LINE_STROKE_FILL, LINE_STROKE_WIDTH, resolveSeriesColor } from "./constants.js";
|
|
5
5
|
import { LineChartActiveContext, LineChartDataContext } from "./LineChartContext.js";
|
|
6
6
|
import { warnLineChartLine } from "./lib/warn.js";
|
|
7
7
|
const wrap = (cb)=>cb ? (_props, event)=>cb(event) : void 0;
|
|
@@ -34,7 +34,11 @@ const LineChartLine = ({ seriesKey, curve = 'monotone', disableAnimation = false
|
|
|
34
34
|
strokeLinecap: "round",
|
|
35
35
|
strokeLinejoin: "round",
|
|
36
36
|
dot: false,
|
|
37
|
-
activeDot:
|
|
37
|
+
activeDot: {
|
|
38
|
+
r: LINE_ACTIVE_DOT_RADIUS,
|
|
39
|
+
stroke,
|
|
40
|
+
strokeWidth: 0
|
|
41
|
+
},
|
|
38
42
|
opacity: opacity,
|
|
39
43
|
connectNulls: connectNulls,
|
|
40
44
|
isAnimationActive: disableAnimation ? false : 'auto',
|
|
@@ -2,15 +2,15 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { useContext, useMemo } from "react";
|
|
3
3
|
import { Tooltip, usePlotArea } from "recharts";
|
|
4
4
|
import { lineChartTooltipCenterClasses } from "./classes.js";
|
|
5
|
-
import { HOVER_POPOVER_TOP
|
|
5
|
+
import { HOVER_POPOVER_TOP } from "./constants.js";
|
|
6
6
|
import { LineChartDataContext, LineChartSelectionContext, LineChartZoomContext } from "./LineChartContext.js";
|
|
7
7
|
import { LineChartHoverPopover } from "./LineChartHoverPopover.js";
|
|
8
8
|
import { LineChartHoverPopoverRow } from "./LineChartHoverPopoverRow.js";
|
|
9
9
|
import { LineChartHoverPopoverTimestamp } from "./LineChartHoverPopoverTimestamp.js";
|
|
10
10
|
const TOOLTIP_CURSOR = {
|
|
11
|
-
stroke: 'var(--color-border-
|
|
11
|
+
stroke: 'var(--color-border-primary-light)',
|
|
12
12
|
strokeWidth: 1,
|
|
13
|
-
strokeDasharray:
|
|
13
|
+
strokeDasharray: '4 4'
|
|
14
14
|
};
|
|
15
15
|
const TOOLTIP_ALLOW_ESCAPE = {
|
|
16
16
|
x: false,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useContext } from "react";
|
|
3
3
|
import { XAxis } from "recharts";
|
|
4
|
+
import { LINE_AXIS_TICK_TEXT_PROPS } from "./constants.js";
|
|
4
5
|
import { LineChartDataContext } from "./LineChartContext.js";
|
|
5
|
-
import { renderAxisTick } from "./lib/renderAxisTick.js";
|
|
6
6
|
const DENSITY_MIN_TICK_GAP = {
|
|
7
7
|
sparse: 64,
|
|
8
8
|
normal: 32,
|
|
@@ -11,16 +11,7 @@ const DENSITY_MIN_TICK_GAP = {
|
|
|
11
11
|
const AXIS_LINE_PROPS = {
|
|
12
12
|
stroke: 'var(--color-border-primary-light)'
|
|
13
13
|
};
|
|
14
|
-
const
|
|
15
|
-
if (0 === index) return {
|
|
16
|
-
anchor: 'start'
|
|
17
|
-
};
|
|
18
|
-
if (index === visibleTicksCount - 1) return {
|
|
19
|
-
anchor: 'end'
|
|
20
|
-
};
|
|
21
|
-
return {};
|
|
22
|
-
});
|
|
23
|
-
const LineChartXAxis = ({ tickFormatter, density, interval = 'preserveStartEnd', tickCount, ticks, minTickGap, domain, padding, type, hideTicks = false, axisLine = true })=>{
|
|
14
|
+
const LineChartXAxis = ({ tickFormatter, density, interval, tickCount, ticks, minTickGap, domain, padding, type, hideTicks = false, axisLine = true })=>{
|
|
24
15
|
const dataCtx = useContext(LineChartDataContext);
|
|
25
16
|
const xKey = dataCtx?.xKey ?? 'x';
|
|
26
17
|
const resolvedMinTickGap = minTickGap ?? (density ? DENSITY_MIN_TICK_GAP[density] : void 0);
|
|
@@ -29,7 +20,7 @@ const LineChartXAxis = ({ tickFormatter, density, interval = 'preserveStartEnd',
|
|
|
29
20
|
type: type,
|
|
30
21
|
tickLine: false,
|
|
31
22
|
axisLine: axisLine ? AXIS_LINE_PROPS : false,
|
|
32
|
-
tick: hideTicks ? false :
|
|
23
|
+
tick: hideTicks ? false : LINE_AXIS_TICK_TEXT_PROPS,
|
|
33
24
|
tickFormatter: tickFormatter,
|
|
34
25
|
interval: interval,
|
|
35
26
|
tickCount: tickCount,
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
|
-
import { YAxis } from "recharts";
|
|
3
|
-
import { LINE_Y_LABEL_WIDTH } from "./constants.js";
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
import { Text, YAxis } from "recharts";
|
|
3
|
+
import { LINE_AXIS_TICK_TEXT_PROPS, LINE_Y_LABEL_WIDTH } from "./constants.js";
|
|
4
|
+
const renderTick = (props)=>{
|
|
5
|
+
const { x, y, payload, visibleTicksCount, textAnchor, verticalAnchor, tickFormatter, index } = props;
|
|
6
|
+
if (index === visibleTicksCount - 1) return null;
|
|
7
|
+
const value = tickFormatter ? tickFormatter(payload.value, payload.index) : payload.value;
|
|
8
|
+
return /*#__PURE__*/ jsx(Text, {
|
|
9
|
+
x: x,
|
|
10
|
+
y: y,
|
|
11
|
+
textAnchor: textAnchor,
|
|
12
|
+
verticalAnchor: verticalAnchor,
|
|
13
|
+
...LINE_AXIS_TICK_TEXT_PROPS,
|
|
14
|
+
children: value
|
|
15
|
+
});
|
|
16
|
+
};
|
|
8
17
|
const LineChartYAxis = ({ tickFormatter, tickCount, ticks, domain, padding, width = LINE_Y_LABEL_WIDTH, hideTicks = false })=>/*#__PURE__*/ jsx(YAxis, {
|
|
9
18
|
width: width,
|
|
10
19
|
tickLine: false,
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useContext, useEffect, useMemo, useRef } from "react";
|
|
3
3
|
import { createPortal } from "react-dom";
|
|
4
|
-
import { ReferenceArea
|
|
5
|
-
import { formatChartDateTime } from "../lib/timeFormatters.js";
|
|
4
|
+
import { ReferenceArea } from "recharts";
|
|
6
5
|
import { lineChartZoomCursorPopoverClasses } from "./classes.js";
|
|
7
6
|
import { useZoomPendingListeners } from "./hooks/useZoomPendingListeners.js";
|
|
8
7
|
import { LineChartDataContext, LineChartZoomContext } from "./LineChartContext.js";
|
|
@@ -10,24 +9,12 @@ import { LineChartZoomPopover } from "./LineChartZoomPopover.js";
|
|
|
10
9
|
import { LineChartZoomPopoverConfirm } from "./LineChartZoomPopoverConfirm.js";
|
|
11
10
|
import { LineChartZoomPopoverRange } from "./LineChartZoomPopoverRange.js";
|
|
12
11
|
import { formatRange as formatRange_js_formatRange } from "./lib/formatRange.js";
|
|
13
|
-
const defaultFormatRange = formatRange_js_formatRange((value)=>formatChartDateTime(value) || String(value));
|
|
14
12
|
const POPOVER_OFFSET_X = 12;
|
|
15
|
-
const
|
|
13
|
+
const POPOVER_OFFSET_Y = 12;
|
|
14
|
+
const LineChartZoomBrush = ({ disabled = false, formatRange = formatRange_js_formatRange, confirmLabel = 'Zoom in', container })=>{
|
|
16
15
|
const dataCtx = useContext(LineChartDataContext);
|
|
17
16
|
const zoomCtx = useContext(LineChartZoomContext);
|
|
18
17
|
const popoverRef = useRef(null);
|
|
19
|
-
const plotArea = usePlotArea();
|
|
20
|
-
const rootRef = zoomCtx?.rootRef;
|
|
21
|
-
const centerY = useMemo(()=>{
|
|
22
|
-
if (!plotArea || !rootRef?.current) return null;
|
|
23
|
-
const surface = rootRef.current.querySelector('.recharts-surface');
|
|
24
|
-
if (!surface) return null;
|
|
25
|
-
const rect = surface.getBoundingClientRect();
|
|
26
|
-
return rect.top + plotArea.y + plotArea.height / 2;
|
|
27
|
-
}, [
|
|
28
|
-
plotArea,
|
|
29
|
-
rootRef
|
|
30
|
-
]);
|
|
31
18
|
const registerEnabled = zoomCtx?.registerEnabled;
|
|
32
19
|
useEffect(()=>{
|
|
33
20
|
if (disabled || !registerEnabled) return;
|
|
@@ -40,6 +27,7 @@ const LineChartZoomBrush = ({ disabled = false, formatRange = defaultFormatRange
|
|
|
40
27
|
const pending = zoomCtx?.pending ?? null;
|
|
41
28
|
const cancelPending = zoomCtx?.cancelPending;
|
|
42
29
|
const confirmZoom = zoomCtx?.confirmZoom;
|
|
30
|
+
const rootRef = zoomCtx?.rootRef;
|
|
43
31
|
useZoomPendingListeners({
|
|
44
32
|
enabled: null !== pending,
|
|
45
33
|
rootRef,
|
|
@@ -79,9 +67,9 @@ const LineChartZoomBrush = ({ disabled = false, formatRange = defaultFormatRange
|
|
|
79
67
|
"data-zoom-state": isPending ? 'pending' : 'dragging',
|
|
80
68
|
className: lineChartZoomCursorPopoverClasses,
|
|
81
69
|
style: {
|
|
82
|
-
top:
|
|
70
|
+
top: popoverPosition.clientY - POPOVER_OFFSET_Y,
|
|
83
71
|
left: popoverPosition.clientX + POPOVER_OFFSET_X,
|
|
84
|
-
transform: 'translateY(-
|
|
72
|
+
transform: 'translateY(-100%)',
|
|
85
73
|
pointerEvents: isPending ? 'auto' : 'none'
|
|
86
74
|
},
|
|
87
75
|
onMouseDown: (e)=>e.stopPropagation(),
|
|
@@ -104,7 +92,8 @@ const LineChartZoomBrush = ({ disabled = false, formatRange = defaultFormatRange
|
|
|
104
92
|
x2: range.to,
|
|
105
93
|
fill: "var(--color-states-primary-hover)",
|
|
106
94
|
fillOpacity: 1,
|
|
107
|
-
stroke: "
|
|
95
|
+
stroke: "var(--color-border-primary-light)",
|
|
96
|
+
strokeOpacity: 1,
|
|
108
97
|
ifOverflow: "visible"
|
|
109
98
|
}) : null,
|
|
110
99
|
popoverContent ? /*#__PURE__*/ createPortal(popoverContent, container ?? document.body) : null
|
|
@@ -6,7 +6,7 @@ const lineChartZoomCursorPopoverClasses = "fixed z-30 pointer-events-none";
|
|
|
6
6
|
const lineChartLegendVariants = cva('flex', {
|
|
7
7
|
variants: {
|
|
8
8
|
orientation: {
|
|
9
|
-
horizontal: 'flex-row flex-wrap items-center gap-6 pl-12 py-2
|
|
9
|
+
horizontal: 'flex-row flex-wrap items-center gap-6 pl-12 py-2',
|
|
10
10
|
vertical: 'flex-col gap-6 px-12 py-2 max-w-160'
|
|
11
11
|
},
|
|
12
12
|
align: {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export { CHART_PALETTE_FILL as LINE_STROKE_FILL, resolveChartColor as resolveSeriesColor, } from '../lib/chartPalette';
|
|
2
2
|
export declare const LINE_STROKE_WIDTH = 2;
|
|
3
3
|
export declare const LINE_DASH_DASHARRAY = "6 4";
|
|
4
|
+
export declare const LINE_ACTIVE_DOT_RADIUS = 4;
|
|
4
5
|
export declare const LINE_GRID_DASHARRAY = "4 4";
|
|
5
|
-
export declare const LINE_CURSOR_DASHARRAY = "4 2";
|
|
6
6
|
export declare const LINE_ANIMATION_DURATION = 400;
|
|
7
7
|
export declare const LINE_ANIMATION_BEGIN = 0;
|
|
8
8
|
export declare const LINE_INACTIVE_OPACITY = 0.3;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { CHART_PALETTE_FILL, resolveChartColor } from "../lib/chartPalette.js";
|
|
2
2
|
const LINE_STROKE_WIDTH = 2;
|
|
3
3
|
const LINE_DASH_DASHARRAY = '6 4';
|
|
4
|
+
const LINE_ACTIVE_DOT_RADIUS = 4;
|
|
4
5
|
const LINE_GRID_DASHARRAY = '4 4';
|
|
5
|
-
const LINE_CURSOR_DASHARRAY = '4 2';
|
|
6
6
|
const LINE_ANIMATION_DURATION = 400;
|
|
7
7
|
const LINE_ANIMATION_BEGIN = 0;
|
|
8
8
|
const LINE_INACTIVE_OPACITY = 0.3;
|
|
@@ -23,4 +23,4 @@ const LINE_AXIS_TICK_TEXT_PROPS = {
|
|
|
23
23
|
fontFamily: 'var(--font-sans)',
|
|
24
24
|
fontWeight: 400
|
|
25
25
|
};
|
|
26
|
-
export { HOVER_POPOVER_TOP, LINE_ANIMATION_BEGIN, LINE_ANIMATION_DURATION, LINE_AXIS_TICK_TEXT_PROPS, LINE_CARD_HEIGHT,
|
|
26
|
+
export { HOVER_POPOVER_TOP, LINE_ACTIVE_DOT_RADIUS, LINE_ANIMATION_BEGIN, LINE_ANIMATION_DURATION, LINE_AXIS_TICK_TEXT_PROPS, LINE_CARD_HEIGHT, LINE_DASH_DASHARRAY, LINE_DEFAULT_BODY_MARGIN, LINE_GRID_DASHARRAY, LINE_HEADER_HEIGHT, LINE_INACTIVE_OPACITY, CHART_PALETTE_FILL as LINE_STROKE_FILL, LINE_STROKE_WIDTH, LINE_X_LABEL_HEIGHT, LINE_Y_LABEL_WIDTH, resolveChartColor as resolveSeriesColor };
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import type { LineChartZoomRange } from '../LineChartContext';
|
|
2
|
+
/**
|
|
3
|
+
* Default range text for the zoom popover. Renders as `from → to` using the
|
|
4
|
+
* shared {@link formatChartDateTime} helper for numeric (timestamp) X values,
|
|
5
|
+
* which respects the app-level `order` / `hourCycle` defaults from the
|
|
6
|
+
* `DateFormatProvider`. Falls back to `String(value)` for non-numeric X values.
|
|
7
|
+
* Consumers override via the `formatRange` prop on `<LineChartZoomBrush>`.
|
|
8
|
+
*/
|
|
9
|
+
export declare const formatRange: (range: LineChartZoomRange) => string;
|
|
@@ -1,2 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import { formatChartDateTime } from "../../lib/timeFormatters.js";
|
|
2
|
+
const formatRange = (range)=>{
|
|
3
|
+
const from = formatChartDateTime(range.from) || String(range.from);
|
|
4
|
+
const to = formatChartDateTime(range.to) || String(range.to);
|
|
5
|
+
return `${from} → ${to}`;
|
|
6
|
+
};
|
|
2
7
|
export { formatRange };
|
|
@@ -3,7 +3,7 @@ const jitter = (i, seed, salt)=>{
|
|
|
3
3
|
return v - Math.floor(v);
|
|
4
4
|
};
|
|
5
5
|
const genHourly = (count, seed = 1)=>{
|
|
6
|
-
const start =
|
|
6
|
+
const start = Date.UTC(2025, 0, 1, 0, 0, 0);
|
|
7
7
|
const out = [];
|
|
8
8
|
for(let i = 0; i < count; i++){
|
|
9
9
|
const t = start + 60 * i * 60000;
|
|
@@ -20,7 +20,7 @@ const genHourly = (count, seed = 1)=>{
|
|
|
20
20
|
return out;
|
|
21
21
|
};
|
|
22
22
|
const genDaily = (count)=>{
|
|
23
|
-
const start =
|
|
23
|
+
const start = Date.UTC(2024, 0, 1, 0, 0, 0);
|
|
24
24
|
const out = [];
|
|
25
25
|
for(let i = 0; i < count; i++){
|
|
26
26
|
const t = start + 24 * i * 3600000;
|
|
@@ -90,7 +90,7 @@ const dailyData60 = genDaily(60);
|
|
|
90
90
|
const hourlyData1000 = genHourly(1000);
|
|
91
91
|
const singlePointData = [
|
|
92
92
|
{
|
|
93
|
-
timestamp:
|
|
93
|
+
timestamp: Date.UTC(2025, 0, 1, 12, 0, 0),
|
|
94
94
|
requests: 142
|
|
95
95
|
}
|
|
96
96
|
];
|
|
@@ -7,6 +7,11 @@ export interface ChartTimeFormatters {
|
|
|
7
7
|
formatHourWithTimezone: (value: unknown) => ReactNode;
|
|
8
8
|
formatDateWithTimezone: (value: unknown) => ReactNode;
|
|
9
9
|
formatDateTimeWithTimezone: (value: unknown) => ReactNode;
|
|
10
|
+
/** `from → to` using `formatDate`. Suitable for `<LineChartZoomBrush formatRange>`. */
|
|
11
|
+
formatDateRange: (range: {
|
|
12
|
+
from: unknown;
|
|
13
|
+
to: unknown;
|
|
14
|
+
}) => string;
|
|
10
15
|
}
|
|
11
16
|
/**
|
|
12
17
|
* Memoised bundle of chart time formatters bound to `DateFormatProvider`
|
|
@@ -22,7 +22,8 @@ const useChartTimeFormatters = ()=>{
|
|
|
22
22
|
formatTimezone: formatChartTimezone,
|
|
23
23
|
formatHourWithTimezone: withTimezoneChip(formatHour),
|
|
24
24
|
formatDateWithTimezone: withTimezoneChip(formatDate),
|
|
25
|
-
formatDateTimeWithTimezone: withTimezoneChip(formatDateTime)
|
|
25
|
+
formatDateTimeWithTimezone: withTimezoneChip(formatDateTime),
|
|
26
|
+
formatDateRange: ({ from, to })=>`${formatDate(from)} → ${formatDate(to)}`
|
|
26
27
|
};
|
|
27
28
|
}, [
|
|
28
29
|
order,
|
package/package.json
CHANGED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { ReactNode } from 'react';
|
|
2
|
-
import type { TextAnchor } from 'recharts/types/component/Text';
|
|
3
|
-
import type { XAxisTickContentProps, YAxisTickContentProps } from 'recharts/types/util/types';
|
|
4
|
-
export interface AxisTickDecision {
|
|
5
|
-
/** When `true`, render nothing for this tick. */
|
|
6
|
-
skip?: boolean;
|
|
7
|
-
/** Override the axis-derived text anchor. */
|
|
8
|
-
anchor?: TextAnchor;
|
|
9
|
-
}
|
|
10
|
-
type AxisTickProps = XAxisTickContentProps | YAxisTickContentProps;
|
|
11
|
-
type Decide<T extends AxisTickProps> = (props: T) => AxisTickDecision;
|
|
12
|
-
export declare const renderAxisTick: <T extends AxisTickProps>(decide?: Decide<T>) => (props: T) => ReactNode;
|
|
13
|
-
export {};
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { jsx } from "react/jsx-runtime";
|
|
2
|
-
import { Text } from "recharts";
|
|
3
|
-
import { LINE_AXIS_TICK_TEXT_PROPS } from "../constants.js";
|
|
4
|
-
const renderAxisTick = (decide)=>(props)=>{
|
|
5
|
-
const decision = decide?.(props) ?? {};
|
|
6
|
-
if (decision.skip) return null;
|
|
7
|
-
const { x, y, payload, textAnchor, verticalAnchor, tickFormatter, orientation } = props;
|
|
8
|
-
const value = tickFormatter ? tickFormatter(payload.value, payload.index) : payload.value;
|
|
9
|
-
const isHorizontal = 'top' === orientation || 'bottom' === orientation;
|
|
10
|
-
const anchorToCoordinate = null != decision.anchor;
|
|
11
|
-
return /*#__PURE__*/ jsx(Text, {
|
|
12
|
-
x: anchorToCoordinate && isHorizontal ? payload.coordinate : x,
|
|
13
|
-
y: anchorToCoordinate && !isHorizontal ? payload.coordinate : y,
|
|
14
|
-
textAnchor: decision.anchor ?? textAnchor,
|
|
15
|
-
verticalAnchor: verticalAnchor,
|
|
16
|
-
...LINE_AXIS_TICK_TEXT_PROPS,
|
|
17
|
-
children: value
|
|
18
|
-
});
|
|
19
|
-
};
|
|
20
|
-
export { renderAxisTick };
|