@wallarm-org/design-system 0.35.0 → 0.36.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/Attribute/AttributeActionsTarget.js +1 -1
- package/dist/components/Ip/IpList/IpListHorizontal.js +1 -1
- package/dist/components/OverflowList/OverflowList.js +1 -1
- package/dist/components/Popover/PopoverContent.js +1 -1
- package/dist/components/SimpleCharts/LineChart/LineChart.d.ts +70 -0
- package/dist/components/SimpleCharts/LineChart/LineChart.figma.d.ts +1 -0
- package/dist/components/SimpleCharts/LineChart/LineChart.figma.js +163 -0
- package/dist/components/SimpleCharts/LineChart/LineChart.js +136 -0
- package/dist/components/SimpleCharts/LineChart/LineChartBody.d.ts +12 -0
- package/dist/components/SimpleCharts/LineChart/LineChartBody.js +66 -0
- package/dist/components/SimpleCharts/LineChart/LineChartContext.d.ts +151 -0
- package/dist/components/SimpleCharts/LineChart/LineChartContext.js +16 -0
- package/dist/components/SimpleCharts/LineChart/LineChartEmpty.d.ts +15 -0
- package/dist/components/SimpleCharts/LineChart/LineChartEmpty.js +71 -0
- package/dist/components/SimpleCharts/LineChart/LineChartGrid.d.ts +19 -0
- package/dist/components/SimpleCharts/LineChart/LineChartGrid.js +17 -0
- package/dist/components/SimpleCharts/LineChart/LineChartHoverPopover.d.ts +5 -0
- package/dist/components/SimpleCharts/LineChart/LineChartHoverPopover.js +14 -0
- package/dist/components/SimpleCharts/LineChart/LineChartHoverPopoverDot.d.ts +14 -0
- package/dist/components/SimpleCharts/LineChart/LineChartHoverPopoverDot.js +20 -0
- package/dist/components/SimpleCharts/LineChart/LineChartHoverPopoverRow.d.ts +12 -0
- package/dist/components/SimpleCharts/LineChart/LineChartHoverPopoverRow.js +33 -0
- package/dist/components/SimpleCharts/LineChart/LineChartHoverPopoverTimestamp.d.ts +5 -0
- package/dist/components/SimpleCharts/LineChart/LineChartHoverPopoverTimestamp.js +12 -0
- package/dist/components/SimpleCharts/LineChart/LineChartLegend.d.ts +15 -0
- package/dist/components/SimpleCharts/LineChart/LineChartLegend.js +19 -0
- package/dist/components/SimpleCharts/LineChart/LineChartLegendItem.d.ts +14 -0
- package/dist/components/SimpleCharts/LineChart/LineChartLegendItem.js +112 -0
- package/dist/components/SimpleCharts/LineChart/LineChartLine.d.ts +17 -0
- package/dist/components/SimpleCharts/LineChart/LineChartLine.js +57 -0
- package/dist/components/SimpleCharts/LineChart/LineChartTooltip.d.ts +31 -0
- package/dist/components/SimpleCharts/LineChart/LineChartTooltip.js +75 -0
- package/dist/components/SimpleCharts/LineChart/LineChartXAxis.d.ts +51 -0
- package/dist/components/SimpleCharts/LineChart/LineChartXAxis.js +34 -0
- package/dist/components/SimpleCharts/LineChart/LineChartYAxis.d.ts +24 -0
- package/dist/components/SimpleCharts/LineChart/LineChartYAxis.js +30 -0
- package/dist/components/SimpleCharts/LineChart/LineChartZoomBrush.d.ts +32 -0
- package/dist/components/SimpleCharts/LineChart/LineChartZoomBrush.js +104 -0
- package/dist/components/SimpleCharts/LineChart/LineChartZoomPopover.d.ts +5 -0
- package/dist/components/SimpleCharts/LineChart/LineChartZoomPopover.js +14 -0
- package/dist/components/SimpleCharts/LineChart/LineChartZoomPopoverConfirm.d.ts +5 -0
- package/dist/components/SimpleCharts/LineChart/LineChartZoomPopoverConfirm.js +14 -0
- package/dist/components/SimpleCharts/LineChart/LineChartZoomPopoverRange.d.ts +5 -0
- package/dist/components/SimpleCharts/LineChart/LineChartZoomPopoverRange.js +12 -0
- package/dist/components/SimpleCharts/LineChart/classes.d.ts +28 -0
- package/dist/components/SimpleCharts/LineChart/classes.js +95 -0
- package/dist/components/SimpleCharts/LineChart/constants.d.ts +25 -0
- package/dist/components/SimpleCharts/LineChart/constants.js +26 -0
- package/dist/components/SimpleCharts/LineChart/hooks/index.d.ts +5 -0
- package/dist/components/SimpleCharts/LineChart/hooks/index.js +6 -0
- package/dist/components/SimpleCharts/LineChart/hooks/useLineChartActiveKey.d.ts +33 -0
- package/dist/components/SimpleCharts/LineChart/hooks/useLineChartActiveKey.js +33 -0
- package/dist/components/SimpleCharts/LineChart/hooks/useLineChartDataWarnings.d.ts +18 -0
- package/dist/components/SimpleCharts/LineChart/hooks/useLineChartDataWarnings.js +47 -0
- package/dist/components/SimpleCharts/LineChart/hooks/useLineChartZoomState.d.ts +37 -0
- package/dist/components/SimpleCharts/LineChart/hooks/useLineChartZoomState.js +111 -0
- package/dist/components/SimpleCharts/LineChart/hooks/useZoomDragListeners.d.ts +16 -0
- package/dist/components/SimpleCharts/LineChart/hooks/useZoomDragListeners.js +27 -0
- package/dist/components/SimpleCharts/LineChart/hooks/useZoomPendingListeners.d.ts +21 -0
- package/dist/components/SimpleCharts/LineChart/hooks/useZoomPendingListeners.js +35 -0
- package/dist/components/SimpleCharts/LineChart/index.d.ts +19 -0
- package/dist/components/SimpleCharts/LineChart/index.js +19 -0
- package/dist/components/SimpleCharts/LineChart/lib/dropEdgeGridLines.d.ts +30 -0
- package/dist/components/SimpleCharts/LineChart/lib/dropEdgeGridLines.js +48 -0
- package/dist/components/SimpleCharts/LineChart/lib/formatRange.d.ts +9 -0
- package/dist/components/SimpleCharts/LineChart/lib/formatRange.js +7 -0
- package/dist/components/SimpleCharts/LineChart/lib/sampleData.d.ts +15 -0
- package/dist/components/SimpleCharts/LineChart/lib/sampleData.js +109 -0
- package/dist/components/SimpleCharts/LineChart/lib/tickHorizontalCoordinates.d.ts +15 -0
- package/dist/components/SimpleCharts/LineChart/lib/tickHorizontalCoordinates.js +13 -0
- package/dist/components/SimpleCharts/LineChart/lib/warn.d.ts +4 -0
- package/dist/components/SimpleCharts/LineChart/lib/warn.js +6 -0
- package/dist/components/SimpleCharts/PieChart/PieChartContext.js +5 -2
- package/dist/components/SimpleCharts/PieChart/constants.d.ts +1 -2
- package/dist/components/SimpleCharts/PieChart/constants.js +2 -15
- package/dist/components/SimpleCharts/hooks/index.d.ts +1 -0
- package/dist/components/SimpleCharts/hooks/index.js +2 -0
- package/dist/components/SimpleCharts/hooks/useChartTimeFormatters.d.ts +21 -0
- package/dist/components/SimpleCharts/hooks/useChartTimeFormatters.js +33 -0
- package/dist/components/SimpleCharts/index.d.ts +3 -0
- package/dist/components/SimpleCharts/index.js +4 -1
- package/dist/components/SimpleCharts/lib/chartPalette.d.ts +10 -0
- package/dist/components/SimpleCharts/lib/chartPalette.js +20 -0
- package/dist/components/SimpleCharts/lib/hoverSync.d.ts +9 -0
- package/dist/components/SimpleCharts/lib/hoverSync.js +5 -0
- package/dist/components/SimpleCharts/lib/index.d.ts +2 -0
- package/dist/components/SimpleCharts/lib/index.js +3 -0
- package/dist/components/SimpleCharts/lib/timeFormatters.d.ts +25 -0
- package/dist/components/SimpleCharts/lib/timeFormatters.js +57 -0
- package/dist/hooks/useOverflowItems.js +1 -1
- package/dist/metadata/components.json +3665 -2
- package/dist/utils/formatDateTime.d.ts +4 -0
- package/dist/utils/formatDateTime.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { LineChartDatum, LineChartZoomDragState, LineChartZoomPendingState, LineChartZoomRange } from '../LineChartContext';
|
|
2
|
+
interface UseLineChartZoomStateResult {
|
|
3
|
+
enabled: boolean;
|
|
4
|
+
drag: LineChartZoomDragState | null;
|
|
5
|
+
pending: LineChartZoomPendingState | null;
|
|
6
|
+
registerEnabled: () => () => void;
|
|
7
|
+
startDrag: (index: number, clientX: number, clientY: number) => void;
|
|
8
|
+
updateDrag: (index: number, clientX: number, clientY: number) => void;
|
|
9
|
+
endDrag: () => void;
|
|
10
|
+
cancelDrag: () => void;
|
|
11
|
+
confirmZoom: () => void;
|
|
12
|
+
cancelPending: () => void;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Two-phase zoom selection state machine.
|
|
16
|
+
*
|
|
17
|
+
* - `drag` is the live mouse-held selection — updated on every mousemove,
|
|
18
|
+
* torn down on Escape, promoted to `pending` on mouseup.
|
|
19
|
+
* - `pending` is the released-but-unconfirmed selection — renders the confirm
|
|
20
|
+
* popover. Commits via `confirmZoom` (fires `onZoomChange`) or dismisses via
|
|
21
|
+
* `cancelPending` (no emit).
|
|
22
|
+
*
|
|
23
|
+
* `enabledCount` (not a boolean) so multiple brush instances can't race when
|
|
24
|
+
* one unmounts while another remains. Window-level listeners are wired up by
|
|
25
|
+
* `useZoomDragListeners` so the popover keeps tracking when the cursor leaves
|
|
26
|
+
* the SVG and a mouseup outside the chart still releases into pending.
|
|
27
|
+
*
|
|
28
|
+
* Dataset changes invalidate cached indices on both `drag` and `pending`, so
|
|
29
|
+
* both reset whenever `data` or `xKey` flips — otherwise a stale range could
|
|
30
|
+
* be committed against a refreshed dataset.
|
|
31
|
+
*/
|
|
32
|
+
export declare const useLineChartZoomState: ({ data, xKey, onZoomChange, }: {
|
|
33
|
+
data: LineChartDatum[];
|
|
34
|
+
xKey: string;
|
|
35
|
+
onZoomChange: ((range: LineChartZoomRange | null) => void) | undefined;
|
|
36
|
+
}) => UseLineChartZoomStateResult;
|
|
37
|
+
export {};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { useCallback, useEffect, useState } from "react";
|
|
2
|
+
import { useZoomDragListeners } from "./useZoomDragListeners.js";
|
|
3
|
+
const useLineChartZoomState = ({ data, xKey, onZoomChange })=>{
|
|
4
|
+
const [zoomEnabledCount, setZoomEnabledCount] = useState(0);
|
|
5
|
+
const [zoomDrag, setZoomDrag] = useState(null);
|
|
6
|
+
const [zoomPending, setZoomPending] = useState(null);
|
|
7
|
+
const registerEnabled = useCallback(()=>{
|
|
8
|
+
setZoomEnabledCount((n)=>n + 1);
|
|
9
|
+
return ()=>setZoomEnabledCount((n)=>n - 1);
|
|
10
|
+
}, []);
|
|
11
|
+
const startDrag = useCallback((index, clientX, clientY)=>{
|
|
12
|
+
setZoomPending(null);
|
|
13
|
+
setZoomDrag({
|
|
14
|
+
startIndex: index,
|
|
15
|
+
endIndex: index,
|
|
16
|
+
clientX,
|
|
17
|
+
clientY
|
|
18
|
+
});
|
|
19
|
+
}, []);
|
|
20
|
+
const updateDrag = useCallback((index, clientX, clientY)=>{
|
|
21
|
+
setZoomDrag((prev)=>{
|
|
22
|
+
if (!prev) return null;
|
|
23
|
+
if (prev.endIndex === index && prev.clientX === clientX && prev.clientY === clientY) return prev;
|
|
24
|
+
return {
|
|
25
|
+
startIndex: prev.startIndex,
|
|
26
|
+
endIndex: index,
|
|
27
|
+
clientX,
|
|
28
|
+
clientY
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
}, []);
|
|
32
|
+
const cancelDrag = useCallback(()=>{
|
|
33
|
+
setZoomDrag(null);
|
|
34
|
+
}, []);
|
|
35
|
+
const endDrag = useCallback(()=>{
|
|
36
|
+
setZoomDrag((currentDrag)=>{
|
|
37
|
+
if (!currentDrag) return null;
|
|
38
|
+
const lo = Math.min(currentDrag.startIndex, currentDrag.endIndex);
|
|
39
|
+
const hi = Math.max(currentDrag.startIndex, currentDrag.endIndex);
|
|
40
|
+
if (lo === hi) return null;
|
|
41
|
+
const fromDatum = data[lo];
|
|
42
|
+
const toDatum = data[hi];
|
|
43
|
+
const from = fromDatum?.[xKey];
|
|
44
|
+
const to = toDatum?.[xKey];
|
|
45
|
+
if (null != from && null != to) setZoomPending({
|
|
46
|
+
range: {
|
|
47
|
+
fromIndex: lo,
|
|
48
|
+
toIndex: hi,
|
|
49
|
+
from,
|
|
50
|
+
to
|
|
51
|
+
},
|
|
52
|
+
clientX: currentDrag.clientX,
|
|
53
|
+
clientY: currentDrag.clientY
|
|
54
|
+
});
|
|
55
|
+
return null;
|
|
56
|
+
});
|
|
57
|
+
}, [
|
|
58
|
+
data,
|
|
59
|
+
xKey
|
|
60
|
+
]);
|
|
61
|
+
const confirmZoom = useCallback(()=>{
|
|
62
|
+
setZoomPending((currentPending)=>{
|
|
63
|
+
if (currentPending) onZoomChange?.(currentPending.range);
|
|
64
|
+
return null;
|
|
65
|
+
});
|
|
66
|
+
}, [
|
|
67
|
+
onZoomChange
|
|
68
|
+
]);
|
|
69
|
+
const cancelPending = useCallback(()=>{
|
|
70
|
+
setZoomPending(null);
|
|
71
|
+
}, []);
|
|
72
|
+
useEffect(()=>{
|
|
73
|
+
setZoomDrag(null);
|
|
74
|
+
setZoomPending(null);
|
|
75
|
+
}, [
|
|
76
|
+
data,
|
|
77
|
+
xKey
|
|
78
|
+
]);
|
|
79
|
+
const isZoomDragging = null !== zoomDrag;
|
|
80
|
+
const handleDragMove = useCallback((clientX, clientY)=>{
|
|
81
|
+
setZoomDrag((prev)=>{
|
|
82
|
+
if (!prev) return null;
|
|
83
|
+
if (prev.clientX === clientX && prev.clientY === clientY) return prev;
|
|
84
|
+
return {
|
|
85
|
+
...prev,
|
|
86
|
+
clientX,
|
|
87
|
+
clientY
|
|
88
|
+
};
|
|
89
|
+
});
|
|
90
|
+
}, []);
|
|
91
|
+
const handleDragEscape = useCallback(()=>setZoomDrag(null), []);
|
|
92
|
+
useZoomDragListeners({
|
|
93
|
+
enabled: isZoomDragging,
|
|
94
|
+
onMove: handleDragMove,
|
|
95
|
+
onEnd: endDrag,
|
|
96
|
+
onEscape: handleDragEscape
|
|
97
|
+
});
|
|
98
|
+
return {
|
|
99
|
+
enabled: zoomEnabledCount > 0,
|
|
100
|
+
drag: zoomDrag,
|
|
101
|
+
pending: zoomPending,
|
|
102
|
+
registerEnabled,
|
|
103
|
+
startDrag,
|
|
104
|
+
updateDrag,
|
|
105
|
+
endDrag,
|
|
106
|
+
cancelDrag,
|
|
107
|
+
confirmZoom,
|
|
108
|
+
cancelPending
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
export { useLineChartZoomState };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Window-level drag follower. While `enabled` is `true` the chart's zoom drag
|
|
3
|
+
* is in progress: mousemove updates the cursor coordinates, mouseup releases
|
|
4
|
+
* into the pending state, Escape cancels outright. Listeners live on `window`
|
|
5
|
+
* rather than the chart body so the popover keeps tracking when the cursor
|
|
6
|
+
* leaves the SVG and a release outside the chart still commits.
|
|
7
|
+
*
|
|
8
|
+
* Pulled out of `<LineChart>` so the keyboard contract has a single source of
|
|
9
|
+
* truth that can be tested in isolation.
|
|
10
|
+
*/
|
|
11
|
+
export declare const useZoomDragListeners: ({ enabled, onMove, onEnd, onEscape, }: {
|
|
12
|
+
enabled: boolean;
|
|
13
|
+
onMove: (clientX: number, clientY: number) => void;
|
|
14
|
+
onEnd: () => void;
|
|
15
|
+
onEscape: () => void;
|
|
16
|
+
}) => void;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
const useZoomDragListeners = ({ enabled, onMove, onEnd, onEscape })=>{
|
|
3
|
+
useEffect(()=>{
|
|
4
|
+
if (!enabled) return;
|
|
5
|
+
const handleMove = (event)=>onMove(event.clientX, event.clientY);
|
|
6
|
+
const handleUp = ()=>onEnd();
|
|
7
|
+
const handleKey = (event)=>{
|
|
8
|
+
if ('Escape' !== event.key) return;
|
|
9
|
+
event.preventDefault();
|
|
10
|
+
onEscape();
|
|
11
|
+
};
|
|
12
|
+
window.addEventListener('mousemove', handleMove);
|
|
13
|
+
window.addEventListener('mouseup', handleUp);
|
|
14
|
+
window.addEventListener('keydown', handleKey);
|
|
15
|
+
return ()=>{
|
|
16
|
+
window.removeEventListener('mousemove', handleMove);
|
|
17
|
+
window.removeEventListener('mouseup', handleUp);
|
|
18
|
+
window.removeEventListener('keydown', handleKey);
|
|
19
|
+
};
|
|
20
|
+
}, [
|
|
21
|
+
enabled,
|
|
22
|
+
onMove,
|
|
23
|
+
onEnd,
|
|
24
|
+
onEscape
|
|
25
|
+
]);
|
|
26
|
+
};
|
|
27
|
+
export { useZoomDragListeners };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type RefObject } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Pending-phase listeners for the zoom confirm popover. Runs while `enabled`
|
|
4
|
+
* is `true` (i.e. a pending range exists):
|
|
5
|
+
*
|
|
6
|
+
* - Mousedown outside the popover dismisses the pending range. `mousedown`
|
|
7
|
+
* rather than `click` so a fresh drag (which starts on the chart body's
|
|
8
|
+
* `mousedown`) sees an already-cleared pending state.
|
|
9
|
+
* - Enter confirms, Escape cancels. Scope is gated to events whose target is
|
|
10
|
+
* inside `rootRef`, inside `popoverRef` (portalled), or ambient (focus on
|
|
11
|
+
* `<body>` / `<html>`). The ref-based scope is load-bearing: a generic
|
|
12
|
+
* `closest('[data-slot="line-chart"]')` selector would let sibling charts
|
|
13
|
+
* confirm a pending range that does not belong to them.
|
|
14
|
+
*/
|
|
15
|
+
export declare const useZoomPendingListeners: ({ enabled, rootRef, popoverRef, onConfirm, onCancel, }: {
|
|
16
|
+
enabled: boolean;
|
|
17
|
+
rootRef: RefObject<HTMLElement | null> | undefined;
|
|
18
|
+
popoverRef: RefObject<HTMLElement | null>;
|
|
19
|
+
onConfirm?: () => void;
|
|
20
|
+
onCancel?: () => void;
|
|
21
|
+
}) => void;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
const useZoomPendingListeners = ({ enabled, rootRef, popoverRef, onConfirm, onCancel })=>{
|
|
3
|
+
useEffect(()=>{
|
|
4
|
+
if (!enabled) return;
|
|
5
|
+
const handleMouseDown = (event)=>{
|
|
6
|
+
const target = event.target;
|
|
7
|
+
if (target instanceof Node && popoverRef.current?.contains(target)) return;
|
|
8
|
+
onCancel?.();
|
|
9
|
+
};
|
|
10
|
+
const handleKey = (event)=>{
|
|
11
|
+
if ('Enter' !== event.key && 'Escape' !== event.key) return;
|
|
12
|
+
const target = event.target;
|
|
13
|
+
const isAmbient = null === target || target === document.body || target === document.documentElement;
|
|
14
|
+
const isInPopover = target instanceof Node && (popoverRef.current?.contains(target) ?? false);
|
|
15
|
+
const isInOwningChart = target instanceof Node && (rootRef?.current?.contains(target) ?? false);
|
|
16
|
+
if (!isAmbient && !isInPopover && !isInOwningChart) return;
|
|
17
|
+
event.preventDefault();
|
|
18
|
+
if ('Enter' === event.key) onConfirm?.();
|
|
19
|
+
else onCancel?.();
|
|
20
|
+
};
|
|
21
|
+
window.addEventListener('mousedown', handleMouseDown);
|
|
22
|
+
window.addEventListener('keydown', handleKey);
|
|
23
|
+
return ()=>{
|
|
24
|
+
window.removeEventListener('mousedown', handleMouseDown);
|
|
25
|
+
window.removeEventListener('keydown', handleKey);
|
|
26
|
+
};
|
|
27
|
+
}, [
|
|
28
|
+
enabled,
|
|
29
|
+
rootRef,
|
|
30
|
+
popoverRef,
|
|
31
|
+
onConfirm,
|
|
32
|
+
onCancel
|
|
33
|
+
]);
|
|
34
|
+
};
|
|
35
|
+
export { useZoomPendingListeners };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export { LineChart, type LineChartProps } from './LineChart';
|
|
2
|
+
export { LineChartBody, type LineChartBodyProps } from './LineChartBody';
|
|
3
|
+
export type { LineChartDatum, LineChartLegendOrientation, LineChartSeries, LineChartZoomRange, } from './LineChartContext';
|
|
4
|
+
export { LineChartEmpty, type LineChartEmptyProps } from './LineChartEmpty';
|
|
5
|
+
export { LineChartGrid, type LineChartGridProps } from './LineChartGrid';
|
|
6
|
+
export { LineChartHoverPopover, type LineChartHoverPopoverProps, } from './LineChartHoverPopover';
|
|
7
|
+
export { LineChartHoverPopoverDot, type LineChartHoverPopoverDotProps, } from './LineChartHoverPopoverDot';
|
|
8
|
+
export { LineChartHoverPopoverRow, type LineChartHoverPopoverRowProps, } from './LineChartHoverPopoverRow';
|
|
9
|
+
export { LineChartHoverPopoverTimestamp, type LineChartHoverPopoverTimestampProps, } from './LineChartHoverPopoverTimestamp';
|
|
10
|
+
export { LineChartLegend, type LineChartLegendProps } from './LineChartLegend';
|
|
11
|
+
export { LineChartLegendItem, type LineChartLegendItemProps, } from './LineChartLegendItem';
|
|
12
|
+
export { LineChartLine, type LineChartLineProps } from './LineChartLine';
|
|
13
|
+
export { LineChartTooltip, type LineChartTooltipProps, type LineChartTooltipRenderArgs, type LineChartTooltipRow, } from './LineChartTooltip';
|
|
14
|
+
export { LineChartXAxis, type LineChartXAxisDensity, type LineChartXAxisProps, } from './LineChartXAxis';
|
|
15
|
+
export { LineChartYAxis, type LineChartYAxisProps } from './LineChartYAxis';
|
|
16
|
+
export { LineChartZoomBrush, type LineChartZoomBrushProps, } from './LineChartZoomBrush';
|
|
17
|
+
export { LineChartZoomPopover, type LineChartZoomPopoverProps, } from './LineChartZoomPopover';
|
|
18
|
+
export { LineChartZoomPopoverConfirm, type LineChartZoomPopoverConfirmProps, } from './LineChartZoomPopoverConfirm';
|
|
19
|
+
export { LineChartZoomPopoverRange, type LineChartZoomPopoverRangeProps, } from './LineChartZoomPopoverRange';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { LineChart } from "./LineChart.js";
|
|
2
|
+
import { LineChartBody } from "./LineChartBody.js";
|
|
3
|
+
import { LineChartEmpty } from "./LineChartEmpty.js";
|
|
4
|
+
import { LineChartGrid } from "./LineChartGrid.js";
|
|
5
|
+
import { LineChartHoverPopover } from "./LineChartHoverPopover.js";
|
|
6
|
+
import { LineChartHoverPopoverDot } from "./LineChartHoverPopoverDot.js";
|
|
7
|
+
import { LineChartHoverPopoverRow } from "./LineChartHoverPopoverRow.js";
|
|
8
|
+
import { LineChartHoverPopoverTimestamp } from "./LineChartHoverPopoverTimestamp.js";
|
|
9
|
+
import { LineChartLegend } from "./LineChartLegend.js";
|
|
10
|
+
import { LineChartLegendItem } from "./LineChartLegendItem.js";
|
|
11
|
+
import { LineChartLine } from "./LineChartLine.js";
|
|
12
|
+
import { LineChartTooltip } from "./LineChartTooltip.js";
|
|
13
|
+
import { LineChartXAxis } from "./LineChartXAxis.js";
|
|
14
|
+
import { LineChartYAxis } from "./LineChartYAxis.js";
|
|
15
|
+
import { LineChartZoomBrush } from "./LineChartZoomBrush.js";
|
|
16
|
+
import { LineChartZoomPopover } from "./LineChartZoomPopover.js";
|
|
17
|
+
import { LineChartZoomPopoverConfirm } from "./LineChartZoomPopoverConfirm.js";
|
|
18
|
+
import { LineChartZoomPopoverRange } from "./LineChartZoomPopoverRange.js";
|
|
19
|
+
export { LineChart, LineChartBody, LineChartEmpty, LineChartGrid, LineChartHoverPopover, LineChartHoverPopoverDot, LineChartHoverPopoverRow, LineChartHoverPopoverTimestamp, LineChartLegend, LineChartLegendItem, LineChartLine, LineChartTooltip, LineChartXAxis, LineChartYAxis, LineChartZoomBrush, LineChartZoomPopover, LineChartZoomPopoverConfirm, LineChartZoomPopoverRange };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type ReactElement } from 'react';
|
|
2
|
+
import type { GridLineTypeFunctionProps } from 'recharts/types/cartesian/CartesianGrid';
|
|
3
|
+
interface DropEdgeGridLinesOptions {
|
|
4
|
+
/** Keep the topmost horizontal grid line unconditionally. */
|
|
5
|
+
keepTop?: boolean;
|
|
6
|
+
/** Keep the bottommost horizontal grid line unconditionally. */
|
|
7
|
+
keepBottom?: boolean;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* `<CartesianGrid horizontal>` render function that conditionally omits the
|
|
11
|
+
* topmost and bottommost horizontal grid lines.
|
|
12
|
+
*
|
|
13
|
+
* - **Top** — implements the Figma "no top x-line" rule (anatomy node
|
|
14
|
+
* `7533-3334`). Dropped only when the gap from the chart's top edge to the
|
|
15
|
+
* topmost grid line is less than {@link TOP_LINE_GAP_THRESHOLD} of one
|
|
16
|
+
* tick-to-tick spacing; above that ratio the gap reads as deliberate
|
|
17
|
+
* whitespace and the line stays (useful for axes whose nice-tick max sits
|
|
18
|
+
* well below the data max).
|
|
19
|
+
* - **Bottom** — dropped unconditionally so the solid X-axis line owns the
|
|
20
|
+
* bottom rail (otherwise the dashed grid line and the solid axis line stack
|
|
21
|
+
* on top of each other and read as a double line).
|
|
22
|
+
*
|
|
23
|
+
* Either drop can be suppressed via `keepTop` / `keepBottom` (e.g. when the
|
|
24
|
+
* caller hides the X-axis line and wants the grid to draw the bottom rail).
|
|
25
|
+
*
|
|
26
|
+
* Returning an empty `<g/>` (rather than `null`) keeps the type contract:
|
|
27
|
+
* `GridLineType`'s function form requires a `ReactElement<SVGElement>`.
|
|
28
|
+
*/
|
|
29
|
+
export declare const dropEdgeGridLines: ({ keepTop, keepBottom, }?: DropEdgeGridLinesOptions) => (props: GridLineTypeFunctionProps) => ReactElement<SVGElement>;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { createElement } from "react";
|
|
2
|
+
const TOP_LINE_GAP_THRESHOLD = 0.5;
|
|
3
|
+
const collectTickYs = (yAxis)=>{
|
|
4
|
+
const { scale } = yAxis;
|
|
5
|
+
const tickValues = yAxis.niceTicks ?? yAxis.ticks;
|
|
6
|
+
if (!scale || !tickValues?.length) return [];
|
|
7
|
+
const ys = [];
|
|
8
|
+
for (const tick of tickValues){
|
|
9
|
+
const y = scale.map(tick);
|
|
10
|
+
if ('number' == typeof y) ys.push(y);
|
|
11
|
+
}
|
|
12
|
+
ys.sort((a, b)=>a - b);
|
|
13
|
+
return ys;
|
|
14
|
+
};
|
|
15
|
+
const asSvgElement = (el)=>el;
|
|
16
|
+
const dropEdgeGridLines = ({ keepTop = false, keepBottom = false } = {})=>{
|
|
17
|
+
let cachedYs = null;
|
|
18
|
+
let cachedAxis = null;
|
|
19
|
+
return (props)=>{
|
|
20
|
+
const { offset, x1, x2, y1, y2, key, index: _index, yAxis, ...rest } = props;
|
|
21
|
+
const line = asSvgElement(createElement('line', {
|
|
22
|
+
...rest,
|
|
23
|
+
x1,
|
|
24
|
+
x2,
|
|
25
|
+
y1,
|
|
26
|
+
y2
|
|
27
|
+
}));
|
|
28
|
+
if ('number' != typeof y1 || !yAxis) return line;
|
|
29
|
+
if (cachedAxis !== yAxis) {
|
|
30
|
+
cachedAxis = yAxis;
|
|
31
|
+
cachedYs = collectTickYs(yAxis);
|
|
32
|
+
}
|
|
33
|
+
const ys = cachedYs;
|
|
34
|
+
const [topY, secondY] = ys;
|
|
35
|
+
const bottomY = ys[ys.length - 1];
|
|
36
|
+
if (void 0 === topY || void 0 === secondY || void 0 === bottomY) return line;
|
|
37
|
+
const spacing = secondY - topY;
|
|
38
|
+
if (!keepBottom && Math.abs(y1 - bottomY) < 0.5) return asSvgElement(createElement('g', {
|
|
39
|
+
key
|
|
40
|
+
}));
|
|
41
|
+
if (keepTop || spacing <= 0 || Math.abs(y1 - topY) >= 0.5) return line;
|
|
42
|
+
if (topY - offset.top < spacing * TOP_LINE_GAP_THRESHOLD) return asSvgElement(createElement('g', {
|
|
43
|
+
key
|
|
44
|
+
}));
|
|
45
|
+
return line;
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
export { dropEdgeGridLines };
|
|
@@ -0,0 +1,9 @@
|
|
|
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;
|
|
@@ -0,0 +1,7 @@
|
|
|
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
|
+
};
|
|
7
|
+
export { formatRange };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { LineChartDatum, LineChartSeries } from '../LineChartContext';
|
|
2
|
+
export declare const genHourly: (count: number, seed?: number) => LineChartDatum[];
|
|
3
|
+
export declare const genDaily: (count: number) => LineChartDatum[];
|
|
4
|
+
export declare const singleSeries: LineChartSeries[];
|
|
5
|
+
export declare const multiSeries: LineChartSeries[];
|
|
6
|
+
export declare const dashedSeries: LineChartSeries[];
|
|
7
|
+
export declare const customColorSeries: LineChartSeries[];
|
|
8
|
+
export declare const hourlyData24: LineChartDatum[];
|
|
9
|
+
export declare const hourlyDataA: LineChartDatum[];
|
|
10
|
+
export declare const hourlyDataB: LineChartDatum[];
|
|
11
|
+
export declare const dailyData60: LineChartDatum[];
|
|
12
|
+
export declare const hourlyData1000: LineChartDatum[];
|
|
13
|
+
export declare const singlePointData: LineChartDatum[];
|
|
14
|
+
export declare const dataWithErrorGaps: LineChartDatum[];
|
|
15
|
+
export declare const unitsByKey: Record<string, string>;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
const jitter = (i, seed, salt)=>{
|
|
2
|
+
const v = 43758.5453 * Math.sin((i + seed) * salt);
|
|
3
|
+
return v - Math.floor(v);
|
|
4
|
+
};
|
|
5
|
+
const genHourly = (count, seed = 1)=>{
|
|
6
|
+
const start = Date.UTC(2025, 0, 1, 0, 0, 0);
|
|
7
|
+
const out = [];
|
|
8
|
+
for(let i = 0; i < count; i++){
|
|
9
|
+
const t = start + 60 * i * 60000;
|
|
10
|
+
const requests = Math.round(120 + 30 * Math.sin((i + seed) / 4) + (jitter(i, seed, 12.9898) - 0.5) * 80);
|
|
11
|
+
const errors = Math.round(24 + 10 * Math.cos((i + seed) / 5) + (jitter(i, seed, 78.233) - 0.5) * 32);
|
|
12
|
+
const latency = Math.round(80 + 14 * Math.sin((i + seed) / 6) + (jitter(i, seed, 39.346) - 0.5) * 40);
|
|
13
|
+
out.push({
|
|
14
|
+
timestamp: t,
|
|
15
|
+
requests,
|
|
16
|
+
errors,
|
|
17
|
+
latency
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
return out;
|
|
21
|
+
};
|
|
22
|
+
const genDaily = (count)=>{
|
|
23
|
+
const start = Date.UTC(2024, 0, 1, 0, 0, 0);
|
|
24
|
+
const out = [];
|
|
25
|
+
for(let i = 0; i < count; i++){
|
|
26
|
+
const t = start + 24 * i * 3600000;
|
|
27
|
+
const requests = Math.round(1000 + 400 * Math.sin(i / 9) + i % 13 * 12);
|
|
28
|
+
const errors = Math.round(80 + 30 * Math.cos(i / 11) + i % 7 * 3);
|
|
29
|
+
out.push({
|
|
30
|
+
timestamp: t,
|
|
31
|
+
requests,
|
|
32
|
+
errors
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
return out;
|
|
36
|
+
};
|
|
37
|
+
const singleSeries = [
|
|
38
|
+
{
|
|
39
|
+
key: 'requests',
|
|
40
|
+
label: 'Requests',
|
|
41
|
+
color: 'brand'
|
|
42
|
+
}
|
|
43
|
+
];
|
|
44
|
+
const multiSeries = [
|
|
45
|
+
{
|
|
46
|
+
key: 'requests',
|
|
47
|
+
label: 'Requests',
|
|
48
|
+
color: 'brand'
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
key: 'errors',
|
|
52
|
+
label: 'Errors',
|
|
53
|
+
color: 'red'
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
key: 'latency',
|
|
57
|
+
label: 'Latency',
|
|
58
|
+
color: 'blue'
|
|
59
|
+
}
|
|
60
|
+
];
|
|
61
|
+
const dashedSeries = [
|
|
62
|
+
{
|
|
63
|
+
key: 'requests',
|
|
64
|
+
label: 'Requests',
|
|
65
|
+
color: 'brand'
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
key: 'errors',
|
|
69
|
+
label: 'Errors (target)',
|
|
70
|
+
color: 'red',
|
|
71
|
+
variant: 'dashed'
|
|
72
|
+
}
|
|
73
|
+
];
|
|
74
|
+
const customColorSeries = [
|
|
75
|
+
{
|
|
76
|
+
key: 'requests',
|
|
77
|
+
label: 'Requests',
|
|
78
|
+
color: 'var(--color-violet-500)'
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
key: 'errors',
|
|
82
|
+
label: 'Errors',
|
|
83
|
+
color: 'var(--color-emerald-500)'
|
|
84
|
+
}
|
|
85
|
+
];
|
|
86
|
+
const hourlyData24 = genHourly(24);
|
|
87
|
+
const hourlyDataA = genHourly(24, 1);
|
|
88
|
+
const hourlyDataB = genHourly(24, 7);
|
|
89
|
+
const dailyData60 = genDaily(60);
|
|
90
|
+
const hourlyData1000 = genHourly(1000);
|
|
91
|
+
const singlePointData = [
|
|
92
|
+
{
|
|
93
|
+
timestamp: Date.UTC(2025, 0, 1, 12, 0, 0),
|
|
94
|
+
requests: 142
|
|
95
|
+
}
|
|
96
|
+
];
|
|
97
|
+
const dataWithErrorGaps = hourlyData24.map((d, i)=>{
|
|
98
|
+
if (i >= 6 && i <= 8 || i >= 15 && i <= 16) return {
|
|
99
|
+
...d,
|
|
100
|
+
errors: null
|
|
101
|
+
};
|
|
102
|
+
return d;
|
|
103
|
+
});
|
|
104
|
+
const unitsByKey = {
|
|
105
|
+
requests: ' req',
|
|
106
|
+
errors: ' err',
|
|
107
|
+
latency: ' ms'
|
|
108
|
+
};
|
|
109
|
+
export { customColorSeries, dailyData60, dashedSeries, dataWithErrorGaps, genDaily, genHourly, hourlyData1000, hourlyData24, hourlyDataA, hourlyDataB, multiSeries, singlePointData, singleSeries, unitsByKey };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { HorizontalCoordinatesGenerator } from 'recharts/types/cartesian/CartesianGrid';
|
|
2
|
+
/**
|
|
3
|
+
* `<CartesianGrid horizontalCoordinatesGenerator>` that emits one grid line
|
|
4
|
+
* per Y-axis tick.
|
|
5
|
+
*
|
|
6
|
+
* The recharts v3 default skips some ticks (notably the second-from-top) and
|
|
7
|
+
* folds the topmost grid line onto the plot's top edge instead of placing it
|
|
8
|
+
* at the topmost tick. That breaks the one-line-per-label visual contract:
|
|
9
|
+
* a label can appear with no corresponding grid line.
|
|
10
|
+
*
|
|
11
|
+
* Using `yAxis.niceTicks ?? yAxis.ticks` and mapping each through the axis
|
|
12
|
+
* scale produces a grid line at every rendered tick value. `dropEdgeGridLines`
|
|
13
|
+
* then handles top/bottom culling against the resulting coordinates.
|
|
14
|
+
*/
|
|
15
|
+
export declare const tickHorizontalCoordinates: HorizontalCoordinatesGenerator;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const tickHorizontalCoordinates = ({ yAxis })=>{
|
|
2
|
+
if (!yAxis) return [];
|
|
3
|
+
const { scale } = yAxis;
|
|
4
|
+
const tickValues = yAxis.niceTicks ?? yAxis.ticks;
|
|
5
|
+
if (!scale || !tickValues?.length) return [];
|
|
6
|
+
const coords = [];
|
|
7
|
+
for (const tick of tickValues){
|
|
8
|
+
const y = scale.map(tick);
|
|
9
|
+
if ('number' == typeof y && Number.isFinite(y)) coords.push(y);
|
|
10
|
+
}
|
|
11
|
+
return coords;
|
|
12
|
+
};
|
|
13
|
+
export { tickHorizontalCoordinates };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
const PROD = ()=>{};
|
|
2
|
+
const DEV = console.warn;
|
|
3
|
+
const make = (component)=>'production' === process.env.NODE_ENV ? PROD : DEV.bind(console, `[${component}]`);
|
|
4
|
+
const warnLineChart = make('LineChart');
|
|
5
|
+
const warnLineChartLine = make('LineChartLine');
|
|
6
|
+
export { warnLineChart, warnLineChartLine };
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { createContext } from "react";
|
|
2
|
+
import { makeIsHoverSyncTarget } from "../lib/hoverSync.js";
|
|
2
3
|
const EMPTY_SELECTION = new Set();
|
|
3
|
-
const
|
|
4
|
-
|
|
4
|
+
const isHoverSyncTarget = makeIsHoverSyncTarget([
|
|
5
|
+
'pie-chart-legend-item',
|
|
6
|
+
'pie-chart-slice'
|
|
7
|
+
]);
|
|
5
8
|
const PieChartDataContext = createContext(null);
|
|
6
9
|
const PieChartActiveContext = createContext({
|
|
7
10
|
activeName: null
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
export { CHART_PALETTE_FILL as PIE_SLICE_FILL } from '../lib/chartPalette';
|
|
2
2
|
export declare const PIE_DONUT_SIZE = 120;
|
|
3
3
|
export declare const PIE_DONUT_OUTER_RADIUS = 60;
|
|
4
4
|
export declare const PIE_DONUT_INNER_RADIUS = 48;
|
|
@@ -6,4 +6,3 @@ export declare const PIE_DONUT_CORNER_RADIUS = 2;
|
|
|
6
6
|
export declare const PIE_DONUT_PADDING_ANGLE = 2;
|
|
7
7
|
export declare const PIE_DONUT_ANIMATION_DURATION = 400;
|
|
8
8
|
export declare const PIE_DONUT_ANIMATION_BEGIN = 0;
|
|
9
|
-
export declare const PIE_SLICE_FILL: Record<ChartColor, string>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CHART_PALETTE_FILL } from "../lib/chartPalette.js";
|
|
1
2
|
const PIE_DONUT_SIZE = 120;
|
|
2
3
|
const PIE_DONUT_OUTER_RADIUS = 60;
|
|
3
4
|
const PIE_DONUT_INNER_RADIUS = 48;
|
|
@@ -5,18 +6,4 @@ const PIE_DONUT_CORNER_RADIUS = 2;
|
|
|
5
6
|
const PIE_DONUT_PADDING_ANGLE = 2;
|
|
6
7
|
const PIE_DONUT_ANIMATION_DURATION = 400;
|
|
7
8
|
const PIE_DONUT_ANIMATION_BEGIN = 0;
|
|
8
|
-
|
|
9
|
-
brand: 'var(--color-w-orange-500)',
|
|
10
|
-
blue: 'var(--color-blue-500)',
|
|
11
|
-
green: 'var(--color-green-500)',
|
|
12
|
-
red: 'var(--color-red-500)',
|
|
13
|
-
amber: 'var(--color-amber-500)',
|
|
14
|
-
purple: 'var(--color-purple-500)',
|
|
15
|
-
slate: 'var(--color-badge-slate-dark-alt)',
|
|
16
|
-
teal: 'var(--color-teal-500)',
|
|
17
|
-
cyan: 'var(--color-cyan-500)',
|
|
18
|
-
indigo: 'var(--color-indigo-500)',
|
|
19
|
-
pink: 'var(--color-pink-500)',
|
|
20
|
-
rose: 'var(--color-rose-500)'
|
|
21
|
-
};
|
|
22
|
-
export { PIE_DONUT_ANIMATION_BEGIN, PIE_DONUT_ANIMATION_DURATION, PIE_DONUT_CORNER_RADIUS, PIE_DONUT_INNER_RADIUS, PIE_DONUT_OUTER_RADIUS, PIE_DONUT_PADDING_ANGLE, PIE_DONUT_SIZE, PIE_SLICE_FILL };
|
|
9
|
+
export { PIE_DONUT_ANIMATION_BEGIN, PIE_DONUT_ANIMATION_DURATION, PIE_DONUT_CORNER_RADIUS, PIE_DONUT_INNER_RADIUS, PIE_DONUT_OUTER_RADIUS, PIE_DONUT_PADDING_ANGLE, PIE_DONUT_SIZE, CHART_PALETTE_FILL as PIE_SLICE_FILL };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { type ChartTimeFormatters, useChartTimeFormatters, } from './useChartTimeFormatters';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
export interface ChartTimeFormatters {
|
|
3
|
+
formatHour: (value: unknown) => string;
|
|
4
|
+
formatDate: (value: unknown) => string;
|
|
5
|
+
formatDateTime: (value: unknown) => string;
|
|
6
|
+
formatTimezone: (value: unknown) => string;
|
|
7
|
+
formatHourWithTimezone: (value: unknown) => ReactNode;
|
|
8
|
+
formatDateWithTimezone: (value: unknown) => ReactNode;
|
|
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;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Memoised bundle of chart time formatters bound to `DateFormatProvider`
|
|
18
|
+
* settings. Falls back to `day-first` + `hourCycle: 24` when no provider is
|
|
19
|
+
* mounted (mirrors `useDateFormat`).
|
|
20
|
+
*/
|
|
21
|
+
export declare const useChartTimeFormatters: () => ChartTimeFormatters;
|