@cloud-ru/uikit-product-charts 0.13.12

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 (45) hide show
  1. package/CHANGELOG.md +843 -0
  2. package/LICENSE +201 -0
  3. package/README.md +8 -0
  4. package/package.json +67 -0
  5. package/src/components/BagelChart/BagelChart.tsx +47 -0
  6. package/src/components/BagelChart/index.ts +1 -0
  7. package/src/components/BagelChart/styles.module.scss +29 -0
  8. package/src/components/BagelChart/utils.ts +14 -0
  9. package/src/components/HeatMapChart/HeatMapChart.tsx +154 -0
  10. package/src/components/HeatMapChart/constants.ts +4 -0
  11. package/src/components/HeatMapChart/helpers/constants.ts +6 -0
  12. package/src/components/HeatMapChart/helpers/getComputedColor.ts +5 -0
  13. package/src/components/HeatMapChart/helpers/getContrastColor.ts +16 -0
  14. package/src/components/HeatMapChart/helpers/getStyles.ts +34 -0
  15. package/src/components/HeatMapChart/helpers/getTickValues.ts +63 -0
  16. package/src/components/HeatMapChart/helpers/index.ts +4 -0
  17. package/src/components/HeatMapChart/index.ts +2 -0
  18. package/src/components/HeatMapChart/styles.module.scss +96 -0
  19. package/src/components/HeatMapChart/types.ts +40 -0
  20. package/src/components/InteractiveChart/InteractiveChart.tsx +75 -0
  21. package/src/components/InteractiveChart/configurations/boxPlot.ts +75 -0
  22. package/src/components/InteractiveChart/configurations/defaultPlot.ts +63 -0
  23. package/src/components/InteractiveChart/configurations/index.ts +2 -0
  24. package/src/components/InteractiveChart/constants.ts +19 -0
  25. package/src/components/InteractiveChart/helpers/pathRenderer.ts +48 -0
  26. package/src/components/InteractiveChart/hooks/useComputedColors.ts +32 -0
  27. package/src/components/InteractiveChart/hooks/useLayer.ts +33 -0
  28. package/src/components/InteractiveChart/index.ts +2 -0
  29. package/src/components/InteractiveChart/plugins/boxPlotPlugin.ts +132 -0
  30. package/src/components/InteractiveChart/plugins/columnHighlightPlugin.ts +78 -0
  31. package/src/components/InteractiveChart/plugins/legendAsTooltipPlugin.ts +69 -0
  32. package/src/components/InteractiveChart/plugins/wheelZoomPlugin.ts +115 -0
  33. package/src/components/InteractiveChart/styles.module.scss +39 -0
  34. package/src/components/InteractiveChart/types.ts +7 -0
  35. package/src/components/PieChart/Legend/Legend.tsx +71 -0
  36. package/src/components/PieChart/Legend/index.ts +1 -0
  37. package/src/components/PieChart/Legend/styles.module.scss +45 -0
  38. package/src/components/PieChart/Pie.tsx +71 -0
  39. package/src/components/PieChart/PieChart.tsx +154 -0
  40. package/src/components/PieChart/index.ts +2 -0
  41. package/src/components/PieChart/styles.module.scss +56 -0
  42. package/src/components/PieChart/types.ts +42 -0
  43. package/src/components/index.ts +4 -0
  44. package/src/constants/colors.ts +70 -0
  45. package/src/index.ts +1 -0
@@ -0,0 +1,96 @@
1
+ @use '@sbercloud/figma-tokens-cloud-platform/build/scss/styles-theme-variables.scss';
2
+
3
+ $title-height: 74;
4
+ $legend-height: 94;
5
+ $x-axis-label-height: 32;
6
+ $ticks-size: 34;
7
+
8
+ .wrapper {
9
+ padding: 24px;
10
+ background-color: styles-theme-variables.$sys-neutral-background1-level;
11
+ border-radius: 8px;
12
+ }
13
+
14
+ .gridWrapper {
15
+ position: relative;
16
+ display: block;
17
+
18
+ &[data-grid] {
19
+ display: grid;
20
+ grid-auto-rows: minmax(min-content, max-content);
21
+ grid-template-columns: auto 1fr;
22
+ }
23
+ }
24
+
25
+ .title {
26
+ @include styles-theme-variables.composite-var(styles-theme-variables.$sans-title-m);
27
+ margin-bottom: 24px;
28
+ font-size: 20px;
29
+ font-weight: bold;
30
+ color: styles-theme-variables.$sys-neutral-text-main;
31
+ height: #{$title-height - 24}px;
32
+ }
33
+
34
+ .legend {
35
+ margin-top: 24px;
36
+ height: #{$legend-height - 24}px;
37
+ }
38
+
39
+ .gradient {
40
+ height: 20px;
41
+ margin: 24px 0 8px;
42
+ background: var(--gradient);
43
+ /* stylelint-disable-next-line declaration-no-important */
44
+ background-color: styles-theme-variables.$sys-red-accent-default !important;
45
+ border-radius: 4px;
46
+ }
47
+
48
+ .legendTicksWrapper {
49
+ display: flex;
50
+ justify-content: space-between;
51
+ }
52
+
53
+ .tick {
54
+ @include styles-theme-variables.composite-var(styles-theme-variables.$sans-body-m);
55
+
56
+ color: styles-theme-variables.$sys-neutral-text-support;
57
+ font-size: 12px;
58
+ }
59
+
60
+ .cell {
61
+ @include styles-theme-variables.composite-var(styles-theme-variables.$sans-body-s);
62
+
63
+ color: var(--color);
64
+ text-overflow: ellipsis;
65
+ overflow: hidden;
66
+ }
67
+
68
+ .xAxisLabel {
69
+ display: flex;
70
+ justify-content: center;
71
+ margin-top: 8px;
72
+ color: styles-theme-variables.$sys-neutral-text-support;
73
+ font-size: 12px;
74
+ font-weight: 600;
75
+ height: #{$x-axis-label-height - 8}px;
76
+ margin-left: 6px;
77
+ }
78
+
79
+ .yAxisLabel {
80
+ margin-right: 16px;
81
+ transform: rotate(180deg);
82
+ transform-origin: right, top;
83
+ writing-mode: vertical-rl;
84
+ text-align: center;
85
+ font-size: 12px;
86
+ font-weight: 600;
87
+ color: styles-theme-variables.$sys-neutral-text-support;
88
+
89
+ &[data-x-axis-position='top'] {
90
+ margin-top: #{$ticks-size}px;
91
+ }
92
+
93
+ &[data-x-axis-position='bottom'] {
94
+ margin-bottom: #{$ticks-size}px;
95
+ }
96
+ }
@@ -0,0 +1,40 @@
1
+ import { ReactNode } from 'react';
2
+
3
+ import { XAxisPosition } from './constants';
4
+
5
+ export type HeatMapChartAxisOptions = {
6
+ label?: string;
7
+ ticks?: string[];
8
+ };
9
+
10
+ export type HeatMapChartLegendOptions = {
11
+ show?: boolean;
12
+ };
13
+
14
+ export type HeatMapChartAxesOptions = {
15
+ xAxis?: HeatMapChartAxisOptions & { position?: XAxisPosition };
16
+ yAxis?: HeatMapChartAxisOptions;
17
+ };
18
+
19
+ export type HeatMapChartStyles = {
20
+ xLabelsStyle: (index: number) => Record<string, string | number>;
21
+ yLabelsStyle: (index: number) => Record<string, string | number>;
22
+ cellStyle: (x: number, y: number, ratio: number) => Record<string, string | number>;
23
+ };
24
+
25
+ export type HeatMapChartOptions = {
26
+ title?: string;
27
+ height?: number;
28
+ formatter?: (value: number) => string;
29
+ axes?: HeatMapChartAxesOptions;
30
+ domain: [number, number];
31
+ cellRender?: (x: number, y: number, value: number) => ReactNode;
32
+ legend?: HeatMapChartLegendOptions;
33
+ styles?: Partial<HeatMapChartStyles>;
34
+ };
35
+
36
+ export type HeatMapChartProps = {
37
+ data: number[][];
38
+ options: HeatMapChartOptions;
39
+ className?: string;
40
+ };
@@ -0,0 +1,75 @@
1
+ import 'uplot/dist/uPlot.min.css';
2
+
3
+ import './styles.module.scss';
4
+
5
+ import cn from 'classnames';
6
+ import merge from 'lodash.merge';
7
+ import { CSSProperties, useMemo } from 'react';
8
+ import uPlot from 'uplot';
9
+ import UPlotReact from 'uplot-react';
10
+
11
+ import { extractSupportProps, WithSupportProps } from '@cloud-ru/uikit-product-utils';
12
+
13
+ import {
14
+ CHART_OTHER_COLORS,
15
+ CHART_SERIES_COLORS,
16
+ COLOR_CONTAINER_CLASSNAME,
17
+ SeriesColorMap,
18
+ } from '../../constants/colors';
19
+ import { getBoxPlotOptions, getDefaultPlotOptions } from './configurations';
20
+ import { PLOT_TYPES } from './constants';
21
+ import { useComputedColors } from './hooks/useComputedColors';
22
+ import { PlotType } from './types';
23
+
24
+ export type InteractiveChartProps = {
25
+ data: uPlot.AlignedData;
26
+ options?: Partial<uPlot.Options>;
27
+ type?: PlotType;
28
+ className?: string;
29
+ };
30
+
31
+ function chooseBaseOptions({
32
+ type,
33
+ computedColors,
34
+ }: {
35
+ type: PlotType;
36
+ computedColors: SeriesColorMap;
37
+ }): uPlot.Options {
38
+ switch (type) {
39
+ case PLOT_TYPES.BoxPlot:
40
+ return getBoxPlotOptions({ computedColors });
41
+ case PLOT_TYPES.Default:
42
+ default:
43
+ return getDefaultPlotOptions({ computedColors });
44
+ }
45
+ }
46
+
47
+ export function InteractiveChart({
48
+ data,
49
+ options,
50
+ type = PLOT_TYPES.Default,
51
+ className,
52
+ ...rest
53
+ }: WithSupportProps<InteractiveChartProps>) {
54
+ const style = useMemo(
55
+ () =>
56
+ Object.entries({ ...CHART_SERIES_COLORS, ...CHART_OTHER_COLORS }).reduce((styles, [key, value]) => {
57
+ styles[`--${key}`] = value;
58
+ return styles;
59
+ }, {} as CSSProperties),
60
+ [],
61
+ );
62
+
63
+ const computedColors = useComputedColors();
64
+
65
+ const resultOptions = useMemo(
66
+ () => merge({}, chooseBaseOptions({ type, computedColors }), options),
67
+ [computedColors, options, type],
68
+ );
69
+
70
+ return (
71
+ <div {...extractSupportProps(rest)} className={cn(COLOR_CONTAINER_CLASSNAME, className)} style={style}>
72
+ <UPlotReact options={resultOptions} data={data} />
73
+ </div>
74
+ );
75
+ }
@@ -0,0 +1,75 @@
1
+ import uPlot from 'uplot';
2
+
3
+ import { ColorMap } from '../../../constants/colors';
4
+ import { boxPlotPlugin } from '../plugins/boxPlotPlugin';
5
+ import { columnHighlightPlugin } from '../plugins/columnHighlightPlugin';
6
+ import { legendAsTooltipPlugin } from '../plugins/legendAsTooltipPlugin';
7
+
8
+ export const getBoxPlotOptions = ({ computedColors }: { computedColors: ColorMap }): uPlot.Options => ({
9
+ id: 'boxPlot',
10
+ title: 'Distribution of object predictions by bin',
11
+ width: 800,
12
+ height: 600,
13
+ cursor: {
14
+ drag: {
15
+ x: false,
16
+ y: false,
17
+ },
18
+ },
19
+ plugins: [
20
+ boxPlotPlugin({ computedColors }),
21
+ columnHighlightPlugin({ computedColors }),
22
+ legendAsTooltipPlugin({ computedColors }),
23
+ ],
24
+ series: [
25
+ {
26
+ label: 'bin',
27
+ },
28
+ {
29
+ label: 'X1',
30
+ },
31
+ {
32
+ label: 'Q1',
33
+ },
34
+ {
35
+ label: 'Median',
36
+ },
37
+ {
38
+ label: 'Q3',
39
+ },
40
+ {
41
+ label: 'X2',
42
+ },
43
+ ],
44
+ scales: {
45
+ x: {
46
+ distr: 2,
47
+ time: false,
48
+ range: (self, fromMin, fromMax) => [fromMin - 1, fromMax + 1],
49
+ },
50
+ y: {
51
+ time: false,
52
+ auto: true,
53
+ },
54
+ },
55
+ axes: [
56
+ {
57
+ label: 'Bin Number',
58
+ labelSize: 30,
59
+ stroke: '#808080',
60
+ grid: {
61
+ stroke: 'rgba(128, 128, 128, 0.15)',
62
+ width: 1 / devicePixelRatio,
63
+ },
64
+ },
65
+ {
66
+ label: 'Prediction',
67
+ labelSize: 30,
68
+ stroke: '#808080',
69
+ grid: {
70
+ stroke: 'rgba(128, 128, 128, 0.15)',
71
+ width: 1 / devicePixelRatio,
72
+ },
73
+ },
74
+ ],
75
+ });
@@ -0,0 +1,63 @@
1
+ import Color from 'color';
2
+ import uPlot from 'uplot';
3
+
4
+ import { ColorMap, OTHER_COLORS } from '../../../constants/colors';
5
+ import { wheelZoomPlugin } from '../plugins/wheelZoomPlugin';
6
+
7
+ export const getDefaultPlotOptions = ({ computedColors }: { computedColors: ColorMap }): uPlot.Options => {
8
+ const axisColor = new Color(computedColors[OTHER_COLORS.AxisColor]).alpha(0.8).rgb().string();
9
+ const labelColor = computedColors[OTHER_COLORS.LabelColor];
10
+
11
+ return {
12
+ id: 'defaultPlot',
13
+ title: 'Title',
14
+ width: 800,
15
+ height: 600,
16
+ cursor: {
17
+ drag: {
18
+ x: true,
19
+ y: true,
20
+ dist: 10,
21
+ uni: 10,
22
+ },
23
+ },
24
+ plugins: [wheelZoomPlugin({ factor: 0.75 })],
25
+ series: [
26
+ {
27
+ label: 'x',
28
+ },
29
+ ],
30
+ scales: {
31
+ x: {
32
+ time: false,
33
+ auto: true,
34
+ },
35
+ y: {
36
+ time: false,
37
+ auto: true,
38
+ },
39
+ },
40
+ axes: [
41
+ {
42
+ show: true,
43
+ label: 'x',
44
+ labelSize: 30,
45
+ stroke: labelColor,
46
+ grid: {
47
+ stroke: axisColor,
48
+ width: 1 / devicePixelRatio,
49
+ },
50
+ },
51
+ {
52
+ show: true,
53
+ label: 'y',
54
+ labelSize: 30,
55
+ stroke: labelColor,
56
+ grid: {
57
+ stroke: axisColor,
58
+ width: 1 / devicePixelRatio,
59
+ },
60
+ },
61
+ ],
62
+ };
63
+ };
@@ -0,0 +1,2 @@
1
+ export * from './boxPlot';
2
+ export * from './defaultPlot';
@@ -0,0 +1,19 @@
1
+ export const PLOT_TYPES = {
2
+ BoxPlot: 'boxPlot',
3
+ Default: 'default',
4
+ } as const;
5
+
6
+ export const LINE_INTERPOLATIONS = {
7
+ Linear: 'linear',
8
+ StepAfter: 'stepAfter',
9
+ StepBefore: 'stepBefore',
10
+ Spline: 'spline',
11
+ } as const;
12
+
13
+ export const DRAW_STYLES = {
14
+ Line: 'line',
15
+ Bars: 'bars',
16
+ Points: 'points',
17
+ BarsLeft: 'barsLeft',
18
+ BarsRight: 'barsRight',
19
+ } as const;
@@ -0,0 +1,48 @@
1
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
2
+ // @ts-nocheck
3
+ /* eslint-disable */
4
+
5
+ import uPlot from 'uplot';
6
+
7
+ import { LINE_INTERPOLATIONS, DRAW_STYLES } from '../constants';
8
+ import { DrawStyle, LineInterpolation } from '../types';
9
+
10
+ const { linear, stepped, bars, spline } = uPlot.paths;
11
+
12
+ const _bars60_100 = bars({ size: [0.6, 100] });
13
+ const _bars100Left = bars({ size: [1], align: 1 });
14
+ const _bars100Right = bars({ size: [1], align: -1 });
15
+ const _stepBefore = stepped({ align: -1 });
16
+ const _stepAfter = stepped({ align: 1 });
17
+ const _linear = linear();
18
+ const _spline = spline();
19
+
20
+ export function getPathRenderer(drawStyle: DrawStyle, lineInterpolation?: LineInterpolation) {
21
+ return function pathRenderer(self: uPlot, seriesIdx: number, idx0: number, idx1: number): uPlot.Series.Paths | null {
22
+ const style = drawStyle;
23
+ const interp = lineInterpolation;
24
+
25
+ const renderer =
26
+ style === DRAW_STYLES.Line
27
+ ? interp === LINE_INTERPOLATIONS.Linear
28
+ ? _linear
29
+ : interp === LINE_INTERPOLATIONS.StepAfter
30
+ ? _stepAfter
31
+ : interp === LINE_INTERPOLATIONS.StepBefore
32
+ ? _stepBefore
33
+ : interp === LINE_INTERPOLATIONS.Spline
34
+ ? _spline
35
+ : null
36
+ : style === DRAW_STYLES.Bars
37
+ ? _bars60_100
38
+ : style === DRAW_STYLES.BarsLeft
39
+ ? _bars100Left
40
+ : style === DRAW_STYLES.BarsRight
41
+ ? _bars100Right
42
+ : style === DRAW_STYLES.Points
43
+ ? () => null
44
+ : () => null;
45
+
46
+ return renderer(self, seriesIdx, idx0, idx1);
47
+ };
48
+ }
@@ -0,0 +1,32 @@
1
+ import { useState } from 'react';
2
+
3
+ import { useTheme } from '@cloud-ru/uikit-product-utils';
4
+ import { useLayoutEffect } from '@snack-uikit/utils';
5
+
6
+ import { COLOR_CONTAINER_CLASSNAME, OTHER_COLORS, SERIES_COLORS, SeriesColorMap } from '../../../constants/colors';
7
+
8
+ const EMPTY_COLORS = {};
9
+
10
+ export function useComputedColors() {
11
+ const { theme } = useTheme();
12
+ const [computedColors, setComputedColors] = useState<SeriesColorMap>(EMPTY_COLORS);
13
+
14
+ useLayoutEffect(() => {
15
+ const colorContainer = document.querySelector('.' + COLOR_CONTAINER_CLASSNAME);
16
+
17
+ if (!colorContainer) {
18
+ return;
19
+ }
20
+
21
+ const styles = getComputedStyle(colorContainer);
22
+
23
+ setComputedColors(
24
+ Object.values({ ...SERIES_COLORS, ...OTHER_COLORS }).reduce((colors, color) => {
25
+ colors[color] = styles.getPropertyValue(`--${color}`);
26
+ return colors;
27
+ }, {} as SeriesColorMap),
28
+ );
29
+ }, [theme]);
30
+
31
+ return computedColors;
32
+ }
@@ -0,0 +1,33 @@
1
+ import Color from 'color';
2
+ import { useMemo } from 'react';
3
+ import { Series } from 'uplot';
4
+
5
+ import { SeriesColor } from '../../../constants/colors';
6
+ import { getPathRenderer } from '../helpers/pathRenderer';
7
+ import { DrawStyle, LineInterpolation } from '../types';
8
+ import { useComputedColors } from './useComputedColors';
9
+
10
+ type Layer = Partial<Series>;
11
+ type UseLayerProps = {
12
+ label: string;
13
+ color: SeriesColor;
14
+ drawStyle: DrawStyle;
15
+ lineInterpolation?: LineInterpolation;
16
+ };
17
+
18
+ export function useLayer({ label, color, drawStyle, lineInterpolation }: UseLayerProps): Layer {
19
+ const computedColors = useComputedColors();
20
+ const stroke = computedColors[color];
21
+ const fill = new Color(stroke).alpha(0.1).rgb().string();
22
+
23
+ return useMemo(
24
+ () => ({
25
+ label,
26
+ stroke,
27
+ fill,
28
+ width: 2,
29
+ paths: getPathRenderer(drawStyle, lineInterpolation),
30
+ }),
31
+ [drawStyle, label, stroke, fill, lineInterpolation],
32
+ );
33
+ }
@@ -0,0 +1,2 @@
1
+ export * from './InteractiveChart';
2
+ export * from './hooks/useLayer';
@@ -0,0 +1,132 @@
1
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
2
+ // @ts-nocheck
3
+ /* eslint-disable */
4
+ import Color from 'color';
5
+ import uPlot from 'uplot';
6
+
7
+ import { SERIES_COLORS, ColorMap, OTHER_COLORS } from '../../../constants/colors';
8
+
9
+ export function boxPlotPlugin({
10
+ gap = 5,
11
+ bodyMaxWidth = 60,
12
+ shadowWidth = 3,
13
+ computedColors,
14
+ }: {
15
+ gap?: number;
16
+ bodyMaxWidth?: number;
17
+ shadowWidth?: number;
18
+ computedColors: ColorMap;
19
+ }) {
20
+ const shadowColor = computedColors[OTHER_COLORS.ShadowColor];
21
+ const lineColor = new Color(computedColors[OTHER_COLORS.LineColor]).alpha(0.5).rgb().string();
22
+
23
+ function roundRect(ctx, x, y, width, height, radius) {
24
+ ctx.beginPath();
25
+ ctx.moveTo(x + radius, y);
26
+ ctx.lineTo(x + width - radius, y);
27
+ ctx.quadraticCurveTo(x + width, y, x + width, y - radius);
28
+ ctx.lineTo(x + width, y + height + radius);
29
+ ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
30
+ ctx.lineTo(x + radius, y + height);
31
+ ctx.quadraticCurveTo(x, y + height, x, y + height + radius);
32
+ ctx.lineTo(x, y - radius);
33
+ ctx.quadraticCurveTo(x, y, x + radius, y);
34
+ ctx.closePath();
35
+ ctx.fill();
36
+ }
37
+
38
+ function drawBoxes(u) {
39
+ u.ctx.save();
40
+
41
+ const offset = (shadowWidth % 2) / 2;
42
+
43
+ u.ctx.translate(offset, offset);
44
+
45
+ const [iMin, iMax] = u.series[0].idxs;
46
+
47
+ for (let i = iMin; i <= iMax; i++) {
48
+ const xVal = u.scales.x.distr === 2 ? i : u.data[0][i];
49
+ const open = u.data[1][i];
50
+ const low = u.data[2][i];
51
+ const median = u.data[3][i];
52
+ const high = u.data[4][i];
53
+ const close = u.data[5][i];
54
+
55
+ const timeAsX = u.valToPos(xVal, 'x', true);
56
+ const openAsY = u.valToPos(open, 'y', true);
57
+ const lowAsY = u.valToPos(low, 'y', true);
58
+ const medianAsY = u.valToPos(median, 'y', true);
59
+ const highAsY = u.valToPos(high, 'y', true);
60
+ const closeAsY = u.valToPos(close, 'y', true);
61
+
62
+ // moustache
63
+ const shadowHeight = closeAsY - openAsY;
64
+ const shadowX = timeAsX - shadowWidth / 2;
65
+ const shadowY = openAsY;
66
+ const columnWidth = u.bbox.width / (iMax - iMin + 2);
67
+ const bodyWidth = Math.min(bodyMaxWidth, columnWidth - gap);
68
+
69
+ u.ctx.fillStyle = shadowColor;
70
+ u.ctx.fillRect(Math.round(shadowX), Math.round(shadowY), Math.round(shadowWidth), Math.round(shadowHeight));
71
+ u.ctx.fillRect(
72
+ Math.round(timeAsX - bodyWidth / 4),
73
+ Math.round(shadowY - shadowWidth / 2),
74
+ Math.round(bodyWidth / 2),
75
+ Math.round(shadowWidth),
76
+ );
77
+ u.ctx.fillRect(
78
+ Math.round(timeAsX - bodyWidth / 4),
79
+ Math.round(shadowY + shadowHeight - shadowWidth / 2),
80
+ Math.round(bodyWidth / 2),
81
+ Math.round(shadowWidth),
82
+ );
83
+
84
+ // body rect
85
+ const bodyHeight = highAsY - lowAsY;
86
+ const bodyX = timeAsX - bodyWidth / 2;
87
+ const bodyY = lowAsY;
88
+
89
+ if (Math.abs(bodyHeight) > 8) {
90
+ u.ctx.fillStyle = computedColors?.[Object.values(SERIES_COLORS)[i % Object.keys(SERIES_COLORS).length]];
91
+
92
+ roundRect(
93
+ u.ctx,
94
+ Math.round(bodyX),
95
+ Math.round(bodyY),
96
+ Math.round(bodyWidth),
97
+ Math.round(bodyHeight),
98
+ 8,
99
+ true,
100
+ true,
101
+ );
102
+
103
+ u.ctx.fillStyle = lineColor;
104
+ u.ctx.fillRect(Math.round(bodyX), medianAsY - shadowWidth / 2, Math.round(bodyWidth), Math.round(shadowWidth));
105
+ }
106
+ }
107
+
108
+ u.ctx.translate(-offset, -offset);
109
+
110
+ u.ctx.restore();
111
+ }
112
+
113
+ return {
114
+ opts: (u, opts) => {
115
+ uPlot.assign(opts, {
116
+ cursor: {
117
+ points: {
118
+ show: false,
119
+ },
120
+ },
121
+ });
122
+
123
+ opts.series.forEach(series => {
124
+ series.paths = () => null;
125
+ series.points = { show: false };
126
+ });
127
+ },
128
+ hooks: {
129
+ draw: drawBoxes,
130
+ },
131
+ };
132
+ }
@@ -0,0 +1,78 @@
1
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
2
+ // @ts-nocheck
3
+ /* eslint-disable */
4
+ import Color from 'color';
5
+ import uPlot from 'uplot';
6
+ import { ColorMap, OTHER_COLORS } from '../../../constants/colors';
7
+
8
+ export function columnHighlightPlugin({
9
+ className = '',
10
+ computedColors,
11
+ }: {
12
+ className?: string;
13
+ computedColors: ColorMap;
14
+ }) {
15
+ const backgroundColor = new Color(computedColors[OTHER_COLORS.ColumnHighlightColor]).alpha(0.5).rgb().string();
16
+
17
+ let underEl, overEl, currIdx: number;
18
+
19
+ function init(u: uPlot) {
20
+ underEl = u.under;
21
+ overEl = u.over;
22
+
23
+ const highlightEl = document.createElement('div');
24
+
25
+ className && highlightEl.classList.add(className);
26
+
27
+ uPlot.assign(highlightEl.style, {
28
+ pointerEvents: 'none',
29
+ display: 'none',
30
+ position: 'absolute',
31
+ left: 0,
32
+ top: 0,
33
+ height: '100%',
34
+ backgroundColor,
35
+ });
36
+
37
+ underEl.appendChild(highlightEl);
38
+
39
+ u._highlightEl = highlightEl;
40
+
41
+ // show/hide highlight on enter/exit
42
+ overEl.addEventListener('mouseenter', () => {
43
+ highlightEl.style.display = null;
44
+ });
45
+ overEl.addEventListener('mouseleave', () => {
46
+ highlightEl.style.display = 'none';
47
+ });
48
+ }
49
+
50
+ function update(u) {
51
+ if (currIdx !== u.cursor.idx) {
52
+ currIdx = u.cursor.idx;
53
+ const highlightEl = u._highlightEl;
54
+
55
+ const dx = u.scales.x.max - u.scales.x.min;
56
+ const width = u.bbox.width / dx / devicePixelRatio;
57
+ const left = u.valToPos(currIdx, 'x') - width / 2;
58
+
59
+ highlightEl.style.transform = 'translateX(' + Math.round(left) + 'px)';
60
+ highlightEl.style.width = Math.round(width) + 'px';
61
+ }
62
+ }
63
+
64
+ return {
65
+ opts: (u, opts) => {
66
+ uPlot.assign(opts, {
67
+ cursor: {
68
+ x: false,
69
+ y: false,
70
+ },
71
+ });
72
+ },
73
+ hooks: {
74
+ init: init,
75
+ setCursor: update,
76
+ },
77
+ };
78
+ }