@oliasoft-open-source/charts-library 0.0.2-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.
Files changed (73) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +3 -0
  3. package/index.js +13 -0
  4. package/package.json +100 -0
  5. package/release-notes.md +178 -0
  6. package/src/assets/icons/line-and-point.svg +1 -0
  7. package/src/assets/icons/line-only.svg +1 -0
  8. package/src/assets/icons/list-hide.svg +1 -0
  9. package/src/assets/icons/point-only.svg +1 -0
  10. package/src/components/bar-chart/bar-chart-prop-types.js +188 -0
  11. package/src/components/bar-chart/bar-chart.interface.ts +84 -0
  12. package/src/components/bar-chart/bar-chart.jsx +243 -0
  13. package/src/components/bar-chart/bar-chart.module.less +61 -0
  14. package/src/components/bar-chart/get-bar-chart-data-labels.js +42 -0
  15. package/src/components/bar-chart/get-bar-chart-scales.js +123 -0
  16. package/src/components/bar-chart/get-bar-chart-tooltips.js +100 -0
  17. package/src/components/controls/axes-options/axes-options-form-state.js +95 -0
  18. package/src/components/controls/axes-options/axes-options.jsx +166 -0
  19. package/src/components/controls/controls.jsx +104 -0
  20. package/src/components/controls/controls.module.less +12 -0
  21. package/src/components/controls/drag-options.jsx +77 -0
  22. package/src/components/controls/legend-options.jsx +25 -0
  23. package/src/components/controls/line-options.jsx +54 -0
  24. package/src/components/line-chart/axis-scales/axis-scales.js +165 -0
  25. package/src/components/line-chart/datalabels-alignment/get-alignment-condition.js +13 -0
  26. package/src/components/line-chart/datalabels-alignment/get-alignment-data.js +20 -0
  27. package/src/components/line-chart/datalabels-alignment/get-datalabels-position.js +25 -0
  28. package/src/components/line-chart/get-axes-ranges-from-chart.js +10 -0
  29. package/src/components/line-chart/get-line-chart-data-labels.js +21 -0
  30. package/src/components/line-chart/get-line-chart-scales.js +120 -0
  31. package/src/components/line-chart/get-line-chart-tooltips.js +91 -0
  32. package/src/components/line-chart/line-chart-consts.js +7 -0
  33. package/src/components/line-chart/line-chart-prop-types.js +212 -0
  34. package/src/components/line-chart/line-chart-utils.js +192 -0
  35. package/src/components/line-chart/line-chart.interface.ts +107 -0
  36. package/src/components/line-chart/line-chart.jsx +531 -0
  37. package/src/components/line-chart/line-chart.minor-gridlines-plugin.js +88 -0
  38. package/src/components/line-chart/line-chart.module.less +77 -0
  39. package/src/components/line-chart/state/action-types.js +11 -0
  40. package/src/components/line-chart/state/initial-state.js +69 -0
  41. package/src/components/line-chart/state/line-chart-reducer.js +101 -0
  42. package/src/components/pie-chart/pie-chart-prop-types.js +111 -0
  43. package/src/components/pie-chart/pie-chart-utils.js +32 -0
  44. package/src/components/pie-chart/pie-chart.interface.ts +61 -0
  45. package/src/components/pie-chart/pie-chart.jsx +450 -0
  46. package/src/components/pie-chart/pie-chart.module.less +61 -0
  47. package/src/components/scatter-chart/scatter-chart.intefrace.ts +33 -0
  48. package/src/components/scatter-chart/scatter-chart.jsx +21 -0
  49. package/src/components/scatter-chart/scatter-chart.module.less +4 -0
  50. package/src/helpers/chart-border-plugin.js +19 -0
  51. package/src/helpers/chart-consts.js +62 -0
  52. package/src/helpers/chart-interface.ts +76 -0
  53. package/src/helpers/chart-utils.js +183 -0
  54. package/src/helpers/container.jsx +60 -0
  55. package/src/helpers/disabled-context.js +8 -0
  56. package/src/helpers/enums.js +87 -0
  57. package/src/helpers/get-chart-annotation.js +143 -0
  58. package/src/helpers/get-custom-legend-plugin-example.js +80 -0
  59. package/src/helpers/numbers/numbers.js +44 -0
  60. package/src/helpers/range/estimate-data-series-have-close-values.js +54 -0
  61. package/src/helpers/range/range.js +95 -0
  62. package/src/helpers/styles.js +68 -0
  63. package/src/helpers/text.js +6 -0
  64. package/src/style/external.less +4 -0
  65. package/src/style/fonts/lato/Lato-Bold.woff2 +0 -0
  66. package/src/style/fonts/lato/Lato-BoldItalic.woff2 +0 -0
  67. package/src/style/fonts/lato/Lato-Italic.woff2 +0 -0
  68. package/src/style/fonts/lato/Lato-Regular.woff2 +0 -0
  69. package/src/style/fonts.less +27 -0
  70. package/src/style/global.less +43 -0
  71. package/src/style/reset/reset.less +28 -0
  72. package/src/style/shared.less +24 -0
  73. package/src/style/variables.less +91 -0
@@ -0,0 +1,166 @@
1
+ import React, { useReducer } from 'react';
2
+ import {
3
+ Button,
4
+ Field,
5
+ Flex,
6
+ Input,
7
+ InputGroup,
8
+ InputGroupAddon,
9
+ Popover,
10
+ Text,
11
+ Tooltip,
12
+ } from '@oliasoft-open-source/react-ui-library';
13
+ import { RiRuler2Line } from 'react-icons/ri';
14
+ import { toNum } from '../../line-chart/line-chart-utils';
15
+ import {
16
+ actionTypes,
17
+ reducer,
18
+ initializeFormState,
19
+ validateAxes,
20
+ } from './axes-options-form-state';
21
+
22
+ const AxesOptionsPopover = ({
23
+ initialAxesRanges,
24
+ axes,
25
+ controlsAxesLabels,
26
+ onUpdateAxes,
27
+ onResetAxes,
28
+ close,
29
+ }) => {
30
+ const [formState, dispatch] = useReducer(
31
+ reducer,
32
+ {
33
+ axes,
34
+ initialAxesRanges,
35
+ },
36
+ initializeFormState,
37
+ );
38
+
39
+ const { errors, valid } = validateAxes(formState);
40
+
41
+ const onEditValue = ({ name, value, id }) => {
42
+ dispatch({
43
+ type: actionTypes.valueUpdated,
44
+ payload: { name, value, id },
45
+ });
46
+ };
47
+
48
+ const onDone = () => {
49
+ if (valid) {
50
+ const sanitizedFormState = formState.map((axis) => ({
51
+ ...axis,
52
+ min: toNum(axis.min),
53
+ max: toNum(axis.max),
54
+ }));
55
+ onUpdateAxes({
56
+ axes: sanitizedFormState,
57
+ });
58
+ }
59
+ close();
60
+ };
61
+
62
+ const onReset = () => {
63
+ //reset local form
64
+ dispatch({
65
+ type: actionTypes.reset,
66
+ payload: { axes, initialAxesRanges },
67
+ });
68
+ //reset parent state
69
+ onResetAxes();
70
+ };
71
+
72
+ const isCustomValue = axes.filter((axis) => axis.max || axis.min).length > 0;
73
+ const handleInputFocus = (e) => e.target.select();
74
+ return (
75
+ <>
76
+ {axes.map((axis, i) => {
77
+ const axisLabel = controlsAxesLabels.find(
78
+ (el) => el.id === axis.id,
79
+ )?.label;
80
+
81
+ const min = formState.find((a) => a.id === axis.id)?.min;
82
+ const max = formState.find((a) => a.id === axis.id)?.max;
83
+ const minError = errors?.find((a) => a.id === axis.id)?.min?.[0];
84
+ const maxError = errors?.find((a) => a.id === axis.id)?.max?.[0];
85
+ return (
86
+ <Field key={i} label={axisLabel || axis.id || ''}>
87
+ <InputGroup small>
88
+ <Input
89
+ name="min"
90
+ value={min}
91
+ error={minError}
92
+ size={5}
93
+ width="100%"
94
+ onChange={(evt) =>
95
+ onEditValue({
96
+ name: evt.target.name,
97
+ value: evt.target.value,
98
+ id: axis.id,
99
+ })
100
+ }
101
+ onFocus={handleInputFocus}
102
+ />
103
+ <InputGroupAddon>to</InputGroupAddon>
104
+ <Input
105
+ name="max"
106
+ value={max}
107
+ error={maxError}
108
+ size={5}
109
+ width="100%"
110
+ onChange={(evt) =>
111
+ onEditValue({
112
+ name: evt.target.name,
113
+ value: evt.target.value,
114
+ id: axis.id,
115
+ })
116
+ }
117
+ onFocus={handleInputFocus}
118
+ />
119
+ </InputGroup>
120
+ </Field>
121
+ );
122
+ })}
123
+ <Flex gap="8px" alignItems="center">
124
+ <Button small colored label="Done" onClick={onDone} disabled={!valid} />
125
+ <Button
126
+ small
127
+ name="resetAxes"
128
+ label="Reset Axes"
129
+ onClick={onReset}
130
+ disabled={!isCustomValue}
131
+ />
132
+ <Text small muted>
133
+ or click on canvas
134
+ </Text>
135
+ </Flex>
136
+ </>
137
+ );
138
+ };
139
+
140
+ export const AxesOptions = ({
141
+ initialAxesRanges,
142
+ axes,
143
+ controlsAxesLabels,
144
+ onUpdateAxes,
145
+ onResetAxes,
146
+ }) => {
147
+ return (
148
+ <Popover
149
+ placement="bottom"
150
+ overflowContainer
151
+ content={
152
+ <AxesOptionsPopover
153
+ initialAxesRanges={initialAxesRanges}
154
+ axes={axes}
155
+ controlsAxesLabels={controlsAxesLabels}
156
+ onUpdateAxes={onUpdateAxes}
157
+ onResetAxes={onResetAxes}
158
+ />
159
+ }
160
+ >
161
+ <Tooltip text="Axes options" placement="bottom" display="flex">
162
+ <Button small basic colored="muted" round icon={<RiRuler2Line />} />
163
+ </Tooltip>
164
+ </Popover>
165
+ );
166
+ };
@@ -0,0 +1,104 @@
1
+ import React from 'react';
2
+ import { Button, Text, Tooltip } from '@oliasoft-open-source/react-ui-library';
3
+ import {
4
+ RiFileDownloadLine,
5
+ RiLineChartLine,
6
+ RiTableLine,
7
+ } from 'react-icons/ri';
8
+ import { LineOptions } from './line-options';
9
+ import { DragOptions } from './drag-options';
10
+ import { AxesOptions } from './axes-options/axes-options';
11
+ import { LegendOptions } from './legend-options';
12
+ import styles from './controls.module.less';
13
+
14
+ const Controls = ({
15
+ axes,
16
+ controlsAxesLabels,
17
+ chart,
18
+ headerComponent,
19
+ legendEnabled,
20
+ lineEnabled,
21
+ onDownload,
22
+ onResetAxes,
23
+ onUpdateAxes,
24
+ onToggleLegend,
25
+ onToggleLine,
26
+ onTogglePan,
27
+ onTogglePoints,
28
+ onToggleTable,
29
+ onToggleZoom,
30
+ panEnabled,
31
+ pointsEnabled,
32
+ initialAxesRanges,
33
+ showTable,
34
+ subheaderComponent,
35
+ table,
36
+ zoomEnabled,
37
+ }) => {
38
+ return (
39
+ <>
40
+ <div className={styles.controls}>
41
+ {!!chart.options.title && <Text bold>{chart.options.title}</Text>}
42
+ {headerComponent}
43
+ <div className={styles.buttons}>
44
+ {!showTable && (
45
+ <>
46
+ <AxesOptions
47
+ initialAxesRanges={initialAxesRanges}
48
+ axes={axes}
49
+ controlsAxesLabels={controlsAxesLabels}
50
+ onUpdateAxes={onUpdateAxes}
51
+ onResetAxes={onResetAxes}
52
+ />
53
+ <LineOptions
54
+ lineEnabled={lineEnabled}
55
+ pointsEnabled={pointsEnabled}
56
+ onToggleLine={onToggleLine}
57
+ onTogglePoints={onTogglePoints}
58
+ />
59
+ <LegendOptions
60
+ legendEnabled={legendEnabled}
61
+ onToggleLegend={onToggleLegend}
62
+ />
63
+ <DragOptions
64
+ panEnabled={panEnabled}
65
+ zoomEnabled={zoomEnabled}
66
+ onTogglePan={onTogglePan}
67
+ onToggleZoom={onToggleZoom}
68
+ />
69
+ <Tooltip text="Download as PNG" placement="bottom-end">
70
+ <Button
71
+ small
72
+ basic
73
+ colored="muted"
74
+ round
75
+ icon={<RiFileDownloadLine />}
76
+ onClick={onDownload}
77
+ />
78
+ </Tooltip>
79
+ </>
80
+ )}
81
+
82
+ {table ? (
83
+ <Tooltip
84
+ text={showTable ? 'Show chart' : 'Show table'}
85
+ placement="bottom-end"
86
+ >
87
+ <Button
88
+ small
89
+ basic
90
+ colored="muted"
91
+ round
92
+ icon={showTable ? <RiLineChartLine /> : <RiTableLine />}
93
+ onClick={onToggleTable}
94
+ />
95
+ </Tooltip>
96
+ ) : null}
97
+ </div>
98
+ </div>
99
+ {subheaderComponent}
100
+ </>
101
+ );
102
+ };
103
+
104
+ export default Controls;
@@ -0,0 +1,12 @@
1
+ .controls {
2
+ display: flex;
3
+ align-items: center;
4
+ flex-wrap: wrap;
5
+ gap: 8px;
6
+ }
7
+
8
+ .buttons {
9
+ display: flex;
10
+ margin-left: auto;
11
+ gap: 4px;
12
+ }
@@ -0,0 +1,77 @@
1
+ import React from 'react';
2
+ import {
3
+ Button,
4
+ Flex,
5
+ Text,
6
+ Tooltip,
7
+ } from '@oliasoft-open-source/react-ui-library';
8
+ import { RiDragMove2Line, RiForbidLine, RiZoomInLine } from 'react-icons/ri';
9
+
10
+ export const DragOptions = ({
11
+ onTogglePan,
12
+ onToggleZoom,
13
+ panEnabled,
14
+ zoomEnabled,
15
+ }) => {
16
+ // TODO: Translate strings
17
+ const options = [
18
+ {
19
+ label: (
20
+ <Flex direction="column">
21
+ <Text>Drag to zoom</Text>
22
+ {/* <Text small muted>
23
+ Hold shift to change axis
24
+ </Text> */}
25
+ <Text small muted>
26
+ Click on canvas to reset
27
+ </Text>
28
+ </Flex>
29
+ ),
30
+ icon: <RiZoomInLine />,
31
+ selected: zoomEnabled,
32
+ onClick: () => {
33
+ onToggleZoom();
34
+ onTogglePan();
35
+ },
36
+ },
37
+ {
38
+ label: (
39
+ <Flex direction="column">
40
+ <Text>Drag to pan</Text>
41
+ {/* <Text small muted>
42
+ Hold shift to change axis
43
+ </Text> */}
44
+ <Text small muted>
45
+ Click on canvas to reset
46
+ </Text>
47
+ </Flex>
48
+ ),
49
+ icon: <RiDragMove2Line />,
50
+ selected: panEnabled,
51
+ onClick: () => {
52
+ onTogglePan();
53
+ },
54
+ },
55
+ {
56
+ label: 'Drag disabled',
57
+ icon: <RiForbidLine />,
58
+ selected: !zoomEnabled && !panEnabled,
59
+ onClick: () => {
60
+ onToggleZoom();
61
+ },
62
+ },
63
+ ];
64
+ const selectedOption = options.filter((option) => option.selected)[0];
65
+ return (
66
+ <Tooltip text={selectedOption.label} placement="bottom-end">
67
+ <Button
68
+ small
69
+ basic
70
+ colored="muted"
71
+ round
72
+ icon={selectedOption.icon}
73
+ onClick={selectedOption.onClick}
74
+ />
75
+ </Tooltip>
76
+ );
77
+ };
@@ -0,0 +1,25 @@
1
+ import React from 'react';
2
+ import { Button, Icon, Tooltip } from '@oliasoft-open-source/react-ui-library';
3
+ import { RiListUnordered } from 'react-icons/ri';
4
+ import listHideIcon from '../../assets/icons/list-hide.svg';
5
+
6
+ export const LegendOptions = ({ legendEnabled, onToggleLegend }) => {
7
+ // TODO: Translate strings
8
+ return (
9
+ <Tooltip
10
+ text={legendEnabled ? 'Legend shown' : 'Legend hidden'}
11
+ placement="bottom"
12
+ >
13
+ <Button
14
+ small
15
+ basic
16
+ colored="muted"
17
+ round
18
+ icon={
19
+ legendEnabled ? <RiListUnordered /> : <Icon icon={listHideIcon} />
20
+ }
21
+ onClick={onToggleLegend}
22
+ />
23
+ </Tooltip>
24
+ );
25
+ };
@@ -0,0 +1,54 @@
1
+ import React from 'react';
2
+ import { Button, Icon, Tooltip } from '@oliasoft-open-source/react-ui-library';
3
+ import lineOnlyIcon from '../../assets/icons/line-only.svg';
4
+ import pointOnlyIcon from '../../assets/icons/point-only.svg';
5
+ import lineAndPointIcon from '../../assets/icons/line-and-point.svg';
6
+
7
+ export const LineOptions = ({
8
+ lineEnabled,
9
+ onToggleLine,
10
+ onTogglePoints,
11
+ pointsEnabled,
12
+ }) => {
13
+ // TODO: Translate strings
14
+ const options = [
15
+ {
16
+ label: 'Points & lines',
17
+ icon: <Icon icon={lineAndPointIcon} />,
18
+ selected: pointsEnabled && lineEnabled,
19
+ onClick: () => {
20
+ onTogglePoints();
21
+ },
22
+ },
23
+ {
24
+ label: 'Lines only',
25
+ icon: <Icon icon={lineOnlyIcon} />,
26
+ selected: !pointsEnabled && lineEnabled,
27
+ onClick: () => {
28
+ onTogglePoints();
29
+ onToggleLine();
30
+ },
31
+ },
32
+ {
33
+ label: 'Points only',
34
+ icon: <Icon icon={pointOnlyIcon} />,
35
+ selected: pointsEnabled && !lineEnabled,
36
+ onClick: () => {
37
+ onToggleLine();
38
+ },
39
+ },
40
+ ];
41
+ const selectedOption = options.filter((option) => option.selected)[0];
42
+ return (
43
+ <Tooltip text={selectedOption.label} placement="bottom">
44
+ <Button
45
+ small
46
+ basic
47
+ colored="muted"
48
+ round
49
+ icon={selectedOption.icon}
50
+ onClick={selectedOption.onClick}
51
+ />
52
+ </Tooltip>
53
+ );
54
+ };
@@ -0,0 +1,165 @@
1
+ import getLineChartScales from '../get-line-chart-scales';
2
+ import { AxisType } from '../../../helpers/enums';
3
+ import { getAxisTypeFromKey } from '../line-chart-utils';
4
+ import { estimateDataSeriesHaveCloseValues } from '../../../helpers/range/estimate-data-series-have-close-values';
5
+ import { getSuggestedAxisRange } from '../../../helpers/range/range';
6
+
7
+ /**
8
+ Function that counts the number of occurences of "x" and "y" in an array and returns the one with the highest count.
9
+ @param {Array} scalesKeys - An array of strings that may contain "x" or "y".
10
+ @return {string} - Returns "x" if "x" occurs more times, "y" if "y" occurs more times, or null if they occur the same number of times.
11
+ */
12
+ const checkMultiAxis = (scalesKeys) => {
13
+ let counts = {
14
+ x: 0,
15
+ y: 0,
16
+ };
17
+
18
+ scalesKeys.forEach((axeKey) => {
19
+ const key = getAxisTypeFromKey(axeKey);
20
+ counts[key]++;
21
+ });
22
+ const res =
23
+ (counts.x > counts.y && AxisType.X) ||
24
+ (counts.y > counts.x && AxisType.Y) ||
25
+ (counts.x === counts.y && null);
26
+
27
+ return res;
28
+ };
29
+
30
+ /**
31
+ Function that returns an object containing all the values for a specific key in an array of objects,
32
+ grouped by the unique values of another key in the same objects.
33
+ @param {Array} data - An array of objects to search for the keys.
34
+ @return {Object} - Returns an object with keys representing the unique values for the annotationAxis key in the data array,
35
+ and values representing an array of all values for the value key in the data array.
36
+ */
37
+ const getAnnotationsData = (data) => {
38
+ return data.reduce((acc, obj) => {
39
+ return {
40
+ ...acc,
41
+ [obj.annotationAxis]: [...(acc[obj.annotationAxis] || []), obj.value],
42
+ };
43
+ }, {});
44
+ };
45
+
46
+ /**
47
+ * return data for each axes
48
+ * @function getAxesData
49
+ * @return {object}
50
+ */
51
+ const getAxesData = (scalesKeys, datasets, annotationsData) => {
52
+ const allData =
53
+ datasets?.reduce((acc, item) => [...acc, ...item.data], []) ?? [];
54
+
55
+ /**
56
+ * return data by key and depends on axes count
57
+ * @function getData
58
+ * @return {object}
59
+ */
60
+ const getData = () => {
61
+ return scalesKeys.reduce((acc, axeKey) => {
62
+ const key = getAxisTypeFromKey(axeKey);
63
+ const data = getAnnotationsData(annotationsData);
64
+ const formData = allData
65
+ ?.map((val) => val?.[key])
66
+ .concat(data[key] || []);
67
+
68
+ return {
69
+ ...acc,
70
+ [axeKey]: [...new Set(formData)],
71
+ };
72
+ }, {});
73
+ };
74
+
75
+ /**
76
+ Function that returns an object containing the unique values for a multi axis key in an array of objects.
77
+ @return {Object} - Returns an object with key representing the found key in the scalesKeys array,
78
+ and values representing an array of unique values for that key in the datasets array. If no key is found, an empty object is returned.
79
+ */
80
+ const getDataForMultiAxes = () => {
81
+ const multiAxesKey = checkMultiAxis(scalesKeys);
82
+ const axes = scalesKeys?.filter((key) => key?.includes(multiAxesKey)) ?? [];
83
+ const data = getAnnotationsData(annotationsData);
84
+ const [first, second] = axes;
85
+
86
+ /**
87
+ * Reduces an array of objects and returns an array of values from the specified key
88
+ * @param {Array} arr - The array of objects to be reduced
89
+ * @param {string} key - The key to extract the values from
90
+ * @returns {Array} - An array of values from the specified key
91
+ */
92
+ const reduceData = (arr, key) =>
93
+ arr.reduce((acc, { [key]: value }) => [...acc, value], []);
94
+
95
+ return datasets.reduce(
96
+ (acc, obj) => {
97
+ const include = Object.values(obj).some((val) => axes.includes(val));
98
+ const key = include ? second : first;
99
+
100
+ return {
101
+ ...acc,
102
+ [key]: [
103
+ ...new Set([
104
+ ...acc[key],
105
+ ...reduceData(obj?.data, multiAxesKey),
106
+ ...((!include && data?.[multiAxesKey]) || []),
107
+ ]),
108
+ ],
109
+ };
110
+ },
111
+ { [first]: [], [second]: [] },
112
+ );
113
+ };
114
+
115
+ return {
116
+ ...getData(),
117
+ ...getDataForMultiAxes(),
118
+ };
119
+ };
120
+
121
+ /**
122
+ * Auto scales axis ranges (mix, max) if no explicit range is set
123
+ * - overrides some edge cases not handled well by default chart.js
124
+ * - supports optional padding when `autoAxisPadding` is set
125
+ * - otherwise does not set min/max (falls back to chart.js default)
126
+ *
127
+ * @function autoScale
128
+ * @return {object} scales
129
+ */
130
+ export const autoScale = (options, state, generatedDatasets) => {
131
+ const { additionalAxesOptions = {}, annotations = {} } = options || {};
132
+ const { annotationsData = [] } = annotations || {};
133
+ const scales = getLineChartScales(options, state) || {};
134
+
135
+ if (
136
+ !additionalAxesOptions?.autoAxisPadding &&
137
+ !estimateDataSeriesHaveCloseValues(generatedDatasets)
138
+ ) {
139
+ return scales;
140
+ }
141
+ const scalesKeys = Object.keys(scales) ?? [];
142
+ const data =
143
+ getAxesData(scalesKeys, generatedDatasets, annotationsData) ?? {};
144
+
145
+ const adjustedScales = scalesKeys?.reduce((acc, key) => {
146
+ const { min: propMin = undefined, max: propMax = undefined } = scales[key];
147
+ const { min, max } = getSuggestedAxisRange({
148
+ data: data[key],
149
+ beginAtZero: additionalAxesOptions?.beginAtZero,
150
+ autoAxisPadding: additionalAxesOptions?.autoAxisPadding,
151
+ });
152
+
153
+ const res = {
154
+ [key]: {
155
+ ...scales[key],
156
+ min: propMin ?? min,
157
+ max: propMax ?? max,
158
+ },
159
+ };
160
+
161
+ return { ...acc, ...res };
162
+ }, {});
163
+
164
+ return adjustedScales ?? scales;
165
+ };
@@ -0,0 +1,13 @@
1
+ /** returning boolean condition depends on label position and chart area
2
+ *
3
+ * @return {object} - returning object with boolean props
4
+ */
5
+ export const getCondition = (x, y, left, right, bottom) => {
6
+ const threshold = 100;
7
+ const overLeftSide = x - threshold <= left;
8
+ const overRightSide = x + threshold >= right;
9
+ const overBottomSide =
10
+ x + threshold >= left && x + threshold < right && y + threshold >= bottom;
11
+
12
+ return { overLeftSide, overRightSide, overBottomSide };
13
+ };
@@ -0,0 +1,20 @@
1
+ /** returning destructured data from context
2
+ *
3
+ * @return {object}
4
+ */
5
+ export const getAlignmentData = (context) => {
6
+ const { chart = {}, dataIndex = 0, datasetIndex = 0 } = context || {};
7
+ const { chartArea = {} } = chart;
8
+ const { left = null, right = null, bottom = null } = chartArea;
9
+
10
+ const meta = chart.getDatasetMeta(datasetIndex);
11
+ const { x = null, y = null } = meta?.data?.[dataIndex] || {};
12
+
13
+ return {
14
+ x,
15
+ y,
16
+ left,
17
+ right,
18
+ bottom,
19
+ };
20
+ };
@@ -0,0 +1,25 @@
1
+ import { getCondition } from './get-alignment-condition';
2
+ import { getAlignmentData } from './get-alignment-data';
3
+ import { AlignOptions } from '../../../helpers/enums';
4
+
5
+ /** returning position depends on condition
6
+ *
7
+ * @return {string} - position
8
+ */
9
+ export const getPosition = () => {
10
+ return (context) => {
11
+ const { x, y, left, right, bottom } = getAlignmentData(context);
12
+ const {
13
+ overLeftSide = false,
14
+ overRightSide = false,
15
+ overBottomSide = false,
16
+ } = getCondition(x, y, left, right, bottom);
17
+
18
+ return (
19
+ (overLeftSide && AlignOptions.Right) ||
20
+ (overRightSide && AlignOptions.Left) ||
21
+ (overBottomSide && AlignOptions.End) ||
22
+ AlignOptions.Start
23
+ );
24
+ };
25
+ };
@@ -0,0 +1,10 @@
1
+ export const getAxesRangesFromChart = (chartRef) => {
2
+ const { scales = {} } = chartRef.current || {};
3
+ return Object.entries(scales).map(([key, { min, max }]) => {
4
+ return {
5
+ id: key,
6
+ min,
7
+ max,
8
+ };
9
+ });
10
+ };
@@ -0,0 +1,21 @@
1
+ import { AUTO } from '../../helpers/chart-consts';
2
+ import { getPosition } from './datalabels-alignment/get-datalabels-position';
3
+
4
+ /**
5
+ * adjusts the position of the label depends on chart area
6
+ *
7
+ * @param {import('./line-chart.interface').ILineChartOptions} options - line chart options object
8
+ * @return {object} - returning position, if label exist in datasets item
9
+ */
10
+ const getLineChartDataLabels = (options) => {
11
+ return options.graph.showDataLabels
12
+ ? {
13
+ display: AUTO,
14
+ align: getPosition(),
15
+ formatter: (value, context) =>
16
+ context.dataset.data[context.dataIndex].label || '',
17
+ }
18
+ : { display: false };
19
+ };
20
+
21
+ export default getLineChartDataLabels;