@uniai-fe/chart-legacy 0.1.1

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 (54) hide show
  1. package/LICENSE +27 -0
  2. package/README.md +3 -0
  3. package/package.json +74 -0
  4. package/src/chart.scss +13 -0
  5. package/src/components/Contents.tsx +17 -0
  6. package/src/components/control/Button.tsx +39 -0
  7. package/src/components/control/Container.tsx +27 -0
  8. package/src/components/control/Provider.tsx +117 -0
  9. package/src/components/control/index.tsx +12 -0
  10. package/src/components/custom/ActivePieSector.tsx +29 -0
  11. package/src/components/graphs/ArcGauge.tsx +58 -0
  12. package/src/components/graphs/ArcMeter.tsx +112 -0
  13. package/src/components/graphs/Area.tsx +132 -0
  14. package/src/components/graphs/Bar.tsx +179 -0
  15. package/src/components/graphs/Doughnut.tsx +90 -0
  16. package/src/components/graphs/Line.tsx +123 -0
  17. package/src/components/graphs/index.tsx +17 -0
  18. package/src/components/ticks/XAxis.tsx +39 -0
  19. package/src/components/ticks/index.tsx +7 -0
  20. package/src/components.tsx +17 -0
  21. package/src/icon/Control.tsx +40 -0
  22. package/src/icon/index.tsx +7 -0
  23. package/src/index.tsx +3 -0
  24. package/src/styled.tsx +7 -0
  25. package/src/styles/scss/arc-gauge.scss +11 -0
  26. package/src/styles/scss/arc-meter.scss +6 -0
  27. package/src/styles/scss/control.scss +34 -0
  28. package/src/styles/scss/filter.scss +0 -0
  29. package/src/styles/scss/graph.scss +3 -0
  30. package/src/styles/scss/icon.scss +3 -0
  31. package/src/styles/scss/legend.scss +35 -0
  32. package/src/styles/scss/level.color.scss +7 -0
  33. package/src/styles/scss/tick.scss +48 -0
  34. package/src/styles/scss/tooltip.scss +70 -0
  35. package/src/styles/styled/arc-gauge.ts +65 -0
  36. package/src/styles/styled/arc-meter.ts +42 -0
  37. package/src/styles/styled/common.ts +14 -0
  38. package/src/styles/styled/control.ts +31 -0
  39. package/src/styles/styled/icon.ts +17 -0
  40. package/src/styles/styled/legend.ts +11 -0
  41. package/src/svg/control/pan-left.svg +4 -0
  42. package/src/svg/control/pan-right.svg +4 -0
  43. package/src/svg/control/zoom-in.svg +4 -0
  44. package/src/svg/control/zoom-out.svg +4 -0
  45. package/src/svg/gauge-track.min.svg +1 -0
  46. package/src/types/axis.d.ts +15 -0
  47. package/src/types/control.d.ts +109 -0
  48. package/src/types/data.d.ts +41 -0
  49. package/src/types/dot.d.ts +3 -0
  50. package/src/types/index.d.ts +7 -0
  51. package/src/types/legend.d.ts +124 -0
  52. package/src/types/props.d.ts +271 -0
  53. package/src/types/tick.d.ts +30 -0
  54. package/src/utils/getLevel.ts +23 -0
package/LICENSE ADDED
@@ -0,0 +1,27 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 UNIAI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ ---
24
+
25
+ This project includes third-party software governed by additional licenses,
26
+ including Apache License 2.0. Refer to `THIRD_PARTY_NOTICES.md` for the full
27
+ text of those notices and any required attributions.
package/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # client / chart-legacy
2
+
3
+ 스타일링 도구와 토큰, 디자인 시스템, 컴포넌트 등 관리
package/package.json ADDED
@@ -0,0 +1,74 @@
1
+ {
2
+ "name": "@uniai-fe/chart-legacy",
3
+ "version": "0.1.1",
4
+ "description": "Legacy Chart Toolkit for UNIAI FE Projects",
5
+ "type": "module",
6
+ "private": false,
7
+ "sideEffects": false,
8
+ "license": "MIT",
9
+ "homepage": "https://www.uniai.co.kr/",
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "packageManager": "pnpm@10.23.0",
14
+ "engines": {
15
+ "node": ">=24",
16
+ "pnpm": ">=10"
17
+ },
18
+ "author": {
19
+ "name": "GraffitoRyu",
20
+ "email": "yth4135@naver.com",
21
+ "url": "https://github.com/GraffitoRyu"
22
+ },
23
+ "files": [
24
+ "src"
25
+ ],
26
+ "scripts": {
27
+ "lint": "eslint . --max-warnings=0",
28
+ "typecheck": "tsc --project tsconfig.build.json --noEmit",
29
+ "build": "pnpm typecheck",
30
+ "dev": "tsc --project tsconfig.build.json --watch --noEmit",
31
+ "module:lint": "pnpm lint",
32
+ "module:typecheck": "pnpm typecheck",
33
+ "module:build": "pnpm build",
34
+ "chart-legacy:build": "pnpm run build",
35
+ "chart-legacy:dev": "pnpm run dev"
36
+ },
37
+ "module": "./src/index.tsx",
38
+ "main": "./src/index.tsx",
39
+ "types": "./src/types/index.d.ts",
40
+ "exports": {
41
+ ".": "./src/index.tsx",
42
+ "./types": "./src/types/index.d.ts",
43
+ "./scss": "./src/index.scss"
44
+ },
45
+ "peerDependencies": {
46
+ "sass": ">= 15",
47
+ "react": ">= 19",
48
+ "react-dom": ">= 19",
49
+ "styled-components": ">= 6"
50
+ },
51
+ "devDependencies": {
52
+ "@types/node": "^24.10.1",
53
+ "@types/react": "^19.2.7",
54
+ "@types/react-dom": "^19.2.3",
55
+ "@uniai-fe/eslint-config": "workspace:*",
56
+ "@uniai-fe/next-devkit": "workspace:*",
57
+ "@uniai-fe/tsconfig": "workspace:*",
58
+ "@uniai-fe/util-functions": "workspace:*",
59
+ "@uniai-fe/util-react": "workspace:*",
60
+ "@uniai-fe/react-hooks": "workspace:*",
61
+ "@uniai-fe/ui-legacy": "workspace:*",
62
+ "autoprefixer": "^10.4.22",
63
+ "clsx": "^2.1.1",
64
+ "eslint": "^9.39.1",
65
+ "jotai": "^2.15.1",
66
+ "prettier": "^3.6.2",
67
+ "react": "^19.2.0",
68
+ "react-dom": "^19.2.0",
69
+ "recharts": "^3.5.0",
70
+ "sass": "^1.94.2",
71
+ "styled-components": "^6.1.19",
72
+ "typescript": "~5.9.3"
73
+ }
74
+ }
package/src/chart.scss ADDED
@@ -0,0 +1,13 @@
1
+ @use "./styles/scss/level.color.scss";
2
+
3
+ @use "./styles/scss/tick.scss";
4
+ @use "./styles/scss/graph.scss";
5
+ @use "./styles/scss/legend.scss";
6
+ @use "./styles/scss/tooltip.scss";
7
+ @use "./styles/scss/control.scss";
8
+ @use "./styles/scss/filter.scss";
9
+
10
+ @use "./styles/scss/arc-gauge.scss";
11
+ @use "./styles/scss/arc-meter.scss";
12
+
13
+ @use "./styles/scss/icon.scss";
@@ -0,0 +1,17 @@
1
+ "use client";
2
+
3
+ import StyledChartCommon from "../styles/styled/common";
4
+
5
+ export default function ChartContents({
6
+ className,
7
+ children,
8
+ }: {
9
+ className?: string;
10
+ children: React.ReactNode;
11
+ }) {
12
+ return (
13
+ <StyledChartCommon.container className={className}>
14
+ {children}
15
+ </StyledChartCommon.container>
16
+ );
17
+ }
@@ -0,0 +1,39 @@
1
+ "use client";
2
+
3
+ import { clsx } from "clsx";
4
+ import StyledChartControl from "../../styles/styled/control";
5
+ import IconChartControl from "../../icon/Control";
6
+
7
+ /**
8
+ * 차트 인터렉션 컨트롤 버튼
9
+ * @component
10
+ * @property {"zoom-in" | "zoom-out" | "pan-left" | "pan-right"} role - 버튼 역할
11
+ * @property {Function} clickEvent - 클릭 이벤트 핸들러
12
+ * @property {string} [className] - 추가 클래스 이름
13
+ * @property {React.ReactNode} [children] - 버튼 내부에 렌더링
14
+ */
15
+ export default function ChartControlButton({
16
+ className,
17
+ role,
18
+ clickEvent,
19
+ children,
20
+ disabled,
21
+ }: {
22
+ role: "zoom-in" | "zoom-out" | "pan-left" | "pan-right";
23
+ clickEvent: () => void;
24
+ } & Partial<{
25
+ className: string;
26
+ children: React.ReactNode;
27
+ disabled: boolean;
28
+ }>) {
29
+ return (
30
+ <StyledChartControl.button
31
+ type="button"
32
+ className={clsx("chart-control-button", className)}
33
+ disabled={disabled}
34
+ onClick={!disabled ? clickEvent : undefined}
35
+ >
36
+ {children || <IconChartControl {...{ role, width: 20, height: 20 }} />}
37
+ </StyledChartControl.button>
38
+ );
39
+ }
@@ -0,0 +1,27 @@
1
+ "use client";
2
+
3
+ import { clsx } from "clsx";
4
+ import StyledChartControl from "../../styles/styled/control";
5
+
6
+ /**
7
+ * 차트 인터렉션 컨트롤 컨테이너
8
+ * @component
9
+ * @property {string} [className] - 추가 클래스 이름
10
+ * @property {React.ReactNode} children - 자식 컴포넌트
11
+ */
12
+ export default function ChartControlContainer({
13
+ className,
14
+ children,
15
+ }: {
16
+ children: React.ReactNode;
17
+ } & Partial<{
18
+ className: string;
19
+ }>) {
20
+ return (
21
+ <StyledChartControl.container
22
+ className={clsx("chart-control-container", className)}
23
+ >
24
+ {children}
25
+ </StyledChartControl.container>
26
+ );
27
+ }
@@ -0,0 +1,117 @@
1
+ "use client";
2
+
3
+ import {
4
+ createContext,
5
+ useContext,
6
+ useLayoutEffect,
7
+ useRef,
8
+ useState,
9
+ } from "react";
10
+ import type { ChartControlContext, ChartControlRange } from "../../types";
11
+
12
+ /**
13
+ * 차트 컨트롤을 위한 데이터 컨텍스트
14
+ * @context
15
+ * @desc
16
+ * - {object[]} originData 원본 차트 데이터
17
+ * - {{ start: number; end: number; }} indexRange 현재 X축 인덱스 범위
18
+ * - {function} setRange X축 범위 업데이트 함수
19
+ */
20
+ const ChartControlContext = createContext<ChartControlContext>({
21
+ originData: [],
22
+ indexRange: { start: 0, end: 0 },
23
+ setIndexRange: () => {},
24
+ });
25
+
26
+ /**
27
+ * 차트 컨트롤을 위한 데이터 컨텍스트 공급자
28
+ * @component
29
+ * @param {object} props
30
+ * @param {object[]} props.originData 원본 차트 데이터
31
+ * @param {React.ReactNode} props.children
32
+ */
33
+ export default function ChartControlProvider<
34
+ ChartData extends object = object,
35
+ >({
36
+ originData,
37
+ children,
38
+ }: {
39
+ /**
40
+ * 원본 차트 데이터
41
+ * @type {object[]}
42
+ */
43
+ originData: ChartData[];
44
+ children: React.ReactNode;
45
+ }) {
46
+ const [indexRange, setIndexRange] = useState<ChartControlRange>({
47
+ start: 0,
48
+ end: originData.length > 1 ? originData.length - 1 : 0,
49
+ });
50
+
51
+ // 초기 데이터 로딩 시(0 -> N) 전체 범위로 동기 업데이트하여 깜빡임 방지
52
+ const prevLenRef = useRef<number>(originData.length);
53
+ useLayoutEffect(() => {
54
+ const prev = prevLenRef.current;
55
+ const next = originData.length;
56
+ if (prev === 0 && next > 0) {
57
+ setIndexRange({ start: 0, end: Math.max(0, next - 1) });
58
+ }
59
+ prevLenRef.current = next;
60
+ }, [originData.length]);
61
+
62
+ // 데이터 길이가 변할 때 현재 indexRange를 데이터 범위에 맞춰 정돈
63
+ useLayoutEffect(() => {
64
+ const total = originData.length;
65
+ if (total < 0) return;
66
+ const maxEnd = Math.max(0, total - 1);
67
+
68
+ // 비어있는 경우 0,0 고정
69
+ if (total === 0) {
70
+ if (indexRange.start !== 0 || indexRange.end !== 0)
71
+ setIndexRange({ start: 0, end: 0 });
72
+ return;
73
+ }
74
+
75
+ // 현재 윈도우 길이 보존 시도
76
+ const length = Math.max(1, indexRange.end - indexRange.start + 1);
77
+
78
+ // 범위를 넘어가면 오른쪽 끝에 맞춰 재정렬
79
+ if (indexRange.end > maxEnd || indexRange.start > maxEnd) {
80
+ const newEnd = maxEnd;
81
+ const newStart = Math.max(0, newEnd - Math.min(length, total) + 1);
82
+ if (newStart !== indexRange.start || newEnd !== indexRange.end)
83
+ setIndexRange({ start: newStart, end: newEnd });
84
+ }
85
+ }, [originData.length, indexRange.start, indexRange.end]);
86
+
87
+ return (
88
+ <ChartControlContext.Provider
89
+ value={{
90
+ originData,
91
+ indexRange,
92
+ setIndexRange,
93
+ }}
94
+ >
95
+ {children}
96
+ </ChartControlContext.Provider>
97
+ );
98
+ }
99
+
100
+ /**
101
+ * 차트 컨트롤 데이터 컨텍스트를 사용하기 위한 커스텀 훅
102
+ * @hook
103
+ * @desc
104
+ * - {object[]} originData 원본 차트 데이터
105
+ * - {{ start: number; end: number; }} indexRange 현재 X축 인덱스 범위
106
+ * - {function} setIndexRange X축 범위 업데이트 함수
107
+ * @return {ChartControlContext} { originData, indexRange, setIndexRange }
108
+ */
109
+ export function useChartContext<
110
+ ChartData extends object,
111
+ >(): ChartControlContext<ChartData> {
112
+ const context = useContext(ChartControlContext);
113
+ if (!context) {
114
+ throw new Error("차트 데이터에 대한 컨텍스트를 찾을 수 없습니다.");
115
+ }
116
+ return context as ChartControlContext<ChartData>;
117
+ }
@@ -0,0 +1,12 @@
1
+ import ChartControlButton from "./Button";
2
+ import ChartControlContainer from "./Container";
3
+ import ChartControlProvider, { useChartContext } from "./Provider";
4
+
5
+ const ChartControl = {
6
+ Provider: ChartControlProvider,
7
+ useContext: useChartContext,
8
+ Container: ChartControlContainer,
9
+ Button: ChartControlButton,
10
+ };
11
+
12
+ export default ChartControl;
@@ -0,0 +1,29 @@
1
+ "use client";
2
+
3
+ import { Sector } from "recharts";
4
+ import type { Props } from "recharts/types/shape/Sector";
5
+
6
+ export default function ChartActivePieSector({
7
+ cx,
8
+ cy,
9
+ innerRadius,
10
+ outerRadius,
11
+ startAngle,
12
+ endAngle,
13
+ fill,
14
+ }: Props) {
15
+ return (
16
+ <Sector
17
+ cx={cx}
18
+ cy={cy}
19
+ innerRadius={innerRadius}
20
+ outerRadius={
21
+ typeof outerRadius === "number" ? outerRadius + 2 : outerRadius
22
+ }
23
+ startAngle={startAngle}
24
+ endAngle={endAngle}
25
+ fill={fill}
26
+ cornerRadius={0}
27
+ />
28
+ );
29
+ }
@@ -0,0 +1,58 @@
1
+ "use client";
2
+
3
+ import { useMemo } from "react";
4
+ import { lengthFormat } from "@uniai-fe/util-functions";
5
+ import StyledArcGauge from "../../styles/styled/arc-gauge";
6
+ import ArcGaugeTrack from "../../svg/gauge-track.min.svg";
7
+
8
+ /**
9
+ * 차트 템플릿; 반원 게이지
10
+ * - 체중균일도 차트 등에 사용
11
+ */
12
+ export default function ChartArcGauge({
13
+ className,
14
+ value,
15
+ unit,
16
+ }: {
17
+ className?: string;
18
+ value: number | "";
19
+ unit: string;
20
+ }) {
21
+ const v = useMemo((): string => {
22
+ if (typeof value !== "number") return "-";
23
+ return lengthFormat(value, 0);
24
+ }, [value]);
25
+
26
+ const level = useMemo((): number => {
27
+ if (typeof value !== "number") return 0;
28
+
29
+ if (value <= 25) return 1;
30
+ else if (25 < value && value <= 50) return 2;
31
+ else if (50 < value && value <= 75) return 3;
32
+ else if (75 < value) return 4;
33
+ return 0;
34
+ }, [value]);
35
+
36
+ const degree = useMemo((): number => {
37
+ if (typeof value !== "number") return 0;
38
+
39
+ if (value <= 0) return 0;
40
+ else if (value > 100) return 180;
41
+ return ((value * Math.PI) / 180) * 100;
42
+ }, [value]);
43
+
44
+ return (
45
+ <StyledArcGauge.container className={className}>
46
+ <StyledArcGauge.track>
47
+ <ArcGaugeTrack />
48
+ {value !== "" && (
49
+ <StyledArcGauge.point $level={level} $degree={degree} />
50
+ )}
51
+ </StyledArcGauge.track>
52
+ <StyledArcGauge.value>
53
+ <strong>{v}</strong>
54
+ <span>{unit}</span>
55
+ </StyledArcGauge.value>
56
+ </StyledArcGauge.container>
57
+ );
58
+ }
@@ -0,0 +1,112 @@
1
+ "use client";
2
+
3
+ import { useMemo } from "react";
4
+
5
+ import ChartDoughnut from "./Doughnut";
6
+ import getChartLevel from "../../utils/getLevel";
7
+
8
+ import type { ChartDoughnutProps } from "../../types";
9
+ import StyledArcMeter from "../../styles/styled/arc-meter";
10
+ import { lengthFormat } from "@uniai-fe/util-functions";
11
+
12
+ /**
13
+ * 차트 템플릿; 반원 미터
14
+ * - AI 건강지수 차트 등에 사용
15
+ * @param {ChartDoughnutProps} props
16
+ * @param {string} [props.className]
17
+ * @param {object} props.data 데이터
18
+ * @param {string} props.data.name 데이터 이름
19
+ * @param {number | ""} props.data.value 값 (스코어)
20
+ * @param {string} [props.data.unit] 데이터 단위
21
+ * @param {number} [props.max] 최대값 (기본값 100)
22
+ * @param {ChartDoughnutProps} [props.doughnutOptions] <ChartDoughnut /> Props 옵션
23
+ */
24
+ export default function ChartArcMeter({
25
+ className,
26
+ data: d,
27
+ max = 100,
28
+ doughnutOptions,
29
+ }: {
30
+ className?: string;
31
+ data: { name: string; value: number | ""; unit?: string };
32
+ max?: number;
33
+ doughnutOptions?: ChartDoughnutProps;
34
+ }) {
35
+ const START_ANGLE = 180;
36
+ const WIDTH = 180;
37
+ const THICKNESS = 14;
38
+
39
+ const POINT_BORDER = 5;
40
+ const POINT_RADIUS = THICKNESS / 2 + POINT_BORDER / 2;
41
+
42
+ const point = useMemo((): { x: number; y: number } => {
43
+ const pos = { x: 0, y: 0 };
44
+ if (typeof d.value !== "number") return pos;
45
+
46
+ // -90 ~ 90
47
+ const startDegree = -90;
48
+ const rangeDegree = 180;
49
+ const angle = (d.value / max) * rangeDegree + startDegree;
50
+ const distance = WIDTH / 2 - THICKNESS / 2;
51
+ const radian = (angle * Math.PI) / rangeDegree;
52
+
53
+ pos.x = WIDTH / 2 + distance * Math.sin(radian);
54
+ pos.y = WIDTH / 2 - distance * Math.cos(radian);
55
+
56
+ return pos;
57
+ }, [max, d.value]);
58
+
59
+ const color = useMemo((): string => {
60
+ const level = getChartLevel(d.value, max);
61
+ return `var(--chart-level-color-${level})`;
62
+ }, [max, d.value]);
63
+
64
+ return (
65
+ <StyledArcMeter.container className={className} $height={WIDTH / 2}>
66
+ <ChartDoughnut
67
+ width={WIDTH}
68
+ height={WIDTH / 2}
69
+ thickness={THICKNESS}
70
+ startAngle={START_ANGLE}
71
+ endAngle={START_ANGLE - 180}
72
+ pieOptions={{
73
+ cy: WIDTH / 2,
74
+ cornerRadius: THICKNESS / 2,
75
+ dataKey: "value",
76
+ }}
77
+ chartData={[
78
+ {
79
+ name: d.name,
80
+ value: typeof d.value === "number" ? d.value : 0,
81
+ track: max,
82
+ fill: color,
83
+ unit: d.unit || "",
84
+ },
85
+ {
86
+ name: "",
87
+ value: typeof d.value === "number" ? max - d.value : max,
88
+ track: max,
89
+ fill: "transparent",
90
+ unit: "",
91
+ },
92
+ ]}
93
+ {...doughnutOptions}
94
+ >
95
+ {typeof d.value === "number" && (
96
+ <circle
97
+ cx={point.x}
98
+ cy={point.y}
99
+ r={POINT_RADIUS}
100
+ fill="var(--color_0)"
101
+ stroke={color}
102
+ strokeWidth={POINT_BORDER}
103
+ />
104
+ )}
105
+ </ChartDoughnut>
106
+ <StyledArcMeter.text $color={color}>
107
+ <strong>{lengthFormat(d.value)}</strong>
108
+ {d.unit && <span>{d.unit}</span>}
109
+ </StyledArcMeter.text>
110
+ </StyledArcMeter.container>
111
+ );
112
+ }
@@ -0,0 +1,132 @@
1
+ "use client";
2
+
3
+ import {
4
+ Area,
5
+ AreaChart,
6
+ CartesianGrid,
7
+ ResponsiveContainer,
8
+ Tooltip,
9
+ XAxis,
10
+ YAxis,
11
+ type XAxisProps,
12
+ type YAxisProps,
13
+ } from "recharts";
14
+ import type { ChartAreaDataLegendProps, ChartAreaProps } from "../../types";
15
+ import { useChartSize } from "@uniai-fe/react-hooks";
16
+
17
+ export default function ChartArea({
18
+ chartData,
19
+ legendData,
20
+ yAxisData,
21
+ chartLayoutOptions,
22
+ xAxisOptions,
23
+ yAxisOptions,
24
+ isHideGrid,
25
+ tooltipOptions,
26
+ containerOptions,
27
+ children: extraComponents,
28
+ }: ChartAreaProps) {
29
+ const { sizeOptions } = useChartSize();
30
+
31
+ const iaValidGradient = ({ areaGradient }: ChartAreaDataLegendProps) =>
32
+ areaGradient && areaGradient.selectorId && areaGradient.gradient.length > 1;
33
+
34
+ return (
35
+ <ResponsiveContainer
36
+ width="100%"
37
+ height="100%"
38
+ {...sizeOptions(containerOptions)}
39
+ >
40
+ <AreaChart
41
+ // margin={{ top: 0, left: 0, right: 0, bottom: 0 }}
42
+ {...sizeOptions({
43
+ margin: { top: 0, left: 0, right: 0, bottom: 0 },
44
+ ...chartLayoutOptions,
45
+ })}
46
+ data={chartData}
47
+ >
48
+ {!isHideGrid && yAxisData?.[0] && (
49
+ <CartesianGrid vertical={false} yAxisId={yAxisData[0].axisCategory} />
50
+ )}
51
+ {tooltipOptions && <Tooltip {...tooltipOptions} />}
52
+ <XAxis
53
+ axisLine={false}
54
+ {...sizeOptions<XAxisProps>({
55
+ padding: { left: 30, right: 30 },
56
+ tickSize: 0,
57
+ tickMargin: 10,
58
+ ...xAxisOptions,
59
+ })}
60
+ />
61
+ {yAxisData.map(({ key, axisCategory }) => (
62
+ <YAxis
63
+ key={key}
64
+ yAxisId={axisCategory}
65
+ axisLine={false}
66
+ hide={true}
67
+ {...sizeOptions<YAxisProps>({ tickSize: 0, ...yAxisOptions })}
68
+ />
69
+ ))}
70
+ {legendData.filter(iaValidGradient).length > 0 ? (
71
+ <defs>
72
+ {legendData.filter(iaValidGradient).map(
73
+ ({ areaGradient }) =>
74
+ areaGradient && (
75
+ <linearGradient
76
+ key={areaGradient.selectorId}
77
+ id={areaGradient.selectorId}
78
+ x1="0"
79
+ y1="0"
80
+ x2="0"
81
+ y2="1"
82
+ >
83
+ {areaGradient.gradient.map(({ key, offset, color }) => (
84
+ <stop key={key} offset={offset} stopColor={color} />
85
+ ))}
86
+ </linearGradient>
87
+ ),
88
+ )}
89
+ </defs>
90
+ ) : null}
91
+ {legendData
92
+ .filter(({ active }) => active)
93
+ .map(
94
+ ({
95
+ key,
96
+ code,
97
+ axisCategory,
98
+ name,
99
+ unit,
100
+ color,
101
+ areaColor,
102
+ areaGradient,
103
+ highlight,
104
+ }) => (
105
+ <Area
106
+ key={key}
107
+ type="monotone"
108
+ dataKey={code}
109
+ yAxisId={axisCategory}
110
+ name={name}
111
+ unit={unit}
112
+ stroke={color}
113
+ strokeWidth="var(--chart-data-line-width)"
114
+ fill={
115
+ areaGradient?.selectorId
116
+ ? `url(#${areaGradient.selectorId})`
117
+ : areaColor || undefined
118
+ }
119
+ {...(typeof highlight === "boolean" && highlight === false
120
+ ? { strokeOpacity: 0.3 }
121
+ : {})}
122
+ dot={false}
123
+ isAnimationActive={false}
124
+ />
125
+ ),
126
+ )}
127
+
128
+ {extraComponents}
129
+ </AreaChart>
130
+ </ResponsiveContainer>
131
+ );
132
+ }