@oliasoft-open-source/charts-library 2.15.0 → 2.16.0-beta-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.
- package/package.json +1 -1
- package/release-notes.md +2 -2
- package/src/components/controls/axes-options/axes-options.jsx +6 -19
- package/src/components/line-chart/controls/axes-options/axes-options-form-state.js +4 -4
- package/src/components/line-chart/controls/controls.jsx +2 -7
- package/src/components/line-chart/controls/drag-options.jsx +6 -14
- package/src/components/line-chart/controls/legend-options.jsx +2 -6
- package/src/components/line-chart/controls/line-options.jsx +3 -4
- package/src/components/line-chart/hooks/use-chart-options.js +20 -0
- package/src/components/line-chart/line-chart-prop-types.js +27 -2
- package/src/components/line-chart/line-chart.jsx +4 -6
- package/src/components/line-chart/plugins/chart-area-text-plugin.js +149 -0
- package/src/components/line-chart/utils/generate-line-chart-datasets.js +2 -8
- package/src/helpers/chart-interface.ts +8 -4
- package/src/helpers/chart-utils.js +1 -1
- package/src/helpers/get-chart-annotation.js +7 -44
- package/src/components/line-chart/constants/default-translations.js +0 -24
- package/src/components/line-chart/utils/get-translations/get-translations.js +0 -17
- /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.
|
|
3
|
+
"version": "2.16.0-beta-2",
|
|
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,7 @@
|
|
|
1
1
|
# Charts Library Release Notes
|
|
2
2
|
|
|
3
|
-
## 2.
|
|
4
|
-
- Added
|
|
3
|
+
## 2.16.0
|
|
4
|
+
- Added common chart area text plugin
|
|
5
5
|
|
|
6
6
|
## 2.14.1
|
|
7
7
|
- Fix save initAxesRange when dataset changed by parent component([OW-11332](https://oliasoft.atlassian.net/browse/OW-11332))
|
|
@@ -31,7 +31,6 @@ const AxesOptionsPopover = ({
|
|
|
31
31
|
onResetAxes,
|
|
32
32
|
close,
|
|
33
33
|
depthType,
|
|
34
|
-
translations,
|
|
35
34
|
}) => {
|
|
36
35
|
const [depthTypeState, setDepthTypeState] = useState(
|
|
37
36
|
depthType?.selectedDepthType,
|
|
@@ -125,7 +124,7 @@ const AxesOptionsPopover = ({
|
|
|
125
124
|
<Input
|
|
126
125
|
name="min"
|
|
127
126
|
value={min}
|
|
128
|
-
error={
|
|
127
|
+
error={minError}
|
|
129
128
|
size={5}
|
|
130
129
|
width="100%"
|
|
131
130
|
onChange={(evt) =>
|
|
@@ -141,7 +140,7 @@ const AxesOptionsPopover = ({
|
|
|
141
140
|
<Input
|
|
142
141
|
name="max"
|
|
143
142
|
value={max}
|
|
144
|
-
error={
|
|
143
|
+
error={maxError}
|
|
145
144
|
size={5}
|
|
146
145
|
width="100%"
|
|
147
146
|
onChange={(evt) =>
|
|
@@ -191,22 +190,16 @@ const AxesOptionsPopover = ({
|
|
|
191
190
|
) : null}
|
|
192
191
|
|
|
193
192
|
<Flex gap="8px" alignItems="center">
|
|
194
|
-
<Button
|
|
195
|
-
type="submit"
|
|
196
|
-
small
|
|
197
|
-
colored
|
|
198
|
-
label={translations.done}
|
|
199
|
-
disabled={!valid}
|
|
200
|
-
/>
|
|
193
|
+
<Button type="submit" small colored label="Done" disabled={!valid} />
|
|
201
194
|
<Button
|
|
202
195
|
small
|
|
203
196
|
name="resetAxes"
|
|
204
|
-
label=
|
|
197
|
+
label="Reset Axes"
|
|
205
198
|
onClick={onReset}
|
|
206
199
|
disabled={!isCustomValue}
|
|
207
200
|
/>
|
|
208
201
|
<Text small muted>
|
|
209
|
-
|
|
202
|
+
or double click on canvas
|
|
210
203
|
</Text>
|
|
211
204
|
</Flex>
|
|
212
205
|
</form>
|
|
@@ -220,7 +213,6 @@ export const AxesOptions = ({
|
|
|
220
213
|
onUpdateAxes,
|
|
221
214
|
onResetAxes,
|
|
222
215
|
depthType,
|
|
223
|
-
translations,
|
|
224
216
|
}) => {
|
|
225
217
|
return (
|
|
226
218
|
<Popover
|
|
@@ -234,15 +226,10 @@ export const AxesOptions = ({
|
|
|
234
226
|
onUpdateAxes={onUpdateAxes}
|
|
235
227
|
onResetAxes={onResetAxes}
|
|
236
228
|
depthType={depthType}
|
|
237
|
-
translations={translations}
|
|
238
229
|
/>
|
|
239
230
|
}
|
|
240
231
|
>
|
|
241
|
-
<Tooltip
|
|
242
|
-
text={translations.axesOptions}
|
|
243
|
-
placement="bottom"
|
|
244
|
-
display="flex"
|
|
245
|
-
>
|
|
232
|
+
<Tooltip text="Axes options" placement="bottom" display="flex">
|
|
246
233
|
<Button small basic colored="muted" round icon={<RiRuler2Line />} />
|
|
247
234
|
</Tooltip>
|
|
248
235
|
</Popover>
|
|
@@ -36,13 +36,13 @@ const isEmptyString = (value) => value === '';
|
|
|
36
36
|
|
|
37
37
|
const createErrorMessages = (value, compareTo, type) => {
|
|
38
38
|
const errors = [];
|
|
39
|
-
if (isEmptyString(value)) errors.push('
|
|
40
|
-
if (!validNumber(value)) errors.push('
|
|
39
|
+
if (isEmptyString(value)) errors.push('Must have a value');
|
|
40
|
+
if (!validNumber(value)) errors.push('Must be a number');
|
|
41
41
|
|
|
42
42
|
if (type === 'min' && !isLessThanMax(value, compareTo)) {
|
|
43
|
-
errors.push('
|
|
43
|
+
errors.push('Must be less than max');
|
|
44
44
|
} else if (type === 'max' && !isGreaterThanMin(value, compareTo)) {
|
|
45
|
-
errors.push('
|
|
45
|
+
errors.push('Must be greater than min');
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
return errors;
|
|
@@ -21,7 +21,6 @@ const Controls = ({
|
|
|
21
21
|
options,
|
|
22
22
|
dispatch,
|
|
23
23
|
generatedDatasets,
|
|
24
|
-
translations,
|
|
25
24
|
}) => {
|
|
26
25
|
const {
|
|
27
26
|
enableDragPoints,
|
|
@@ -73,21 +72,18 @@ const Controls = ({
|
|
|
73
72
|
onUpdateAxes={onUpdateAxes}
|
|
74
73
|
onResetAxes={onResetAxes}
|
|
75
74
|
depthType={options.depthType}
|
|
76
|
-
translations={translations}
|
|
77
75
|
/>
|
|
78
76
|
<LineOptions
|
|
79
77
|
lineEnabled={lineEnabled}
|
|
80
78
|
pointsEnabled={pointsEnabled}
|
|
81
79
|
onToggleLine={onToggleLine}
|
|
82
80
|
onTogglePoints={onTogglePoints}
|
|
83
|
-
translations={translations}
|
|
84
81
|
/>
|
|
85
82
|
<LegendOptions
|
|
86
83
|
legendEnabled={legendEnabled}
|
|
87
84
|
onToggleLegend={onToggleLegend}
|
|
88
|
-
translations={translations}
|
|
89
85
|
/>
|
|
90
|
-
<Tooltip text=
|
|
86
|
+
<Tooltip text="Download as PNG" placement="bottom-end">
|
|
91
87
|
<Button
|
|
92
88
|
small
|
|
93
89
|
basic
|
|
@@ -106,14 +102,13 @@ const Controls = ({
|
|
|
106
102
|
isDragDataAllowed={dragData.enableDragData}
|
|
107
103
|
onToggleDragPoints={onToggleDragPoints}
|
|
108
104
|
onDisableDragOptions={onDisableDragOptions}
|
|
109
|
-
translations={translations}
|
|
110
105
|
/>
|
|
111
106
|
</>
|
|
112
107
|
)}
|
|
113
108
|
|
|
114
109
|
{table ? (
|
|
115
110
|
<Tooltip
|
|
116
|
-
text={showTable ?
|
|
111
|
+
text={showTable ? 'Show chart' : 'Show table'}
|
|
117
112
|
placement="bottom-end"
|
|
118
113
|
>
|
|
119
114
|
<Button
|
|
@@ -13,24 +13,16 @@ export const DragOptions = ({
|
|
|
13
13
|
isDragDataAllowed,
|
|
14
14
|
onToggleDragPoints,
|
|
15
15
|
onDisableDragOptions,
|
|
16
|
-
translations: {
|
|
17
|
-
dragToZoom,
|
|
18
|
-
doubleClickToReset,
|
|
19
|
-
dragToPan,
|
|
20
|
-
orDoubleClickToCanvas,
|
|
21
|
-
dragToMovePoints,
|
|
22
|
-
dragDisabled,
|
|
23
|
-
},
|
|
24
16
|
}) => {
|
|
25
17
|
const options = useMemo(
|
|
26
18
|
() => [
|
|
27
19
|
{
|
|
28
|
-
buttonLabel:
|
|
20
|
+
buttonLabel: 'Drag to zoom',
|
|
29
21
|
label: (
|
|
30
22
|
<Flex direction="column">
|
|
31
23
|
<Text>Drag to zoom</Text>
|
|
32
24
|
<Text small muted>
|
|
33
|
-
|
|
25
|
+
Double click on canvas to reset
|
|
34
26
|
</Text>
|
|
35
27
|
</Flex>
|
|
36
28
|
),
|
|
@@ -39,12 +31,12 @@ export const DragOptions = ({
|
|
|
39
31
|
onClick: onToggleZoom,
|
|
40
32
|
},
|
|
41
33
|
{
|
|
42
|
-
buttonLabel:
|
|
34
|
+
buttonLabel: 'Drag to pan',
|
|
43
35
|
label: (
|
|
44
36
|
<Flex direction="column">
|
|
45
37
|
<Text>Drag to pan</Text>
|
|
46
38
|
<Text small muted>
|
|
47
|
-
|
|
39
|
+
Double click on canvas to reset
|
|
48
40
|
</Text>
|
|
49
41
|
</Flex>
|
|
50
42
|
),
|
|
@@ -55,7 +47,7 @@ export const DragOptions = ({
|
|
|
55
47
|
...(isDragDataAllowed
|
|
56
48
|
? [
|
|
57
49
|
{
|
|
58
|
-
label:
|
|
50
|
+
label: 'Drag to move points',
|
|
59
51
|
icon: <TbHandStop />,
|
|
60
52
|
selected: enableDragPoints,
|
|
61
53
|
type: 'Option',
|
|
@@ -64,7 +56,7 @@ export const DragOptions = ({
|
|
|
64
56
|
]
|
|
65
57
|
: []),
|
|
66
58
|
{
|
|
67
|
-
label:
|
|
59
|
+
label: 'Drag disabled',
|
|
68
60
|
icon: <RiForbidLine />,
|
|
69
61
|
selected: !zoomEnabled && !panEnabled && !enableDragPoints,
|
|
70
62
|
onClick: onDisableDragOptions,
|
|
@@ -4,12 +4,8 @@ import { Button, Icon, Tooltip } from '@oliasoft-open-source/react-ui-library';
|
|
|
4
4
|
import { RiListUnordered } from 'react-icons/ri';
|
|
5
5
|
import listHideIcon from '../../../assets/icons/list-hide.svg';
|
|
6
6
|
|
|
7
|
-
export const LegendOptions = ({
|
|
8
|
-
legendEnabled
|
|
9
|
-
onToggleLegend,
|
|
10
|
-
translations: { hideLegend, showLegend },
|
|
11
|
-
}) => {
|
|
12
|
-
const tooltipText = legendEnabled ? hideLegend : showLegend;
|
|
7
|
+
export const LegendOptions = ({ legendEnabled, onToggleLegend }) => {
|
|
8
|
+
const tooltipText = legendEnabled ? 'Hide Legend' : 'Show Legend';
|
|
13
9
|
const icon = legendEnabled ? (
|
|
14
10
|
<RiListUnordered />
|
|
15
11
|
) : (
|
|
@@ -10,11 +10,10 @@ export const LineOptions = ({
|
|
|
10
10
|
onToggleLine,
|
|
11
11
|
onTogglePoints,
|
|
12
12
|
pointsEnabled,
|
|
13
|
-
translations,
|
|
14
13
|
}) => {
|
|
15
14
|
const options = [
|
|
16
15
|
{
|
|
17
|
-
label:
|
|
16
|
+
label: 'Points & lines',
|
|
18
17
|
icon: <Icon icon={lineAndPointIcon} />,
|
|
19
18
|
selected: pointsEnabled && lineEnabled,
|
|
20
19
|
onClick: () => {
|
|
@@ -22,7 +21,7 @@ export const LineOptions = ({
|
|
|
22
21
|
},
|
|
23
22
|
},
|
|
24
23
|
{
|
|
25
|
-
label:
|
|
24
|
+
label: 'Lines only',
|
|
26
25
|
icon: <Icon icon={lineOnlyIcon} />,
|
|
27
26
|
selected: !pointsEnabled && lineEnabled,
|
|
28
27
|
onClick: () => {
|
|
@@ -31,7 +30,7 @@ export const LineOptions = ({
|
|
|
31
30
|
},
|
|
32
31
|
},
|
|
33
32
|
{
|
|
34
|
-
label:
|
|
33
|
+
label: 'Points only',
|
|
35
34
|
icon: <Icon icon={pointOnlyIcon} />,
|
|
36
35
|
selected: pointsEnabled && !lineEnabled,
|
|
37
36
|
onClick: () => {
|
|
@@ -38,6 +38,17 @@ export const useChartOptions = ({
|
|
|
38
38
|
}) => {
|
|
39
39
|
const {
|
|
40
40
|
interactions: { onAnimationComplete },
|
|
41
|
+
annotations: {
|
|
42
|
+
labelAnnotation: {
|
|
43
|
+
showLabel,
|
|
44
|
+
text,
|
|
45
|
+
fontSize,
|
|
46
|
+
xOffset,
|
|
47
|
+
yOffset,
|
|
48
|
+
maxWidth,
|
|
49
|
+
lineHeight,
|
|
50
|
+
},
|
|
51
|
+
},
|
|
41
52
|
} = options;
|
|
42
53
|
|
|
43
54
|
const {
|
|
@@ -122,6 +133,15 @@ export const useChartOptions = ({
|
|
|
122
133
|
chartAreaBorder: {
|
|
123
134
|
borderColor: BORDER_COLOR,
|
|
124
135
|
},
|
|
136
|
+
chartAreaText: {
|
|
137
|
+
showLabel,
|
|
138
|
+
text,
|
|
139
|
+
fontSize,
|
|
140
|
+
xOffset,
|
|
141
|
+
yOffset,
|
|
142
|
+
maxWidth,
|
|
143
|
+
lineHeight,
|
|
144
|
+
},
|
|
125
145
|
...dragData,
|
|
126
146
|
};
|
|
127
147
|
|
|
@@ -72,7 +72,18 @@ export const LineChartPropTypes = {
|
|
|
72
72
|
showMinorGridlines: PropTypes.bool,
|
|
73
73
|
}),
|
|
74
74
|
annotations: PropTypes.shape({
|
|
75
|
-
labelAnnotation: PropTypes.
|
|
75
|
+
labelAnnotation: PropTypes.shape({
|
|
76
|
+
showLabel: PropTypes.bool,
|
|
77
|
+
text: PropTypes.oneOfType([
|
|
78
|
+
PropTypes.string,
|
|
79
|
+
PropTypes.arrayOf(PropTypes.string),
|
|
80
|
+
]),
|
|
81
|
+
fontSize: PropTypes.number,
|
|
82
|
+
xOffset: PropTypes.number,
|
|
83
|
+
yOffset: PropTypes.number,
|
|
84
|
+
maxWidth: PropTypes.number,
|
|
85
|
+
lineHeight: PropTypes.number,
|
|
86
|
+
}),
|
|
76
87
|
showAnnotations: PropTypes.bool,
|
|
77
88
|
controlAnnotation: PropTypes.bool,
|
|
78
89
|
annotationsData: PropTypes.arrayOf(
|
|
@@ -201,7 +212,21 @@ export const getDefaultProps = (props) => {
|
|
|
201
212
|
props.chart.options.graph.showMinorGridlines || false,
|
|
202
213
|
},
|
|
203
214
|
annotations: {
|
|
204
|
-
labelAnnotation:
|
|
215
|
+
labelAnnotation: {
|
|
216
|
+
showLabel:
|
|
217
|
+
props.chart.options.annotations.labelAnnotation?.showLabel ?? false,
|
|
218
|
+
text: props.chart.options.annotations.labelAnnotation?.text ?? '',
|
|
219
|
+
fontSize:
|
|
220
|
+
props.chart.options.annotations.labelAnnotation?.fontSize ?? 12,
|
|
221
|
+
xOffset:
|
|
222
|
+
props.chart.options.annotations.labelAnnotation?.xOffset ?? 5,
|
|
223
|
+
yOffset:
|
|
224
|
+
props.chart.options.annotations.labelAnnotation?.yOffset ?? 10,
|
|
225
|
+
maxWidth:
|
|
226
|
+
props.chart.options.annotations.labelAnnotation?.maxWidth ?? 300,
|
|
227
|
+
lineHeight:
|
|
228
|
+
props.chart.options.annotations.labelAnnotation?.lineHeight ?? 12,
|
|
229
|
+
},
|
|
205
230
|
showAnnotations:
|
|
206
231
|
props.chart.options.annotations.showAnnotations ?? false,
|
|
207
232
|
controlAnnotation:
|
|
@@ -29,8 +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 {
|
|
33
|
-
import { getTranslations } from './utils/get-translations/get-translations';
|
|
32
|
+
import { chartAreaTextPlugin } from './plugins/chart-area-text-plugin';
|
|
34
33
|
|
|
35
34
|
ChartJS.register(
|
|
36
35
|
LinearScale,
|
|
@@ -46,6 +45,7 @@ ChartJS.register(
|
|
|
46
45
|
dataLabelsPlugin,
|
|
47
46
|
annotationPlugin,
|
|
48
47
|
dragDataPlugin,
|
|
48
|
+
chartAreaTextPlugin,
|
|
49
49
|
);
|
|
50
50
|
|
|
51
51
|
/**
|
|
@@ -55,8 +55,7 @@ ChartJS.register(
|
|
|
55
55
|
const LineChart = (props) => {
|
|
56
56
|
setDefaultTheme();
|
|
57
57
|
const chartRef = useRef(null);
|
|
58
|
-
const { table
|
|
59
|
-
const translations = getTranslations(translationsRaw);
|
|
58
|
+
const { table } = props;
|
|
60
59
|
const chart = getDefaultProps(props);
|
|
61
60
|
const {
|
|
62
61
|
data: { datasets },
|
|
@@ -79,7 +78,7 @@ const LineChart = (props) => {
|
|
|
79
78
|
);
|
|
80
79
|
|
|
81
80
|
const generatedDatasets = useMemo(() => {
|
|
82
|
-
return generateLineChartDatasets(datasets, state, options
|
|
81
|
+
return generateLineChartDatasets(datasets, state, options);
|
|
83
82
|
}, [state.lineEnabled, state.pointsEnabled, axes, annotations, graph]);
|
|
84
83
|
|
|
85
84
|
// Call the custom hooks.
|
|
@@ -135,7 +134,6 @@ const LineChart = (props) => {
|
|
|
135
134
|
options={options}
|
|
136
135
|
dispatch={dispatch}
|
|
137
136
|
generatedDatasets={generatedDatasets}
|
|
138
|
-
translations={translations}
|
|
139
137
|
/>
|
|
140
138
|
{table && state.showTable ? (
|
|
141
139
|
<div className={styles.table}>{table}</div>
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
const WORD_SEPARATOR = ' ';
|
|
2
|
+
const TRANSPARENT = 'rgba(0, 0, 0, 0.5)';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Splits the input text into words based on the predefined WORD_SEPARATOR.
|
|
6
|
+
* If the input is an array, it first joins the array and then splits it.
|
|
7
|
+
*
|
|
8
|
+
* @param {string | string[]} text - The text to split into words.
|
|
9
|
+
* @returns {string[]} An array of words.
|
|
10
|
+
*/
|
|
11
|
+
const getWords = (text) => {
|
|
12
|
+
return Array.isArray(text)
|
|
13
|
+
? text.join(WORD_SEPARATOR).split(WORD_SEPARATOR)
|
|
14
|
+
: text.split(WORD_SEPARATOR);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Counts the number of lines required to render the words within the specified maxWidth.
|
|
19
|
+
* It takes into consideration the context (ctx) provided to calculate the text width.
|
|
20
|
+
*
|
|
21
|
+
* @param {string[]} words - The array of words to be processed.
|
|
22
|
+
* @param {number} maxWidth - The maximum width allowed for the text.
|
|
23
|
+
* @param {CanvasRenderingContext2D} ctx - The canvas rendering context to measure the text width.
|
|
24
|
+
* @returns {number} The number of lines required.
|
|
25
|
+
*/
|
|
26
|
+
const countLines = (words, maxWidth, ctx) => {
|
|
27
|
+
let line = '';
|
|
28
|
+
let lines = 0;
|
|
29
|
+
|
|
30
|
+
for (const word of words) {
|
|
31
|
+
const testLine = `${line}${word}${WORD_SEPARATOR}`;
|
|
32
|
+
const { width: testWidth } = ctx.measureText(testLine);
|
|
33
|
+
|
|
34
|
+
if (testWidth > maxWidth) {
|
|
35
|
+
line = `${word}${WORD_SEPARATOR}`;
|
|
36
|
+
lines++;
|
|
37
|
+
} else {
|
|
38
|
+
line = testLine;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
lines++; // Add the last line
|
|
42
|
+
return lines;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Renders the lines of text on the canvas context.
|
|
47
|
+
* It iterates over the array of lines and renders each line with the provided styling.
|
|
48
|
+
*
|
|
49
|
+
* @param {CanvasRenderingContext2D} ctx - The canvas rendering context to draw on.
|
|
50
|
+
* @param {string[]} lines - The array of lines to be rendered.
|
|
51
|
+
* @param {number} lineHeight - The height of each line.
|
|
52
|
+
* @param {number} x - The x-coordinate of the starting position.
|
|
53
|
+
* @param {number} y - The y-coordinate of the starting position.
|
|
54
|
+
*/
|
|
55
|
+
const drawText = (ctx, lines, lineHeight, x, y) => {
|
|
56
|
+
lines.forEach((line, index) => {
|
|
57
|
+
ctx.fillText(line, x, y - (lines.length - 1 - index) * lineHeight);
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Calculates the maximum width for the text based on the initial maximum width
|
|
63
|
+
* and the chart area width.
|
|
64
|
+
* @param {number} initialMaxWidth - The initial maximum width.
|
|
65
|
+
* @param {number} chartAreaWidth - The width of the chart area.
|
|
66
|
+
* @returns {number} The updated maximum width.
|
|
67
|
+
*/
|
|
68
|
+
const calculateMaxWidth = (initialMaxWidth, chartAreaWidth) => {
|
|
69
|
+
const factorMiddle = 0.5;
|
|
70
|
+
const factorSmall = 0.7;
|
|
71
|
+
const maxWidthFactor =
|
|
72
|
+
chartAreaWidth < 500
|
|
73
|
+
? factorMiddle
|
|
74
|
+
: chartAreaWidth < 700
|
|
75
|
+
? factorSmall
|
|
76
|
+
: 1;
|
|
77
|
+
return initialMaxWidth * maxWidthFactor;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export const chartAreaTextPlugin = {
|
|
81
|
+
id: 'chartAreaText',
|
|
82
|
+
|
|
83
|
+
beforeLayout: (chart, args, options) => {
|
|
84
|
+
const { showLabel, text, fontStyle, fontSize, maxWidth, lineHeight } =
|
|
85
|
+
options;
|
|
86
|
+
const { ctx } = chart;
|
|
87
|
+
|
|
88
|
+
if (!showLabel || !text) return;
|
|
89
|
+
|
|
90
|
+
ctx.save();
|
|
91
|
+
ctx.font = `${fontStyle} ${fontSize}px Arial`;
|
|
92
|
+
|
|
93
|
+
const words = getWords(text);
|
|
94
|
+
const lines = countLines(words, maxWidth, ctx);
|
|
95
|
+
|
|
96
|
+
// Calculate and set the padding needed at the bottom of the chart
|
|
97
|
+
const paddingNeeded = lines * lineHeight + lineHeight;
|
|
98
|
+
chart.options.layout.padding.bottom = paddingNeeded;
|
|
99
|
+
|
|
100
|
+
ctx.restore();
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
beforeDraw: (chart, args, options) => {
|
|
104
|
+
const {
|
|
105
|
+
showLabel,
|
|
106
|
+
text,
|
|
107
|
+
fontSize,
|
|
108
|
+
xOffset,
|
|
109
|
+
yOffset,
|
|
110
|
+
lineHeight,
|
|
111
|
+
maxWidth: initialMaxWidth,
|
|
112
|
+
} = options;
|
|
113
|
+
const { ctx, chartArea } = chart;
|
|
114
|
+
|
|
115
|
+
if (!showLabel || !text) return;
|
|
116
|
+
|
|
117
|
+
// Determine the maxWidth based on chartArea width
|
|
118
|
+
const maxWidth = calculateMaxWidth(initialMaxWidth, chartArea.width);
|
|
119
|
+
|
|
120
|
+
// Split the text into words and calculate the number of lines
|
|
121
|
+
const words = getWords(text);
|
|
122
|
+
let line = '';
|
|
123
|
+
let lines = [];
|
|
124
|
+
for (let i = 0; i < words.length; i++) {
|
|
125
|
+
const testLine = `${line}${words[i]}${WORD_SEPARATOR}`;
|
|
126
|
+
const { width: testWidth } = ctx.measureText(testLine);
|
|
127
|
+
if (testWidth > maxWidth) {
|
|
128
|
+
lines.push(line.trim());
|
|
129
|
+
line = `${words[i]}${WORD_SEPARATOR}`;
|
|
130
|
+
} else {
|
|
131
|
+
line = testLine;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
lines.push(line.trim()); // Add the last line
|
|
135
|
+
|
|
136
|
+
// Wrap the text based on the maxWidth and draw it on the canvas
|
|
137
|
+
ctx.save();
|
|
138
|
+
ctx.font = `${fontSize}px Arial`;
|
|
139
|
+
ctx.fillStyle = TRANSPARENT;
|
|
140
|
+
ctx.textAlign = 'right';
|
|
141
|
+
|
|
142
|
+
const y = chartArea.bottom - yOffset;
|
|
143
|
+
const x = chartArea.right - xOffset; // Subtract margin on the right
|
|
144
|
+
|
|
145
|
+
drawText(ctx, lines, lineHeight, x, y);
|
|
146
|
+
|
|
147
|
+
ctx.restore();
|
|
148
|
+
},
|
|
149
|
+
};
|
|
@@ -15,15 +15,9 @@ import { generateRandomColor } from '../../../helpers/chart-utils';
|
|
|
15
15
|
* @param {Array} datasets - The initial datasets for the line chart.
|
|
16
16
|
* @param {Object} state - The state object containing chart settings (e.g., lineEnabled, pointsEnabled, axes).
|
|
17
17
|
* @param {Object} options - The options object containing additional settings (e.g., annotations, graph).
|
|
18
|
-
* @param {Object} translations - The translations object with the label property
|
|
19
18
|
* @returns {Array} - The generated line chart datasets with applied settings and configurations.
|
|
20
19
|
*/
|
|
21
|
-
export const generateLineChartDatasets = (
|
|
22
|
-
datasets,
|
|
23
|
-
state,
|
|
24
|
-
options,
|
|
25
|
-
{ label },
|
|
26
|
-
) => {
|
|
20
|
+
export const generateLineChartDatasets = (datasets, state, options) => {
|
|
27
21
|
const copyDataset = [...datasets];
|
|
28
22
|
const { annotations, graph } = options;
|
|
29
23
|
const {
|
|
@@ -94,7 +88,7 @@ export const generateLineChartDatasets = (
|
|
|
94
88
|
// Return the dataset with applied settings and configurations
|
|
95
89
|
return {
|
|
96
90
|
...line,
|
|
97
|
-
label: line.label ||
|
|
91
|
+
label: line.label || `Label ${i + 1}`,
|
|
98
92
|
data: filteredData,
|
|
99
93
|
showLine: lineEnabled,
|
|
100
94
|
lineTension,
|
|
@@ -26,12 +26,16 @@ export interface IChartAnnotationsData {
|
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
export interface ILabelAnnotation {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
showLabel: boolean;
|
|
30
|
+
text: string | string[];
|
|
31
|
+
fontSize?: number;
|
|
32
|
+
xOffset: number
|
|
33
|
+
yOffset: number
|
|
34
|
+
maxWidth: number
|
|
35
|
+
lineHeight: number
|
|
33
36
|
}
|
|
34
37
|
|
|
38
|
+
|
|
35
39
|
export interface IChartAnnotations {
|
|
36
40
|
showAnnotations: boolean;
|
|
37
41
|
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;
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
export const defaultTranslations = Object.freeze({
|
|
2
|
-
label: 'Label',
|
|
3
|
-
pointsLines: 'Points & lines',
|
|
4
|
-
linesOnly: 'Lines only',
|
|
5
|
-
pointsOnly: 'Points only',
|
|
6
|
-
axesOptions: 'Axes options',
|
|
7
|
-
resetAxes: 'Reset Axes',
|
|
8
|
-
done: 'Done',
|
|
9
|
-
downloadAsPNG: 'Download as PNG',
|
|
10
|
-
showChart: 'Show chart',
|
|
11
|
-
showTable: 'Show table',
|
|
12
|
-
dragToZoom: 'Drag to zoom',
|
|
13
|
-
dragToPan: 'Drag to pan',
|
|
14
|
-
dragToMovePoints: 'Drag to move points',
|
|
15
|
-
dragDisabled: 'Drag disabled',
|
|
16
|
-
hideLegend: 'Hide Legend',
|
|
17
|
-
showLegend: 'Show Legend',
|
|
18
|
-
mustHaveAValue: 'Must have a value',
|
|
19
|
-
mustBeANumber: 'Must be a number',
|
|
20
|
-
mustBeLessThanMax: 'Must be less than max',
|
|
21
|
-
mustBeGreaterThanMin: 'Must be greater than min',
|
|
22
|
-
doubleClickToReset: 'Double click on canvas to reset',
|
|
23
|
-
orDoubleClickToCanvas: 'or double click on canvas',
|
|
24
|
-
});
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { defaultTranslations } from '../../constants/default-translations';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Merges custom translations with the default translations.
|
|
5
|
-
* If a custom translation is provided for a key, it will override the default one.
|
|
6
|
-
* @param {object} translations - Custom translations.
|
|
7
|
-
* @returns {object} - The resulting translations object, containing both default and custom translations.
|
|
8
|
-
*/
|
|
9
|
-
export const getTranslations = (translations = {}) => {
|
|
10
|
-
return Object.keys(defaultTranslations).reduce(
|
|
11
|
-
(acc, key) => ({
|
|
12
|
-
...acc,
|
|
13
|
-
[key]: translations[key] || defaultTranslations[key],
|
|
14
|
-
}),
|
|
15
|
-
{},
|
|
16
|
-
);
|
|
17
|
-
};
|