forked from patternfly/patternfly-react
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathChartLegendTooltip.tsx
414 lines (404 loc) · 16.4 KB
/
ChartLegendTooltip.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
import * as React from 'react';
import hoistNonReactStatics from 'hoist-non-react-statics';
import {
Helpers,
NumberOrCallback,
OrientationOrCallback,
StringOrNumberOrCallback,
TextAnchorType,
VictoryStyleObject
} from 'victory-core';
import { VictoryTooltip } from 'victory-tooltip';
import { ChartCursorTooltip, ChartCursorTooltipProps } from '../ChartCursorTooltip/ChartCursorTooltip';
import { ChartLegendTooltipContent } from './ChartLegendTooltipContent';
import { ChartLegendTooltipStyles } from '../ChartTheme/ChartStyles';
import { ChartThemeDefinition } from '../ChartTheme/ChartTheme';
import { ChartTooltip } from '../ChartTooltip/ChartTooltip';
import {
getLegendTooltipDataProps,
getLegendTooltipSize,
getLegendTooltipVisibleData,
getLegendTooltipVisibleText
} from '../ChartUtils/chart-tooltip';
import { getTheme } from '../ChartUtils/chart-theme';
/**
* The ChartLegendTooltip is based on ChartCursorTooltip, which is intended to be used with a voronoi cursor
* container. This works best with charts such as area and line, for example.
*
* See https://github.com/FormidableLabs/victory/blob/main/packages/victory-tooltip/src/index.d.ts
*/
export interface ChartLegendTooltipProps extends Omit<ChartCursorTooltipProps, 'title'> {
/**
* The active prop specifies whether the tooltip component should be displayed.
*/
active?: boolean;
/**
* When true, tooltip events will set the active prop on both data and label elements.
*/
activateData?: boolean;
/**
* The activePoints prop specifies the active data
*
* Note: This prop should not be set manually.
*
* @private
* @hide
*/
activePoints?: any[];
/**
* The angle prop specifies the angle to rotate the tooltip around its origin point.
*/
angle?: number;
/**
* The center prop determines the position of the center of the tooltip flyout. This prop should be given as an object
* that describes the desired x and y svg coordinates of the center of the tooltip. This prop is useful for
* positioning the flyout of a tooltip independent from the pointer. When ChartTooltip is used with
* ChartVoronoiContainer, the center prop is what enables the mouseFollowTooltips option. When this prop is set,
* non-zero pointerLength values will no longer be respected.
*/
center?: { x: number; y: number };
/**
* The centerOffset prop determines the position of the center of the tooltip flyout in relation to the flyout
* pointer. This prop should be given as an object of x and y, where each is either a numeric offset value or a
* function that returns a numeric value. When this prop is set, non-zero pointerLength values will no longer be
* respected.
*
* @propType { x: number | Function, y: number | Function }
*/
centerOffset?: {
x?: NumberOrCallback;
y?: NumberOrCallback;
};
/**
* The constrainToVisibleArea prop determines whether to coerce tooltips so that they fit within the visible area of
* the chart. When this prop is set to true, tooltip pointers will still point to the correct data point, but the
* center of the tooltip will be shifted to fit within the overall width and height of the svg Victory renders.
*/
constrainToVisibleArea?: boolean;
/**
* The cornerRadius prop determines corner radius of the flyout container. This prop may be given as a positive number
* or a function of datum.
*
* @propType number | Function
*/
cornerRadius?: NumberOrCallback;
/**
* Victory components can pass a data prop to their label component. This can be useful in custom components that need
* to make use of the entire dataset.
*/
data?: any[];
/**
* Victory components can pass a datum prop to their label component. This can be used to calculate functional styles,
* and determine text.
*/
datum?: {};
/**
* The dx prop defines a horizontal shift from the x coordinate.
*
* @propType number | Function
*/
dx?: NumberOrCallback;
/**
* The dy prop defines a vertical shift from the y coordinate.
*
* @propType number | Function
*/
dy?: NumberOrCallback;
/**
* The events prop attaches arbitrary event handlers to the label component. This prop should be given as an object of
* event names and corresponding event handlers. When events are provided via Victory’s event system, event handlers
* will be called with the event, the props of the component is attached to, and an eventKey.
*
* @propType object
* @example events={{onClick: (evt) => alert("x: " + evt.clientX)}}
*/
events?: { [key: string]: (event: React.SyntheticEvent<any>) => void };
/**
* The flyoutComponent prop takes a component instance which will be used to create the flyout path for each tooltip.
* The new element created from the passed flyoutComponent will be supplied with the following properties: x, y, dx, dy,
* index, datum, cornerRadius, pointerLength, pointerWidth, width, height, orientation, style, and events.
* Any of these props may be overridden by passing in props to the supplied component, or modified or ignored within
* the custom component itself. If flyoutComponent is omitted, a default Flyout component will be created with props
* described above.
*
* @example flyoutComponent={<Flyout x={50} y={50}/>}, flyoutComponent={<MyCustomFlyout/>}
*/
flyoutComponent?: React.ReactElement<any>;
/**
* The flyoutHeight prop defines the height of the tooltip flyout. This prop may be given as a positive number or a function
* of datum. If this prop is not set, height will be determined based on an approximate text size calculated from the
* text and style props provided to ChartTooltip.
*
* @propType number | Function
*/
flyoutHeight?: NumberOrCallback;
/**
* The style prop applies SVG style properties to the rendered flyout container. These props will be passed to the
* flyoutComponent.
*
* @propType number | Function
*/
flyoutStyle?: VictoryStyleObject;
/**
* The flyoutWidth prop defines the width of the tooltip flyout. This prop may be given as a positive number or a
* function of datum. If this prop is not set, flyoutWidth will be determined based on an approximate text size
* calculated from the text and style props provided to VictoryTooltip.
*
* @propType number | Function
*/
flyoutWidth?: NumberOrCallback;
/**
* The groupComponent prop takes a component instance which will be used to create group elements for use within
* container elements. This prop defaults to a <g> tag.}
*/
groupComponent?: React.ReactElement<any>;
/**
* This prop refers to the height of the svg that ChartLegendTooltip is rendered within. This prop is passed from
* parents of ChartLegendTooltip, and should not be set manually. In versions before ^33.0.0 this prop referred to
* the height of the tooltip flyout. Please use flyoutHeight instead
*
* Note: This prop should not be set manually.
*
* @private
* @hide
*/
height?: number;
/**
* The horizontal prop determines whether to plot the flyouts to the left / right of the (x, y) coordinate rather than top / bottom.
* This is useful when an orientation prop is not provided, and data will determine the default orientation. i.e.
* negative values result in a left orientation and positive values will result in a right orientation by default.
*/
horizontal?: boolean;
/**
* The ChartLegendTooltip is based on ChartCursorTooltip, which is intended to be used with a voronoi cursor
* container. When isCursorTooltip is true (default), ChartCursorTooltip is used. If false, ChartTooltip is used.
*/
isCursorTooltip?: boolean;
/**
* The index prop represents the index of the datum in the data array.
*/
index?: number | string;
/**
* The labelComponent prop takes a component instance which will be used to render each tooltip label. The new element
* created from the passed labelComponent will be supplied with the following properties: x, y, index, datum,
* verticalAnchor, textAnchor, style, text, and events. Any of these props may be overridden by passing in props to
* the supplied component, or modified or ignored within the custom component itself. If labelComponent is omitted, a
* new ChartLabel will be created with the props described above.
*
* @example labelComponent={<ChartLabel dy={20}/>}, labelComponent={<MyCustomLabel/>}
*/
labelComponent?: React.ReactElement<any>;
/**
* Defines how the labelComponent text is horizontally positioned relative to its `x` and `y` coordinates. Valid
* values are 'start', 'middle', 'end', and 'inherit'.
*
* @propType string | Function
*/
labelTextAnchor?: TextAnchorType | (() => TextAnchorType);
/**
* Specify data via the data prop. ChartLegend expects data as an array of objects with name (required), symbol, and
* labels properties. The childName is used to sync the data series associated with the given chart child name.
*
* The data prop must be given as an array.
*
* @example
*
* legendData={[{ name: `GBps capacity - 45%` }, { name: 'Unused' }]}
* legendData={[{ childName: `cats`, name: `Total cats` }, { childName: `dogs`, name: 'Total dogs' }]}
*/
legendData?: {
childName?: string;
name?: string;
labels?: {
fill?: string;
};
symbol?: {
fill?: string;
type?: string;
};
}[];
/**
* The orientation prop determines which side of the (x, y) coordinate the tooltip should be rendered on.
* This prop can be given as “top”, “bottom”, “left”, “right”, or as a function of datum that returns one of these
* values. If this prop is not provided it will be determined from the sign of the datum, and the value of the
* horizontal prop.
*
* @propType string | Function
*/
orientation?: OrientationOrCallback;
/**
* The patternScale prop is an optional prop that defines patterns to apply, where applicable. This prop should be
* given as a string array of pattern URLs. Patterns will be assigned to children by index and will repeat when there
* are more children than patterns in the provided patternScale. Use null to omit the pattern for a given index.
*
* Note: Not all components are supported; for example, ChartLine, ChartBullet, ChartThreshold, etc.
*
* @example patternScale={[ 'url("#pattern1")', 'url("#pattern2")', null ]}
*/
patternScale?: string[];
/**
* The pointerLength prop determines the length of the triangular pointer extending from the flyout. This prop may be
* given as a positive number or a function of datum.
*
* @propType number | Function
*/
pointerLength?: NumberOrCallback;
/**
* This prop determines which side of the tooltip flyout the pointer should originate on. When this prop is not set,
* it will be determined based on the overall orientation of the flyout in relation to its data point, and any center
* or centerOffset values. Valid values are 'top', 'bottom', 'left' and 'right.
*
* @propType string | Function
*/
pointerOrientation?: OrientationOrCallback;
/**
* The pointerWidth prop determines the width of the base of the triangular pointer extending from
* the flyout. This prop may be given as a positive number or a function of datum.
*
* @propType number | Function
*/
pointerWidth?: NumberOrCallback;
/**
* When renderInPortal is true, rendered tooltips will be wrapped in VictoryPortal and rendered within the Portal element
* within ChartContainer. Note: This prop should not be set to true when using a custom container element.
*/
renderInPortal?: boolean;
/**
* The style prop applies CSS properties to the rendered `<text>` element.
*/
style?: React.CSSProperties | React.CSSProperties[];
/**
* The text prop defines the text ChartTooltip will render. The text prop may be given as a string, number, or
* function of datum. When ChartLabel is used as the labelComponent, strings may include newline characters, which
* ChartLabel will split in to separate <tspan/> elements.
*
* @propType number | string | Function | string[] | number[]
*/
text?: string[] | StringOrNumberOrCallback;
/**
* The theme prop specifies a theme to use for determining styles and layout properties for a component. Any styles or
* props defined in theme may be overwritten by props specified on the component instance.
*
* Note: Theme may be overridden when flyout is rendered
*
* @propType object
*/
theme?: ChartThemeDefinition;
/**
* Specifies the theme color. Valid values are 'blue', 'green', 'multi', etc.
*
* Note: Not compatible with theme prop
*
* @example themeColor={ChartThemeColor.blue}
*/
themeColor?: string;
/**
* The title prop specifies a title to render with the legend.
*
* @propType number | string | Function | string[]
* @example title={(datum) => datum.x}
*/
title?: string[] | StringOrNumberOrCallback;
/**
* This prop refers to the width of the svg that ChartLegendTooltip is rendered within. This prop is passed from
* parents of ChartLegendTooltip, and should not be set manually. In versions before ^33.0.0 this prop referred to the
* width of the tooltip flyout. Please use flyoutWidth instead
*
* Note: This prop should not be set manually.
*
* @private
* @hide
*/
width?: number;
/**
* The x prop defines the x coordinate to use as a basis for horizontal positioning.
*/
x?: number;
/**
* The y prop defines the y coordinate to use as a basis for vertical positioning.
*/
y?: number;
}
interface FlyoutProps {
height: number; // legend height
width: number; // legend width
}
export const ChartLegendTooltip: React.FunctionComponent<ChartLegendTooltipProps> = ({
activePoints,
center = { x: 0, y: 0 },
datum,
flyoutHeight,
flyoutWidth,
height,
isCursorTooltip = true,
labelComponent = <ChartLegendTooltipContent />,
legendData,
patternScale,
text,
themeColor,
title,
width,
// destructure last
theme = getTheme(themeColor),
...rest
}: ChartLegendTooltipProps) => {
const pointerLength = theme && theme.tooltip ? Helpers.evaluateProp(theme.tooltip.pointerLength) : 10;
const legendTooltipProps = () => ({
legendData: getLegendTooltipVisibleData({ activePoints, legendData, text, theme }),
legendProps: getLegendTooltipDataProps(
labelComponent.props.legendComponent ? labelComponent.props.legendComponent.props : undefined
),
text: getLegendTooltipVisibleText({ activePoints, legendData, text }),
theme
});
// Returns flyout height based on legend size
const getFlyoutHeight = ({ height }: FlyoutProps) => {
const _flyoutHeight = height + ChartLegendTooltipStyles.flyout.padding;
return title ? _flyoutHeight : _flyoutHeight - 10;
};
// Returns flyout width based on legend size
const getFlyoutWidth = ({ width }: FlyoutProps) => width + ChartLegendTooltipStyles.flyout.padding;
// Returns the tooltip content component
const getTooltipContentComponent = (props: FlyoutProps) =>
React.cloneElement(labelComponent, {
center,
flyoutHeight: flyoutHeight || getFlyoutHeight(props),
flyoutWidth: flyoutWidth || getFlyoutWidth(props),
height,
legendData,
patternScale,
title,
width,
...labelComponent.props
});
// Returns the tooltip component
const getTooltipComponent = () => {
// There must be at least one active, visible item or else this will return zero for height & width.
const legendSize = getLegendTooltipSize(legendTooltipProps());
if (legendSize.height === 0 && legendSize.width === 0) {
return null;
}
const _flyoutWidth = getFlyoutWidth(legendSize);
const tooltipComponent = isCursorTooltip ? <ChartCursorTooltip /> : <ChartTooltip />;
return React.cloneElement(tooltipComponent, {
activePoints,
center,
datum,
flyoutHeight: flyoutHeight || getFlyoutHeight(legendSize),
flyoutWidth: flyoutWidth || getFlyoutWidth(legendSize),
height,
labelComponent: getTooltipContentComponent(legendSize),
...(flyoutWidth === undefined && {
showPointer: width > _flyoutWidth + center.x + pointerLength || center.x > _flyoutWidth + pointerLength
}),
text,
theme,
width,
...rest
});
};
return getTooltipComponent();
};
ChartLegendTooltip.displayName = 'ChartLegendTooltip';
// Note: VictoryTooltip.defaultEvents must be hoisted
hoistNonReactStatics(ChartLegendTooltip, VictoryTooltip);