@gravity-ui/chartkit 0.13.0 → 1.0.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.0.0](https://github.com/gravity-ui/chartkit/compare/v0.13.0...v1.0.0) (2022-12-07)
4
+
5
+
6
+ ### ⚠ BREAKING CHANGES
7
+
8
+ * update yagr to v2 ([#82](https://github.com/gravity-ui/chartkit/issues/82))
9
+
10
+ ### Features
11
+
12
+ * update yagr to v2 ([#82](https://github.com/gravity-ui/chartkit/issues/82)) ([ae3c347](https://github.com/gravity-ui/chartkit/commit/ae3c3471bc7bb0d4a2d5af850bac1ecb4eb8e3bb))
13
+
14
+
15
+ ### chore
16
+
17
+ * bump major ([966c40b](https://github.com/gravity-ui/chartkit/commit/966c40b89d4795acbadb798fcd1e2031481d76a2))
18
+
3
19
  ## [0.13.0](https://github.com/gravity-ui/chartkit/compare/v0.12.3...v0.13.0) (2022-12-05)
4
20
 
5
21
 
@@ -9,28 +9,30 @@ export const line10 = {
9
9
  id: '0',
10
10
  name: 'Serie 1',
11
11
  color: '#6c59c2',
12
- visible: true,
13
- data: [25, 52, 89, 72, 39, 49, 82, 59, 36, 5],
12
+ data: [45, 52, 89, 72, 39, 49, 82, 59, 36, 5],
14
13
  },
15
14
  {
16
15
  id: '1',
17
16
  name: 'Serie 2',
18
17
  color: '#6e8188',
19
- visible: true,
20
18
  data: [37, 6, 51, 10, 65, 35, 72, 0, 94, 54],
21
19
  },
22
20
  {
23
21
  id: '2',
24
22
  name: 'Serie 3',
25
23
  color: '#e21576',
26
- visible: true,
27
24
  data: [26, 54, 15, 40, 43, 18, 65, 46, 51, 33],
28
25
  },
29
26
  ],
30
27
  },
31
28
  libraryConfig: {
32
29
  chart: {
33
- type: 'line',
30
+ series: {
31
+ type: 'line',
32
+ },
33
+ select: {
34
+ zoom: false,
35
+ },
34
36
  },
35
37
  title: {
36
38
  text: 'line: random 10 pts',
@@ -51,11 +53,8 @@ export const line10 = {
51
53
  style: 'solid 2px rgba(230, 2, 7, 0.3)',
52
54
  },
53
55
  },
54
- settings: {
55
- adaptive: true,
56
- },
57
56
  tooltip: {
58
- enabled: true,
57
+ show: true,
59
58
  boundClassName: '.wrapper',
60
59
  tracking: 'sticky',
61
60
  className: 'chartkit-theme_common',
@@ -1,179 +1,22 @@
1
1
  import React from 'react';
2
- import moment from 'moment';
3
- import debounce from 'lodash/debounce';
4
2
  import isEmpty from 'lodash/isEmpty';
5
- import { useThemeType } from '@gravity-ui/uikit';
6
3
  import YagrComponent from 'yagr/dist/react';
7
- import { defaults, } from 'yagr';
8
4
  import { i18n } from '../../../i18n';
9
- import { settings, CHARTKIT_ERROR_CODE, ChartKitError } from '../../../libs';
10
- import { formatTooltip } from './tooltip/tooltip';
11
- import { synchronizeTooltipTablesCellsWidth } from './synchronizeTooltipTablesCellsWidth';
5
+ import { CHARTKIT_ERROR_CODE, ChartKitError } from '../../../libs';
6
+ import { useWidgetData } from './useWidgetData';
7
+ import { checkFocus, detectClickOutside, synchronizeTooltipTablesCellsWidth } from './utils';
12
8
  import './polyfills';
13
9
  import './YagrWidget.css';
14
- const calcOption = (d) => {
15
- return typeof d === 'object'
16
- ? Object.values(d).reduce((_, t) => {
17
- return t;
18
- })
19
- : d;
20
- };
21
- /*
22
- * Default tooltip renderer.
23
- * Adapter between native Yagr tooltip config and ChartKit
24
- * tooltip renderer.
25
- */
26
- const renderTooltip = (data) => {
27
- var _a, _b;
28
- const cfg = data.yagr.config;
29
- const timeMultiplier = ((_a = cfg.settings) === null || _a === void 0 ? void 0 : _a.timeMultiplier) || 1;
30
- const locale = (_b = cfg.settings) === null || _b === void 0 ? void 0 : _b.locale;
31
- const opts = data.options;
32
- const { x, pinned } = data;
33
- let sumTotal = 0;
34
- const rows = Object.values(data.scales).reduce((acc, scale) => {
35
- sumTotal += scale.sum || 0;
36
- return acc.concat(scale.rows);
37
- }, []);
38
- const lines = rows.length;
39
- const sum = calcOption(opts.sum);
40
- const maxLines = calcOption(opts.maxLines);
41
- const valueFormatter = calcOption(opts.value);
42
- // eslint-disable-next-line no-nested-ternary
43
- const hiddenRowsNumber = pinned
44
- ? undefined
45
- : lines > maxLines
46
- ? Math.abs(maxLines - lines)
47
- : undefined;
48
- const hiddenRowsSum = hiddenRowsNumber
49
- ? valueFormatter(rows
50
- .slice(-hiddenRowsNumber)
51
- .reduce((sum, { originalValue }) => sum + (originalValue || 0), 0))
52
- : undefined;
53
- const tooltipFormatOptions = {
54
- activeRowAlwaysFirstInTooltip: rows.length > 1,
55
- tooltipHeader: moment(x / timeMultiplier).format('DD MMMM YYYY HH:mm:ss'),
56
- shared: true,
57
- lines: rows.map((row, i) => (Object.assign(Object.assign({}, row), { seriesName: row.name || 'Serie ' + (i + 1), seriesColor: row.color, selectedSeries: row.active, seriesIdx: row.seriesIdx, percentValue: typeof row.transformed === 'number' ? row.transformed.toFixed(1) : '' }))),
58
- withPercent: calcOption(opts.percent),
59
- hiddenRowsNumber: hiddenRowsNumber,
60
- hiddenRowsSum,
61
- };
62
- if (sum) {
63
- tooltipFormatOptions.sum = valueFormatter(sumTotal);
64
- }
65
- return formatTooltip(tooltipFormatOptions, {
66
- options: { locale },
67
- lastVisibleRowIndex: pinned ? rows.length - 1 : maxLines - 1,
68
- });
69
- };
70
- const getXAxisFormatter = (msm = 1) => (_, ticks) => {
71
- const range = (ticks[ticks.length - 1] - ticks[0]) / msm;
72
- return ticks.map((rawValue) => {
73
- const d = moment(rawValue / msm);
74
- if (d.hour() === 0 && d.minutes() === 0 && d.seconds() === 0) {
75
- return d.format('DD.MM.YY');
76
- }
77
- return d.format(range < 300 ? 'HH:mm:ss' : 'HH:mm');
78
- });
79
- };
80
10
  const YagrWidget = React.forwardRef((props, forwardedRef) => {
81
- var _a, _b;
11
+ const { id, data: { data }, onLoad, } = props;
82
12
  const yagrRef = React.useRef(null);
83
- const { data, libraryConfig } = props.data;
84
- const { id, onLoad } = props;
85
- const theme = useThemeType();
86
13
  if (!data || isEmpty(data)) {
87
14
  throw new ChartKitError({
88
15
  code: CHARTKIT_ERROR_CODE.NO_DATA,
89
16
  message: i18n('error', 'label_no-data'),
90
17
  });
91
18
  }
92
- const handlers = {
93
- mouseMove: null,
94
- mouseDown: null,
95
- };
96
- const checkFocus = (tooltip) => (event) => {
97
- var _a, _b;
98
- const yagr = (_a = yagrRef === null || yagrRef === void 0 ? void 0 : yagrRef.current) === null || _a === void 0 ? void 0 : _a.chart;
99
- if (!yagr) {
100
- return;
101
- }
102
- const target = event.target;
103
- const seriesIdx = target && tooltip.contains(target) && target.tagName === 'TD'
104
- ? (_b = target.parentElement) === null || _b === void 0 ? void 0 : _b.dataset['seriesIdx']
105
- : undefined;
106
- const serie = seriesIdx ? yagr.uplot.series[Number(seriesIdx)] : null;
107
- yagr.focus(serie ? serie.id : null, true);
108
- };
109
- const detectClickOutside = (tooltip, actions) => (event) => {
110
- var _a, _b;
111
- const yagr = (_a = yagrRef === null || yagrRef === void 0 ? void 0 : yagrRef.current) === null || _a === void 0 ? void 0 : _a.chart;
112
- if (!yagr) {
113
- return;
114
- }
115
- const target = event.target;
116
- if (target instanceof Element) {
117
- const isClickInsideTooltip = target && tooltip.contains(target);
118
- const isClickOnUplotOver = target && ((_b = yagr.root.querySelector('.u-over')) === null || _b === void 0 ? void 0 : _b.contains(target));
119
- if (!isClickInsideTooltip && !isClickOnUplotOver) {
120
- actions.pin(false);
121
- actions.hide();
122
- }
123
- }
124
- };
125
- const config = Object.assign(Object.assign({}, libraryConfig), { timeline: data.timeline, series: data.graphs });
126
- config.settings = Object.assign({ locale: settings.get('lang'), theme }, (config.settings || {}));
127
- if (((_a = config.tooltip) === null || _a === void 0 ? void 0 : _a.enabled) !== false) {
128
- config.tooltip = config.tooltip || {};
129
- config.tooltip.render = ((_b = config.tooltip) === null || _b === void 0 ? void 0 : _b.render) || renderTooltip;
130
- // "className" property prevent default yagr styles adding
131
- config.tooltip.className = 'chartkit-yagr-tooltip';
132
- config.tooltip.onStateChange = (tooltip, { action, actions }) => {
133
- switch (action) {
134
- case 'pin': {
135
- handlers.mouseMove = checkFocus(tooltip);
136
- handlers.mouseDown = detectClickOutside(tooltip, actions);
137
- document.addEventListener('mousemove', handlers.mouseMove);
138
- document.addEventListener('mousedown', handlers.mouseDown);
139
- break;
140
- }
141
- case 'unpin': {
142
- if (handlers.mouseMove) {
143
- document.removeEventListener('mousemove', handlers.mouseMove);
144
- handlers.mouseMove = null;
145
- }
146
- if (handlers.mouseDown) {
147
- document.removeEventListener('mousedown', handlers.mouseDown);
148
- handlers.mouseDown = null;
149
- }
150
- break;
151
- }
152
- case 'render': {
153
- synchronizeTooltipTablesCellsWidth(tooltip);
154
- }
155
- }
156
- };
157
- }
158
- config.axes = config.axes || {};
159
- const xAxis = config.axes[defaults.DEFAULT_X_SCALE];
160
- if (xAxis && !xAxis.values) {
161
- xAxis.values = getXAxisFormatter(config.settings.timeMultiplier);
162
- }
163
- if (!xAxis) {
164
- config.axes[defaults.DEFAULT_X_SCALE] = {
165
- values: getXAxisFormatter(config.settings.timeMultiplier),
166
- };
167
- }
168
- const debugFileName = props.data.sources
169
- ? Object.values(props.data.sources)
170
- .map((source) => {
171
- var _a;
172
- return (_a = source === null || source === void 0 ? void 0 : source.data) === null || _a === void 0 ? void 0 : _a.program;
173
- })
174
- .filter(Boolean)
175
- .join(', ') || id
176
- : id;
19
+ const { config, debug } = useWidgetData(Object.assign(Object.assign({}, props.data), { id }));
177
20
  const handleChartLoading = React.useCallback((chart, { renderTime }) => {
178
21
  onLoad === null || onLoad === void 0 ? void 0 : onLoad(Object.assign(Object.assign({}, data), { widget: chart, widgetRendering: renderTime }));
179
22
  }, [onLoad, data]);
@@ -193,13 +36,36 @@ const YagrWidget = React.forwardRef((props, forwardedRef) => {
193
36
  onWindowResize();
194
37
  },
195
38
  }), [onWindowResize]);
196
- React.useEffect(() => {
197
- const debouncedOnWindowResize = debounce(onWindowResize, 50);
198
- window.addEventListener('resize', debouncedOnWindowResize);
199
- return () => {
200
- window.removeEventListener('resize', debouncedOnWindowResize);
39
+ React.useLayoutEffect(() => {
40
+ var _a, _b, _c, _d;
41
+ const yagr = (_a = yagrRef.current) === null || _a === void 0 ? void 0 : _a.chart;
42
+ if (!yagr) {
43
+ return;
44
+ }
45
+ const handlers = {
46
+ mouseMove: null,
47
+ mouseDown: null,
201
48
  };
202
- }, [onWindowResize]);
203
- return (React.createElement(YagrComponent, { ref: yagrRef, id: id, config: config, onChartLoad: handleChartLoading, debug: { filename: debugFileName } }));
49
+ (_b = yagr.plugins.tooltip) === null || _b === void 0 ? void 0 : _b.on('render', (tooltip) => {
50
+ synchronizeTooltipTablesCellsWidth(tooltip);
51
+ });
52
+ (_c = yagr.plugins.tooltip) === null || _c === void 0 ? void 0 : _c.on('pin', (tooltip, { actions }) => {
53
+ handlers.mouseMove = checkFocus({ tooltip, yagr });
54
+ handlers.mouseDown = detectClickOutside({ tooltip, actions, yagr });
55
+ document.addEventListener('mousemove', handlers.mouseMove);
56
+ document.addEventListener('mousedown', handlers.mouseDown);
57
+ });
58
+ (_d = yagr.plugins.tooltip) === null || _d === void 0 ? void 0 : _d.on('unpin', () => {
59
+ if (handlers.mouseMove) {
60
+ document.removeEventListener('mousemove', handlers.mouseMove);
61
+ handlers.mouseMove = null;
62
+ }
63
+ if (handlers.mouseDown) {
64
+ document.removeEventListener('mousedown', handlers.mouseDown);
65
+ handlers.mouseDown = null;
66
+ }
67
+ });
68
+ });
69
+ return (React.createElement(YagrComponent, { ref: yagrRef, id: id, config: config, debug: debug, onChartLoad: handleChartLoading }));
204
70
  });
205
71
  export default YagrWidget;
@@ -0,0 +1 @@
1
+ export { renderTooltip } from './renderTooltip';
@@ -0,0 +1 @@
1
+ export { renderTooltip } from './renderTooltip';
@@ -0,0 +1,2 @@
1
+ import { TooltipRenderOptions } from 'yagr';
2
+ export declare const renderTooltip: (data: TooltipRenderOptions) => string;
@@ -0,0 +1,55 @@
1
+ import moment from 'moment';
2
+ import { formatTooltip } from './tooltip';
3
+ const calcOption = (d) => {
4
+ return typeof d === 'object'
5
+ ? Object.values(d).reduce((_, t) => {
6
+ return t;
7
+ })
8
+ : d;
9
+ };
10
+ /*
11
+ * Default tooltip renderer.
12
+ * Adapter between native Yagr tooltip config and ChartKit
13
+ * tooltip renderer.
14
+ */
15
+ export const renderTooltip = (data) => {
16
+ const cfg = data.yagr.config;
17
+ const timeMultiplier = cfg.chart.timeMultiplier || 1;
18
+ const opts = data.options;
19
+ const { x, pinned } = data;
20
+ let sumTotal = 0;
21
+ const rows = Object.values(data.scales).reduce((acc, scale) => {
22
+ sumTotal += scale.sum || 0;
23
+ return acc.concat(scale.rows);
24
+ }, []);
25
+ const lines = rows.length;
26
+ const sum = calcOption(opts.sum);
27
+ const maxLines = calcOption(opts.maxLines);
28
+ const valueFormatter = calcOption(opts.value);
29
+ // eslint-disable-next-line no-nested-ternary
30
+ const hiddenRowsNumber = pinned
31
+ ? undefined
32
+ : lines > maxLines
33
+ ? Math.abs(maxLines - lines)
34
+ : undefined;
35
+ const hiddenRowsSum = hiddenRowsNumber
36
+ ? valueFormatter(rows
37
+ .slice(-hiddenRowsNumber)
38
+ .reduce((acc, { originalValue }) => acc + (originalValue || 0), 0))
39
+ : undefined;
40
+ const tooltipFormatOptions = {
41
+ activeRowAlwaysFirstInTooltip: rows.length > 1,
42
+ tooltipHeader: moment(x / timeMultiplier).format('DD MMMM YYYY HH:mm:ss'),
43
+ shared: true,
44
+ lines: rows.map((row, i) => (Object.assign(Object.assign({}, row), { seriesName: row.name || 'Serie ' + (i + 1), seriesColor: row.color, selectedSeries: row.active, seriesIdx: row.seriesIdx, percentValue: typeof row.transformed === 'number' ? row.transformed.toFixed(1) : '' }))),
45
+ withPercent: calcOption(opts.percent),
46
+ hiddenRowsNumber: hiddenRowsNumber,
47
+ hiddenRowsSum,
48
+ };
49
+ if (sum) {
50
+ tooltipFormatOptions.sum = valueFormatter(sumTotal);
51
+ }
52
+ return formatTooltip(tooltipFormatOptions, {
53
+ lastVisibleRowIndex: pinned ? rows.length - 1 : maxLines - 1,
54
+ });
55
+ };
@@ -1,141 +1,3 @@
1
+ import type { TooltipData, TooltipExtraData } from './types';
1
2
  import './tooltip.css';
2
- export declare const SERIES_NAME_DATA_ATTRIBUTE = "data-series-name";
3
- export declare const SERIES_IDX_DATA_ATTRIBUTE = "data-series-idx";
4
- export declare const TOOLTIP_CONTAINER_CLASS_NAME = "_tooltip";
5
- export declare const TOOLTIP_ROW_NAME_CLASS_NANE = "_tooltip-rows__name-td";
6
- export declare const TOOLTIP_ROW_CLASS_NAME = "_tooltip-row";
7
- export declare const TOOLTIP_HEADER_CLASS_NAME = "_tooltip-header";
8
- export declare const TOOLTIP_LIST_CLASS_NAME = "_tooltip-list";
9
- export declare const TOOLTIP_FOOTER_CLASS_NAME = "_tooltip-footer";
10
- declare type Tooltip = any;
11
- export interface TooltipData {
12
- /**
13
- * Массив строк выводящихся в тултипе (см. TooltipLine)
14
- */
15
- lines: Array<TooltipLine>;
16
- /**
17
- * массив комментариев (задаются в диалоге комментариев)
18
- */
19
- xComments?: Array<{
20
- /**
21
- * текст комментария
22
- */
23
- text: string;
24
- /**
25
- * цвет отображающийся над комментарием
26
- */
27
- color: string;
28
- }>;
29
- /**
30
- * текст комментария (задаётся через manageTooltipConfig)
31
- */
32
- commentDateText?: string;
33
- /**
34
- * флаг, указывающий что нужно всегда дублировать активную строку выводя её поверх основного списка (дефолтное
35
- * поведение - активная строка выводится поверх основного списка только если "не поместилась" в тултип)
36
- */
37
- activeRowAlwaysFirstInTooltip?: boolean;
38
- /**
39
- * флаг, указыващий что чарт отображается в режиме split tooltip
40
- */
41
- splitTooltip?: boolean;
42
- /**
43
- * текст хедера тултипа
44
- */
45
- tooltipHeader?: string;
46
- /**
47
- * флаг, указывающий, что в тултипе выводится колонка с названием строки
48
- */
49
- shared?: boolean;
50
- /**
51
- * флаг, указывающий, что в тултипе выводится колонка с процентным значением
52
- */
53
- withPercent?: boolean;
54
- /**
55
- * флаг, указывающий, что в тултипе выводится колонка с diff-ом
56
- */
57
- useCompareFrom?: boolean;
58
- /**
59
- * флаг, указывающий, что в тултипе выводится блок с информацией о праздничном дне
60
- */
61
- holiday?: boolean;
62
- /**
63
- * название праздничного дня
64
- */
65
- holidayText?: string;
66
- /**
67
- * регион для которого актуален праздничный день
68
- */
69
- region?: string;
70
- /**
71
- * сумма значений выводящихся в тултипе строк
72
- */
73
- sum?: number | string;
74
- /**
75
- * количество скрытых строк "не поместившихся" в тултип
76
- */
77
- hiddenRowsNumber: number;
78
- /**
79
- * сумма значений скрытых ("не поместившихся" в тултип) строк
80
- */
81
- hiddenRowsSum?: number | string;
82
- }
83
- export interface TooltipLine {
84
- /**
85
- * цвет выводящийся в соответсвующей ячейке
86
- */
87
- seriesColor: string;
88
- /**
89
- * название измерения выводящееся в соответсвующей ячейке
90
- */
91
- seriesName: string;
92
- /**
93
- * индекс линии
94
- */
95
- seriesIdx?: number;
96
- /**
97
- * флаг, указывающий, должно ли отображаться название строки
98
- */
99
- hideSeriesName?: boolean;
100
- /**
101
- * процентное значение выводящееся в соответвующей ячейке
102
- */
103
- percentValue?: number | string;
104
- /**
105
- * значение diff-а выводящееся в соответвующей ячейке
106
- */
107
- diff?: string;
108
- /**
109
- * отформатированное числовое значение для текущего измерения выводящееся в соответствующей ячейке
110
- */
111
- value: string;
112
- /**
113
- * Комментарий к строке (отображается под соответствующей строкой), задаётся через manageTooltipConfig
114
- */
115
- commentText?: string;
116
- /**
117
- * Комментарий к строке (отображается под соответствующей строкой), задаётся через диалог комментариев
118
- */
119
- xyCommentText?: string;
120
- /**
121
- * флаг, указывающий, что данная строка активна
122
- */
123
- selectedSeries?: boolean;
124
- /**
125
- * кастомный рендер соответсвующей строки (строка с текстом либо html разметкой)
126
- */
127
- customRender?: string;
128
- /**
129
- * объект, где ключи - индексы ячеек, контент которых должен быть заменён, значения - функции возвращающие
130
- * строку (с текстом либо html разметкой) которая будет вставлена в ячейку на соответствующем индексе
131
- */
132
- replaceCellAt?: Record<number, (line: TooltipLine) => string>;
133
- /**
134
- * объект, где ключи - индексы на которые будут вставлены новые ячейки (ячейка ранее располагавшаяся на этом
135
- * индексе и последующие за ней будут сдвинуты), значения - функции возвращающие строку (с текстом либо html
136
- * разметкой) которая будет вставлена в добавившуюся ячейку
137
- */
138
- insertCellAt?: Record<number, (line: TooltipLine) => string>;
139
- }
140
- export declare const formatTooltip: (data: TooltipData, tooltip: Tooltip) => string;
141
- export {};
3
+ export declare const formatTooltip: (data: TooltipData, tooltip: TooltipExtraData) => string;
@@ -1,14 +1,15 @@
1
+ /* eslint complexity: 0 */
1
2
  import { i18n } from '../../../../i18n';
2
3
  import { escapeHTML } from './helpers/escapeHTML';
3
4
  import './tooltip.css';
4
- export const SERIES_NAME_DATA_ATTRIBUTE = 'data-series-name';
5
- export const SERIES_IDX_DATA_ATTRIBUTE = 'data-series-idx';
6
- export const TOOLTIP_CONTAINER_CLASS_NAME = '_tooltip';
7
- export const TOOLTIP_ROW_NAME_CLASS_NANE = '_tooltip-rows__name-td';
8
- export const TOOLTIP_ROW_CLASS_NAME = '_tooltip-row';
9
- export const TOOLTIP_HEADER_CLASS_NAME = '_tooltip-header';
10
- export const TOOLTIP_LIST_CLASS_NAME = '_tooltip-list';
11
- export const TOOLTIP_FOOTER_CLASS_NAME = '_tooltip-footer';
5
+ const SERIES_NAME_DATA_ATTRIBUTE = 'data-series-name';
6
+ const SERIES_IDX_DATA_ATTRIBUTE = 'data-series-idx';
7
+ const TOOLTIP_CONTAINER_CLASS_NAME = '_tooltip';
8
+ const TOOLTIP_ROW_NAME_CLASS_NANE = '_tooltip-rows__name-td';
9
+ const TOOLTIP_ROW_CLASS_NAME = '_tooltip-row';
10
+ const TOOLTIP_HEADER_CLASS_NAME = '_tooltip-header';
11
+ const TOOLTIP_LIST_CLASS_NAME = '_tooltip-list';
12
+ const TOOLTIP_FOOTER_CLASS_NAME = '_tooltip-footer';
12
13
  const renderEmptyCell = () => '<td />';
13
14
  const renderColorCell = (line) => `<td class="_tooltip-rows__bubble-td">
14
15
  <div class="_tooltip-rows__bubble-div" style="background-color:${line.seriesColor};"></div>
@@ -154,7 +155,7 @@ export const formatTooltip = (data, tooltip) => {
154
155
  tooltipContainerClassNames += ` ${TOOLTIP_CONTAINER_CLASS_NAME}_split-tooltip`;
155
156
  }
156
157
  return `
157
- <div class="${tooltipContainerClassNames}" style="${tooltip.preFixationHeight ? `height: ${tooltip.preFixationHeight}px; ` : ''}max-height: ${splitTooltip ? 'auto' : `${windowHeight}px`}">
158
+ <div class="${tooltipContainerClassNames}" style="max-height: ${splitTooltip ? 'auto' : `${windowHeight}px`}">
158
159
  ${data.tooltipHeader
159
160
  ? `<div title="${(document.createRange().createContextualFragment(data.tooltipHeader).textContent ||
160
161
  '').trim()}" class="_tooltip-date">
@@ -0,0 +1,74 @@
1
+ export declare type TooltipData = {
2
+ /** Tooltip lines data */
3
+ lines: Array<TooltipLine>;
4
+ /** Tooltip comments */
5
+ xComments?: Array<{
6
+ text: string;
7
+ color: string;
8
+ }>;
9
+ commentDateText?: string;
10
+ /**
11
+ * Indicating that active line duplicated by displaying it on top of the main list
12
+ * default behavior - the active line is displayed on top of the main list only if it"does not fit" in the tooltip
13
+ */
14
+ activeRowAlwaysFirstInTooltip?: boolean;
15
+ /** Indicating that the chart is displayed in "split tooltip" mode */
16
+ splitTooltip?: boolean;
17
+ /** Text of the header of the tooltip */
18
+ tooltipHeader?: string;
19
+ /** Indicating that a column with the line name is displayed in the tooltip */
20
+ shared?: boolean;
21
+ /** Indicating that a column with a percentage value is displayed in the tooltip */
22
+ withPercent?: boolean;
23
+ /** Indicating that a column with a diff is displayed in the tooltip */
24
+ useCompareFrom?: boolean;
25
+ /** Indicating that the tooltip displays a block with information about the holiday */
26
+ holiday?: boolean;
27
+ /** Name of the holiday */
28
+ holidayText?: string;
29
+ /** Region for which the holiday is relevant */
30
+ region?: string;
31
+ /** Sum of the values of the rows displayed in the tooltip */
32
+ sum?: number | string;
33
+ /** Number of hidden lines "not fit" in the tooltip */
34
+ hiddenRowsNumber: number;
35
+ /** Sum of the values of the hidden ("not fit" in the tooltip) rows */
36
+ hiddenRowsSum?: number | string;
37
+ };
38
+ export declare type TooltipExtraData = {
39
+ lastVisibleRowIndex: number;
40
+ };
41
+ export declare type TooltipLine = {
42
+ /** Color displayed in a separate cell */
43
+ seriesColor: string;
44
+ /** Series name */
45
+ seriesName: string;
46
+ /** Series index */
47
+ seriesIdx?: number;
48
+ /** Indicating whether the series name should be displayed */
49
+ hideSeriesName?: boolean;
50
+ /** Percentage value displayed in a separate cell */
51
+ percentValue?: number | string;
52
+ /** Diff value displayed in the separate cell */
53
+ diff?: string;
54
+ /** Formatted numeric value for the current series displayed in a separate cell */
55
+ value: string;
56
+ /** Comment to the line (displayed under the corresponding line), set via manageTooltipConfig */
57
+ commentText?: string;
58
+ /** Comment to the line (displayed under the corresponding line) */
59
+ xyCommentText?: string;
60
+ /** Indicating that line is active */
61
+ selectedSeries?: boolean;
62
+ /** Custom renderer of the line (a string with text or html markup) */
63
+ customRender?: string;
64
+ replaceCellAt?: Record<number, (line: TooltipLine) => string>;
65
+ insertCellAt?: Record<number, (line: TooltipLine) => string>;
66
+ };
67
+ export declare type RowRenderingConfig = {
68
+ cellsRenderers: Array<(line: TooltipLine) => string>;
69
+ isSelectedLine?: boolean;
70
+ allowComment?: boolean;
71
+ withDarkBackground?: boolean;
72
+ isSingleLine?: boolean;
73
+ rowIndex?: number;
74
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,9 @@
1
+ import type { MinimalValidConfig } from 'yagr';
2
+ import type { YagrChartProps } from 'yagr/dist/react';
3
+ import type { YagrWidgetData } from '../types';
4
+ export declare const useWidgetData: (args: YagrWidgetData & {
5
+ id: string;
6
+ }) => {
7
+ config: MinimalValidConfig;
8
+ debug: YagrChartProps['debug'];
9
+ };
@@ -0,0 +1,43 @@
1
+ import React from 'react';
2
+ import { useThemeType } from '@gravity-ui/uikit';
3
+ import { defaults } from 'yagr';
4
+ import { settings } from '../../../libs';
5
+ import { renderTooltip } from './tooltip';
6
+ import { getXAxisFormatter } from './utils';
7
+ export const useWidgetData = (args) => {
8
+ const { id, data, sources, libraryConfig } = args;
9
+ const theme = useThemeType();
10
+ const config = React.useMemo(() => {
11
+ var _a, _b, _c;
12
+ const result = Object.assign(Object.assign({}, libraryConfig), { timeline: data.timeline, series: data.graphs });
13
+ result.chart = Object.assign({ appereance: Object.assign({ locale: settings.get('lang'), theme }, (_a = result.chart) === null || _a === void 0 ? void 0 : _a.appereance) }, result.chart);
14
+ if (((_b = result.tooltip) === null || _b === void 0 ? void 0 : _b.show) !== false) {
15
+ result.tooltip = result.tooltip || {};
16
+ result.tooltip.render = ((_c = result.tooltip) === null || _c === void 0 ? void 0 : _c.render) || renderTooltip;
17
+ }
18
+ result.axes = result.axes || {};
19
+ const xAxis = result.axes[defaults.DEFAULT_X_SCALE];
20
+ if (xAxis && !xAxis.values) {
21
+ xAxis.values = getXAxisFormatter(result.chart.timeMultiplier);
22
+ }
23
+ if (!xAxis) {
24
+ result.axes[defaults.DEFAULT_X_SCALE] = {
25
+ values: getXAxisFormatter(result.chart.timeMultiplier),
26
+ };
27
+ }
28
+ return result;
29
+ }, [data, libraryConfig, theme]);
30
+ const debug = React.useMemo(() => {
31
+ const filename = sources
32
+ ? Object.values(sources)
33
+ .map((source) => {
34
+ var _a;
35
+ return (_a = source === null || source === void 0 ? void 0 : source.data) === null || _a === void 0 ? void 0 : _a.program;
36
+ })
37
+ .filter(Boolean)
38
+ .join(', ') || id
39
+ : id;
40
+ return { filename };
41
+ }, [id, sources]);
42
+ return { config, debug };
43
+ };
@@ -0,0 +1,15 @@
1
+ import type { default as Yagr } from 'yagr';
2
+ export declare const synchronizeTooltipTablesCellsWidth: (tooltipContainer: HTMLElement) => void;
3
+ export declare const checkFocus: (args: {
4
+ tooltip: HTMLElement;
5
+ yagr?: Yagr;
6
+ }) => (event: MouseEvent) => void;
7
+ export declare const detectClickOutside: (args: {
8
+ tooltip: HTMLElement;
9
+ actions: {
10
+ pin: (state: boolean) => void;
11
+ hide: () => void;
12
+ };
13
+ yagr?: Yagr<import("yagr").MinimalValidConfig> | undefined;
14
+ }) => (event: MouseEvent) => void;
15
+ export declare const getXAxisFormatter: (msm?: number) => (_: unknown, ticks: number[]) => string[];
@@ -0,0 +1,80 @@
1
+ import moment from 'moment';
2
+ const TOOLTIP_HEADER_CLASS_NAME = '_tooltip-header';
3
+ const TOOLTIP_LIST_CLASS_NAME = '_tooltip-list';
4
+ export const synchronizeTooltipTablesCellsWidth = (tooltipContainer) => {
5
+ const tHeadNode = tooltipContainer.querySelector(`.${TOOLTIP_HEADER_CLASS_NAME}`);
6
+ const tBodyNode = tooltipContainer.querySelector(`.${TOOLTIP_LIST_CLASS_NAME}`);
7
+ if (!tHeadNode || !tBodyNode || !tHeadNode.children.length) {
8
+ return;
9
+ }
10
+ const tHeadNodeFirstRow = tHeadNode.children[0];
11
+ for (let j = 0; j < tHeadNodeFirstRow.children.length; j++) {
12
+ const cell = tHeadNodeFirstRow.children[j];
13
+ cell.removeAttribute('style');
14
+ if ((tBodyNode === null || tBodyNode === void 0 ? void 0 : tBodyNode.children.length) === 1) {
15
+ cell.innerHTML = '&nbsp;';
16
+ }
17
+ }
18
+ const tBodyNodeFirstRow = tBodyNode.children[0];
19
+ for (let j = 0; j < tBodyNodeFirstRow.children.length; j++) {
20
+ const cell = tBodyNodeFirstRow.children[j];
21
+ cell.removeAttribute('style');
22
+ }
23
+ const tHeadRowsWidth = tHeadNode.children[0].getBoundingClientRect().width;
24
+ const tBodyRowsWidth = tBodyNode.children[0].getBoundingClientRect().width;
25
+ const nodeWithWidesRows = tHeadRowsWidth > tBodyRowsWidth ? tHeadNode : tBodyNode;
26
+ const nodeWithWidesRowsCellsWidth = Array.prototype.reduce.call(nodeWithWidesRows.children[0].children, (accum, cellNode) => {
27
+ accum.push(cellNode.getBoundingClientRect().width);
28
+ return accum;
29
+ }, []);
30
+ const nodeToSetCellsWidth = nodeWithWidesRows === tHeadNode ? tBodyNode : tHeadNode;
31
+ const nodeToSetCellsWidthFirstRow = nodeToSetCellsWidth.children[0];
32
+ for (let j = 0; j < nodeToSetCellsWidthFirstRow.children.length; j++) {
33
+ const cell = nodeToSetCellsWidthFirstRow.children[j];
34
+ cell.setAttribute('style', `width: ${nodeWithWidesRowsCellsWidth[j]}px`);
35
+ }
36
+ if (tBodyNode.children.length === 1) {
37
+ for (const cell of tHeadNodeFirstRow.children) {
38
+ cell.innerHTML = '';
39
+ }
40
+ }
41
+ };
42
+ export const checkFocus = (args) => (event) => {
43
+ var _a;
44
+ const { tooltip, yagr } = args;
45
+ if (!yagr) {
46
+ return;
47
+ }
48
+ const target = event.target;
49
+ const seriesIdx = target && tooltip.contains(target) && target.tagName === 'TD'
50
+ ? (_a = target.parentElement) === null || _a === void 0 ? void 0 : _a.dataset['seriesIdx']
51
+ : undefined;
52
+ const serie = seriesIdx ? yagr.uplot.series[Number(seriesIdx)] : null;
53
+ yagr.setFocus(serie ? serie.id : null, true);
54
+ };
55
+ export const detectClickOutside = (args) => (event) => {
56
+ var _a;
57
+ const { tooltip, actions, yagr } = args;
58
+ if (!yagr) {
59
+ return;
60
+ }
61
+ const target = event.target;
62
+ if (target instanceof Element) {
63
+ const isClickInsideTooltip = target && tooltip.contains(target);
64
+ const isClickOnUplotOver = target && ((_a = yagr.root.querySelector('.u-over')) === null || _a === void 0 ? void 0 : _a.contains(target));
65
+ if (!isClickInsideTooltip && !isClickOnUplotOver) {
66
+ actions.pin(false);
67
+ actions.hide();
68
+ }
69
+ }
70
+ };
71
+ export const getXAxisFormatter = (msm = 1) => (_, ticks) => {
72
+ const range = (ticks[ticks.length - 1] - ticks[0]) / msm;
73
+ return ticks.map((rawValue) => {
74
+ const d = moment(rawValue / msm);
75
+ if (d.hour() === 0 && d.minutes() === 0 && d.seconds() === 0) {
76
+ return d.format('DD.MM.YY');
77
+ }
78
+ return d.format(range < 300 ? 'HH:mm:ss' : 'HH:mm');
79
+ });
80
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/chartkit",
3
- "version": "0.13.0",
3
+ "version": "1.0.0",
4
4
  "description": "React component used to render charts based on any sources you need",
5
5
  "license": "MIT",
6
6
  "repository": "git@github.com:gravity-ui/ChartKit.git",
@@ -18,7 +18,7 @@
18
18
  "highcharts-react-official": "^3.0.0",
19
19
  "lodash": "^4.17.21",
20
20
  "react-split-pane": "^0.1.92",
21
- "yagr": "1.3.7"
21
+ "yagr": "^2.0.1"
22
22
  },
23
23
  "devDependencies": {
24
24
  "@gravity-ui/eslint-config": "^1.0.2",
@@ -1 +0,0 @@
1
- export declare function synchronizeTooltipTablesCellsWidth(tooltipContainer: any): false | undefined;
@@ -1,43 +0,0 @@
1
- const TOOLTIP_HEADER_CLASS_NAME = '_tooltip-header';
2
- const TOOLTIP_LIST_CLASS_NAME = '_tooltip-list';
3
- // @ts-ignore
4
- export function synchronizeTooltipTablesCellsWidth(tooltipContainer) {
5
- const tHeadNode = tooltipContainer.querySelector(`.${TOOLTIP_HEADER_CLASS_NAME}`);
6
- const tBodyNode = tooltipContainer.querySelector(`.${TOOLTIP_LIST_CLASS_NAME}`);
7
- if (!tHeadNode || !tHeadNode.children.length) {
8
- return false;
9
- }
10
- const tHeadNodeFirstRow = tHeadNode.children[0];
11
- for (let j = 0; j < tHeadNodeFirstRow.children.length; j++) {
12
- const cell = tHeadNodeFirstRow.children[j];
13
- cell.removeAttribute('style');
14
- if (tBodyNode.children.length === 1) {
15
- cell.innerHTML = '&nbsp;';
16
- }
17
- }
18
- const tBodyNodeFirstRow = tBodyNode.children[0];
19
- for (let j = 0; j < tBodyNodeFirstRow.children.length; j++) {
20
- const cell = tBodyNodeFirstRow.children[j];
21
- cell.removeAttribute('style');
22
- }
23
- const tHeadRowsWidth = tHeadNode.children[0].getBoundingClientRect().width;
24
- const tBodyRowsWidth = tBodyNode.children[0].getBoundingClientRect().width;
25
- const nodeWithWidesRows = tHeadRowsWidth > tBodyRowsWidth ? tHeadNode : tBodyNode;
26
- const nodeWithWidesRowsCellsWidth = Array.prototype.reduce.call(nodeWithWidesRows.children[0].children, (accum, cellNode) => {
27
- // @ts-ignore
28
- accum.push(cellNode.getBoundingClientRect().width);
29
- return accum;
30
- }, []);
31
- const nodeToSetCellsWidth = nodeWithWidesRows === tHeadNode ? tBodyNode : tHeadNode;
32
- const nodeToSetCellsWidthFirstRow = nodeToSetCellsWidth.children[0];
33
- for (let j = 0; j < nodeToSetCellsWidthFirstRow.children.length; j++) {
34
- const cell = nodeToSetCellsWidthFirstRow.children[j];
35
- // @ts-ignore
36
- cell.setAttribute('style', `width: ${nodeWithWidesRowsCellsWidth[j]}px`);
37
- }
38
- if (tBodyNode.children.length === 1) {
39
- for (const cell of tHeadNodeFirstRow.children) {
40
- cell.innerHTML = '';
41
- }
42
- }
43
- }