@automattic/charts 1.4.3 → 1.5.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/CHANGELOG.md +9 -0
- package/SECURITY.md +0 -1
- package/dist/index.cjs +449 -137
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +38 -0
- package/dist/index.css.map +1 -1
- package/dist/index.d.cts +22 -1
- package/dist/index.d.ts +22 -1
- package/dist/index.js +747 -435
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/charts/area-chart/area-chart.tsx +24 -10
- package/src/charts/area-chart/test/area-chart.test.tsx +73 -3
- package/src/charts/area-chart/types.ts +15 -0
- package/src/charts/line-chart/line-chart.tsx +16 -5
- package/src/charts/line-chart/types.ts +6 -0
- package/src/charts/private/test/x-zoom.test.tsx +142 -0
- package/src/charts/private/x-zoom.module.scss +45 -0
- package/src/charts/private/x-zoom.tsx +162 -0
- package/src/types.ts +1 -1
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { DataContext } from '@visx/xychart';
|
|
2
|
+
import { __ } from '@wordpress/i18n';
|
|
3
|
+
import { useCallback, useContext, useMemo, useState } from 'react';
|
|
4
|
+
import styles from './x-zoom.module.scss';
|
|
5
|
+
import type { SingleChartRef } from './single-chart-context';
|
|
6
|
+
import type { AxisScale } from '@visx/axis';
|
|
7
|
+
import type { EventHandlerParams } from '@visx/xychart';
|
|
8
|
+
import type { MutableRefObject } from 'react';
|
|
9
|
+
|
|
10
|
+
const MIN_DRAG_PIXELS = 6;
|
|
11
|
+
|
|
12
|
+
type Drag = { a: number; b: number };
|
|
13
|
+
type PointerHandler = ( params: EventHandlerParams< object > ) => void;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Drag-to-zoom state + pointer handlers for an XY chart. Designed to be
|
|
17
|
+
* embedded in a chart parent: the parent owns the result, spreads the
|
|
18
|
+
* `domain` into its `xScale.domain` config, and renders the selection
|
|
19
|
+
* rect and reset button this returns.
|
|
20
|
+
*
|
|
21
|
+
* The X scale `.invert()` is read lazily from the chart's existing
|
|
22
|
+
* `internalChartRef.getScales()` at commit time, so no DataContext access
|
|
23
|
+
* is required from the parent.
|
|
24
|
+
*
|
|
25
|
+
* @param params - Hook params.
|
|
26
|
+
* @param params.enabled - When false, the hook becomes a passthrough.
|
|
27
|
+
* @param params.chartRef - Chart's internal scales ref.
|
|
28
|
+
* @param params.userHandlers - User-supplied pointer handlers to chain.
|
|
29
|
+
* @param params.userHandlers.onPointerDown - Forwarded user pointerdown handler.
|
|
30
|
+
* @param params.userHandlers.onPointerMove - Forwarded user pointermove handler.
|
|
31
|
+
* @param params.userHandlers.onPointerUp - Forwarded user pointerup handler.
|
|
32
|
+
* @return An object with `domain`, `drag`, `reset`, and chained `handlers`.
|
|
33
|
+
*/
|
|
34
|
+
export function useXZoom< T extends Date | number = Date >( {
|
|
35
|
+
enabled,
|
|
36
|
+
chartRef,
|
|
37
|
+
userHandlers,
|
|
38
|
+
}: {
|
|
39
|
+
enabled: boolean;
|
|
40
|
+
chartRef: MutableRefObject< SingleChartRef | null >;
|
|
41
|
+
userHandlers?: {
|
|
42
|
+
onPointerDown?: PointerHandler;
|
|
43
|
+
onPointerMove?: PointerHandler;
|
|
44
|
+
onPointerUp?: PointerHandler;
|
|
45
|
+
};
|
|
46
|
+
} ) {
|
|
47
|
+
const [ domain, setDomain ] = useState< [ T, T ] | null >( null );
|
|
48
|
+
const [ drag, setDrag ] = useState< Drag | null >( null );
|
|
49
|
+
|
|
50
|
+
const reset = useCallback( () => setDomain( null ), [] );
|
|
51
|
+
|
|
52
|
+
const onPointerDown = useCallback< PointerHandler >(
|
|
53
|
+
params => {
|
|
54
|
+
userHandlers?.onPointerDown?.( params );
|
|
55
|
+
if ( ! enabled || ! params.svgPoint ) return;
|
|
56
|
+
setDrag( { a: params.svgPoint.x, b: params.svgPoint.x } );
|
|
57
|
+
},
|
|
58
|
+
[ enabled, userHandlers ]
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
const onPointerMove = useCallback< PointerHandler >(
|
|
62
|
+
params => {
|
|
63
|
+
userHandlers?.onPointerMove?.( params );
|
|
64
|
+
if ( ! enabled || ! params.svgPoint ) return;
|
|
65
|
+
setDrag( current => ( current ? { a: current.a, b: params.svgPoint!.x } : current ) );
|
|
66
|
+
},
|
|
67
|
+
[ enabled, userHandlers ]
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
const onPointerUp = useCallback< PointerHandler >(
|
|
71
|
+
params => {
|
|
72
|
+
userHandlers?.onPointerUp?.( params );
|
|
73
|
+
if ( ! enabled ) return;
|
|
74
|
+
const finalDrag = drag;
|
|
75
|
+
setDrag( null );
|
|
76
|
+
if ( ! finalDrag ) return;
|
|
77
|
+
const lo = Math.min( finalDrag.a, finalDrag.b );
|
|
78
|
+
const hi = Math.max( finalDrag.a, finalDrag.b );
|
|
79
|
+
if ( hi - lo < MIN_DRAG_PIXELS ) return;
|
|
80
|
+
const xScale = chartRef.current?.getScales()?.xScale as
|
|
81
|
+
| ( AxisScale & { invert?: ( v: number ) => T } )
|
|
82
|
+
| undefined;
|
|
83
|
+
if ( ! xScale || typeof xScale.invert !== 'function' ) return;
|
|
84
|
+
setDomain( [ xScale.invert( lo ), xScale.invert( hi ) ] );
|
|
85
|
+
},
|
|
86
|
+
[ enabled, drag, chartRef, userHandlers ]
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
return useMemo(
|
|
90
|
+
() => ( {
|
|
91
|
+
domain,
|
|
92
|
+
drag,
|
|
93
|
+
reset,
|
|
94
|
+
handlers: { onPointerDown, onPointerMove, onPointerUp },
|
|
95
|
+
} ),
|
|
96
|
+
[ domain, drag, reset, onPointerDown, onPointerMove, onPointerUp ]
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Live selection rectangle drawn inside `<XYChart>` while the user is
|
|
102
|
+
* dragging. Reads plot dimensions from visx's `DataContext`.
|
|
103
|
+
*
|
|
104
|
+
* @param props - Props.
|
|
105
|
+
* @param props.drag - Current drag, or null when idle.
|
|
106
|
+
* @return JSX or null.
|
|
107
|
+
*/
|
|
108
|
+
export function ZoomSelectionRect( { drag }: { drag: Drag | null } ) {
|
|
109
|
+
const { margin, innerHeight } = useContext( DataContext );
|
|
110
|
+
if ( ! drag || drag.a === drag.b ) return null;
|
|
111
|
+
const x = Math.min( drag.a, drag.b );
|
|
112
|
+
const w = Math.abs( drag.b - drag.a );
|
|
113
|
+
return (
|
|
114
|
+
<rect
|
|
115
|
+
className={ styles[ 'x-zoom__selection' ] }
|
|
116
|
+
x={ x }
|
|
117
|
+
y={ margin?.top ?? 0 }
|
|
118
|
+
width={ w }
|
|
119
|
+
height={ innerHeight ?? 0 }
|
|
120
|
+
data-testid="chart-zoom-selection"
|
|
121
|
+
/>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Visible icon-only reset button rendered as an HTML overlay on top of
|
|
127
|
+
* the chart container. The host should wrap its SVG in a `position: relative`
|
|
128
|
+
* container so the button anchors correctly.
|
|
129
|
+
*
|
|
130
|
+
* @param props - Props.
|
|
131
|
+
* @param props.onClick - Click handler. Typically the `reset` from `useXZoom`.
|
|
132
|
+
* @return JSX element.
|
|
133
|
+
*/
|
|
134
|
+
export function ZoomResetButton( { onClick }: { onClick: () => void } ) {
|
|
135
|
+
const label = __( 'Reset zoom', 'jetpack-charts' );
|
|
136
|
+
return (
|
|
137
|
+
<button
|
|
138
|
+
type="button"
|
|
139
|
+
className={ styles[ 'x-zoom__reset' ] }
|
|
140
|
+
onClick={ onClick }
|
|
141
|
+
aria-label={ label }
|
|
142
|
+
title={ label }
|
|
143
|
+
data-testid="chart-zoom-reset"
|
|
144
|
+
>
|
|
145
|
+
<svg
|
|
146
|
+
className={ styles[ 'x-zoom__reset-icon' ] }
|
|
147
|
+
viewBox="0 0 24 24"
|
|
148
|
+
fill="none"
|
|
149
|
+
stroke="currentColor"
|
|
150
|
+
strokeWidth="2"
|
|
151
|
+
strokeLinecap="round"
|
|
152
|
+
strokeLinejoin="round"
|
|
153
|
+
aria-hidden="true"
|
|
154
|
+
focusable="false"
|
|
155
|
+
>
|
|
156
|
+
<circle cx="10" cy="10" r="6" />
|
|
157
|
+
<line x1="15" y1="15" x2="20" y2="20" />
|
|
158
|
+
<line x1="7" y1="10" x2="13" y2="10" />
|
|
159
|
+
</svg>
|
|
160
|
+
</button>
|
|
161
|
+
);
|
|
162
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -362,7 +362,7 @@ export type ScaleOptions = {
|
|
|
362
362
|
* an explicit `domain` to keep the tick values you set exactly.
|
|
363
363
|
*/
|
|
364
364
|
nice?: boolean;
|
|
365
|
-
domain?: [ number, number ];
|
|
365
|
+
domain?: [ number, number ] | [ Date, Date ];
|
|
366
366
|
range?: [ number, number ];
|
|
367
367
|
/**
|
|
368
368
|
* For band scale, shortcut for setting `paddingInner` and `paddingOuter` to the same value.
|