@gravity-ui/charts 1.26.0 → 1.27.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 (113) hide show
  1. package/dist/cjs/components/AxisX/AxisX.d.ts +5 -20
  2. package/dist/cjs/components/AxisX/AxisX.js +167 -212
  3. package/dist/cjs/components/AxisX/prepare-axis-data.d.ts +10 -0
  4. package/dist/cjs/components/AxisX/prepare-axis-data.js +267 -0
  5. package/dist/cjs/components/AxisX/styles.css +7 -10
  6. package/dist/cjs/components/AxisX/types.d.ts +85 -0
  7. package/dist/cjs/components/AxisX/types.js +1 -0
  8. package/dist/cjs/components/AxisY/AxisY.js +9 -3
  9. package/dist/cjs/components/AxisY/prepare-axis-data.d.ts +1 -1
  10. package/dist/cjs/components/AxisY/prepare-axis-data.js +17 -81
  11. package/dist/cjs/components/AxisY/prepare-axis-title.d.ts +16 -0
  12. package/dist/cjs/components/AxisY/prepare-axis-title.js +149 -0
  13. package/dist/cjs/components/AxisY/types.d.ts +16 -4
  14. package/dist/cjs/components/AxisY/utils.js +1 -8
  15. package/dist/cjs/components/ChartInner/index.js +62 -50
  16. package/dist/cjs/components/ChartInner/useChartInnerProps.d.ts +7 -7
  17. package/dist/cjs/components/ChartInner/useChartInnerProps.js +15 -20
  18. package/dist/cjs/components/ChartInner/useChartInnerState.d.ts +3 -2
  19. package/dist/cjs/components/ChartInner/useChartInnerState.js +23 -4
  20. package/dist/cjs/components/Legend/index.js +21 -14
  21. package/dist/cjs/components/RangeSlider/index.d.ts +5 -3
  22. package/dist/cjs/components/RangeSlider/index.js +36 -4
  23. package/dist/cjs/components/Title/index.js +2 -2
  24. package/dist/cjs/components/utils.d.ts +9 -0
  25. package/dist/cjs/components/utils.js +34 -0
  26. package/dist/cjs/constants/defaults/axis.js +1 -1
  27. package/dist/cjs/hooks/index.d.ts +0 -1
  28. package/dist/cjs/hooks/index.js +0 -1
  29. package/dist/cjs/hooks/useAxis/index.js +10 -0
  30. package/dist/cjs/hooks/useAxis/types.d.ts +3 -0
  31. package/dist/cjs/hooks/useAxis/x-axis.d.ts +2 -1
  32. package/dist/cjs/hooks/useAxis/x-axis.js +36 -28
  33. package/dist/cjs/hooks/useAxis/y-axis.js +38 -11
  34. package/dist/cjs/hooks/useAxisScales/index.d.ts +4 -1
  35. package/dist/cjs/hooks/useAxisScales/index.js +8 -7
  36. package/dist/cjs/hooks/useAxisScales/utils.d.ts +6 -0
  37. package/dist/cjs/hooks/useAxisScales/utils.js +17 -0
  38. package/dist/cjs/hooks/useChartDimensions/utils.js +7 -1
  39. package/dist/cjs/hooks/useNormalizedOriginalData/index.d.ts +1 -7
  40. package/dist/cjs/hooks/useRangeSlider/index.js +1 -2
  41. package/dist/cjs/hooks/useRangeSlider/types.d.ts +1 -3
  42. package/dist/cjs/hooks/useZoom/index.d.ts +1 -0
  43. package/dist/cjs/hooks/useZoom/index.js +12 -2
  44. package/dist/cjs/types/chart/axis.d.ts +45 -17
  45. package/dist/cjs/types/chart/title.d.ts +3 -0
  46. package/dist/{esm/utils/chart/axis.d.ts → cjs/utils/chart/axis/common.d.ts} +4 -16
  47. package/dist/{esm/utils/chart/axis.js → cjs/utils/chart/axis/common.js} +7 -40
  48. package/dist/cjs/utils/chart/axis/x-axis.d.ts +12 -0
  49. package/dist/cjs/utils/chart/axis/x-axis.js +78 -0
  50. package/dist/cjs/utils/chart/axis-generators/bottom.js +1 -1
  51. package/dist/cjs/utils/chart/index.d.ts +1 -1
  52. package/dist/cjs/utils/chart/index.js +1 -1
  53. package/dist/cjs/utils/chart/text.d.ts +0 -6
  54. package/dist/cjs/utils/chart/text.js +7 -19
  55. package/dist/esm/components/AxisX/AxisX.d.ts +5 -20
  56. package/dist/esm/components/AxisX/AxisX.js +167 -212
  57. package/dist/esm/components/AxisX/prepare-axis-data.d.ts +10 -0
  58. package/dist/esm/components/AxisX/prepare-axis-data.js +267 -0
  59. package/dist/esm/components/AxisX/styles.css +7 -10
  60. package/dist/esm/components/AxisX/types.d.ts +85 -0
  61. package/dist/esm/components/AxisX/types.js +1 -0
  62. package/dist/esm/components/AxisY/AxisY.js +9 -3
  63. package/dist/esm/components/AxisY/prepare-axis-data.d.ts +1 -1
  64. package/dist/esm/components/AxisY/prepare-axis-data.js +17 -81
  65. package/dist/esm/components/AxisY/prepare-axis-title.d.ts +16 -0
  66. package/dist/esm/components/AxisY/prepare-axis-title.js +149 -0
  67. package/dist/esm/components/AxisY/types.d.ts +16 -4
  68. package/dist/esm/components/AxisY/utils.js +1 -8
  69. package/dist/esm/components/ChartInner/index.js +62 -50
  70. package/dist/esm/components/ChartInner/useChartInnerProps.d.ts +7 -7
  71. package/dist/esm/components/ChartInner/useChartInnerProps.js +15 -20
  72. package/dist/esm/components/ChartInner/useChartInnerState.d.ts +3 -2
  73. package/dist/esm/components/ChartInner/useChartInnerState.js +23 -4
  74. package/dist/esm/components/Legend/index.js +21 -14
  75. package/dist/esm/components/RangeSlider/index.d.ts +5 -3
  76. package/dist/esm/components/RangeSlider/index.js +36 -4
  77. package/dist/esm/components/Title/index.js +2 -2
  78. package/dist/esm/components/utils.d.ts +9 -0
  79. package/dist/esm/components/utils.js +34 -0
  80. package/dist/esm/constants/defaults/axis.js +1 -1
  81. package/dist/esm/hooks/index.d.ts +0 -1
  82. package/dist/esm/hooks/index.js +0 -1
  83. package/dist/esm/hooks/useAxis/index.js +10 -0
  84. package/dist/esm/hooks/useAxis/types.d.ts +3 -0
  85. package/dist/esm/hooks/useAxis/x-axis.d.ts +2 -1
  86. package/dist/esm/hooks/useAxis/x-axis.js +36 -28
  87. package/dist/esm/hooks/useAxis/y-axis.js +38 -11
  88. package/dist/esm/hooks/useAxisScales/index.d.ts +4 -1
  89. package/dist/esm/hooks/useAxisScales/index.js +8 -7
  90. package/dist/esm/hooks/useAxisScales/utils.d.ts +6 -0
  91. package/dist/esm/hooks/useAxisScales/utils.js +17 -0
  92. package/dist/esm/hooks/useChartDimensions/utils.js +7 -1
  93. package/dist/esm/hooks/useNormalizedOriginalData/index.d.ts +1 -7
  94. package/dist/esm/hooks/useRangeSlider/index.js +1 -2
  95. package/dist/esm/hooks/useRangeSlider/types.d.ts +1 -3
  96. package/dist/esm/hooks/useZoom/index.d.ts +1 -0
  97. package/dist/esm/hooks/useZoom/index.js +12 -2
  98. package/dist/esm/types/chart/axis.d.ts +45 -17
  99. package/dist/esm/types/chart/title.d.ts +3 -0
  100. package/dist/{cjs/utils/chart/axis.d.ts → esm/utils/chart/axis/common.d.ts} +4 -16
  101. package/dist/{cjs/utils/chart/axis.js → esm/utils/chart/axis/common.js} +7 -40
  102. package/dist/esm/utils/chart/axis/x-axis.d.ts +12 -0
  103. package/dist/esm/utils/chart/axis/x-axis.js +78 -0
  104. package/dist/esm/utils/chart/axis-generators/bottom.js +1 -1
  105. package/dist/esm/utils/chart/index.d.ts +1 -1
  106. package/dist/esm/utils/chart/index.js +1 -1
  107. package/dist/esm/utils/chart/text.d.ts +0 -6
  108. package/dist/esm/utils/chart/text.js +7 -19
  109. package/package.json +1 -1
  110. package/dist/cjs/hooks/useChartOptions/index.d.ts +0 -16
  111. package/dist/cjs/hooks/useChartOptions/index.js +0 -21
  112. package/dist/esm/hooks/useChartOptions/index.d.ts +0 -16
  113. package/dist/esm/hooks/useChartOptions/index.js +0 -21
@@ -0,0 +1,149 @@
1
+ import { calculateCos, calculateSin, getLabelsSize, getTextSizeFn, getTextWithElipsis, wrapText, } from '../../utils';
2
+ export async function prepareSvgYAxisTitle({ axis, axisTop, axisHeight, axisWidth, axisLabelsWidth, }) {
3
+ if (!axis.title.text || axis.title.html) {
4
+ return null;
5
+ }
6
+ const getTitleTextSize = getTextSizeFn({ style: axis.title.style });
7
+ const rotateAngle = axis.title.rotation;
8
+ const sin = Math.abs(calculateSin(rotateAngle));
9
+ const cos = Math.abs(calculateCos(rotateAngle));
10
+ const titleContent = [];
11
+ const titleMaxWidth = rotateAngle === 0 ? axis.title.maxWidth : sin * axisHeight;
12
+ if (axis.title.maxRowCount > 1) {
13
+ let titleTextRows = await wrapText({
14
+ text: axis.title.text,
15
+ style: axis.title.style,
16
+ width: titleMaxWidth,
17
+ getTextSize: getTitleTextSize,
18
+ });
19
+ titleTextRows = titleTextRows.reduce((acc, row, index) => {
20
+ if (index < axis.title.maxRowCount) {
21
+ acc.push(row);
22
+ }
23
+ else {
24
+ acc[axis.title.maxRowCount - 1].text += row.text;
25
+ }
26
+ return acc;
27
+ }, []);
28
+ for (let i = 0; i < titleTextRows.length; i++) {
29
+ const textRow = titleTextRows[i];
30
+ let textRowContent = textRow.text.trim();
31
+ if (i === titleTextRows.length - 1) {
32
+ textRowContent = await getTextWithElipsis({
33
+ text: textRowContent,
34
+ maxWidth: titleMaxWidth,
35
+ getTextWidth: async (s) => (await getTitleTextSize(s)).width,
36
+ });
37
+ }
38
+ const textRowSize = await getTitleTextSize(textRowContent);
39
+ titleContent.push({
40
+ text: textRowContent,
41
+ x: 0,
42
+ y: textRow.y,
43
+ size: textRowSize,
44
+ });
45
+ }
46
+ }
47
+ else {
48
+ const text = await getTextWithElipsis({
49
+ text: axis.title.text,
50
+ maxWidth: titleMaxWidth,
51
+ getTextWidth: async (s) => (await getTitleTextSize(s)).width,
52
+ });
53
+ titleContent.push({
54
+ text,
55
+ x: 0,
56
+ y: 0,
57
+ size: await getTitleTextSize(text),
58
+ });
59
+ }
60
+ const originalTextSize = titleContent.reduce((acc, item) => {
61
+ acc.width = Math.max(acc.width, item.size.width);
62
+ acc.height += item.size.height;
63
+ return acc;
64
+ }, { width: 0, height: 0 });
65
+ const rotatedTitleSize = rotateAngle === 0
66
+ ? originalTextSize
67
+ : {
68
+ width: sin * originalTextSize.height + cos * originalTextSize.width,
69
+ height: sin * originalTextSize.width + cos * originalTextSize.height,
70
+ };
71
+ const bottom = Math.max(0, calculateSin(rotateAngle) * originalTextSize.width);
72
+ let y = 0;
73
+ switch (axis.title.align) {
74
+ case 'left': {
75
+ y = -bottom + axisHeight;
76
+ break;
77
+ }
78
+ case 'center': {
79
+ y = -bottom + axisHeight / 2 + rotatedTitleSize.height / 2;
80
+ break;
81
+ }
82
+ case 'right': {
83
+ y = -bottom + rotatedTitleSize.height;
84
+ break;
85
+ }
86
+ }
87
+ const left = rotateAngle === 0
88
+ ? Math.min(originalTextSize.width, axis.title.maxWidth)
89
+ : Math.min(0, calculateCos(rotateAngle) * originalTextSize.width);
90
+ const x = axis.position === 'left'
91
+ ? -left - axisLabelsWidth - axis.labels.margin - axis.title.margin
92
+ : -left + axisWidth + axisLabelsWidth + axis.labels.margin + axis.title.margin;
93
+ return {
94
+ html: false,
95
+ content: titleContent,
96
+ style: axis.title.style,
97
+ size: rotatedTitleSize,
98
+ x,
99
+ y: axisTop + y,
100
+ rotate: rotateAngle,
101
+ offset: -(originalTextSize.height / titleContent.length) * (titleContent.length - 1),
102
+ };
103
+ }
104
+ export async function prepareHtmlYAxisTitle({ axis, axisTop, axisHeight, axisWidth, axisLabelsWidth, }) {
105
+ if (!axis.title.text || !axis.title.html) {
106
+ return null;
107
+ }
108
+ const content = axis.title.text;
109
+ const rotateAngle = axis.title.rotation;
110
+ const titleMaxWidth = rotateAngle === 0 ? axis.title.maxWidth : axisHeight;
111
+ const labelSize = await getLabelsSize({
112
+ labels: [content],
113
+ html: true,
114
+ style: Object.assign(Object.assign({}, axis.title.style), { whiteSpace: 'nowrap' }),
115
+ });
116
+ const size = { width: labelSize.maxWidth, height: labelSize.maxHeight };
117
+ const rotatedTitleSize = rotateAngle === 0 ? size : { width: size.height, height: size.width };
118
+ let y = 0;
119
+ switch (axis.title.align) {
120
+ case 'left': {
121
+ const yOffset = rotateAngle === 0 ? -rotatedTitleSize.height : 0;
122
+ y = axisHeight + yOffset;
123
+ break;
124
+ }
125
+ case 'center': {
126
+ const yOffset = rotateAngle === 0 ? -rotatedTitleSize.height / 2 : rotatedTitleSize.height / 2;
127
+ y = axisHeight / 2 + yOffset;
128
+ break;
129
+ }
130
+ case 'right': {
131
+ y = rotateAngle === 0 ? 0 : rotatedTitleSize.height;
132
+ break;
133
+ }
134
+ }
135
+ const x = axis.position === 'left'
136
+ ? -Math.min(titleMaxWidth, rotatedTitleSize.width) -
137
+ axisLabelsWidth -
138
+ axis.labels.margin -
139
+ axis.title.margin
140
+ : axisWidth + axisLabelsWidth + axis.labels.margin + axis.title.margin;
141
+ return {
142
+ html: true,
143
+ content: `<div style="max-width: ${titleMaxWidth}px; overflow: hidden; white-space: nowrap; transform-origin: 0 0; transform: rotate(${rotateAngle}deg);">${content}</div>`,
144
+ style: axis.title.style,
145
+ size: rotatedTitleSize,
146
+ x,
147
+ y: axisTop + y,
148
+ };
149
+ }
@@ -1,5 +1,5 @@
1
1
  import type { DashStyle } from 'src/constants';
2
- import type { BaseTextStyle, HtmlItem, PlotLayerPlacement } from '../../types';
2
+ import type { BaseTextStyle, HtmlItem, PlotLayerPlacement, PointPosition } from '../../types';
3
3
  export type TextRowData = {
4
4
  text: string;
5
5
  x: number;
@@ -22,14 +22,15 @@ export type AxisSvgLabelData = {
22
22
  };
23
23
  };
24
24
  export type AxisTickLine = {
25
- points: [number, number][];
25
+ points: PointPosition[];
26
26
  };
27
27
  export type AxisTickData = {
28
28
  line: AxisTickLine | null;
29
29
  svgLabel: AxisSvgLabelData | null;
30
30
  htmlLabel: HtmlItem | null;
31
31
  };
32
- export type AxisTitleData = {
32
+ export type SvgAxisTitleData = {
33
+ html: false;
33
34
  content: TextRowData[];
34
35
  style: BaseTextStyle;
35
36
  size: {
@@ -41,6 +42,17 @@ export type AxisTitleData = {
41
42
  rotate: number;
42
43
  offset: number;
43
44
  };
45
+ export type HtmlAxisTitleData = {
46
+ html: true;
47
+ content: string;
48
+ style: BaseTextStyle & React.CSSProperties;
49
+ size: {
50
+ width: number;
51
+ height: number;
52
+ };
53
+ x: number;
54
+ y: number;
55
+ };
44
56
  export type AxisPlotLineLabel = {
45
57
  text: string;
46
58
  style: BaseTextStyle;
@@ -76,7 +88,7 @@ export type AxisDomainData = {
76
88
  };
77
89
  export type AxisYData = {
78
90
  id: string;
79
- title: AxisTitleData | null;
91
+ title: HtmlAxisTitleData | SvgAxisTitleData | null;
80
92
  domain: AxisDomainData | null;
81
93
  ticks: AxisTickData[];
82
94
  plotLines: AxisPlotLineData[];
@@ -1,11 +1,4 @@
1
- import { getDomainDataYBySeries, getMinSpaceBetween, getTicksCount, isBandScale } from '../../utils';
2
- function thinOut(items, delta) {
3
- const arr = [];
4
- for (let i = 0; i < items.length; i = i + delta) {
5
- arr.push(items[i]);
6
- }
7
- return arr;
8
- }
1
+ import { getDomainDataYBySeries, getMinSpaceBetween, getTicksCount, isBandScale, thinOut, } from '../../utils';
9
2
  export function getTickValues({ scale, axis, labelLineHeight, series, }) {
10
3
  if ('ticks' in scale && typeof scale.ticks === 'function') {
11
4
  const range = scale.range();
@@ -1,19 +1,22 @@
1
1
  import React from 'react';
2
- import { duration } from '@gravity-ui/date-utils';
3
2
  import { ArrowRotateLeft } from '@gravity-ui/icons';
4
3
  import { Button, ButtonIcon, useUniqId } from '@gravity-ui/uikit';
5
4
  import { useCrosshair } from '../../hooks';
6
5
  import { getPreparedRangeSlider } from '../../hooks/useAxis/range-slider';
6
+ import { getPreparedChart } from '../../hooks/useChartOptions/chart';
7
+ import { getPreparedTitle } from '../../hooks/useChartOptions/title';
7
8
  import { getPreparedTooltip } from '../../hooks/useChartOptions/tooltip';
8
- import { EventType, block, getDispatcher, isBandScale, isTimeScale } from '../../utils';
9
+ import { EventType, block, getDispatcher, isBandScale } from '../../utils';
9
10
  import { AxisX } from '../AxisX/AxisX';
11
+ import { prepareXAxisData } from '../AxisX/prepare-axis-data';
10
12
  import { AxisY } from '../AxisY/AxisY';
11
- import { prepareAxisData } from '../AxisY/prepare-axis-data';
13
+ import { prepareYAxisData } from '../AxisY/prepare-axis-data';
12
14
  import { Legend } from '../Legend';
13
15
  import { PlotTitle } from '../PlotTitle';
14
16
  import { RangeSlider } from '../RangeSlider';
15
17
  import { Title } from '../Title';
16
18
  import { Tooltip } from '../Tooltip';
19
+ import { getInitialRangeSliderState } from '../utils';
17
20
  import { useChartInnerHandlers } from './useChartInnerHandlers';
18
21
  import { useChartInnerProps } from './useChartInnerProps';
19
22
  import { useChartInnerState } from './useChartInnerState';
@@ -22,7 +25,7 @@ import './styles.css';
22
25
  const b = block('chart');
23
26
  const DEBOUNCED_VALUE_DELAY = 10;
24
27
  export const ChartInner = (props) => {
25
- var _a, _b, _c, _d, _e;
28
+ var _a, _b, _c, _d, _e, _f;
26
29
  const { width, height, data } = props;
27
30
  const svgRef = React.useRef(null);
28
31
  const resetZoomButtonRef = React.useRef(null);
@@ -30,8 +33,19 @@ export const ChartInner = (props) => {
30
33
  const plotRef = React.useRef(null);
31
34
  const plotBeforeRef = React.useRef(null);
32
35
  const plotAfterRef = React.useRef(null);
36
+ const rangeSliderRef = React.useRef(null);
33
37
  const dispatcher = React.useMemo(() => getDispatcher(), []);
34
38
  const clipPathId = useUniqId();
39
+ const preparedTitle = React.useMemo(() => {
40
+ return getPreparedTitle({ title: data.title });
41
+ }, [data.title]);
42
+ const preparedChart = React.useMemo(() => {
43
+ return getPreparedChart({
44
+ chart: data.chart,
45
+ seriesData: data.series.data,
46
+ preparedTitle,
47
+ });
48
+ }, [data.chart, data.series.data, preparedTitle]);
35
49
  const preparedTooltip = React.useMemo(() => {
36
50
  return getPreparedTooltip({
37
51
  tooltip: data.tooltip,
@@ -45,12 +59,13 @@ export const ChartInner = (props) => {
45
59
  }, [data.xAxis]);
46
60
  const { initialized, setInitialized, tooltipPinned, togglePinTooltip, unpinTooltip, rangeSliderState, updateRangeSliderState, updateZoomState, zoomState, } = useChartInnerState({
47
61
  dispatcher,
48
- tooltip: preparedTooltip,
62
+ preparedChart,
49
63
  preparedRangeSlider,
64
+ tooltip: preparedTooltip,
50
65
  });
51
- const { allPreparedSeries, boundsHeight, boundsOffsetLeft, boundsOffsetTop, boundsWidth, handleLegendItemClick, isOutsideBounds, legendConfig, legendItems, preparedChart, preparedLegend, preparedSeries, preparedSeriesOptions, preparedSplit, preparedZoom, prevHeight, prevWidth, shapes, shapesData, svgXPos, title, xAxis, xScale, yAxis, yScale, } = useChartInnerProps(Object.assign(Object.assign({}, props), { clipPathId,
66
+ const { allPreparedSeries, boundsHeight, boundsOffsetLeft, boundsOffsetTop, boundsWidth, handleLegendItemClick, isOutsideBounds, legendConfig, legendItems, preparedLegend, preparedSeries, preparedSeriesOptions, preparedSplit, prevHeight, prevWidth, shapes, shapesData, xAxis, xScale, yAxis, yScale, } = useChartInnerProps(Object.assign(Object.assign({}, props), { clipPathId,
52
67
  dispatcher,
53
- htmlLayout, plotNode: plotRef.current, rangeSliderState, svgContainer: svgRef.current, updateZoomState,
68
+ htmlLayout, plotNode: plotRef.current, preparedChart, rangeSliderDomain: (_a = rangeSliderRef.current) === null || _a === void 0 ? void 0 : _a.getDomain(), rangeSliderState, svgContainer: svgRef.current, updateZoomState,
54
69
  zoomState }));
55
70
  const debouncedBoundsWidth = useDebouncedValue({
56
71
  value: boundsWidth,
@@ -80,8 +95,8 @@ export const ChartInner = (props) => {
80
95
  tooltipThrottle: preparedTooltip.throttle,
81
96
  isOutsideBounds,
82
97
  });
83
- const clickHandler = (_b = (_a = data.chart) === null || _a === void 0 ? void 0 : _a.events) === null || _b === void 0 ? void 0 : _b.click;
84
- const pointerMoveHandler = (_d = (_c = data.chart) === null || _c === void 0 ? void 0 : _c.events) === null || _d === void 0 ? void 0 : _d.pointermove;
98
+ const clickHandler = (_c = (_b = data.chart) === null || _b === void 0 ? void 0 : _b.events) === null || _c === void 0 ? void 0 : _c.click;
99
+ const pointerMoveHandler = (_e = (_d = data.chart) === null || _d === void 0 ? void 0 : _d.events) === null || _e === void 0 ? void 0 : _e.pointermove;
85
100
  useCrosshair({
86
101
  split: preparedSplit,
87
102
  plotElement: plotAfterRef.current,
@@ -121,7 +136,7 @@ export const ChartInner = (props) => {
121
136
  const axis = yAxis[i];
122
137
  const scale = yScale === null || yScale === void 0 ? void 0 : yScale[i];
123
138
  if (scale) {
124
- const axisData = await prepareAxisData({
139
+ const axisData = await prepareYAxisData({
125
140
  axis,
126
141
  scale,
127
142
  top: boundsOffsetTop,
@@ -136,48 +151,42 @@ export const ChartInner = (props) => {
136
151
  return items;
137
152
  }, [boundsHeight, boundsOffsetTop, boundsWidth, preparedSeries, preparedSplit, yAxis, yScale]);
138
153
  const yAxisDataItems = useAsyncState([], setYAxisDataItems);
154
+ const setXAxisDataItems = React.useCallback(async () => {
155
+ const items = [];
156
+ const axis = xAxis;
157
+ const scale = xScale;
158
+ if (axis && scale) {
159
+ const axisData = await prepareXAxisData({
160
+ axis,
161
+ scale,
162
+ boundsWidth,
163
+ boundsOffsetLeft: boundsOffsetLeft,
164
+ boundsOffsetRight: width - boundsWidth - boundsOffsetLeft,
165
+ height: boundsHeight,
166
+ });
167
+ items.push(axisData);
168
+ }
169
+ return items;
170
+ }, [boundsHeight, boundsOffsetLeft, boundsWidth, width, xAxis, xScale]);
171
+ const xAxisDataItems = useAsyncState([], setXAxisDataItems);
139
172
  React.useEffect(() => {
140
173
  if (!initialized && xScale) {
141
- const defaultRange = preparedRangeSlider.defaultRange;
142
- if (isBandScale(xScale)) {
174
+ if (!preparedRangeSlider.enabled || isBandScale(xScale)) {
143
175
  setInitialized(true);
144
176
  return;
145
177
  }
146
- if (isTimeScale(xScale)) {
147
- const domain = xScale.domain();
148
- const minDomainMs = domain[0].valueOf();
149
- const maxDomainMs = domain[1].valueOf();
150
- let minRangeMs = minDomainMs;
151
- try {
152
- if (defaultRange === null || defaultRange === void 0 ? void 0 : defaultRange.size) {
153
- const durationMs = duration(defaultRange.size).asMilliseconds();
154
- const minDefaultRangeMs = maxDomainMs - durationMs;
155
- if (minDefaultRangeMs < maxDomainMs) {
156
- minRangeMs = minDefaultRangeMs;
157
- }
158
- }
159
- }
160
- catch (_a) { }
161
- updateRangeSliderState({ min: minRangeMs, max: maxDomainMs });
162
- setInitialized(true);
163
- }
164
- else {
165
- const [minDomain, maxDomain] = xScale.domain();
166
- let minRange = minDomain;
167
- if (typeof (defaultRange === null || defaultRange === void 0 ? void 0 : defaultRange.size) === 'number') {
168
- const minDefaultRange = maxDomain - defaultRange.size;
169
- if (minDefaultRange < maxDomain) {
170
- minRange = minDefaultRange;
171
- }
172
- }
173
- updateRangeSliderState({ min: minRange, max: maxDomain });
174
- setInitialized(true);
175
- }
178
+ const defaultRange = preparedRangeSlider.defaultRange;
179
+ const initialRangeSliderState = getInitialRangeSliderState({
180
+ defaultRange,
181
+ xScale,
182
+ });
183
+ updateRangeSliderState(initialRangeSliderState);
184
+ setInitialized(true);
176
185
  }
177
186
  }, [
178
187
  initialized,
179
188
  preparedRangeSlider.defaultRange,
180
- preparedSeries,
189
+ preparedRangeSlider.enabled,
181
190
  setInitialized,
182
191
  updateRangeSliderState,
183
192
  xScale,
@@ -186,13 +195,12 @@ export const ChartInner = (props) => {
186
195
  React.createElement("defs", null,
187
196
  React.createElement("clipPath", { id: clipPathId },
188
197
  React.createElement("rect", { x: 0, y: 0, width: boundsWidth, height: boundsHeight }))),
189
- title && React.createElement(Title, Object.assign({}, title, { chartWidth: width })),
198
+ preparedTitle && React.createElement(Title, Object.assign({}, preparedTitle, { chartWidth: width })),
190
199
  React.createElement("g", { transform: `translate(0, ${boundsOffsetTop})` }, preparedSplit.plots.map((plot, index) => {
191
200
  return React.createElement(PlotTitle, { key: `plot-${index}`, title: plot.title });
192
201
  })),
193
- React.createElement("g", { width: boundsWidth, height: boundsHeight, transform: `translate(${[boundsOffsetLeft, boundsOffsetTop].join(',')})`, ref: plotRef },
194
- xScale && xAxis && (React.createElement("g", { transform: `translate(0, ${boundsHeight})` },
195
- React.createElement(AxisX, { axis: xAxis, boundsOffsetLeft: boundsOffsetLeft, boundsOffsetTop: boundsOffsetTop, height: boundsHeight, htmlLayout: htmlLayout, leftmostLimit: svgXPos, plotAfterRef: plotAfterRef, plotBeforeRef: plotBeforeRef, scale: xScale, split: preparedSplit, width: boundsWidth }))),
202
+ React.createElement("g", { className: b('content'), width: boundsWidth, height: boundsHeight, transform: `translate(${[boundsOffsetLeft, boundsOffsetTop].join(',')})`, ref: plotRef },
203
+ xScale && xAxisDataItems.length && (React.createElement(AxisX, { htmlLayout: htmlLayout, plotAfterRef: plotAfterRef, plotBeforeRef: plotBeforeRef, preparedAxisData: xAxisDataItems[0] })),
196
204
  Boolean(yAxisDataItems.length) && (React.createElement(React.Fragment, null, yAxisDataItems.map((axisData, index) => {
197
205
  if (!axisData) {
198
206
  return null;
@@ -202,7 +210,7 @@ export const ChartInner = (props) => {
202
210
  React.createElement("g", { ref: plotBeforeRef }),
203
211
  shapes,
204
212
  React.createElement("g", { ref: plotAfterRef })),
205
- ((_e = xAxis === null || xAxis === void 0 ? void 0 : xAxis.rangeSlider) === null || _e === void 0 ? void 0 : _e.enabled) && (React.createElement(RangeSlider, { boundsOffsetLeft: debouncedOffsetLeft, boundsWidth: debouncedBoundsWidth, height: height, htmlLayout: htmlLayout, onUpdate: updateRangeSliderState, preparedChart: preparedChart, preparedLegend: preparedLegend, preparedSeries: debouncedAllPreparedSeries, preparedSeriesOptions: preparedSeriesOptions, preparedRangeSlider: xAxis.rangeSlider, rangeSliderState: rangeSliderState, width: width, xAxis: data.xAxis, yAxis: data.yAxis, zoomState: zoomState })),
213
+ ((_f = xAxis === null || xAxis === void 0 ? void 0 : xAxis.rangeSlider) === null || _f === void 0 ? void 0 : _f.enabled) && (React.createElement(RangeSlider, { boundsOffsetLeft: debouncedOffsetLeft, boundsWidth: debouncedBoundsWidth, height: height, htmlLayout: htmlLayout, onUpdate: updateRangeSliderState, preparedChart: preparedChart, preparedLegend: preparedLegend, preparedSeries: debouncedAllPreparedSeries, preparedSeriesOptions: preparedSeriesOptions, preparedRangeSlider: xAxis.rangeSlider, rangeSliderState: rangeSliderState, ref: rangeSliderRef, width: width, xAxis: data.xAxis, yAxis: data.yAxis })),
206
214
  (preparedLegend === null || preparedLegend === void 0 ? void 0 : preparedLegend.enabled) && legendConfig && (React.createElement(Legend, { chartSeries: preparedSeries, legend: preparedLegend, items: legendItems, config: legendConfig, onItemClick: handleLegendItemClick, onUpdate: unpinTooltip, htmlLayout: htmlLayout }))));
207
215
  return (React.createElement("div", { className: b() },
208
216
  React.createElement("svg", { ref: svgRef, width: width, height: height,
@@ -212,10 +220,14 @@ export const ChartInner = (props) => {
212
220
  React.createElement("div", { className: b('html-layer'), ref: setHtmlLayout, style: {
213
221
  '--g-html-layout-transform': `translate(${boundsOffsetLeft}px, ${boundsOffsetTop}px)`,
214
222
  } }),
215
- Object.keys(zoomState).length > 0 && preparedZoom && (React.createElement(Button, { onClick: () => updateZoomState({}), ref: resetZoomButtonRef, style: getResetZoomButtonStyle(Object.assign({ boundsHeight,
223
+ Object.keys(zoomState).length > 0 && preparedChart.zoom && (React.createElement(Button, { className: b('reset-zoom-button'), onClick: () => {
224
+ var _a;
225
+ updateZoomState({});
226
+ (_a = rangeSliderRef.current) === null || _a === void 0 ? void 0 : _a.resetState();
227
+ }, ref: resetZoomButtonRef, style: getResetZoomButtonStyle(Object.assign({ boundsHeight,
216
228
  boundsOffsetLeft,
217
229
  boundsOffsetTop,
218
- boundsWidth, node: resetZoomButtonRef.current, titleHeight: title === null || title === void 0 ? void 0 : title.height }, preparedZoom.resetButton)) },
230
+ boundsWidth, node: resetZoomButtonRef.current, titleHeight: preparedTitle === null || preparedTitle === void 0 ? void 0 : preparedTitle.height }, preparedChart.zoom.resetButton)) },
219
231
  React.createElement(ButtonIcon, null,
220
232
  React.createElement(ArrowRotateLeft, null)))),
221
233
  React.createElement(Tooltip, { dispatcher: dispatcher, tooltip: preparedTooltip, svgContainer: svgRef.current, xAxis: xAxis, yAxis: yAxis[0], onOutsideClick: unpinTooltip, tooltipPinned: tooltipPinned })));
@@ -1,15 +1,17 @@
1
1
  import React from 'react';
2
2
  import type { Dispatch } from 'd3';
3
- import type { PreparedLegend, RangeSliderState, ZoomState } from '../../hooks';
3
+ import type { PreparedChart, PreparedLegend, RangeSliderState, ZoomState } from '../../hooks';
4
4
  import type { ChartInnerProps } from './types';
5
5
  type Props = ChartInnerProps & {
6
6
  clipPathId: string;
7
7
  dispatcher: Dispatch<object>;
8
8
  htmlLayout: HTMLElement | null;
9
9
  plotNode: SVGGElement | null;
10
+ preparedChart: PreparedChart;
10
11
  svgContainer: SVGGElement | null;
11
12
  updateZoomState: (nextZoomState: Partial<ZoomState>) => void;
12
13
  zoomState: Partial<ZoomState>;
14
+ rangeSliderDomain?: [number, number];
13
15
  rangeSliderState?: RangeSliderState;
14
16
  };
15
17
  export declare function useChartInnerProps(props: Props): {
@@ -34,25 +36,20 @@ export declare function useChartInnerProps(props: Props): {
34
36
  maxWidth: number;
35
37
  } | undefined;
36
38
  legendItems: never[] | import("../../hooks").LegendItem[][];
37
- preparedChart: import("../../hooks").PreparedChart;
38
39
  preparedLegend: PreparedLegend | null;
39
40
  preparedSeries: import("../../hooks").PreparedSeries[];
40
41
  preparedSeriesOptions: import("../../constants").SeriesOptionsDefaults;
41
42
  preparedSplit: import("../../hooks").PreparedSplit;
42
- preparedZoom: import("../../hooks").PreparedZoom | null;
43
43
  prevHeight: number | undefined;
44
44
  prevWidth: number | undefined;
45
45
  shapes: React.ReactElement<any, string | React.JSXElementConstructor<any>>[];
46
46
  shapesData: import("../../hooks").ShapeData[];
47
47
  svgXPos: number | undefined;
48
- title: (import("../..").ChartTitle & {
49
- height: number;
50
- }) | undefined;
51
48
  xAxis: import("../../hooks").PreparedXAxis | null;
52
49
  xScale: import("../../hooks").ChartScale | undefined;
53
50
  yAxis: (Omit<import("../..").ChartAxis, "type" | "labels" | "plotLines" | "plotBands"> & {
54
51
  type: import("../..").ChartAxisType;
55
- labels: Omit<import("../..").ChartAxisLabels, "enabled" | "style" | "padding" | "autoRotation"> & Required<Pick<import("../..").ChartAxisLabels, "margin" | "html" | "enabled" | "padding" | "rotation">> & {
52
+ labels: Omit<import("../..").ChartAxisLabels, "enabled" | "style" | "padding" | "autoRotation"> & Required<Pick<import("../..").ChartAxisLabels, "margin" | "html" | "enabled" | "rotation" | "padding">> & {
56
53
  style: import("../..").BaseTextStyle;
57
54
  rotation: number;
58
55
  height: number;
@@ -68,6 +65,9 @@ export declare function useChartInnerProps(props: Props): {
68
65
  style: import("../..").BaseTextStyle;
69
66
  align: import("../..").ChartAxisTitleAlignment;
70
67
  maxRowCount: number;
68
+ rotation: number;
69
+ maxWidth: number;
70
+ html: boolean;
71
71
  };
72
72
  min?: number;
73
73
  grid: {
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
- import { SERIES_TYPE } from '../../constants';
3
- import { useAxis, useAxisScales, useChartDimensions, useChartOptions, useNormalizedOriginalData, usePrevious, useSeries, useShapes, useSplit, useZoom, } from '../../hooks';
2
+ import { DEFAULT_PALETTE, SERIES_TYPE } from '../../constants';
3
+ import { useAxis, useAxisScales, useChartDimensions, useNormalizedOriginalData, usePrevious, useSeries, useShapes, useSplit, useZoom, } from '../../hooks';
4
4
  import { getYAxisWidth } from '../../hooks/useChartDimensions/utils';
5
5
  import { getLegendComponents } from '../../hooks/useSeries/prepare-legend';
6
6
  import { getPreparedOptions } from '../../hooks/useSeries/prepare-options';
@@ -35,21 +35,18 @@ function getBoundsOffsetLeft(args) {
35
35
  }
36
36
  export function useChartInnerProps(props) {
37
37
  var _a;
38
- const { clipPathId, data, dispatcher, height, htmlLayout, plotNode, rangeSliderState, svgContainer, width, updateZoomState, zoomState, } = props;
38
+ const { clipPathId, data, dispatcher, height, htmlLayout, plotNode, preparedChart, rangeSliderDomain, rangeSliderState, svgContainer, width, updateZoomState, zoomState, } = props;
39
39
  const prevWidth = usePrevious(width);
40
40
  const prevHeight = usePrevious(height);
41
+ const colors = React.useMemo(() => {
42
+ var _a;
43
+ return (_a = data.colors) !== null && _a !== void 0 ? _a : DEFAULT_PALETTE;
44
+ }, [data.colors]);
41
45
  const { normalizedSeriesData, normalizedXAxis, normalizedYAxis } = useNormalizedOriginalData({
42
46
  seriesData: data.series.data,
43
47
  xAxis: data.xAxis,
44
48
  yAxis: data.yAxis,
45
49
  });
46
- const { chart, colors, title } = useChartOptions({
47
- chart: data.chart,
48
- colors: data.colors,
49
- seriesData: normalizedSeriesData,
50
- title: data.title,
51
- xAxis: data.xAxis,
52
- });
53
50
  const preparedSeriesOptions = React.useMemo(() => {
54
51
  return getPreparedOptions(data.series.options);
55
52
  }, [data.series.options]);
@@ -75,14 +72,14 @@ export function useChartInnerProps(props) {
75
72
  return getLegendComponents({
76
73
  chartWidth: width,
77
74
  chartHeight: height,
78
- chartMargin: chart.margin,
75
+ chartMargin: preparedChart.margin,
79
76
  series: preparedSeries,
80
77
  preparedLegend,
81
78
  });
82
- }, [width, height, chart.margin, preparedSeries, preparedLegend]);
79
+ }, [width, height, preparedChart.margin, preparedSeries, preparedLegend]);
83
80
  const { xAxis, yAxis } = useAxis({
84
81
  height,
85
- preparedChart: chart,
82
+ preparedChart,
86
83
  preparedLegend,
87
84
  preparedSeries,
88
85
  preparedSeriesOptions,
@@ -92,7 +89,7 @@ export function useChartInnerProps(props) {
92
89
  });
93
90
  const { boundsWidth, boundsHeight } = useChartDimensions({
94
91
  height,
95
- margin: chart.margin,
92
+ margin: preparedChart.margin,
96
93
  preparedLegend,
97
94
  preparedSeries: preparedSeries,
98
95
  preparedYAxis: yAxis,
@@ -147,19 +144,20 @@ export function useChartInnerProps(props) {
147
144
  plotContainerHeight: boundsHeight,
148
145
  plotContainerWidth: boundsWidth,
149
146
  preparedSplit,
150
- preparedZoom: chart.zoom,
147
+ preparedZoom: preparedChart.zoom,
148
+ rangeSliderDomain,
151
149
  xAxis,
152
150
  xScale,
153
151
  yAxis,
154
152
  yScale,
155
153
  });
156
154
  const boundsOffsetTop = getBoundsOffsetTop({
157
- chartMarginTop: chart.margin.top,
155
+ chartMarginTop: preparedChart.margin.top,
158
156
  preparedLegend,
159
157
  });
160
158
  // We need to calculate the width of each left axis because the first axis can be hidden
161
159
  const boundsOffsetLeft = getBoundsOffsetLeft({
162
- chartMarginLeft: chart.margin.left,
160
+ chartMarginLeft: preparedChart.margin.left,
163
161
  preparedLegend,
164
162
  yAxis,
165
163
  getYAxisWidth,
@@ -175,18 +173,15 @@ export function useChartInnerProps(props) {
175
173
  isOutsideBounds,
176
174
  legendConfig,
177
175
  legendItems,
178
- preparedChart: chart,
179
176
  preparedLegend,
180
177
  preparedSeries,
181
178
  preparedSeriesOptions,
182
179
  preparedSplit,
183
- preparedZoom: chart.zoom,
184
180
  prevHeight,
185
181
  prevWidth,
186
182
  shapes,
187
183
  shapesData,
188
184
  svgXPos: x,
189
- title,
190
185
  xAxis,
191
186
  xScale,
192
187
  yAxis,
@@ -1,8 +1,9 @@
1
1
  import React from 'react';
2
2
  import type { Dispatch } from 'd3';
3
- import type { PreparedRangeSlider, PreparedTooltip, RangeSliderState, ZoomState } from '../../hooks';
3
+ import type { PreparedChart, PreparedRangeSlider, PreparedTooltip, RangeSliderState, ZoomState } from '../../hooks';
4
4
  type Props = {
5
5
  dispatcher: Dispatch<object>;
6
+ preparedChart: PreparedChart;
6
7
  preparedRangeSlider: PreparedRangeSlider;
7
8
  tooltip?: PreparedTooltip;
8
9
  };
@@ -13,7 +14,7 @@ export declare function useChartInnerState(props: Props): {
13
14
  tooltipPinned: boolean;
14
15
  togglePinTooltip: ((value: boolean, event: React.MouseEvent) => void) | undefined;
15
16
  unpinTooltip: (() => void) | undefined;
16
- updateRangeSliderState: (nextRangeSliderState?: RangeSliderState) => void;
17
+ updateRangeSliderState: (nextRangeSliderState?: RangeSliderState, syncZoom?: boolean) => void;
17
18
  updateZoomState: (nextZoomState: Partial<ZoomState>) => void;
18
19
  zoomState: Partial<ZoomState>;
19
20
  };
@@ -1,9 +1,11 @@
1
1
  import React from 'react';
2
2
  import isEqual from 'lodash/isEqual';
3
+ import { ZOOM_TYPE } from '../../constants';
3
4
  import { EventType, isMacintosh } from '../../utils';
5
+ const RANGE_SLIDER_SYNC_ZOOM_TYPES = [ZOOM_TYPE.X, ZOOM_TYPE.XY];
4
6
  export function useChartInnerState(props) {
5
7
  var _a, _b;
6
- const { dispatcher, preparedRangeSlider, tooltip } = props;
8
+ const { dispatcher, preparedChart, preparedRangeSlider, tooltip } = props;
7
9
  const [tooltipPinned, setTooltipPinned] = React.useState(false);
8
10
  const [zoomState, setZoomState] = React.useState({});
9
11
  const [rangeSliderState, setRangeSliderState] = React.useState();
@@ -11,6 +13,7 @@ export function useChartInnerState(props) {
11
13
  const tooltipEnabled = tooltip === null || tooltip === void 0 ? void 0 : tooltip.enabled;
12
14
  const tooltipPinEnabled = (_a = tooltip === null || tooltip === void 0 ? void 0 : tooltip.pin) === null || _a === void 0 ? void 0 : _a.enabled;
13
15
  const modifierKey = (_b = tooltip === null || tooltip === void 0 ? void 0 : tooltip.pin) === null || _b === void 0 ? void 0 : _b.modifierKey;
16
+ const rangeSliderEnabled = preparedRangeSlider.enabled;
14
17
  const togglePinTooltip = React.useCallback((value, event) => {
15
18
  let resultValue = value;
16
19
  if (value && modifierKey) {
@@ -33,9 +36,16 @@ export function useChartInnerState(props) {
33
36
  const updateZoomState = React.useCallback((nextZoomState) => {
34
37
  if (!isEqual(zoomState, nextZoomState)) {
35
38
  setZoomState(nextZoomState);
39
+ if (rangeSliderEnabled && (nextZoomState === null || nextZoomState === void 0 ? void 0 : nextZoomState.x)) {
40
+ const [xMin, xMax] = nextZoomState.x;
41
+ setRangeSliderState({
42
+ max: xMax,
43
+ min: xMin,
44
+ });
45
+ }
36
46
  }
37
- }, [zoomState]);
38
- const updateRangeSliderState = React.useCallback((nextRangeSliderState) => {
47
+ }, [rangeSliderEnabled, zoomState]);
48
+ const updateRangeSliderState = React.useCallback((nextRangeSliderState, syncZoom = true) => {
39
49
  if (!isEqual(rangeSliderState, nextRangeSliderState)) {
40
50
  setRangeSliderState(nextRangeSliderState
41
51
  ? {
@@ -43,8 +53,17 @@ export function useChartInnerState(props) {
43
53
  min: nextRangeSliderState.min,
44
54
  }
45
55
  : undefined);
56
+ if (syncZoom &&
57
+ nextRangeSliderState &&
58
+ preparedChart.zoom &&
59
+ Object.keys(zoomState || {}).length > 0 &&
60
+ RANGE_SLIDER_SYNC_ZOOM_TYPES.includes(preparedChart.zoom.type)) {
61
+ setZoomState({
62
+ x: [nextRangeSliderState.min, nextRangeSliderState.max],
63
+ });
64
+ }
46
65
  }
47
- }, [rangeSliderState]);
66
+ }, [preparedChart.zoom, rangeSliderState, zoomState]);
48
67
  return {
49
68
  initialized,
50
69
  rangeSliderState,