@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,69 @@
1
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
2
+ // @ts-nocheck
3
+ /* eslint-disable */
4
+
5
+ import uPlot from 'uplot';
6
+ import { ColorMap, OTHER_COLORS } from '../../../constants/colors';
7
+
8
+ export function legendAsTooltipPlugin({
9
+ className = '',
10
+ computedColors,
11
+ }: {
12
+ className?: string;
13
+ computedColors: ColorMap;
14
+ }) {
15
+ const backgroundColor = computedColors[OTHER_COLORS.TooltipBackgroundColor];
16
+ const color = computedColors[OTHER_COLORS.TooltipColor];
17
+
18
+ function init(u: uPlot, opts) {
19
+ const legendEl = u.root.querySelector(`.u-legend`) as Element;
20
+
21
+ legendEl.classList.remove('u-inline');
22
+ className && legendEl.classList.add(className);
23
+
24
+ uPlot.assign(legendEl.style, {
25
+ borderRadius: '4px',
26
+ textAlign: 'left',
27
+ pointerEvents: 'none',
28
+ display: 'none',
29
+ position: 'absolute',
30
+ left: 0,
31
+ top: 0,
32
+ zIndex: 100,
33
+ backgroundColor,
34
+ color,
35
+ });
36
+
37
+ // hide series color markers
38
+ const idents = legendEl.querySelectorAll('.u-marker');
39
+
40
+ for (let i = 0; i < idents.length; i++) idents[i].style.display = 'none';
41
+
42
+ const overEl = u.over;
43
+ overEl.style.overflow = 'visible';
44
+
45
+ // move legend into plot bounds
46
+ overEl.appendChild(legendEl);
47
+
48
+ // show/hide tooltip on enter/exit
49
+ overEl.addEventListener('mouseenter', () => {
50
+ legendEl.style.display = null;
51
+ });
52
+ overEl.addEventListener('mouseleave', () => {
53
+ legendEl.style.display = 'none';
54
+ });
55
+ }
56
+
57
+ function update(u) {
58
+ const { left, top } = u.cursor;
59
+ const legendEl = u.root.querySelector(`.u-legend`) as Element;
60
+ legendEl.style.transform = 'translate(' + (left + 15) + 'px, ' + top + 'px)';
61
+ }
62
+
63
+ return {
64
+ hooks: {
65
+ init: init,
66
+ setCursor: update,
67
+ },
68
+ };
69
+ }
@@ -0,0 +1,115 @@
1
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
2
+ // @ts-nocheck
3
+ /* eslint-disable */
4
+
5
+ export function wheelZoomPlugin(opts) {
6
+ const factor = opts.factor || 0.75;
7
+
8
+ let xMin, xMax, yMin, yMax, xRange, yRange;
9
+
10
+ function clamp(nRange, nMin, nMax, fRange, fMin, fMax) {
11
+ if (nRange > fRange) {
12
+ nMin = fMin;
13
+ nMax = fMax;
14
+ } else if (nMin < fMin) {
15
+ nMin = fMin;
16
+ nMax = fMin + nRange;
17
+ } else if (nMax > fMax) {
18
+ nMax = fMax;
19
+ nMin = fMax - nRange;
20
+ }
21
+
22
+ return [nMin, nMax];
23
+ }
24
+
25
+ return {
26
+ hooks: {
27
+ ready: u => {
28
+ xMin = u.scales.x.min;
29
+ xMax = u.scales.x.max;
30
+ yMin = u.scales.y.min;
31
+ yMax = u.scales.y.max;
32
+
33
+ xRange = xMax - xMin;
34
+ yRange = yMax - yMin;
35
+
36
+ const plot = u.root.querySelector('.u-over');
37
+ const rect = plot.getBoundingClientRect();
38
+
39
+ // wheel drag pan
40
+ plot.addEventListener('mousedown', e => {
41
+ if (e.button == 1) {
42
+ // plot.style.cursor = "move";
43
+ e.preventDefault();
44
+
45
+ const left0 = e.clientX;
46
+ // let top0 = e.clientY;
47
+
48
+ const scXMin0 = u.scales.x.min;
49
+ const scXMax0 = u.scales.x.max;
50
+
51
+ const xUnitsPerPx = u.posToVal(1, 'x') - u.posToVal(0, 'x');
52
+
53
+ function onmove(e) {
54
+ e.preventDefault();
55
+
56
+ const left1 = e.clientX;
57
+ // let top1 = e.clientY;
58
+
59
+ const dx = xUnitsPerPx * (left1 - left0);
60
+
61
+ u.setScale('x', {
62
+ min: scXMin0 - dx,
63
+ max: scXMax0 - dx,
64
+ });
65
+ }
66
+
67
+ function onup(e) {
68
+ document.removeEventListener('mousemove', onmove);
69
+ document.removeEventListener('mouseup', onup);
70
+ }
71
+
72
+ document.addEventListener('mousemove', onmove);
73
+ document.addEventListener('mouseup', onup);
74
+ }
75
+ });
76
+
77
+ // wheel scroll zoom
78
+ plot.addEventListener('wheel', e => {
79
+ e.preventDefault();
80
+
81
+ const { left, top } = u.cursor;
82
+
83
+ const leftPct = left / rect.width;
84
+ const btmPct = 1 - top / rect.height;
85
+ const xVal = u.posToVal(left, 'x');
86
+ const yVal = u.posToVal(top, 'y');
87
+ const oxRange = u.scales.x.max - u.scales.x.min;
88
+ const oyRange = u.scales.y.max - u.scales.y.min;
89
+
90
+ const nxRange = e.deltaY < 0 ? oxRange * factor : oxRange / factor;
91
+ let nxMin = xVal - leftPct * nxRange;
92
+ let nxMax = nxMin + nxRange;
93
+ [nxMin, nxMax] = clamp(nxRange, nxMin, nxMax, xRange, xMin, xMax);
94
+
95
+ const nyRange = e.deltaY < 0 ? oyRange * factor : oyRange / factor;
96
+ let nyMin = yVal - btmPct * nyRange;
97
+ let nyMax = nyMin + nyRange;
98
+ [nyMin, nyMax] = clamp(nyRange, nyMin, nyMax, yRange, yMin, yMax);
99
+
100
+ u.batch(() => {
101
+ u.setScale('x', {
102
+ min: nxMin,
103
+ max: nxMax,
104
+ });
105
+
106
+ u.setScale('y', {
107
+ min: nyMin,
108
+ max: nyMax,
109
+ });
110
+ });
111
+ });
112
+ },
113
+ },
114
+ };
115
+ }
@@ -0,0 +1,39 @@
1
+ @use '@sbercloud/figma-tokens-cloud-platform/build/scss/styles-theme-variables';
2
+
3
+ /* stylelint-disable-next-line selector-pseudo-class-no-unknown*/
4
+ :global {
5
+ .interactive-chart-wrapper {
6
+ & .uplot {
7
+ font-family: inherit;
8
+ background-color: styles-theme-variables.$sys-neutral-background1-level;
9
+ padding: 16px 0;
10
+ border-radius: 8px;
11
+ color: styles-theme-variables.$sys-neutral-text-main;
12
+ }
13
+
14
+ & .uplot .u-title {
15
+ font-size: 20px;
16
+ text-align: unset;
17
+ padding-left: 24px;
18
+ }
19
+
20
+ /* mouse cross hair */
21
+ & .u-hz .u-cursor-x,
22
+ & .u-vt .u-cursor-y,
23
+ & .u-hz .u-cursor-y,
24
+ & .u-vt .u-cursor-x {
25
+ border-color: styles-theme-variables.$sys-neutral-decor-default;
26
+ }
27
+
28
+ /* Selected area */
29
+ & .u-select {
30
+ background-color: styles-theme-variables.$sys-neutral-decor-disabled;
31
+ }
32
+
33
+ & table .u-series .u-marker {
34
+ width: 16px;
35
+ height: 16px;
36
+ border-radius: 16px;
37
+ }
38
+ }
39
+ }
@@ -0,0 +1,7 @@
1
+ import { ValueOf } from '@snack-uikit/utils';
2
+
3
+ import { DRAW_STYLES, LINE_INTERPOLATIONS, PLOT_TYPES } from './constants';
4
+
5
+ export type PlotType = ValueOf<typeof PLOT_TYPES>;
6
+ export type LineInterpolation = ValueOf<typeof LINE_INTERPOLATIONS>;
7
+ export type DrawStyle = ValueOf<typeof DRAW_STYLES>;
@@ -0,0 +1,71 @@
1
+ import { Fragment, MouseEvent } from 'react';
2
+
3
+ import { Divider } from '@snack-uikit/divider';
4
+ import { Link } from '@snack-uikit/link';
5
+ import { Typography } from '@snack-uikit/typography';
6
+
7
+ import { TextLike } from '../types';
8
+ import styles from './styles.module.scss';
9
+
10
+ type LegendItem = {
11
+ label: TextLike;
12
+ value: TextLike;
13
+ color?: string;
14
+ id?: string;
15
+ };
16
+
17
+ type LegendItemProps = LegendItem & {
18
+ size: 's' | 'm' | 'l';
19
+ onItemClick?: (event: MouseEvent<HTMLAnchorElement>) => void;
20
+ };
21
+
22
+ function LegendItem({ color, label, value, size, onItemClick }: LegendItemProps) {
23
+ return (
24
+ <div className={styles.legendItemWrapper}>
25
+ <span className={styles.legendItemTitle}>
26
+ {color && <span className={styles.dot} style={{ '--color': color }} />}
27
+ <Link onClick={onItemClick} text={String(label)} size={size} />
28
+ </span>
29
+
30
+ <span className={styles.legendValue}>{value}</span>
31
+ </div>
32
+ );
33
+ }
34
+
35
+ type LegendProps = {
36
+ data: Array<LegendItem>;
37
+ typographySize: 's' | 'm' | 'l';
38
+ legendTitle?: string;
39
+ onItemClick?: (event: MouseEvent<HTMLAnchorElement>, data: LegendItem) => void;
40
+ };
41
+
42
+ export function Legend({ data, legendTitle, typographySize, onItemClick }: LegendProps) {
43
+ return (
44
+ <div className={styles.legend}>
45
+ {legendTitle && (
46
+ <>
47
+ <Typography purpose={'label'} family={'sans'} size={typographySize}>
48
+ {legendTitle}
49
+ </Typography>
50
+ <div className={styles.legendDividerWrapper}>
51
+ <Divider />
52
+ </div>
53
+ </>
54
+ )}
55
+ {data.map((item, index) => (
56
+ <Fragment key={`legend_${item.label}_${index}`}>
57
+ <LegendItem
58
+ {...item}
59
+ size={typographySize}
60
+ onItemClick={onItemClick ? event => onItemClick(event, item) : undefined}
61
+ />
62
+ {index !== data.length - 1 && (
63
+ <div className={styles.legendDividerWrapper}>
64
+ <Divider />
65
+ </div>
66
+ )}
67
+ </Fragment>
68
+ ))}
69
+ </div>
70
+ );
71
+ }
@@ -0,0 +1 @@
1
+ export * from './Legend';
@@ -0,0 +1,45 @@
1
+ @use '@sbercloud/figma-tokens-cloud-platform/build/scss/styles-theme-variables';
2
+
3
+ .legend {
4
+ display: flex;
5
+ flex-direction: column;
6
+ color: styles-theme-variables.$sys-neutral-text-support;
7
+ padding-right: 8px;
8
+ }
9
+
10
+ .legendItemWrapper {
11
+ color: styles-theme-variables.$sys-neutral-text-main;
12
+ display: flex;
13
+ align-items: center;
14
+ flex-direction: row;
15
+ gap: 8px;
16
+ justify-content: space-between;
17
+ }
18
+
19
+ .dot {
20
+ display: inline-block;
21
+ min-width: 8px;
22
+ min-height: 8px;
23
+ width: 8px;
24
+ height: 8px;
25
+ border-radius: 100%;
26
+
27
+ margin-right: 12px;
28
+ background-color: var(--color);
29
+ }
30
+
31
+ .legendDividerWrapper {
32
+ margin: 16px 0;
33
+ }
34
+
35
+ .legendItemTitle {
36
+ @include styles-theme-variables.composite-var(styles-theme-variables.$sans-body-m);
37
+
38
+ display: flex;
39
+ align-items: center;
40
+ }
41
+
42
+ .legendValue {
43
+ @include styles-theme-variables.composite-var(styles-theme-variables.$sans-label-l);
44
+ }
45
+
@@ -0,0 +1,71 @@
1
+ import { arc, pie, PieArcDatum } from 'd3-shape';
2
+ import { CSSProperties, Fragment, MouseEvent } from 'react';
3
+
4
+ import { DataType, LabelRenderFunction } from './types';
5
+
6
+ type PieProps = {
7
+ data: DataType[];
8
+ label: LabelRenderFunction<DataType>;
9
+ onMouseOut: () => void;
10
+ onMouseOver: (event: MouseEvent<SVGPathElement>, dataIndex: number) => void;
11
+ onMouseDown: (event: MouseEvent<SVGPathElement>, dataIndex: number) => void;
12
+ radius: number;
13
+ innerRadius: number;
14
+ segmentsShift: number;
15
+ segmentsStyle: CSSProperties;
16
+ hoveredIndex?: number;
17
+ style: CSSProperties;
18
+ };
19
+
20
+ export function Pie({
21
+ data,
22
+ hoveredIndex,
23
+ radius,
24
+ innerRadius,
25
+ segmentsShift,
26
+ onMouseOut,
27
+ label,
28
+ style,
29
+ segmentsStyle,
30
+ onMouseOver,
31
+ onMouseDown,
32
+ }: PieProps) {
33
+ const pieSegments = pie<DataType>()
34
+ .sort(null)
35
+ .value((d: DataType) => d.value)(data);
36
+
37
+ const getHoveredPath = arc<PieArcDatum<DataType>>()
38
+ .outerRadius(radius + 1)
39
+ .innerRadius(innerRadius + 1)
40
+ .startAngle(d => d.startAngle + Math.PI / 2)
41
+ .endAngle(d => d.endAngle + Math.PI / 2)
42
+ .padAngle(segmentsShift);
43
+
44
+ const getPath = arc<PieArcDatum<DataType>>()
45
+ .outerRadius(radius)
46
+ .innerRadius(innerRadius)
47
+ .startAngle(d => d.startAngle + Math.PI / 2)
48
+ .endAngle(d => d.endAngle + Math.PI / 2)
49
+ .padAngle(segmentsShift);
50
+
51
+ return (
52
+ <svg viewBox='0 0 100 100' width='100%' height='100%' style={style}>
53
+ <g transform='translate(50,50)'>
54
+ {pieSegments.map((segment, index) => (
55
+ <Fragment key={index}>
56
+ <path
57
+ onMouseOver={e => onMouseOver(e, index)}
58
+ onMouseOut={onMouseOut}
59
+ onMouseDown={e => onMouseDown(e, index)}
60
+ fill={segment.data.color}
61
+ d={hoveredIndex === index ? String(getHoveredPath(segment)) : String(getPath(segment))}
62
+ style={segmentsStyle}
63
+ />
64
+
65
+ {label({ dataEntry: segment.data, dataIndex: index })}
66
+ </Fragment>
67
+ ))}
68
+ </g>
69
+ </svg>
70
+ );
71
+ }
@@ -0,0 +1,154 @@
1
+ import { truncateString } from '@cloud-ru/ft-formatters';
2
+ import cn from 'classnames';
3
+ import { MouseEvent, useCallback, useMemo, useState } from 'react';
4
+
5
+ import { extractSupportProps, WithSupportProps } from '@cloud-ru/uikit-product-utils';
6
+ import { Scroll } from '@snack-uikit/scroll';
7
+ import { Typography } from '@snack-uikit/typography';
8
+
9
+ import { CHART_SERIES_COLORS, SERIES_COLORS, SeriesColor } from '../../constants/colors';
10
+ import { Legend } from './Legend';
11
+ import { Pie } from './Pie';
12
+ import styles from './styles.module.scss';
13
+ import { DataType, LabelRenderFunction, LegendType, PieChartProps } from './types';
14
+
15
+ export function PieChart({
16
+ options: { width, height, title, legendTitle, typographySize = 'l' },
17
+ data,
18
+ aggregatedLegend,
19
+ onPieSegmentClick,
20
+ onLegendItemClick,
21
+ className,
22
+ ...rest
23
+ }: WithSupportProps<PieChartProps>) {
24
+ const [hovered, setHovered] = useState<number | undefined>(undefined);
25
+ const colorizedData: DataType[] = useMemo(
26
+ () =>
27
+ data.map((x, index) => {
28
+ const colorsKey = Object.values<SeriesColor>(SERIES_COLORS);
29
+ const chartColor = CHART_SERIES_COLORS[colorsKey[index % colorsKey.length]];
30
+ return {
31
+ ...x,
32
+ color: x.color || chartColor,
33
+ };
34
+ }),
35
+ [data],
36
+ );
37
+
38
+ const pieStyles = useMemo(() => ({ overflow: 'overlay' }), []);
39
+ const segmentStyles = useMemo(() => ({ transition: 'all .3s', cursor: 'pointer' }), []);
40
+ const onMouseOverCallback = useCallback((_: unknown, index: number) => setHovered(index), []);
41
+ const onMouseOutCallback = useCallback(() => setHovered(undefined), []);
42
+ const onMouseDownCallback = useCallback(
43
+ (event: MouseEvent<SVGPathElement>, index: number) => {
44
+ event.preventDefault();
45
+
46
+ if (onPieSegmentClick) {
47
+ onPieSegmentClick(data[index]);
48
+ }
49
+ },
50
+ [data, onPieSegmentClick],
51
+ );
52
+ const onColorizedLegendItemClick = useMemo(() => {
53
+ if (!onLegendItemClick) {
54
+ return undefined;
55
+ }
56
+
57
+ return (event: MouseEvent<HTMLAnchorElement>, item: LegendType) => {
58
+ event.preventDefault();
59
+ onLegendItemClick(item);
60
+ };
61
+ }, [onLegendItemClick]);
62
+ const onAggregatedItemClick = useMemo(() => {
63
+ const handleClick = aggregatedLegend?.onAggregatedLegendItemClick;
64
+ if (!handleClick) {
65
+ return undefined;
66
+ }
67
+
68
+ return (event: MouseEvent<HTMLAnchorElement>, item: LegendType) => {
69
+ event.preventDefault();
70
+ handleClick(item);
71
+ };
72
+ }, [aggregatedLegend]);
73
+
74
+ const labelRenderer = useCallback<LabelRenderFunction<DataType>>(
75
+ ({ dataEntry, dataIndex }) => (
76
+ <>
77
+ <text
78
+ className={styles.svgText}
79
+ x={0}
80
+ y={-4}
81
+ data-hovered={hovered === dataIndex || undefined}
82
+ key={`${dataIndex}_label`}
83
+ >
84
+ {truncateString(String(dataEntry.label), 15)}
85
+ </text>
86
+ <text
87
+ className={styles.svgText}
88
+ x={0}
89
+ y={4}
90
+ data-hovered={hovered === dataIndex || undefined}
91
+ data-bolder='true'
92
+ key={`${dataIndex}_value`}
93
+ >
94
+ {dataEntry.value}
95
+ </text>
96
+ </>
97
+ ),
98
+ [hovered],
99
+ );
100
+
101
+ return (
102
+ <div
103
+ {...extractSupportProps(rest)}
104
+ className={cn(className, styles.wrapper)}
105
+ style={{ '--width': width ? `${width}px` : undefined, '--height': height ? `${height}px` : undefined }}
106
+ >
107
+ <Typography purpose={'title'} family={'sans'} size={typographySize} className={styles.title}>
108
+ {title}
109
+ </Typography>
110
+
111
+ <div className={styles.contentWrapper}>
112
+ <div className={styles.legendWrapper}>
113
+ <Scroll size={'s'}>
114
+ <Legend
115
+ data={colorizedData}
116
+ legendTitle={legendTitle}
117
+ onItemClick={onColorizedLegendItemClick}
118
+ typographySize={typographySize}
119
+ />
120
+ </Scroll>
121
+ </div>
122
+
123
+ <div className={styles.pieWrapper}>
124
+ <Pie
125
+ style={pieStyles}
126
+ radius={46}
127
+ innerRadius={23}
128
+ label={labelRenderer}
129
+ data={colorizedData}
130
+ segmentsStyle={segmentStyles}
131
+ segmentsShift={0.015}
132
+ hoveredIndex={hovered}
133
+ onMouseOver={onMouseOverCallback}
134
+ onMouseOut={onMouseOutCallback}
135
+ onMouseDown={onMouseDownCallback}
136
+ />
137
+ </div>
138
+
139
+ {aggregatedLegend && (
140
+ <div className={styles.legendWrapper}>
141
+ <Scroll size={'s'}>
142
+ <Legend
143
+ data={aggregatedLegend.data}
144
+ legendTitle={aggregatedLegend.title}
145
+ onItemClick={onAggregatedItemClick}
146
+ typographySize={typographySize}
147
+ />
148
+ </Scroll>
149
+ </div>
150
+ )}
151
+ </div>
152
+ </div>
153
+ );
154
+ }
@@ -0,0 +1,2 @@
1
+ export * from './PieChart';
2
+ export type { PieChartProps } from './types';
@@ -0,0 +1,56 @@
1
+ @use '@sbercloud/figma-tokens-cloud-platform/build/scss/styles-theme-variables';
2
+
3
+ .wrapper {
4
+ display: flex;
5
+ width: var(--width, 100%);
6
+ height: var(--height, 100%);
7
+ flex-direction: column;
8
+ box-sizing: border-box;
9
+ background-color: styles-theme-variables.$sys-neutral-background1-level;
10
+ border-radius: 8px;
11
+ padding: 24px;
12
+ }
13
+
14
+ .title {
15
+ padding-bottom: 24px;
16
+ color: styles-theme-variables.$sys-neutral-text-main;
17
+ }
18
+
19
+ .contentWrapper {
20
+ display: flex;
21
+ flex-direction: row;
22
+ gap: 24px;
23
+ height: calc(100% - 50px);
24
+ justify-content: space-between;
25
+ }
26
+
27
+ .legendWrapper {
28
+ min-width: 25%;
29
+ max-width: 33%;
30
+ }
31
+
32
+ .pieWrapper {
33
+ height: 100%;
34
+ text-align: center;
35
+ display: flex;
36
+ align-items: center;
37
+ justify-content: center;
38
+ }
39
+
40
+ .svgText {
41
+ text-anchor: middle;
42
+ visibility: hidden;
43
+ width: 30px;
44
+ font-size: 5px;
45
+ word-break: break-all;
46
+ white-space: nowrap;
47
+ fill: styles-theme-variables.$sys-neutral-text-main;
48
+
49
+ &[data-hovered] {
50
+ visibility: visible;
51
+ }
52
+
53
+ &[data-bolder] {
54
+ font-weight: 700;
55
+ }
56
+ }
@@ -0,0 +1,42 @@
1
+ import { ReactNode } from 'react';
2
+
3
+ export type TextLike = string | number;
4
+
5
+ export type LegendType = {
6
+ label: TextLike;
7
+ value: TextLike;
8
+ id?: string;
9
+ };
10
+
11
+ export type DataType = {
12
+ label: TextLike;
13
+ value: number;
14
+ id?: string;
15
+ color?: string;
16
+ };
17
+
18
+ export type PieChartProps = {
19
+ data: DataType[];
20
+ options: {
21
+ title: string;
22
+ width?: number;
23
+ height?: number;
24
+ legendTitle?: string;
25
+ typographySize?: 's' | 'm' | 'l';
26
+ };
27
+ onPieSegmentClick?: (data: DataType) => void;
28
+ onLegendItemClick?: (data: LegendType) => void;
29
+ aggregatedLegend?: {
30
+ data: LegendType[];
31
+ title: string;
32
+ onAggregatedLegendItemClick?: (data: LegendType) => void;
33
+ };
34
+ className?: string;
35
+ };
36
+
37
+ export type LabelRenderProps<DataType> = {
38
+ dataEntry: DataType;
39
+ dataIndex: number;
40
+ };
41
+
42
+ export type LabelRenderFunction<DataType> = (labelRenderProps: LabelRenderProps<DataType>) => ReactNode;
@@ -0,0 +1,4 @@
1
+ export * from './InteractiveChart';
2
+ export * from './PieChart';
3
+ export * from './HeatMapChart';
4
+ export * from './BagelChart';