@oliasoft-open-source/charts-library 2.17.0-beta-3 → 2.17.1-beta-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/release-notes.md +7 -1
- package/src/components/line-chart/hooks/use-chart-options.js +26 -0
- package/src/components/line-chart/hooks/use-chart-plugins.js +11 -2
- package/src/components/line-chart/initialize/initialize-line-chart.js +3 -3
- package/src/components/line-chart/line-chart-prop-types.js +48 -3
- package/src/components/line-chart/line-chart.jsx +2 -1
- package/src/components/line-chart/plugins/chart-area-text-plugin.js +246 -0
- package/src/components/line-chart/plugins/plugin-constants.js +11 -0
- package/src/helpers/chart-interface.ts +9 -4
- package/src/helpers/chart-utils.js +1 -1
- package/src/helpers/get-chart-annotation.js +7 -44
- /package/src/components/line-chart/{line-chart.minor-gridlines-plugin.js → plugins/line-chart.minor-gridlines-plugin.js} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oliasoft-open-source/charts-library",
|
|
3
|
-
"version": "2.17.
|
|
3
|
+
"version": "2.17.1-beta-1",
|
|
4
4
|
"description": "React Chart Library (based on Chart.js and react-chart-js-2)",
|
|
5
5
|
"homepage": "https://gitlab.com/oliasoft-open-source/charts-library",
|
|
6
6
|
"bugs": {
|
package/release-notes.md
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
# Charts Library Release Notes
|
|
2
2
|
|
|
3
|
+
## 2.17.1
|
|
4
|
+
- Added debounce to prevent maximum call stack size exceeded
|
|
5
|
+
|
|
3
6
|
## 2.17.0
|
|
4
|
-
- Added initialize logic, for improve
|
|
7
|
+
- Added initialize logic, for improve translations
|
|
8
|
+
|
|
9
|
+
## 2.16.0
|
|
10
|
+
- Added common chart area text plugin
|
|
5
11
|
|
|
6
12
|
## 2.15.0
|
|
7
13
|
- Added translation, by provider handled([OW-11237](https://oliasoft.atlassian.net/browse/OW-11237))
|
|
@@ -38,6 +38,19 @@ export const useChartOptions = ({
|
|
|
38
38
|
}) => {
|
|
39
39
|
const {
|
|
40
40
|
interactions: { onAnimationComplete },
|
|
41
|
+
annotations: {
|
|
42
|
+
labelAnnotation: {
|
|
43
|
+
showLabel,
|
|
44
|
+
text,
|
|
45
|
+
position,
|
|
46
|
+
fontSize,
|
|
47
|
+
xOffset,
|
|
48
|
+
yOffset,
|
|
49
|
+
maxWidth,
|
|
50
|
+
lineHeight,
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
chartStyling: { layoutPadding },
|
|
41
54
|
} = options;
|
|
42
55
|
|
|
43
56
|
const {
|
|
@@ -122,11 +135,24 @@ export const useChartOptions = ({
|
|
|
122
135
|
chartAreaBorder: {
|
|
123
136
|
borderColor: BORDER_COLOR,
|
|
124
137
|
},
|
|
138
|
+
chartAreaText: {
|
|
139
|
+
showLabel,
|
|
140
|
+
text,
|
|
141
|
+
position,
|
|
142
|
+
fontSize,
|
|
143
|
+
xOffset,
|
|
144
|
+
yOffset,
|
|
145
|
+
maxWidth,
|
|
146
|
+
lineHeight,
|
|
147
|
+
},
|
|
125
148
|
...dragData,
|
|
126
149
|
};
|
|
127
150
|
|
|
128
151
|
return useMemo(
|
|
129
152
|
() => ({
|
|
153
|
+
layout: {
|
|
154
|
+
padding: layoutPadding,
|
|
155
|
+
},
|
|
130
156
|
onHover: onHover(hoveredPoint, setHoveredPoint),
|
|
131
157
|
maintainAspectRatio: options.chartStyling.maintainAspectRatio,
|
|
132
158
|
aspectRatio: options.chartStyling.squareAspectRatio ? 1 : null,
|
|
@@ -1,17 +1,26 @@
|
|
|
1
1
|
import { useCallback, useMemo } from 'react';
|
|
2
|
+
import { debounce } from 'lodash';
|
|
2
3
|
import { getPlugins } from '../../../helpers/chart-utils';
|
|
3
4
|
import { DOUBLE_CLICK } from '../constants/line-chart-consts';
|
|
4
5
|
|
|
5
6
|
export const useChartPlugins = ({ options, resetZoom }) => {
|
|
6
7
|
const { graph, legend } = options;
|
|
8
|
+
|
|
9
|
+
const debouncedResetZoom = useCallback(
|
|
10
|
+
debounce(() => {
|
|
11
|
+
resetZoom();
|
|
12
|
+
}, 100), // 300ms debounce delay
|
|
13
|
+
[resetZoom],
|
|
14
|
+
);
|
|
15
|
+
|
|
7
16
|
const handleDoubleClick = useCallback(
|
|
8
17
|
(chart, args) => {
|
|
9
18
|
const { event } = args;
|
|
10
19
|
if (event.type === DOUBLE_CLICK) {
|
|
11
|
-
|
|
20
|
+
debouncedResetZoom();
|
|
12
21
|
}
|
|
13
22
|
},
|
|
14
|
-
[
|
|
23
|
+
[debouncedResetZoom],
|
|
15
24
|
);
|
|
16
25
|
|
|
17
26
|
return useMemo(() => {
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { INIT_KEYS } from '../constants/line-chart-consts';
|
|
2
2
|
import { setConfig } from './config';
|
|
3
|
-
import { isPrimitiveValue } from '../utils/line-chart-utils';
|
|
4
3
|
import { getTranslations } from '../utils/translations/get-translations';
|
|
4
|
+
import { isPrimitiveValue } from '../utils/line-chart-utils';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Initialize the charts library with the provided configurations.
|
|
8
8
|
* This function will store the configuration options in a config object.
|
|
9
9
|
* @param {object} options - An object containing the configuration options for the library.
|
|
10
|
-
* @param {object} options.
|
|
11
|
-
* @param {string} options.languageKey - The language key to be stored in the config object, used for
|
|
10
|
+
* @param {object} options.translations - The translations to be used in the library.
|
|
11
|
+
* @param {string} options.languageKey - The language key to be stored in the config object, used for translations.
|
|
12
12
|
* @param {...object} options - Any additional options to be stored in the config object.
|
|
13
13
|
*/
|
|
14
14
|
export const initializeLineChart = ({ languageKey = 'en', ...options }) => {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import PropTypes from 'prop-types';
|
|
2
|
+
import { TextLabelPosition } from './plugins/plugin-constants';
|
|
2
3
|
|
|
3
4
|
export const LineChartPropTypes = {
|
|
4
5
|
table: PropTypes.node,
|
|
@@ -59,6 +60,15 @@ export const LineChartPropTypes = {
|
|
|
59
60
|
staticChartHeight: PropTypes.bool,
|
|
60
61
|
performanceMode: PropTypes.bool,
|
|
61
62
|
squareAspectRatio: PropTypes.bool,
|
|
63
|
+
layoutPadding: PropTypes.oneOfType([
|
|
64
|
+
PropTypes.number,
|
|
65
|
+
PropTypes.shape({
|
|
66
|
+
top: PropTypes.number,
|
|
67
|
+
bottom: PropTypes.number,
|
|
68
|
+
left: PropTypes.number,
|
|
69
|
+
right: PropTypes.number,
|
|
70
|
+
}),
|
|
71
|
+
]),
|
|
62
72
|
}),
|
|
63
73
|
tooltip: PropTypes.shape({
|
|
64
74
|
tooltips: PropTypes.bool,
|
|
@@ -72,7 +82,19 @@ export const LineChartPropTypes = {
|
|
|
72
82
|
showMinorGridlines: PropTypes.bool,
|
|
73
83
|
}),
|
|
74
84
|
annotations: PropTypes.shape({
|
|
75
|
-
labelAnnotation: PropTypes.
|
|
85
|
+
labelAnnotation: PropTypes.shape({
|
|
86
|
+
showLabel: PropTypes.bool,
|
|
87
|
+
text: PropTypes.oneOfType([
|
|
88
|
+
PropTypes.string,
|
|
89
|
+
PropTypes.arrayOf(PropTypes.string),
|
|
90
|
+
]),
|
|
91
|
+
position: PropTypes.string,
|
|
92
|
+
fontSize: PropTypes.number,
|
|
93
|
+
xOffset: PropTypes.number,
|
|
94
|
+
yOffset: PropTypes.number,
|
|
95
|
+
maxWidth: PropTypes.number,
|
|
96
|
+
lineHeight: PropTypes.number,
|
|
97
|
+
}),
|
|
76
98
|
showAnnotations: PropTypes.bool,
|
|
77
99
|
controlAnnotation: PropTypes.bool,
|
|
78
100
|
annotationsData: PropTypes.arrayOf(
|
|
@@ -93,7 +115,7 @@ export const LineChartPropTypes = {
|
|
|
93
115
|
}),
|
|
94
116
|
legend: PropTypes.shape({
|
|
95
117
|
display: PropTypes.bool,
|
|
96
|
-
position: PropTypes.oneOf(['top', 'bottom', 'right']),
|
|
118
|
+
position: PropTypes.oneOf(['top', 'bottom', 'right', 'left']),
|
|
97
119
|
align: PropTypes.oneOf(['start', 'center', 'end']),
|
|
98
120
|
customLegend: PropTypes.shape({
|
|
99
121
|
customLegendPlugin: PropTypes.object,
|
|
@@ -185,6 +207,12 @@ export const getDefaultProps = (props) => {
|
|
|
185
207
|
props.chart.options.chartStyling.performanceMode ?? true,
|
|
186
208
|
squareAspectRatio:
|
|
187
209
|
props.chart.options.chartStyling.squareAspectRatio || false,
|
|
210
|
+
layoutPadding: props.chart.options.chartStyling.layoutPadding || {
|
|
211
|
+
top: 0,
|
|
212
|
+
bottom: 20,
|
|
213
|
+
left: 0,
|
|
214
|
+
right: 0,
|
|
215
|
+
},
|
|
188
216
|
},
|
|
189
217
|
tooltip: {
|
|
190
218
|
tooltips: props.chart.options.tooltip.tooltips ?? true,
|
|
@@ -201,7 +229,24 @@ export const getDefaultProps = (props) => {
|
|
|
201
229
|
props.chart.options.graph.showMinorGridlines || false,
|
|
202
230
|
},
|
|
203
231
|
annotations: {
|
|
204
|
-
labelAnnotation:
|
|
232
|
+
labelAnnotation: {
|
|
233
|
+
showLabel:
|
|
234
|
+
props.chart.options.annotations.labelAnnotation?.showLabel ?? false,
|
|
235
|
+
text: props.chart.options.annotations.labelAnnotation?.text ?? '',
|
|
236
|
+
position:
|
|
237
|
+
props.chart.options.annotations.labelAnnotation?.position ??
|
|
238
|
+
TextLabelPosition.BOTTOM_RIGHT,
|
|
239
|
+
fontSize:
|
|
240
|
+
props.chart.options.annotations.labelAnnotation?.fontSize ?? 12,
|
|
241
|
+
xOffset:
|
|
242
|
+
props.chart.options.annotations.labelAnnotation?.xOffset ?? 5,
|
|
243
|
+
yOffset:
|
|
244
|
+
props.chart.options.annotations.labelAnnotation?.yOffset ?? 5,
|
|
245
|
+
maxWidth:
|
|
246
|
+
props.chart.options.annotations.labelAnnotation?.maxWidth ?? 300,
|
|
247
|
+
lineHeight:
|
|
248
|
+
props.chart.options.annotations.labelAnnotation?.lineHeight ?? 12,
|
|
249
|
+
},
|
|
205
250
|
showAnnotations:
|
|
206
251
|
props.chart.options.annotations.showAnnotations ?? false,
|
|
207
252
|
controlAnnotation:
|
|
@@ -29,6 +29,7 @@ import { useChartOptions } from './hooks/use-chart-options';
|
|
|
29
29
|
import { useChartPlugins } from './hooks/use-chart-plugins';
|
|
30
30
|
import { generateKey } from './utils/line-chart-utils';
|
|
31
31
|
import { useChartState } from './state/use-chart-state';
|
|
32
|
+
import { chartAreaTextPlugin } from './plugins/chart-area-text-plugin';
|
|
32
33
|
import { getConfig } from './initialize/config';
|
|
33
34
|
|
|
34
35
|
ChartJS.register(
|
|
@@ -45,6 +46,7 @@ ChartJS.register(
|
|
|
45
46
|
dataLabelsPlugin,
|
|
46
47
|
annotationPlugin,
|
|
47
48
|
dragDataPlugin,
|
|
49
|
+
chartAreaTextPlugin,
|
|
48
50
|
);
|
|
49
51
|
|
|
50
52
|
/**
|
|
@@ -56,7 +58,6 @@ const LineChart = (props) => {
|
|
|
56
58
|
const chartRef = useRef(null);
|
|
57
59
|
const { table } = props;
|
|
58
60
|
const { translations, languageKey } = getConfig();
|
|
59
|
-
|
|
60
61
|
const chart = getDefaultProps(props);
|
|
61
62
|
const {
|
|
62
63
|
data: { datasets },
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { TextLabelPosition } from './plugin-constants';
|
|
2
|
+
import { AlignOptions } from '../../../helpers/enums';
|
|
3
|
+
|
|
4
|
+
const WORD_SEPARATOR = ' ';
|
|
5
|
+
const SEMI_TRANSPARENT = 'rgba(0, 0, 0, 0.5)';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Splits the input text into words based on the predefined WORD_SEPARATOR.
|
|
9
|
+
* If the input is an array, it first joins the array and then splits it.
|
|
10
|
+
*
|
|
11
|
+
* @param {string | string[]} text - The text to split into words.
|
|
12
|
+
* @returns {string[]} An array of words.
|
|
13
|
+
*/
|
|
14
|
+
const getWords = (text) => {
|
|
15
|
+
return (Array.isArray(text) ? text.join(WORD_SEPARATOR) : text).split(
|
|
16
|
+
WORD_SEPARATOR,
|
|
17
|
+
);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Renders the lines of text on the canvas context.
|
|
22
|
+
* It iterates over the array of lines and renders each line with the provided styling.
|
|
23
|
+
*
|
|
24
|
+
* @param {CanvasRenderingContext2D} ctx - The canvas rendering context to draw on.
|
|
25
|
+
* @param {string[]} lines - The array of lines to be rendered.
|
|
26
|
+
* @param {number} lineHeight - The height of each line.
|
|
27
|
+
* @param {number} x - The x-coordinate of the starting position.
|
|
28
|
+
* @param {number} y - The y-coordinate of the starting position.
|
|
29
|
+
* @param {string} position - The position for the text (one of the values from the TextLabelPosition enum).
|
|
30
|
+
*/
|
|
31
|
+
const drawText = (ctx, lines, lineHeight, x, y, position) => {
|
|
32
|
+
lines.forEach((line, index) => {
|
|
33
|
+
const lineY = position.includes('top')
|
|
34
|
+
? y + lineHeight * (index + 1) - 5
|
|
35
|
+
: y - (lines.length - 1 - index) * lineHeight;
|
|
36
|
+
ctx.fillText(line, x, lineY);
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Calculates the maximum width for the text based on the initial maximum width
|
|
42
|
+
* and the chart area width.
|
|
43
|
+
* @param {number} initialMaxWidth - The initial maximum width.
|
|
44
|
+
* @param {number} chartAreaWidth - The width of the chart area.
|
|
45
|
+
* @returns {number} The updated maximum width.
|
|
46
|
+
*/
|
|
47
|
+
const calculateMaxWidth = (initialMaxWidth, chartAreaWidth) => {
|
|
48
|
+
const factorMiddle = 0.5;
|
|
49
|
+
const factorSmall = 0.7;
|
|
50
|
+
const maxWidthFactor =
|
|
51
|
+
chartAreaWidth < 500
|
|
52
|
+
? factorMiddle
|
|
53
|
+
: chartAreaWidth < 700
|
|
54
|
+
? factorSmall
|
|
55
|
+
: 1;
|
|
56
|
+
return initialMaxWidth * maxWidthFactor;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Determines the legend dimensions (width and height) based on its position.
|
|
61
|
+
*
|
|
62
|
+
* @param {object} chart - The Chart.js chart object.
|
|
63
|
+
* @returns {object} An object containing the legend width and height.
|
|
64
|
+
*/
|
|
65
|
+
const getLegendDimensions = (chart) => {
|
|
66
|
+
const { legend } = chart;
|
|
67
|
+
|
|
68
|
+
const legendWidth =
|
|
69
|
+
legend && legend.display && legend.options.position === 'left'
|
|
70
|
+
? legend.width + legend.options.labels.padding * 2
|
|
71
|
+
: 0;
|
|
72
|
+
|
|
73
|
+
const legendHeight =
|
|
74
|
+
legend &&
|
|
75
|
+
legend.display &&
|
|
76
|
+
(legend.options.position === 'top' || legend.options.position === 'bottom')
|
|
77
|
+
? legend.height + legend.options.labels.padding * 2
|
|
78
|
+
: 0;
|
|
79
|
+
|
|
80
|
+
return { legendWidth, legendHeight };
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Determines the X and Y coordinates for the provided position.
|
|
85
|
+
*
|
|
86
|
+
* @param {string} position - The position for the text (one of the values from the TextLabelPosition enum).
|
|
87
|
+
* @param {object} chart - The Chart.js chart object.
|
|
88
|
+
* @param {number} xOffset - The horizontal offset from the specified position.
|
|
89
|
+
* @param {number} yOffset - The vertical offset from the specified position.
|
|
90
|
+
* @returns {number[]} An array with the X and Y coordinates.
|
|
91
|
+
*/
|
|
92
|
+
const getPositionCoordinates = (position, chart, xOffset, yOffset) => {
|
|
93
|
+
const { chartArea, width } = chart;
|
|
94
|
+
const temporaryChartAreaRight = width - 8;
|
|
95
|
+
|
|
96
|
+
const { legendWidth, legendHeight } = getLegendDimensions(chart);
|
|
97
|
+
|
|
98
|
+
switch (position) {
|
|
99
|
+
case TextLabelPosition.TOP_LEFT:
|
|
100
|
+
return [
|
|
101
|
+
chartArea.left + xOffset + legendWidth,
|
|
102
|
+
chartArea.top + yOffset + legendHeight,
|
|
103
|
+
];
|
|
104
|
+
case TextLabelPosition.TOP_CENTER:
|
|
105
|
+
return [
|
|
106
|
+
chartArea.left + chartArea.width / 2,
|
|
107
|
+
chartArea.top + yOffset + legendHeight,
|
|
108
|
+
];
|
|
109
|
+
case TextLabelPosition.TOP_RIGHT:
|
|
110
|
+
return [
|
|
111
|
+
temporaryChartAreaRight - xOffset, //replace within chartArea.right when resize bug will be fixed
|
|
112
|
+
chartArea.top + yOffset + legendHeight,
|
|
113
|
+
];
|
|
114
|
+
case TextLabelPosition.MIDDLE_LEFT:
|
|
115
|
+
return [
|
|
116
|
+
chartArea.left + xOffset + legendWidth,
|
|
117
|
+
chartArea.top + chartArea.height / 2,
|
|
118
|
+
];
|
|
119
|
+
case TextLabelPosition.MIDDLE_CENTER:
|
|
120
|
+
return [
|
|
121
|
+
chartArea.left + chartArea.width / 2,
|
|
122
|
+
chartArea.top + chartArea.height / 2,
|
|
123
|
+
];
|
|
124
|
+
case TextLabelPosition.MIDDLE_RIGHT:
|
|
125
|
+
return [
|
|
126
|
+
temporaryChartAreaRight - xOffset - legendWidth, //replace within chartArea.right when resize bug will be fixed
|
|
127
|
+
chartArea.top + chartArea.height / 2,
|
|
128
|
+
];
|
|
129
|
+
case TextLabelPosition.BOTTOM_LEFT:
|
|
130
|
+
return [
|
|
131
|
+
chartArea.left + xOffset + legendWidth,
|
|
132
|
+
chartArea.bottom - yOffset - legendHeight,
|
|
133
|
+
];
|
|
134
|
+
case TextLabelPosition.BOTTOM_CENTER:
|
|
135
|
+
return [
|
|
136
|
+
chartArea.left + chartArea.width / 2,
|
|
137
|
+
chartArea.bottom - yOffset - legendHeight,
|
|
138
|
+
];
|
|
139
|
+
case TextLabelPosition.BOTTOM_RIGHT:
|
|
140
|
+
default:
|
|
141
|
+
return [
|
|
142
|
+
temporaryChartAreaRight - xOffset - legendWidth, //replace within chartArea.right when resize bug will be fixed
|
|
143
|
+
chartArea.bottom - yOffset - legendHeight,
|
|
144
|
+
];
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Determines the appropriate text alignment based on the position provided.
|
|
150
|
+
*
|
|
151
|
+
* @param {string} position - The position for the text (one of the values from the TextLabelPosition enum).
|
|
152
|
+
* @returns {string} The text alignment ('left', 'center', or 'right').
|
|
153
|
+
*/
|
|
154
|
+
const getTextAlign = (position) => {
|
|
155
|
+
if (position.includes(AlignOptions.Center)) {
|
|
156
|
+
return AlignOptions.Center;
|
|
157
|
+
} else if (position.includes(AlignOptions.Left)) {
|
|
158
|
+
return AlignOptions.Left;
|
|
159
|
+
} else {
|
|
160
|
+
return AlignOptions.Right;
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Wraps the text into lines based on the maxWidth provided.
|
|
166
|
+
*
|
|
167
|
+
* @param {string[]} words - The array of words to be processed.
|
|
168
|
+
* @param {number} maxWidth - The maximum width allowed for the text.
|
|
169
|
+
* @param {CanvasRenderingContext2D} ctx - The canvas rendering context to measure the text width.
|
|
170
|
+
* @returns {string[]} An array of wrapped lines.
|
|
171
|
+
*/
|
|
172
|
+
const wrapText = (words, maxWidth, ctx) => {
|
|
173
|
+
let line = '';
|
|
174
|
+
let lines = [];
|
|
175
|
+
for (let i = 0; i < words.length; i++) {
|
|
176
|
+
const testLine = `${line}${words[i]}${WORD_SEPARATOR}`;
|
|
177
|
+
const { width: testWidth } = ctx.measureText(testLine);
|
|
178
|
+
if (testWidth > maxWidth) {
|
|
179
|
+
lines.push(line.trim());
|
|
180
|
+
line = `${words[i]}${WORD_SEPARATOR}`;
|
|
181
|
+
} else {
|
|
182
|
+
line = testLine;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
lines.push(line.trim()); // Add the last line
|
|
186
|
+
return lines;
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Renders the wrapped text on the canvas context.
|
|
191
|
+
*
|
|
192
|
+
* @param {CanvasRenderingContext2D} ctx - The canvas rendering context to draw on.
|
|
193
|
+
* @param {object} options - The options object containing text, maxWidth, fontSize, lineHeight, position, and coordinates (x, y).
|
|
194
|
+
*/
|
|
195
|
+
const renderWrappedText = (ctx, options) => {
|
|
196
|
+
const { text, maxWidth, fontSize, lineHeight, x, y, position } = options;
|
|
197
|
+
|
|
198
|
+
const words = getWords(text);
|
|
199
|
+
const wrappedLines = wrapText(words, maxWidth, ctx);
|
|
200
|
+
|
|
201
|
+
ctx.save();
|
|
202
|
+
ctx.font = `${fontSize}px Arial`;
|
|
203
|
+
ctx.fillStyle = SEMI_TRANSPARENT;
|
|
204
|
+
ctx.textAlign = getTextAlign(position);
|
|
205
|
+
|
|
206
|
+
drawText(ctx, wrappedLines, lineHeight, x, y, position);
|
|
207
|
+
|
|
208
|
+
ctx.restore();
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
export const chartAreaTextPlugin = {
|
|
212
|
+
id: 'chartAreaText',
|
|
213
|
+
|
|
214
|
+
beforeDraw: (chart, args, options) => {
|
|
215
|
+
const {
|
|
216
|
+
showLabel,
|
|
217
|
+
text,
|
|
218
|
+
fontSize,
|
|
219
|
+
xOffset,
|
|
220
|
+
yOffset,
|
|
221
|
+
lineHeight,
|
|
222
|
+
maxWidth: initialMaxWidth,
|
|
223
|
+
position,
|
|
224
|
+
} = options;
|
|
225
|
+
const { ctx, chartArea } = chart;
|
|
226
|
+
|
|
227
|
+
if (!showLabel || !text) return;
|
|
228
|
+
|
|
229
|
+
// Determine the maxWidth based on chartArea width
|
|
230
|
+
const maxWidth = calculateMaxWidth(initialMaxWidth, chartArea.width);
|
|
231
|
+
|
|
232
|
+
// Get the position coordinates
|
|
233
|
+
const [x, y] = getPositionCoordinates(position, chart, xOffset, yOffset);
|
|
234
|
+
|
|
235
|
+
// Render the wrapped text
|
|
236
|
+
renderWrappedText(ctx, {
|
|
237
|
+
text,
|
|
238
|
+
maxWidth,
|
|
239
|
+
fontSize,
|
|
240
|
+
lineHeight,
|
|
241
|
+
x,
|
|
242
|
+
y,
|
|
243
|
+
position,
|
|
244
|
+
});
|
|
245
|
+
},
|
|
246
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const TextLabelPosition = {
|
|
2
|
+
TOP_LEFT: 'top-left',
|
|
3
|
+
TOP_CENTER: 'top-center',
|
|
4
|
+
TOP_RIGHT: 'top-right',
|
|
5
|
+
MIDDLE_LEFT: 'middle-left',
|
|
6
|
+
MIDDLE_CENTER: 'middle-center',
|
|
7
|
+
MIDDLE_RIGHT: 'middle-right',
|
|
8
|
+
BOTTOM_LEFT: 'bottom-left',
|
|
9
|
+
BOTTOM_CENTER: 'bottom-center',
|
|
10
|
+
BOTTOM_RIGHT: 'bottom-right',
|
|
11
|
+
};
|
|
@@ -26,12 +26,17 @@ export interface IChartAnnotationsData {
|
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
export interface ILabelAnnotation {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
showLabel: boolean;
|
|
30
|
+
text: string | string[];
|
|
31
|
+
position?: string
|
|
32
|
+
fontSize?: number;
|
|
33
|
+
xOffset?: number
|
|
34
|
+
yOffset?: number
|
|
35
|
+
maxWidth?: number
|
|
36
|
+
lineHeight?: number
|
|
33
37
|
}
|
|
34
38
|
|
|
39
|
+
|
|
35
40
|
export interface IChartAnnotations {
|
|
36
41
|
showAnnotations: boolean;
|
|
37
42
|
controlAnnotation: boolean;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { round } from '@oliasoft-open-source/units';
|
|
2
2
|
import { defaults } from 'chart.js';
|
|
3
3
|
import cx from 'classnames';
|
|
4
|
-
import { chartMinorGridlinesPlugin } from '../components/line-chart/line-chart.minor-gridlines-plugin';
|
|
4
|
+
import { chartMinorGridlinesPlugin } from '../components/line-chart/plugins/line-chart.minor-gridlines-plugin';
|
|
5
5
|
import {
|
|
6
6
|
BORDER_COLOR,
|
|
7
7
|
CUSTOM_LEGEND_PLUGIN_NAME,
|
|
@@ -86,37 +86,6 @@ const generateAnnotations = (options, state) => {
|
|
|
86
86
|
return annotations;
|
|
87
87
|
};
|
|
88
88
|
|
|
89
|
-
/**
|
|
90
|
-
* @param {import('../components/bar-chart/bar-chart.interface').IBarChartOptions |
|
|
91
|
-
* import('../components/line-chart/line-chart.interface').ILineChartOptions} options - chart options object
|
|
92
|
-
* @return {{label1: {}}}
|
|
93
|
-
*/
|
|
94
|
-
const getLabelAnnotation = (options) => {
|
|
95
|
-
const { annotations = {} } = options;
|
|
96
|
-
const { labelAnnotation = {} } = annotations;
|
|
97
|
-
const isDarkModeOn = options.chartStyling.darkMode || false;
|
|
98
|
-
const {
|
|
99
|
-
content = [],
|
|
100
|
-
xAdjust = -200,
|
|
101
|
-
yAdjust = 120,
|
|
102
|
-
fontSize = 12,
|
|
103
|
-
} = labelAnnotation;
|
|
104
|
-
|
|
105
|
-
return {
|
|
106
|
-
label1: {
|
|
107
|
-
type: 'label',
|
|
108
|
-
xAdjust,
|
|
109
|
-
yAdjust,
|
|
110
|
-
backgroundColor: 'rgba(0, 0, 0, 0)',
|
|
111
|
-
color: isDarkModeOn && 'rgba(255, 255, 255, 1)',
|
|
112
|
-
content,
|
|
113
|
-
font: {
|
|
114
|
-
size: fontSize,
|
|
115
|
-
},
|
|
116
|
-
},
|
|
117
|
-
};
|
|
118
|
-
};
|
|
119
|
-
|
|
120
89
|
/**
|
|
121
90
|
* @param {import('../components/bar-chart/bar-chart.interface').IBarChartOptions |
|
|
122
91
|
* import('../components/line-chart/line-chart.interface').ILineChartOptions} options - chart options object
|
|
@@ -124,20 +93,14 @@ const getLabelAnnotation = (options) => {
|
|
|
124
93
|
* @return {{annotations: []}}
|
|
125
94
|
*/
|
|
126
95
|
const getAnnotation = (options, state) => {
|
|
127
|
-
const {
|
|
128
|
-
const
|
|
129
|
-
const isAnnotationDataProvided = annotations?.annotationsData?.length;
|
|
96
|
+
const { showAnnotations, annotationsData } = options.annotations || {};
|
|
97
|
+
const shouldGenerateAnnotations = showAnnotations && annotationsData?.length;
|
|
130
98
|
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
...getLabelAnnotation(options),
|
|
135
|
-
...generateAnnotations(options, state),
|
|
136
|
-
}
|
|
137
|
-
: null;
|
|
99
|
+
const annotations = shouldGenerateAnnotations
|
|
100
|
+
? generateAnnotations(options, state)
|
|
101
|
+
: null;
|
|
138
102
|
|
|
139
|
-
return {
|
|
140
|
-
annotations: formAnnotation,
|
|
141
|
-
};
|
|
103
|
+
return { annotations };
|
|
142
104
|
};
|
|
105
|
+
|
|
143
106
|
export default getAnnotation;
|