@tradingaction/tooltip 2.1.0 → 2.1.2

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.
@@ -0,0 +1,64 @@
1
+ import * as React from "react";
2
+ export interface IndicatorToolbarIcon {
3
+ readonly id: "eye" | "settings" | "code" | "delete" | "more";
4
+ readonly unicode: string;
5
+ readonly onClick: (option: IIndicatorDisplay) => void;
6
+ readonly size?: number;
7
+ readonly fillColor?: string;
8
+ readonly title?: string;
9
+ }
10
+ export interface MultiValueAccessor {
11
+ readonly label: string;
12
+ readonly accessor: (data: any) => number;
13
+ readonly fill?: string;
14
+ readonly showLabel?: boolean;
15
+ }
16
+ export interface IIndicatorDisplay {
17
+ readonly data?: any;
18
+ readonly yAccessor?: (data: any) => number;
19
+ readonly yAccessors?: MultiValueAccessor[];
20
+ readonly type: string;
21
+ readonly stroke: string;
22
+ readonly windowSize?: number;
23
+ readonly displayName?: string;
24
+ readonly icons?: IndicatorToolbarIcon[];
25
+ readonly orientation?: "vertical" | "horizontal";
26
+ readonly fillColor?: string;
27
+ readonly labelFill?: string;
28
+ readonly textFill?: string;
29
+ readonly fontFamily?: string;
30
+ readonly fontSize?: number;
31
+ readonly fontWeight?: number;
32
+ readonly showAccessorLabels?: boolean;
33
+ readonly alwaysShowFill?: boolean;
34
+ }
35
+ export interface IndicatorDisplayTooltipProps {
36
+ readonly className?: string;
37
+ readonly displayFormat: (value: number) => string;
38
+ readonly origin: number[];
39
+ readonly displayInit?: string;
40
+ readonly displayValuesFor?: (props: IndicatorDisplayTooltipProps, moreProps: any) => any;
41
+ readonly onClick?: (event: React.MouseEvent<SVGGElement, MouseEvent>) => void;
42
+ readonly onIconClick?: (event: React.MouseEvent<SVGTSpanElement, MouseEvent>, icon: IndicatorToolbarIcon) => void;
43
+ readonly textFill?: string;
44
+ readonly labelFill?: string;
45
+ readonly fontFamily?: string;
46
+ readonly fontSize?: number;
47
+ readonly fontWeight?: number;
48
+ readonly width?: number;
49
+ readonly options: IIndicatorDisplay[];
50
+ }
51
+ export declare class IndicatorDisplayTooltip extends React.Component<IndicatorDisplayTooltipProps> {
52
+ static defaultProps: {
53
+ className: string;
54
+ displayFormat: (n: number | {
55
+ valueOf(): number;
56
+ }) => string;
57
+ displayInit: string;
58
+ displayValuesFor: (_: any, props: any) => any;
59
+ origin: number[];
60
+ width: number;
61
+ };
62
+ render(): React.JSX.Element;
63
+ private readonly renderSVG;
64
+ }
@@ -0,0 +1,209 @@
1
+ import { functor, GenericChartComponent, last } from "@tradingaction/core";
2
+ import { format } from "d3-format";
3
+ import * as React from "react";
4
+ import { ToolTipText } from "./ToolTipText";
5
+ class SingleIndicatorDisplay extends React.Component {
6
+ constructor(props) {
7
+ super(props);
8
+ this.gRef = React.createRef();
9
+ this.handleMouseEnter = () => {
10
+ this.setState({ isHovered: true });
11
+ };
12
+ this.handleMouseLeave = () => {
13
+ this.setState({ isHovered: false });
14
+ };
15
+ this.handleIconClick = (e, icon) => {
16
+ e.stopPropagation();
17
+ if (this.props.onIconClick) {
18
+ this.props.onIconClick(e, icon);
19
+ }
20
+ if (typeof icon.onClick === "function") {
21
+ icon.onClick(this.props.options);
22
+ }
23
+ };
24
+ this.onClick = (event) => {
25
+ const { onClick, forChart, options } = this.props;
26
+ if (onClick !== undefined) {
27
+ onClick(event, Object.assign({ chartId: forChart }, options));
28
+ }
29
+ };
30
+ this.state = {
31
+ isHovered: false,
32
+ };
33
+ }
34
+ render() {
35
+ var _a, _b;
36
+ const { color, displayName, fontSize, fontFamily, fontWeight, textFill, labelFill, value, multiValues, origin, options, } = this.props;
37
+ const { isHovered } = this.state;
38
+ const translate = `translate(${origin[0]}, ${origin[1]})`;
39
+ const indicators = options.icons || [];
40
+ const orientation = options.orientation || "vertical";
41
+ // Use option-specific colors and fonts if provided, otherwise fall back to props
42
+ const effectiveLabelFill = options.labelFill || labelFill;
43
+ const effectiveTextFill = options.textFill || textFill;
44
+ const effectiveFontFamily = options.fontFamily || fontFamily;
45
+ const effectiveFontSize = (_a = options.fontSize) !== null && _a !== void 0 ? _a : fontSize;
46
+ const effectiveFontWeight = (_b = options.fontWeight) !== null && _b !== void 0 ? _b : fontWeight;
47
+ // Calculate dimensions based on orientation
48
+ let baseWidth = 200;
49
+ let baseHeight = 32;
50
+ let expandedWidth = isHovered ? 280 : 200;
51
+ let horizontalContentWidth = 0;
52
+ let displayNameWidth = 0;
53
+ if (multiValues && multiValues.length > 0) {
54
+ if (orientation === "vertical") {
55
+ // Vertical: height increases, width stays same
56
+ baseHeight = 32 + multiValues.length * 16;
57
+ expandedWidth = isHovered ? 280 : 200;
58
+ }
59
+ else {
60
+ // Horizontal: calculate actual width based on visible labels and display name
61
+ const fontSize = effectiveFontSize || 12;
62
+ const charWidth = fontSize * 0.55; // Approximate character width for monospace/sans-serif
63
+ // Calculate display name width
64
+ displayNameWidth = displayName.length * charWidth + 10; // 10px after name
65
+ // Calculate values width
66
+ horizontalContentWidth = 0;
67
+ multiValues.forEach((multiValue, idx) => {
68
+ const showLabel = multiValue.showLabel !== undefined ? multiValue.showLabel : (options.showAccessorLabels === true);
69
+ if (showLabel) {
70
+ // Label width: "Label: "
71
+ const labelText = `${multiValue.label}: `;
72
+ horizontalContentWidth += labelText.length * charWidth;
73
+ }
74
+ // Value width
75
+ const valueWidth = multiValue.value.length * charWidth;
76
+ horizontalContentWidth += valueWidth;
77
+ // Margin between values
78
+ if (idx < multiValues.length - 1) {
79
+ horizontalContentWidth += 8; // 8px margin between values
80
+ }
81
+ });
82
+ baseWidth = 20 + displayNameWidth + horizontalContentWidth; // 20px left padding + name + values
83
+ expandedWidth = isHovered ? baseWidth + 100 : baseWidth;
84
+ baseHeight = 25; // Reduced from 32 for tighter horizontal layout
85
+ }
86
+ }
87
+ return (React.createElement("g", { ref: this.gRef, transform: translate, onClick: this.onClick, onMouseEnter: this.handleMouseEnter, onMouseLeave: this.handleMouseLeave, style: { cursor: "pointer" } },
88
+ React.createElement("rect", { x: 0, y: 0, width: expandedWidth, height: baseHeight, fill: options.alwaysShowFill ? (options.fillColor || "#dbdfe4") : (isHovered ? (options.fillColor || "#dbdfe4") : "transparent"), stroke: color, strokeWidth: 1, rx: 4, style: { transition: "width 0.2s ease" } }),
89
+ React.createElement("rect", { x: 0, y: 0, width: 3, height: baseHeight, fill: color, rx: 2 }),
90
+ (!multiValues || multiValues.length === 0) && (React.createElement(ToolTipText, { x: 10, y: 18, fontFamily: effectiveFontFamily, fontSize: effectiveFontSize, fontWeight: effectiveFontWeight },
91
+ React.createElement("tspan", { fill: effectiveLabelFill, fontWeight: effectiveFontWeight, fontSize: effectiveFontSize || 12 }, displayName),
92
+ React.createElement("tspan", { x: 10, dy: 0, fill: effectiveTextFill, fontSize: effectiveFontSize || 12 }, "\u00A0"),
93
+ React.createElement("tspan", { fill: effectiveTextFill, fontSize: effectiveFontSize || 12 }, value))),
94
+ multiValues && multiValues.length > 0 && (React.createElement("g", null,
95
+ orientation === "horizontal" && (React.createElement("text", { x: 10, y: 17, fontFamily: effectiveFontFamily, fontSize: effectiveFontSize || 12, fill: effectiveLabelFill, fontWeight: effectiveFontWeight }, displayName)),
96
+ orientation === "vertical" ? (
97
+ // Vertical layout - display name + colon before values
98
+ React.createElement(React.Fragment, null,
99
+ React.createElement("text", { x: 10, y: 17, fontFamily: effectiveFontFamily, fontSize: effectiveFontSize || 12, fill: effectiveLabelFill, fontWeight: effectiveFontWeight }, displayName),
100
+ multiValues.map((multiValue, idx) => {
101
+ const showLabel = multiValue.showLabel !== undefined ? multiValue.showLabel : (options.showAccessorLabels === true);
102
+ return (React.createElement("text", { key: `multi-${idx}`, x: 10, y: 17 + (idx + 1) * 16, fontFamily: effectiveFontFamily, fontSize: effectiveFontSize || 12, fill: multiValue.fill || effectiveTextFill },
103
+ showLabel && (React.createElement("tspan", { fontWeight: effectiveFontWeight, fill: effectiveLabelFill },
104
+ multiValue.label,
105
+ ":\u00A0")),
106
+ React.createElement("tspan", { fill: multiValue.fill || effectiveTextFill }, multiValue.value)));
107
+ }))) : (
108
+ // Horizontal layout - position based on actual content width
109
+ (() => {
110
+ const fontSize = effectiveFontSize || 12;
111
+ const charWidth = fontSize * 0.55;
112
+ const calcDisplayNameWidth = displayName.length * charWidth + 5;
113
+ let cumulativeX = 0 + calcDisplayNameWidth; // Start after display name
114
+ return multiValues.map((multiValue, idx) => {
115
+ const showLabel = multiValue.showLabel !== undefined ? multiValue.showLabel : (options.showAccessorLabels === true);
116
+ const x = cumulativeX;
117
+ // Calculate width for next position
118
+ if (showLabel) {
119
+ const labelText = `${multiValue.label}: `;
120
+ cumulativeX += labelText.length * charWidth;
121
+ }
122
+ cumulativeX += multiValue.value.length * charWidth;
123
+ // Add margin between values
124
+ if (idx < multiValues.length - 1) {
125
+ cumulativeX += 8;
126
+ }
127
+ return (React.createElement("text", { key: `multi-${idx}`, x: x, y: 17, fontFamily: effectiveFontFamily, fontSize: effectiveFontSize || 12, fill: multiValue.fill || effectiveTextFill },
128
+ showLabel && (React.createElement("tspan", { fontWeight: effectiveFontWeight, fill: effectiveLabelFill, fontSize: (effectiveFontSize || 12) - 1 },
129
+ multiValue.label,
130
+ ":\u00A0")),
131
+ React.createElement("tspan", { fill: multiValue.fill || effectiveTextFill }, multiValue.value)));
132
+ });
133
+ })()))),
134
+ isHovered && indicators.length > 0 && (React.createElement("g", null, indicators.map((icon, idx) => {
135
+ let iconX;
136
+ if (orientation === "horizontal" && multiValues && multiValues.length > 0) {
137
+ // Position after calculated content width with spacing
138
+ iconX = 20 + displayNameWidth + horizontalContentWidth + 10 + idx * 16;
139
+ }
140
+ else if (multiValues && multiValues.length === 0 && !value) {
141
+ // Single label only (no value/multiValues) - position close to label
142
+ const fontSize = effectiveFontSize || 12;
143
+ const charWidth = fontSize * 0.55;
144
+ iconX = 10 + displayName.length * charWidth + 10 + idx * 16;
145
+ }
146
+ else {
147
+ // Position for vertical or single value with data
148
+ iconX = 210 + idx * 16;
149
+ }
150
+ return (React.createElement("g", { key: icon.id, onClick: (e) => this.handleIconClick(e, icon) },
151
+ icon.title && React.createElement("title", null, icon.title),
152
+ React.createElement("text", { x: iconX, y: 17, fontSize: icon.size || 14, fill: icon.fillColor || "#9ca3af", style: {
153
+ cursor: "pointer",
154
+ userSelect: "none",
155
+ transition: "fill 0.2s ease",
156
+ pointerEvents: "auto",
157
+ }, onMouseEnter: (e) => (e.target.style.fill = "#fff"), onMouseLeave: (e) => (e.target.style.fill = icon.fillColor || "#9ca3af") }, icon.unicode)));
158
+ })))));
159
+ }
160
+ }
161
+ // tslint:disable-next-line: max-classes-per-file
162
+ export class IndicatorDisplayTooltip extends React.Component {
163
+ constructor() {
164
+ super(...arguments);
165
+ this.renderSVG = (moreProps) => {
166
+ var _a;
167
+ const { chartId, chartConfig, chartConfig: { height = 0 } = {}, fullData } = moreProps;
168
+ const { className, displayInit = IndicatorDisplayTooltip.defaultProps.displayInit, onClick, onIconClick, width = 65, fontFamily, fontSize, fontWeight, textFill, labelFill, origin: originProp, displayFormat, displayValuesFor = IndicatorDisplayTooltip.defaultProps.displayValuesFor, options, } = this.props;
169
+ const currentItem = (_a = displayValuesFor(this.props, moreProps)) !== null && _a !== void 0 ? _a : last(fullData);
170
+ const config = chartConfig;
171
+ const origin = functor(originProp);
172
+ const [x, y] = origin(width, height);
173
+ const [ox, oy] = config.origin;
174
+ return (React.createElement("g", { transform: `translate(${ox + x}, ${oy + y})`, className: className }, options.map((each, idx) => {
175
+ let yValue;
176
+ const multiValues = [];
177
+ // Use single yAccessor if available
178
+ if (each.yAccessor && currentItem) {
179
+ yValue = each.yAccessor(currentItem);
180
+ }
181
+ // Calculate multiple yAccessor values if available
182
+ if (each.yAccessors && currentItem) {
183
+ multiValues.push(...each.yAccessors.map((accessor) => ({
184
+ label: accessor.label,
185
+ value: accessor.accessor(currentItem) ? displayFormat(accessor.accessor(currentItem)) : displayInit,
186
+ fill: accessor.fill,
187
+ showLabel: accessor.showLabel,
188
+ })));
189
+ }
190
+ const tooltipLabel = each.displayName || `${each.type} (${each.windowSize || "n/a"})`;
191
+ // Only show value if yAccessor exists, otherwise pass empty string
192
+ const yDisplayValue = yValue !== undefined ? displayFormat(yValue) : "";
193
+ return (React.createElement(SingleIndicatorDisplay, { key: idx, origin: [idx * 40, idx * 40], color: each.stroke, displayName: tooltipLabel, value: yDisplayValue, multiValues: multiValues, options: each, forChart: chartId, onClick: onClick, onIconClick: onIconClick, fontFamily: fontFamily, fontSize: fontSize, fontWeight: fontWeight, textFill: textFill, labelFill: labelFill }));
194
+ })));
195
+ };
196
+ }
197
+ render() {
198
+ return React.createElement(GenericChartComponent, { clip: false, svgDraw: this.renderSVG, drawOn: ["mousemove"] });
199
+ }
200
+ }
201
+ IndicatorDisplayTooltip.defaultProps = {
202
+ className: "react-financial-charts-tooltip react-financial-charts-indicator-display-tooltip",
203
+ displayFormat: format(".2f"),
204
+ displayInit: "n/a",
205
+ displayValuesFor: (_, props) => props.currentItem,
206
+ origin: [0, 10],
207
+ width: 65,
208
+ };
209
+ //# sourceMappingURL=IndicatorDisplayTooltip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IndicatorDisplayTooltip.js","sourceRoot":"","sources":["../src/IndicatorDisplayTooltip.tsx"],"names":[],"mappings":"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"}
package/lib/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from "./BollingerBandTooltip";
2
2
  export * from "./GroupTooltip";
3
3
  export * from "./HoverTooltip";
4
+ export * from "./IndicatorDisplayTooltip";
4
5
  export * from "./MACDTooltip";
5
6
  export * from "./MovingAverageTooltip";
6
7
  export * from "./OHLCTooltip";
package/lib/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from "./BollingerBandTooltip";
2
2
  export * from "./GroupTooltip";
3
3
  export * from "./HoverTooltip";
4
+ export * from "./IndicatorDisplayTooltip";
4
5
  export * from "./MACDTooltip";
5
6
  export * from "./MovingAverageTooltip";
6
7
  export * from "./OHLCTooltip";
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC;AACvC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,wBAAwB,CAAC;AACvC,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC;AACvC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,eAAe,CAAC;AAC9B,cAAc,wBAAwB,CAAC;AACvC,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tradingaction/tooltip",
3
- "version": "2.1.0",
3
+ "version": "2.1.2",
4
4
  "description": "Tooltips for react-financial-charts",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -38,7 +38,7 @@
38
38
  "watch": "tsc -p tsconfig.json --watch --preserveWatchOutput"
39
39
  },
40
40
  "dependencies": {
41
- "@tradingaction/core": "^2.1.0",
41
+ "@tradingaction/core": "^2.1.1",
42
42
  "d3-array": "^2.9.1",
43
43
  "d3-format": "^2.0.0",
44
44
  "d3-time-format": "^3.0.0"
@@ -47,5 +47,5 @@
47
47
  "react": "^16.0.0 || ^17.0.0 || ^18.0.0",
48
48
  "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0"
49
49
  },
50
- "gitHead": "740ed6b582de489a978207c4016f14b51337a076"
50
+ "gitHead": "7491d9931380aafbdf99dfbdb06e1e2705c99fb9"
51
51
  }
@@ -0,0 +1,466 @@
1
+ import { functor, GenericChartComponent, last, MoreProps } from "@tradingaction/core";
2
+ import { format } from "d3-format";
3
+ import * as React from "react";
4
+ import { ToolTipText } from "./ToolTipText";
5
+
6
+ export interface IndicatorToolbarIcon {
7
+ readonly id: "eye" | "settings" | "code" | "delete" | "more";
8
+ readonly unicode: string;
9
+ readonly onClick: (option: IIndicatorDisplay) => void;
10
+ readonly size?: number;
11
+ readonly fillColor?: string;
12
+ readonly title?: string;
13
+ }
14
+
15
+ export interface MultiValueAccessor {
16
+ readonly label: string;
17
+ readonly accessor: (data: any) => number;
18
+ readonly fill?: string;
19
+ readonly showLabel?: boolean;
20
+ }
21
+
22
+ export interface IIndicatorDisplay {
23
+ readonly data?: any;
24
+ readonly yAccessor?: (data: any) => number;
25
+ readonly yAccessors?: MultiValueAccessor[];
26
+ readonly type: string;
27
+ readonly stroke: string;
28
+ readonly windowSize?: number;
29
+ readonly displayName?: string;
30
+ readonly icons?: IndicatorToolbarIcon[];
31
+ readonly orientation?: "vertical" | "horizontal";
32
+ readonly fillColor?: string;
33
+ readonly labelFill?: string;
34
+ readonly textFill?: string;
35
+ readonly fontFamily?: string;
36
+ readonly fontSize?: number;
37
+ readonly fontWeight?: number;
38
+ readonly showAccessorLabels?: boolean;
39
+ readonly alwaysShowFill?: boolean; // If true, always show background fill; otherwise only show on hover
40
+ }
41
+
42
+ interface SingleIndicatorDisplayProps {
43
+ readonly color: string;
44
+ readonly displayName: string;
45
+ readonly fontFamily?: string;
46
+ readonly fontSize?: number;
47
+ readonly fontWeight?: number;
48
+ readonly forChart: number | string;
49
+ readonly labelFill?: string;
50
+ readonly labelFontWeight?: number;
51
+ readonly textFill?: string;
52
+ readonly value: string;
53
+ readonly multiValues?: Array<{ label: string; value: string; fill?: string; showLabel?: boolean }>;
54
+ readonly options: IIndicatorDisplay;
55
+ readonly origin: [number, number];
56
+ readonly onClick?: (event: React.MouseEvent<SVGGElement, MouseEvent>, details: any) => void;
57
+ readonly onIconClick?: (event: React.MouseEvent<SVGTSpanElement, MouseEvent>, icon: IndicatorToolbarIcon) => void;
58
+ }
59
+
60
+ interface SingleIndicatorDisplayState {
61
+ readonly isHovered: boolean;
62
+ }
63
+
64
+ class SingleIndicatorDisplay extends React.Component<SingleIndicatorDisplayProps, SingleIndicatorDisplayState> {
65
+ private gRef = React.createRef<SVGGElement>();
66
+
67
+ constructor(props: SingleIndicatorDisplayProps) {
68
+ super(props);
69
+ this.state = {
70
+ isHovered: false,
71
+ };
72
+ }
73
+
74
+ private handleMouseEnter = () => {
75
+ this.setState({ isHovered: true });
76
+ };
77
+
78
+ private handleMouseLeave = () => {
79
+ this.setState({ isHovered: false });
80
+ };
81
+
82
+ private handleIconClick = (e: React.MouseEvent<SVGTSpanElement, MouseEvent>, icon: IndicatorToolbarIcon) => {
83
+ e.stopPropagation();
84
+ if (this.props.onIconClick) {
85
+ this.props.onIconClick(e, icon);
86
+ }
87
+ if (typeof icon.onClick === "function") {
88
+ icon.onClick(this.props.options);
89
+ }
90
+ };
91
+
92
+ public render() {
93
+ const {
94
+ color,
95
+ displayName,
96
+ fontSize,
97
+ fontFamily,
98
+ fontWeight,
99
+ textFill,
100
+ labelFill,
101
+ value,
102
+ multiValues,
103
+ origin,
104
+ options,
105
+ } = this.props;
106
+ const { isHovered } = this.state;
107
+
108
+ const translate = `translate(${origin[0]}, ${origin[1]})`;
109
+ const indicators = options.icons || [];
110
+ const orientation = options.orientation || "vertical";
111
+
112
+ // Use option-specific colors and fonts if provided, otherwise fall back to props
113
+ const effectiveLabelFill = options.labelFill || labelFill;
114
+ const effectiveTextFill = options.textFill || textFill;
115
+ const effectiveFontFamily = options.fontFamily || fontFamily;
116
+ const effectiveFontSize = options.fontSize ?? fontSize;
117
+ const effectiveFontWeight = options.fontWeight ?? fontWeight;
118
+
119
+ // Calculate dimensions based on orientation
120
+ let baseWidth = 200;
121
+ let baseHeight = 32;
122
+ let expandedWidth = isHovered ? 280 : 200;
123
+ let horizontalContentWidth = 0;
124
+ let displayNameWidth = 0;
125
+
126
+ if (multiValues && multiValues.length > 0) {
127
+ if (orientation === "vertical") {
128
+ // Vertical: height increases, width stays same
129
+ baseHeight = 32 + multiValues.length * 16;
130
+ expandedWidth = isHovered ? 280 : 200;
131
+ } else {
132
+ // Horizontal: calculate actual width based on visible labels and display name
133
+ const fontSize = effectiveFontSize || 12;
134
+ const charWidth = fontSize * 0.55; // Approximate character width for monospace/sans-serif
135
+
136
+ // Calculate display name width
137
+ displayNameWidth = displayName.length * charWidth + 10; // 10px after name
138
+
139
+ // Calculate values width
140
+ horizontalContentWidth = 0;
141
+ multiValues.forEach((multiValue, idx) => {
142
+ const showLabel = multiValue.showLabel !== undefined ? multiValue.showLabel : (options.showAccessorLabels === true);
143
+
144
+ if (showLabel) {
145
+ // Label width: "Label: "
146
+ const labelText = `${multiValue.label}: `;
147
+ horizontalContentWidth += labelText.length * charWidth;
148
+ }
149
+
150
+ // Value width
151
+ const valueWidth = multiValue.value.length * charWidth;
152
+ horizontalContentWidth += valueWidth;
153
+
154
+ // Margin between values
155
+ if (idx < multiValues.length - 1) {
156
+ horizontalContentWidth += 8; // 8px margin between values
157
+ }
158
+ });
159
+
160
+ baseWidth = 20 + displayNameWidth + horizontalContentWidth; // 20px left padding + name + values
161
+ expandedWidth = isHovered ? baseWidth + 100 : baseWidth;
162
+ baseHeight = 25; // Reduced from 32 for tighter horizontal layout
163
+ }
164
+ }
165
+
166
+ return (
167
+ <g
168
+ ref={this.gRef}
169
+ transform={translate}
170
+ onClick={this.onClick}
171
+ onMouseEnter={this.handleMouseEnter}
172
+ onMouseLeave={this.handleMouseLeave}
173
+ style={{ cursor: "pointer" }}
174
+ >
175
+ {/* Indicator background */}
176
+ <rect
177
+ x={0}
178
+ y={0}
179
+ width={expandedWidth}
180
+ height={baseHeight}
181
+ fill={options.alwaysShowFill ? (options.fillColor || "#dbdfe4") : (isHovered ? (options.fillColor || "#dbdfe4") : "transparent")}
182
+ stroke={color}
183
+ strokeWidth={1}
184
+ rx={4}
185
+ style={{ transition: "width 0.2s ease" }}
186
+ />
187
+
188
+ {/* Colored left border */}
189
+ <rect x={0} y={0} width={3} height={baseHeight} fill={color} rx={2} />
190
+
191
+ {/* Main indicator name and value - only show when no multiValues */}
192
+ {(!multiValues || multiValues.length === 0) && (
193
+ <ToolTipText x={10} y={18} fontFamily={effectiveFontFamily} fontSize={effectiveFontSize} fontWeight={effectiveFontWeight}>
194
+ <tspan fill={effectiveLabelFill} fontWeight={effectiveFontWeight} fontSize={effectiveFontSize || 12}>
195
+ {displayName}
196
+ </tspan>
197
+ <tspan x={10} dy={0} fill={effectiveTextFill} fontSize={effectiveFontSize || 12}>
198
+ &nbsp;
199
+ </tspan>
200
+ <tspan fill={effectiveTextFill} fontSize={effectiveFontSize || 12}>
201
+ {value}
202
+ </tspan>
203
+ </ToolTipText>
204
+ )}
205
+
206
+ {/* Multiple value accessors */}
207
+ {multiValues && multiValues.length > 0 && (
208
+ <g>
209
+ {/* Display name for horizontal layout */}
210
+ {orientation === "horizontal" && (
211
+ <text
212
+ x={10}
213
+ y={17}
214
+ fontFamily={effectiveFontFamily}
215
+ fontSize={effectiveFontSize || 12}
216
+ fill={effectiveLabelFill}
217
+ fontWeight={effectiveFontWeight}
218
+ >
219
+ {displayName}
220
+ </text>
221
+ )}
222
+
223
+ {orientation === "vertical" ? (
224
+ // Vertical layout - display name + colon before values
225
+ <>
226
+ <text
227
+ x={10}
228
+ y={17}
229
+ fontFamily={effectiveFontFamily}
230
+ fontSize={effectiveFontSize || 12}
231
+ fill={effectiveLabelFill}
232
+ fontWeight={effectiveFontWeight}
233
+ >
234
+ {displayName}
235
+ </text>
236
+ {multiValues.map((multiValue, idx) => {
237
+ const showLabel = multiValue.showLabel !== undefined ? multiValue.showLabel : (options.showAccessorLabels === true);
238
+ return (
239
+ <text
240
+ key={`multi-${idx}`}
241
+ x={10}
242
+ y={17 + (idx + 1) * 16}
243
+ fontFamily={effectiveFontFamily}
244
+ fontSize={effectiveFontSize || 12}
245
+ fill={multiValue.fill || effectiveTextFill}
246
+ >
247
+ {showLabel && (
248
+ <tspan fontWeight={effectiveFontWeight} fill={effectiveLabelFill}>
249
+ {multiValue.label}:&nbsp;
250
+ </tspan>
251
+ )}
252
+ <tspan fill={multiValue.fill || effectiveTextFill}>{multiValue.value}</tspan>
253
+ </text>
254
+ );
255
+ })}
256
+ </>
257
+ ) : (
258
+ // Horizontal layout - position based on actual content width
259
+ (() => {
260
+ const fontSize = effectiveFontSize || 12;
261
+ const charWidth = fontSize * 0.55;
262
+ const calcDisplayNameWidth = displayName.length * charWidth + 5;
263
+ let cumulativeX = 0 + calcDisplayNameWidth; // Start after display name
264
+
265
+ return multiValues.map((multiValue, idx) => {
266
+ const showLabel = multiValue.showLabel !== undefined ? multiValue.showLabel : (options.showAccessorLabels === true);
267
+ const x = cumulativeX;
268
+
269
+ // Calculate width for next position
270
+ if (showLabel) {
271
+ const labelText = `${multiValue.label}: `;
272
+ cumulativeX += labelText.length * charWidth;
273
+ }
274
+ cumulativeX += multiValue.value.length * charWidth;
275
+
276
+ // Add margin between values
277
+ if (idx < multiValues.length - 1) {
278
+ cumulativeX += 8;
279
+ }
280
+
281
+ return (
282
+ <text
283
+ key={`multi-${idx}`}
284
+ x={x}
285
+ y={17}
286
+ fontFamily={effectiveFontFamily}
287
+ fontSize={effectiveFontSize || 12}
288
+ fill={multiValue.fill || effectiveTextFill}
289
+ >
290
+ {showLabel && (
291
+ <tspan fontWeight={effectiveFontWeight} fill={effectiveLabelFill} fontSize={(effectiveFontSize || 12) - 1}>
292
+ {multiValue.label}:&nbsp;
293
+ </tspan>
294
+ )}
295
+ <tspan fill={multiValue.fill || effectiveTextFill}>{multiValue.value}</tspan>
296
+ </text>
297
+ );
298
+ });
299
+ })()
300
+ )}
301
+ </g>
302
+ )}
303
+
304
+ {/* Toolbar icons on hover */}
305
+ {isHovered && indicators.length > 0 && (
306
+ <g>
307
+ {indicators.map((icon, idx) => {
308
+ let iconX: number;
309
+ if (orientation === "horizontal" && multiValues && multiValues.length > 0) {
310
+ // Position after calculated content width with spacing
311
+ iconX = 20 + displayNameWidth + horizontalContentWidth + 10 + idx * 16;
312
+ } else if (multiValues && multiValues.length === 0 && !value) {
313
+ // Single label only (no value/multiValues) - position close to label
314
+ const fontSize = effectiveFontSize || 12;
315
+ const charWidth = fontSize * 0.55;
316
+ iconX = 10 + displayName.length * charWidth + 10 + idx * 16;
317
+ } else {
318
+ // Position for vertical or single value with data
319
+ iconX = 210 + idx * 16;
320
+ }
321
+ return (
322
+ <g key={icon.id} onClick={(e: any) => this.handleIconClick(e, icon)}>
323
+ {icon.title && <title>{icon.title}</title>}
324
+ <text
325
+ x={iconX}
326
+ y={17}
327
+ fontSize={icon.size || 14}
328
+ fill={icon.fillColor || "#9ca3af"}
329
+ style={{
330
+ cursor: "pointer",
331
+ userSelect: "none",
332
+ transition: "fill 0.2s ease",
333
+ pointerEvents: "auto",
334
+ }}
335
+ onMouseEnter={(e: any) => (e.target.style.fill = "#fff")}
336
+ onMouseLeave={(e: any) => (e.target.style.fill = icon.fillColor || "#9ca3af")}
337
+ >
338
+ {icon.unicode}
339
+ </text>
340
+ </g>
341
+ );
342
+ })}
343
+ </g>
344
+ )}
345
+ </g>
346
+ );
347
+ }
348
+
349
+ private readonly onClick = (event: React.MouseEvent<SVGGElement, MouseEvent>) => {
350
+ const { onClick, forChart, options } = this.props;
351
+ if (onClick !== undefined) {
352
+ onClick(event, { chartId: forChart, ...options });
353
+ }
354
+ };
355
+ }
356
+
357
+ export interface IndicatorDisplayTooltipProps {
358
+ readonly className?: string;
359
+ readonly displayFormat: (value: number) => string;
360
+ readonly origin: number[];
361
+ readonly displayInit?: string;
362
+ readonly displayValuesFor?: (props: IndicatorDisplayTooltipProps, moreProps: any) => any;
363
+ readonly onClick?: (event: React.MouseEvent<SVGGElement, MouseEvent>) => void;
364
+ readonly onIconClick?: (event: React.MouseEvent<SVGTSpanElement, MouseEvent>, icon: IndicatorToolbarIcon) => void;
365
+ readonly textFill?: string;
366
+ readonly labelFill?: string;
367
+ readonly fontFamily?: string;
368
+ readonly fontSize?: number;
369
+ readonly fontWeight?: number;
370
+ readonly width?: number;
371
+ readonly options: IIndicatorDisplay[];
372
+ }
373
+
374
+ // tslint:disable-next-line: max-classes-per-file
375
+ export class IndicatorDisplayTooltip extends React.Component<IndicatorDisplayTooltipProps> {
376
+ public static defaultProps = {
377
+ className: "react-financial-charts-tooltip react-financial-charts-indicator-display-tooltip",
378
+ displayFormat: format(".2f"),
379
+ displayInit: "n/a",
380
+ displayValuesFor: (_: any, props: any) => props.currentItem,
381
+ origin: [0, 10],
382
+ width: 65,
383
+ };
384
+
385
+ public render() {
386
+ return <GenericChartComponent clip={false} svgDraw={this.renderSVG} drawOn={["mousemove"]} />;
387
+ }
388
+
389
+ private readonly renderSVG = (moreProps: MoreProps) => {
390
+ const { chartId, chartConfig, chartConfig: { height = 0 } = {}, fullData } = moreProps;
391
+
392
+ const {
393
+ className,
394
+ displayInit = IndicatorDisplayTooltip.defaultProps.displayInit,
395
+ onClick,
396
+ onIconClick,
397
+ width = 65,
398
+ fontFamily,
399
+ fontSize,
400
+ fontWeight,
401
+ textFill,
402
+ labelFill,
403
+ origin: originProp,
404
+ displayFormat,
405
+ displayValuesFor = IndicatorDisplayTooltip.defaultProps.displayValuesFor,
406
+ options,
407
+ } = this.props;
408
+
409
+ const currentItem = displayValuesFor(this.props, moreProps) ?? last(fullData);
410
+ const config = chartConfig!;
411
+
412
+ const origin = functor(originProp);
413
+ const [x, y] = origin(width, height);
414
+ const [ox, oy] = config.origin;
415
+
416
+ return (
417
+ <g transform={`translate(${ox + x}, ${oy + y})`} className={className}>
418
+ {options.map((each, idx) => {
419
+ let yValue: number | undefined;
420
+ const multiValues: Array<{ label: string; value: string; fill?: string }> = [];
421
+
422
+ // Use single yAccessor if available
423
+ if (each.yAccessor && currentItem) {
424
+ yValue = each.yAccessor(currentItem);
425
+ }
426
+
427
+ // Calculate multiple yAccessor values if available
428
+ if (each.yAccessors && currentItem) {
429
+ multiValues.push(
430
+ ...each.yAccessors.map((accessor) => ({
431
+ label: accessor.label,
432
+ value: accessor.accessor(currentItem) ? displayFormat(accessor.accessor(currentItem)) : displayInit,
433
+ fill: accessor.fill,
434
+ showLabel: accessor.showLabel,
435
+ })),
436
+ );
437
+ }
438
+
439
+ const tooltipLabel = each.displayName || `${each.type} (${each.windowSize || "n/a"})`;
440
+ // Only show value if yAccessor exists, otherwise pass empty string
441
+ const yDisplayValue = yValue !== undefined ? displayFormat(yValue) : "";
442
+
443
+ return (
444
+ <SingleIndicatorDisplay
445
+ key={idx}
446
+ origin={[idx * 40, idx * 40]}
447
+ color={each.stroke}
448
+ displayName={tooltipLabel}
449
+ value={yDisplayValue}
450
+ multiValues={multiValues}
451
+ options={each}
452
+ forChart={chartId}
453
+ onClick={onClick}
454
+ onIconClick={onIconClick}
455
+ fontFamily={fontFamily}
456
+ fontSize={fontSize}
457
+ fontWeight={fontWeight}
458
+ textFill={textFill}
459
+ labelFill={labelFill}
460
+ />
461
+ );
462
+ })}
463
+ </g>
464
+ );
465
+ };
466
+ }
package/src/index.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from "./BollingerBandTooltip";
2
2
  export * from "./GroupTooltip";
3
3
  export * from "./HoverTooltip";
4
+ export * from "./IndicatorDisplayTooltip";
4
5
  export * from "./MACDTooltip";
5
6
  export * from "./MovingAverageTooltip";
6
7
  export * from "./OHLCTooltip";