@oliasoft-open-source/charts-library 2.13.5 → 2.14.0

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 (33) hide show
  1. package/package.json +2 -2
  2. package/release-notes.md +3 -0
  3. package/src/components/controls/axes-options/axes-options.jsx +25 -4
  4. package/src/components/line-chart/{line-chart-consts.js → constants/line-chart-consts.js} +1 -0
  5. package/src/components/line-chart/controls/axes-options/action-types.js +5 -0
  6. package/src/components/{controls → line-chart/controls}/axes-options/axes-options-form-state.js +18 -19
  7. package/src/components/{controls → line-chart/controls}/controls.jsx +88 -33
  8. package/src/components/line-chart/controls/drag-options.jsx +104 -0
  9. package/src/components/line-chart/controls/legend-options.jsx +32 -0
  10. package/src/components/{controls → line-chart/controls}/line-options.jsx +14 -5
  11. package/src/components/line-chart/hooks/use-chart-functions.js +257 -0
  12. package/src/components/line-chart/hooks/use-chart-options.js +155 -0
  13. package/src/components/line-chart/hooks/use-chart-plugins.js +26 -0
  14. package/src/components/line-chart/hooks/use-toggle-handler.js +33 -0
  15. package/src/components/line-chart/line-chart.jsx +49 -410
  16. package/src/components/line-chart/state/initial-state.js +7 -8
  17. package/src/components/line-chart/state/line-chart-reducer.js +68 -86
  18. package/src/components/line-chart/state/use-chart-state.js +2 -1
  19. package/src/components/line-chart/{axis-scales → utils/axis-scales}/axis-scales.js +3 -3
  20. package/src/components/line-chart/{datalabels-alignment → utils/datalabels-alignment}/get-datalabels-position.js +1 -1
  21. package/src/components/line-chart/utils/generate-line-chart-datasets.js +108 -0
  22. package/src/components/line-chart/{get-line-chart-data-labels.js → utils/get-line-chart-data-labels.js} +2 -2
  23. package/src/components/line-chart/{get-line-chart-scales.js → utils/get-line-chart-scales.js} +11 -11
  24. package/src/components/line-chart/{get-line-chart-tooltips.js → utils/get-line-chart-tooltips.js} +6 -3
  25. package/src/helpers/chart-utils.js +3 -5
  26. package/src/helpers/enums.js +9 -0
  27. package/src/components/controls/drag-options.jsx +0 -98
  28. package/src/components/controls/legend-options.jsx +0 -25
  29. /package/src/components/{controls → line-chart/controls}/controls.module.less +0 -0
  30. /package/src/components/line-chart/{datalabels-alignment → utils/datalabels-alignment}/get-alignment-condition.js +0 -0
  31. /package/src/components/line-chart/{datalabels-alignment → utils/datalabels-alignment}/get-alignment-data.js +0 -0
  32. /package/src/components/line-chart/{get-axes-ranges-from-chart.js → utils/get-axes-ranges-from-chart.js} +0 -0
  33. /package/src/components/line-chart/{line-chart-utils.js → utils/line-chart-utils.js} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oliasoft-open-source/charts-library",
3
- "version": "2.13.5",
3
+ "version": "2.14.0",
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": {
@@ -33,7 +33,7 @@
33
33
  ]
34
34
  },
35
35
  "dependencies": {
36
- "chart.js": "^3.9.1",
36
+ "chart.js": "^4.2.1",
37
37
  "chartjs-plugin-annotation": "^1.4.0",
38
38
  "chartjs-plugin-datalabels": "^2.1.0",
39
39
  "chartjs-plugin-dragdata": "^2.2.5",
package/release-notes.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # Charts Library Release Notes
2
2
 
3
+ ## 2.14.0
4
+ - Refactored line chart, for better performance, readable and reduce not needed re-renders
5
+
3
6
  ## 2.13.5
4
7
 
5
8
  - Fixed reset range when subComponent update data, prevent extra re-renders when range have same deep values but different reference
@@ -14,13 +14,14 @@ import {
14
14
  Tooltip,
15
15
  } from '@oliasoft-open-source/react-ui-library';
16
16
  import { RiRuler2Line } from 'react-icons/ri';
17
- import { toNum } from '../../line-chart/line-chart-utils';
17
+ import PropTypes from 'prop-types';
18
+ import { toNum } from '@oliasoft-open-source/units';
18
19
  import {
19
- actionTypes,
20
- reducer,
21
20
  initializeFormState,
21
+ reducer,
22
22
  validateAxes,
23
- } from './axes-options-form-state';
23
+ } from '../../line-chart/controls/axes-options/axes-options-form-state';
24
+ import { actionTypes } from '../../line-chart/controls/axes-options/action-types';
24
25
 
25
26
  const AxesOptionsPopover = ({
26
27
  initialAxesRanges,
@@ -234,3 +235,23 @@ export const AxesOptions = ({
234
235
  </Popover>
235
236
  );
236
237
  };
238
+
239
+ AxesOptionsPopover.defaultProps = {
240
+ depthType: null,
241
+ initialAxesRanges: [],
242
+ close: () => {},
243
+ };
244
+
245
+ AxesOptionsPopover.propTypes = {
246
+ initialAxesRanges: PropTypes.arrayOf(PropTypes.shape({})),
247
+ axes: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
248
+ controlsAxesLabels: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
249
+ onUpdateAxes: PropTypes.func.isRequired,
250
+ onResetAxes: PropTypes.func.isRequired,
251
+ close: PropTypes.func,
252
+ depthType: PropTypes.shape({
253
+ selectedDepthType: PropTypes.string,
254
+ setSelectedDepthType: PropTypes.func,
255
+ options: PropTypes.arrayOf(PropTypes.string),
256
+ }),
257
+ };
@@ -5,3 +5,4 @@ export const BORDER_JOIN_STYLE = 'round';
5
5
  export const DEFAULT_LINE_POINT_RADIUS = 0;
6
6
  export const DEFAULT_BACKGROUND_COLOR = 'transparent';
7
7
  export const ZOOM_BOX_BACKGROUND_COLOR = 'rgba(0, 0, 0, 0.1)';
8
+ export const DOUBLE_CLICK = 'dblclick';
@@ -0,0 +1,5 @@
1
+ export const actionTypes = Object.freeze({
2
+ reset: 'reset',
3
+ valueUpdated: 'valueUpdated',
4
+ unitUpdated: 'unitUpdated',
5
+ });
@@ -3,13 +3,8 @@ import {
3
3
  isGreaterThanMin,
4
4
  isLessThanMax,
5
5
  validNumber,
6
- } from '../../line-chart/line-chart-utils';
7
-
8
- export const actionTypes = Object.freeze({
9
- reset: 'reset',
10
- valueUpdated: 'valueUpdated',
11
- unitUpdated: 'unitUpdated',
12
- });
6
+ } from '../../utils/line-chart-utils';
7
+ import { actionTypes } from './action-types';
13
8
 
14
9
  /**
15
10
  * Initialize local component form state for a custom loads density table
@@ -39,23 +34,27 @@ export const initializeFormState = ({ initialAxesRanges, axes = [] }) => {
39
34
 
40
35
  const isEmptyString = (value) => value === '';
41
36
 
37
+ const createErrorMessages = (value, compareTo, type) => {
38
+ const errors = [];
39
+ if (isEmptyString(value)) errors.push('Must have a value');
40
+ if (!validNumber(value)) errors.push('Must be a number');
41
+
42
+ if (type === 'min' && !isLessThanMax(value, compareTo)) {
43
+ errors.push('Must be less than max');
44
+ } else if (type === 'max' && !isGreaterThanMin(value, compareTo)) {
45
+ errors.push('Must be greater than min');
46
+ }
47
+
48
+ return errors;
49
+ };
50
+
42
51
  export const validateAxes = (formState) => {
43
52
  return formState.reduce(
44
53
  (acc, { id, min, max }) => {
45
54
  return produce(acc, (draft) => {
46
55
  const errors = {
47
- min: [
48
- ...(isEmptyString(min) ? ['Must have a value'] : []),
49
- ...(!validNumber(min) ? ['Must be a number'] : []),
50
- ...(!isLessThanMax(min, max) ? ['Must be less than max'] : []),
51
- ],
52
- max: [
53
- ...(isEmptyString(max) ? ['Must have a value'] : []),
54
- ...(!validNumber(max) ? ['Must be a number'] : []),
55
- ...(!isGreaterThanMin(max, min)
56
- ? ['Must be greater than min']
57
- : []),
58
- ],
56
+ min: createErrorMessages(min, max, 'min'),
57
+ max: createErrorMessages(max, min, 'max'),
59
58
  };
60
59
  draft.errors.push({
61
60
  id,
@@ -5,56 +5,73 @@ import {
5
5
  RiLineChartLine,
6
6
  RiTableLine,
7
7
  } from 'react-icons/ri';
8
+ import PropTypes from 'prop-types';
8
9
  import { LineOptions } from './line-options';
9
10
  import { DragOptions } from './drag-options';
10
- import { AxesOptions } from './axes-options/axes-options';
11
11
  import { LegendOptions } from './legend-options';
12
12
  import styles from './controls.module.less';
13
+ import { useToggleHandlers } from '../hooks/use-toggle-handler';
14
+ import { useChartFunctions } from '../hooks/use-chart-functions';
15
+ import { AxesOptions } from '../../controls/axes-options/axes-options';
13
16
 
14
17
  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
- depthType,
38
- enableDragPoints,
39
- isDragDataAllowed,
40
- onToggleDragPoints,
41
- onDisableDragOptions,
18
+ props: { headerComponent, subheaderComponent, table },
19
+ chartRef,
20
+ state,
21
+ options,
22
+ dispatch,
23
+ generatedDatasets,
42
24
  }) => {
25
+ const {
26
+ enableDragPoints,
27
+ initialAxesRanges,
28
+ legendEnabled,
29
+ lineEnabled,
30
+ panEnabled,
31
+ pointsEnabled,
32
+ showTable,
33
+ zoomEnabled,
34
+ } = state;
35
+ const { dragData } = options;
36
+ const {
37
+ onToggleLegend,
38
+ onToggleLine,
39
+ onTogglePan,
40
+ onTogglePoints,
41
+ onToggleTable,
42
+ onToggleZoom,
43
+ onToggleDragPoints,
44
+ onDisableDragOptions,
45
+ } = useToggleHandlers(dispatch);
46
+ const {
47
+ handleDownload,
48
+ controlsAxes,
49
+ controlsAxesLabels,
50
+ onResetAxes,
51
+ onUpdateAxes,
52
+ } = useChartFunctions({
53
+ chartRef,
54
+ state,
55
+ options,
56
+ dispatch,
57
+ generatedDatasets,
58
+ });
59
+
43
60
  return (
44
61
  <>
45
62
  <div className={styles.controls}>
46
- {!!chart.options.title && <Text bold>{chart.options.title}</Text>}
63
+ {!!options.title && <Text bold>{options.title}</Text>}
47
64
  {headerComponent}
48
65
  <div className={styles.buttons}>
49
66
  {!showTable && (
50
67
  <>
51
68
  <AxesOptions
52
69
  initialAxesRanges={initialAxesRanges}
53
- axes={axes}
70
+ axes={controlsAxes}
54
71
  controlsAxesLabels={controlsAxesLabels}
55
72
  onUpdateAxes={onUpdateAxes}
56
73
  onResetAxes={onResetAxes}
57
- depthType={depthType}
74
+ depthType={options.depthType}
58
75
  />
59
76
  <LineOptions
60
77
  lineEnabled={lineEnabled}
@@ -73,7 +90,7 @@ const Controls = ({
73
90
  colored="muted"
74
91
  round
75
92
  icon={<RiFileDownloadLine />}
76
- onClick={onDownload}
93
+ onClick={handleDownload}
77
94
  />
78
95
  </Tooltip>
79
96
  <DragOptions
@@ -82,7 +99,7 @@ const Controls = ({
82
99
  onTogglePan={onTogglePan}
83
100
  onToggleZoom={onToggleZoom}
84
101
  enableDragPoints={enableDragPoints}
85
- isDragDataAllowed={isDragDataAllowed}
102
+ isDragDataAllowed={dragData.enableDragData}
86
103
  onToggleDragPoints={onToggleDragPoints}
87
104
  onDisableDragOptions={onDisableDragOptions}
88
105
  />
@@ -111,4 +128,42 @@ const Controls = ({
111
128
  );
112
129
  };
113
130
 
131
+ Controls.defaultProps = {
132
+ props: {
133
+ headerComponent: null,
134
+ subheaderComponent: null,
135
+ table: null,
136
+ },
137
+ state: {
138
+ initialAxesRanges: [],
139
+ },
140
+ options: {},
141
+ };
142
+
143
+ Controls.propTypes = {
144
+ props: PropTypes.shape({
145
+ headerComponent: PropTypes.node,
146
+ subheaderComponent: PropTypes.node,
147
+ table: PropTypes.node,
148
+ }),
149
+ chartRef: PropTypes.object.isRequired,
150
+ state: PropTypes.shape({
151
+ enableDragPoints: PropTypes.bool,
152
+ initialAxesRanges: PropTypes.arrayOf(PropTypes.shape({})),
153
+ legendEnabled: PropTypes.bool,
154
+ lineEnabled: PropTypes.bool,
155
+ panEnabled: PropTypes.bool,
156
+ pointsEnabled: PropTypes.bool,
157
+ showTable: PropTypes.bool,
158
+ zoomEnabled: PropTypes.bool,
159
+ }),
160
+ options: PropTypes.shape({
161
+ dragData: PropTypes.shape({
162
+ enableDragData: PropTypes.bool,
163
+ }),
164
+ }),
165
+ dispatch: PropTypes.func.isRequired,
166
+ generatedDatasets: PropTypes.arrayOf(PropTypes.object).isRequired,
167
+ };
168
+
114
169
  export default Controls;
@@ -0,0 +1,104 @@
1
+ import React, { useMemo } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { Flex, Menu, Text } from '@oliasoft-open-source/react-ui-library';
4
+ import { RiDragMove2Line, RiForbidLine, RiZoomInLine } from 'react-icons/ri';
5
+ import { TbHandStop } from 'react-icons/tb';
6
+
7
+ export const DragOptions = ({
8
+ onTogglePan,
9
+ onToggleZoom,
10
+ panEnabled,
11
+ zoomEnabled,
12
+ enableDragPoints,
13
+ isDragDataAllowed,
14
+ onToggleDragPoints,
15
+ onDisableDragOptions,
16
+ }) => {
17
+ const options = useMemo(
18
+ () => [
19
+ {
20
+ buttonLabel: 'Drag to zoom',
21
+ label: (
22
+ <Flex direction="column">
23
+ <Text>Drag to zoom</Text>
24
+ <Text small muted>
25
+ Double click on canvas to reset
26
+ </Text>
27
+ </Flex>
28
+ ),
29
+ icon: <RiZoomInLine />,
30
+ selected: zoomEnabled,
31
+ onClick: onToggleZoom,
32
+ },
33
+ {
34
+ buttonLabel: 'Drag to pan',
35
+ label: (
36
+ <Flex direction="column">
37
+ <Text>Drag to pan</Text>
38
+ <Text small muted>
39
+ Double click on canvas to reset
40
+ </Text>
41
+ </Flex>
42
+ ),
43
+ icon: <RiDragMove2Line />,
44
+ selected: panEnabled,
45
+ onClick: onTogglePan,
46
+ },
47
+ ...(isDragDataAllowed
48
+ ? [
49
+ {
50
+ label: 'Drag to move points',
51
+ icon: <TbHandStop />,
52
+ selected: enableDragPoints,
53
+ type: 'Option',
54
+ onClick: onToggleDragPoints,
55
+ },
56
+ ]
57
+ : []),
58
+ {
59
+ label: 'Drag disabled',
60
+ icon: <RiForbidLine />,
61
+ selected: !zoomEnabled && !panEnabled && !enableDragPoints,
62
+ onClick: onDisableDragOptions,
63
+ },
64
+ ],
65
+ [zoomEnabled, panEnabled, enableDragPoints, isDragDataAllowed],
66
+ );
67
+
68
+ const selectedOption = options.find((option) => option.selected);
69
+ const optionsWithDragPoints = options.map(
70
+ ({ icon, label, onClick, selected }) => ({
71
+ icon,
72
+ label,
73
+ type: 'Option',
74
+ onClick,
75
+ disabled: selected,
76
+ }),
77
+ );
78
+
79
+ return (
80
+ <Menu
81
+ menu={{
82
+ label: selectedOption.buttonLabel || selectedOption.label,
83
+ sections: optionsWithDragPoints,
84
+ trigger: 'DropDownButton',
85
+ small: true,
86
+ }}
87
+ />
88
+ );
89
+ };
90
+
91
+ DragOptions.defaultProps = {
92
+ enableDragPoints: false,
93
+ };
94
+
95
+ DragOptions.propTypes = {
96
+ onTogglePan: PropTypes.func.isRequired,
97
+ onToggleZoom: PropTypes.func.isRequired,
98
+ panEnabled: PropTypes.bool.isRequired,
99
+ zoomEnabled: PropTypes.bool.isRequired,
100
+ enableDragPoints: PropTypes.bool,
101
+ isDragDataAllowed: PropTypes.bool.isRequired,
102
+ onToggleDragPoints: PropTypes.func.isRequired,
103
+ onDisableDragOptions: PropTypes.func.isRequired,
104
+ };
@@ -0,0 +1,32 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { Button, Icon, Tooltip } from '@oliasoft-open-source/react-ui-library';
4
+ import { RiListUnordered } from 'react-icons/ri';
5
+ import listHideIcon from '../../../assets/icons/list-hide.svg';
6
+
7
+ export const LegendOptions = ({ legendEnabled, onToggleLegend }) => {
8
+ const tooltipText = legendEnabled ? 'Hide Legend' : 'Show Legend';
9
+ const icon = legendEnabled ? (
10
+ <RiListUnordered />
11
+ ) : (
12
+ <Icon icon={listHideIcon} />
13
+ );
14
+
15
+ return (
16
+ <Tooltip text={tooltipText} placement="bottom">
17
+ <Button
18
+ small
19
+ basic
20
+ colored="muted"
21
+ round
22
+ icon={icon}
23
+ onClick={onToggleLegend}
24
+ />
25
+ </Tooltip>
26
+ );
27
+ };
28
+
29
+ LegendOptions.propTypes = {
30
+ legendEnabled: PropTypes.bool.isRequired,
31
+ onToggleLegend: PropTypes.func.isRequired,
32
+ };
@@ -1,8 +1,9 @@
1
1
  import React from 'react';
2
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';
3
+ import PropTypes from 'prop-types';
4
+ import lineOnlyIcon from '../../../assets/icons/line-only.svg';
5
+ import pointOnlyIcon from '../../../assets/icons/point-only.svg';
6
+ import lineAndPointIcon from '../../../assets/icons/line-and-point.svg';
6
7
 
7
8
  export const LineOptions = ({
8
9
  lineEnabled,
@@ -10,7 +11,6 @@ export const LineOptions = ({
10
11
  onTogglePoints,
11
12
  pointsEnabled,
12
13
  }) => {
13
- // TODO: Translate strings
14
14
  const options = [
15
15
  {
16
16
  label: 'Points & lines',
@@ -38,7 +38,9 @@ export const LineOptions = ({
38
38
  },
39
39
  },
40
40
  ];
41
- const selectedOption = options.filter((option) => option.selected)[0];
41
+
42
+ const selectedOption = options.find((option) => option.selected);
43
+
42
44
  return (
43
45
  <Tooltip text={selectedOption.label} placement="bottom">
44
46
  <Button
@@ -52,3 +54,10 @@ export const LineOptions = ({
52
54
  </Tooltip>
53
55
  );
54
56
  };
57
+
58
+ LineOptions.propTypes = {
59
+ lineEnabled: PropTypes.bool.isRequired,
60
+ onToggleLine: PropTypes.func.isRequired,
61
+ onTogglePoints: PropTypes.func.isRequired,
62
+ pointsEnabled: PropTypes.bool.isRequired,
63
+ };