@uniai-fe/ui-legacy-template 0.1.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/LICENSE +27 -0
- package/README.md +5 -0
- package/package.json +86 -0
- package/src/asset-url.ts +7 -0
- package/src/compare/compare.scss +1 -0
- package/src/compare/components/box/Container.tsx +33 -0
- package/src/compare/components/box/TextStrong.tsx +15 -0
- package/src/compare/components/box/TextSub.tsx +15 -0
- package/src/compare/components/box/index.tsx +11 -0
- package/src/compare/components/button/Remove.tsx +28 -0
- package/src/compare/components/button/index.tsx +5 -0
- package/src/compare/components/chart/Container.tsx +33 -0
- package/src/compare/components/chart/graph/Container.tsx +15 -0
- package/src/compare/components/chart/graph/Item.tsx +13 -0
- package/src/compare/components/chart/index.tsx +16 -0
- package/src/compare/components/chart/legend/Container.tsx +23 -0
- package/src/compare/components/chart/legend/Section.tsx +31 -0
- package/src/compare/components/select-list/Container.tsx +15 -0
- package/src/compare/components/select-list/Header.tsx +15 -0
- package/src/compare/components/select-list/Item.tsx +15 -0
- package/src/compare/components/select-list/List.tsx +18 -0
- package/src/compare/components/select-list/index.tsx +13 -0
- package/src/compare/index.tsx +13 -0
- package/src/compare/styled.tsx +17 -0
- package/src/compare/styles/scss/variables.scss +17 -0
- package/src/compare/styles/styled-components/chart/graph.ts +26 -0
- package/src/compare/styles/styled-components/chart/layout.ts +38 -0
- package/src/compare/styles/styled-components/chart/legend.ts +64 -0
- package/src/compare/styles/styled-components/common.ts +57 -0
- package/src/compare/styles/styled-components/select-list.ts +36 -0
- package/src/components.tsx +5 -0
- package/src/filter/components/Container.tsx +39 -0
- package/src/filter/components/Group.tsx +100 -0
- package/src/filter/components/ToggleButton.tsx +26 -0
- package/src/filter/index.tsx +5 -0
- package/src/filter/styled.tsx +3 -0
- package/src/filter/styles/styled-components/filter.ts +66 -0
- package/src/health-score/components/Chart.tsx +39 -0
- package/src/health-score/icon/HealthScore.tsx +46 -0
- package/src/health-score/icon/index.tsx +3 -0
- package/src/health-score/index.tsx +3 -0
- package/src/health-score/styled.tsx +3 -0
- package/src/health-score/styles/styled/chart.ts +27 -0
- package/src/index.scss +1 -0
- package/src/index.tsx +6 -0
- package/src/styled.tsx +5 -0
- package/src/types/compare/chart.d.ts +8 -0
- package/src/types/filter.d.ts +39 -0
- package/src/types/index.d.ts +2 -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
package/package.json
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@uniai-fe/ui-legacy-template",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Legacy UI Templates 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
|
+
"ui-legacy-template:build": "pnpm run build",
|
|
35
|
+
"ui-legacy-template: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
|
+
"./components": "./src/components.tsx"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"jotai": ">= 2",
|
|
47
|
+
"next": ">= 15",
|
|
48
|
+
"react": ">= 19",
|
|
49
|
+
"react-dom": ">= 19",
|
|
50
|
+
"sass": ">= 15",
|
|
51
|
+
"styled-components": ">= 6",
|
|
52
|
+
"react-hook-form": ">= 7",
|
|
53
|
+
"@uniai-fe/chart-legacy": ">= 0.1.0",
|
|
54
|
+
"@uniai-fe/ui-legacy": ">= 0.1.7"
|
|
55
|
+
},
|
|
56
|
+
"peerDependenciesMeta": {
|
|
57
|
+
"next": {
|
|
58
|
+
"optional": true
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@types/node": "^24.10.1",
|
|
63
|
+
"@types/react": "^19.2.7",
|
|
64
|
+
"@types/react-dom": "^19.2.3",
|
|
65
|
+
"@uniai-fe/chart-legacy": "workspace:*",
|
|
66
|
+
"@uniai-fe/eslint-config": "workspace:*",
|
|
67
|
+
"@uniai-fe/next-devkit": "workspace:*",
|
|
68
|
+
"@uniai-fe/react-hooks": "workspace:*",
|
|
69
|
+
"@uniai-fe/tsconfig": "workspace:*",
|
|
70
|
+
"@uniai-fe/ui-legacy": "workspace:*",
|
|
71
|
+
"@uniai-fe/util-functions": "workspace:*",
|
|
72
|
+
"@uniai-fe/util-react": "workspace:*",
|
|
73
|
+
"autoprefixer": "^10.4.22",
|
|
74
|
+
"clsx": "^2.1.1",
|
|
75
|
+
"eslint": "^9.39.1",
|
|
76
|
+
"jotai": "^2.15.1",
|
|
77
|
+
"prettier": "^3.6.2",
|
|
78
|
+
"next": "^15.5.6",
|
|
79
|
+
"react": "^19.2.0",
|
|
80
|
+
"react-dom": "^19.2.0",
|
|
81
|
+
"react-hook-form": "^7.66.1",
|
|
82
|
+
"sass": "^1.94.2",
|
|
83
|
+
"styled-components": "^6.1.19",
|
|
84
|
+
"typescript": "~5.9.3"
|
|
85
|
+
}
|
|
86
|
+
}
|
package/src/asset-url.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@use "./styles/scss/variables.scss";
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import CompareRemoveButton from "../button/Remove";
|
|
4
|
+
import StyledCompareCommon from "../../styles/styled-components/common";
|
|
5
|
+
import type { ComponentBaseProps } from "@uniai-fe/ui-legacy";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 비교 템플릿; 박스
|
|
9
|
+
* @component
|
|
10
|
+
* @param {object} props
|
|
11
|
+
* @param {boolean} [props.isDashed] 테두리 점선 여부
|
|
12
|
+
* @param {boolean} [props.isSelected] 선택 여부
|
|
13
|
+
* @param {Function} props.onRemove 선택해제 이벤트
|
|
14
|
+
*/
|
|
15
|
+
export default function CompareBoxContainer({
|
|
16
|
+
className,
|
|
17
|
+
isDashed,
|
|
18
|
+
isSelected,
|
|
19
|
+
onRemove,
|
|
20
|
+
children,
|
|
21
|
+
}: ComponentBaseProps &
|
|
22
|
+
Partial<{
|
|
23
|
+
isDashed: boolean;
|
|
24
|
+
isSelected: boolean;
|
|
25
|
+
onRemove: () => void;
|
|
26
|
+
}>) {
|
|
27
|
+
return (
|
|
28
|
+
<StyledCompareCommon.box $isDashed={isDashed} className={className}>
|
|
29
|
+
{children}
|
|
30
|
+
{isSelected && onRemove && <CompareRemoveButton onRemove={onRemove} />}
|
|
31
|
+
</StyledCompareCommon.box>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { ComponentBaseProps } from "@uniai-fe/ui-legacy";
|
|
4
|
+
import StyledCompareCommon from "../../styles/styled-components/common";
|
|
5
|
+
|
|
6
|
+
export default function CompareTextStrong({
|
|
7
|
+
className,
|
|
8
|
+
children,
|
|
9
|
+
}: ComponentBaseProps) {
|
|
10
|
+
return (
|
|
11
|
+
<StyledCompareCommon.text.strong className={className}>
|
|
12
|
+
{children}
|
|
13
|
+
</StyledCompareCommon.text.strong>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { ComponentBaseProps } from "@uniai-fe/ui-legacy";
|
|
4
|
+
import StyledCompareCommon from "../../styles/styled-components/common";
|
|
5
|
+
|
|
6
|
+
export default function CompareTextSub({
|
|
7
|
+
className,
|
|
8
|
+
children,
|
|
9
|
+
}: ComponentBaseProps) {
|
|
10
|
+
return (
|
|
11
|
+
<StyledCompareCommon.text.sub className={className}>
|
|
12
|
+
{children}
|
|
13
|
+
</StyledCompareCommon.text.sub>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import CompareBoxContainer from "./Container";
|
|
2
|
+
import CompareTextStrong from "./TextStrong";
|
|
3
|
+
import CompareTextSub from "./TextSub";
|
|
4
|
+
|
|
5
|
+
const CompareBox = {
|
|
6
|
+
Container: CompareBoxContainer,
|
|
7
|
+
TextStrong: CompareTextStrong,
|
|
8
|
+
TextSub: CompareTextSub,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export default CompareBox;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Icon } from "@uniai-fe/ui-legacy";
|
|
4
|
+
import StyledCompareCommon from "../../styles/styled-components/common";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 비교 템플릿; 선택해제 버튼
|
|
8
|
+
* @component
|
|
9
|
+
* @param {object} props
|
|
10
|
+
* @param {Function} props.onRemove 선택해제 이벤트
|
|
11
|
+
*/
|
|
12
|
+
export default function CompareRemoveButton({
|
|
13
|
+
className,
|
|
14
|
+
onRemove,
|
|
15
|
+
}: {
|
|
16
|
+
className?: string;
|
|
17
|
+
onRemove: () => void;
|
|
18
|
+
}) {
|
|
19
|
+
return (
|
|
20
|
+
<StyledCompareCommon.button.remove
|
|
21
|
+
type="button"
|
|
22
|
+
className={className}
|
|
23
|
+
onClick={onRemove}
|
|
24
|
+
>
|
|
25
|
+
<Icon.Button.Remove.Solid />
|
|
26
|
+
</StyledCompareCommon.button.remove>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { CompareChartLegendCategoryDataType } from "../../../types";
|
|
4
|
+
import StyledCompare from "../../styled";
|
|
5
|
+
import CompareChartLegendContainer from "./legend/Container";
|
|
6
|
+
import CompareChartGraphContainer from "./graph/Container";
|
|
7
|
+
import { Scrollbar } from "@uniai-fe/ui-legacy";
|
|
8
|
+
|
|
9
|
+
export default function CompareChartContainer({
|
|
10
|
+
leftWidth,
|
|
11
|
+
legendCategoryData,
|
|
12
|
+
children: graphItems,
|
|
13
|
+
}: {
|
|
14
|
+
legendCategoryData: CompareChartLegendCategoryDataType[];
|
|
15
|
+
children: React.ReactNode;
|
|
16
|
+
} & Partial<{ leftWidth: number | string }>) {
|
|
17
|
+
return (
|
|
18
|
+
<StyledCompare.chart.container>
|
|
19
|
+
<Scrollbar.Container>
|
|
20
|
+
<StyledCompare.chart.wrapper>
|
|
21
|
+
<StyledCompare.chart.left $leftWidth={leftWidth}>
|
|
22
|
+
<CompareChartLegendContainer categoryData={legendCategoryData} />
|
|
23
|
+
</StyledCompare.chart.left>
|
|
24
|
+
<StyledCompare.chart.right $leftWidth={leftWidth}>
|
|
25
|
+
<CompareChartGraphContainer>
|
|
26
|
+
{graphItems}
|
|
27
|
+
</CompareChartGraphContainer>
|
|
28
|
+
</StyledCompare.chart.right>
|
|
29
|
+
</StyledCompare.chart.wrapper>
|
|
30
|
+
</Scrollbar.Container>
|
|
31
|
+
</StyledCompare.chart.container>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import StyledCompareChartGraph from "../../../styles/styled-components/chart/graph";
|
|
4
|
+
|
|
5
|
+
export default function CompareChartGraphContainer({
|
|
6
|
+
children,
|
|
7
|
+
}: {
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
}) {
|
|
10
|
+
return (
|
|
11
|
+
<StyledCompareChartGraph.container>
|
|
12
|
+
{children}
|
|
13
|
+
</StyledCompareChartGraph.container>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import StyledCompareChartGraph from "../../../styles/styled-components/chart/graph";
|
|
4
|
+
|
|
5
|
+
export default function CompareChartGraphItem({
|
|
6
|
+
children,
|
|
7
|
+
}: {
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
}) {
|
|
10
|
+
return (
|
|
11
|
+
<StyledCompareChartGraph.item>{children}</StyledCompareChartGraph.item>
|
|
12
|
+
);
|
|
13
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import CompareChartContainer from "./Container";
|
|
2
|
+
import CompareChartGraphContainer from "./graph/Container";
|
|
3
|
+
import CompareChartGraphItem from "./graph/Item";
|
|
4
|
+
import CompareChartLegendContainer from "./legend/Container";
|
|
5
|
+
import CompareChartLegendSectionContainer from "./legend/Section";
|
|
6
|
+
|
|
7
|
+
const CompareChart = {
|
|
8
|
+
Container: CompareChartContainer,
|
|
9
|
+
Legend: {
|
|
10
|
+
Container: CompareChartLegendContainer,
|
|
11
|
+
Section: CompareChartLegendSectionContainer,
|
|
12
|
+
},
|
|
13
|
+
Graph: { Container: CompareChartGraphContainer, Item: CompareChartGraphItem },
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export default CompareChart;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { CompareChartLegendCategoryDataType } from "../../../../types";
|
|
4
|
+
import StyledCompare from "../../../styled";
|
|
5
|
+
import CompareChartLegendSectionContainer from "./Section";
|
|
6
|
+
|
|
7
|
+
export default function CompareChartLegendContainer({
|
|
8
|
+
categoryData,
|
|
9
|
+
}: {
|
|
10
|
+
categoryData: CompareChartLegendCategoryDataType[];
|
|
11
|
+
}) {
|
|
12
|
+
return (
|
|
13
|
+
<StyledCompare.chart.legend.container>
|
|
14
|
+
{categoryData.map(({ key, title, legend }) => (
|
|
15
|
+
<CompareChartLegendSectionContainer
|
|
16
|
+
key={key}
|
|
17
|
+
dataName={title}
|
|
18
|
+
legendData={legend}
|
|
19
|
+
/>
|
|
20
|
+
))}
|
|
21
|
+
</StyledCompare.chart.legend.container>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { ChartLegendBaseType } from "@uniai-fe/chart-legacy";
|
|
4
|
+
import StyledCompare from "../../../styled";
|
|
5
|
+
|
|
6
|
+
export default function CompareChartLegendSectionContainer({
|
|
7
|
+
dataName,
|
|
8
|
+
legendData,
|
|
9
|
+
}: {
|
|
10
|
+
dataName: React.ReactNode;
|
|
11
|
+
legendData: ChartLegendBaseType[];
|
|
12
|
+
}) {
|
|
13
|
+
return (
|
|
14
|
+
<StyledCompare.chart.legend.section.container>
|
|
15
|
+
<StyledCompare.chart.legend.section.header>
|
|
16
|
+
<h4>{dataName}</h4>
|
|
17
|
+
</StyledCompare.chart.legend.section.header>
|
|
18
|
+
<StyledCompare.chart.legend.section.contents>
|
|
19
|
+
{legendData.map(({ key, name, color }) => (
|
|
20
|
+
<StyledCompare.chart.legend.section.legendItem
|
|
21
|
+
key={key}
|
|
22
|
+
$color={color}
|
|
23
|
+
>
|
|
24
|
+
<figure></figure>
|
|
25
|
+
<span>{name}</span>
|
|
26
|
+
</StyledCompare.chart.legend.section.legendItem>
|
|
27
|
+
))}
|
|
28
|
+
</StyledCompare.chart.legend.section.contents>
|
|
29
|
+
</StyledCompare.chart.legend.section.container>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { ComponentBaseProps } from "@uniai-fe/ui-legacy";
|
|
4
|
+
import StyledCompareSelect from "../../styles/styled-components/select-list";
|
|
5
|
+
|
|
6
|
+
export default function CompareSelectContainer({
|
|
7
|
+
className,
|
|
8
|
+
children,
|
|
9
|
+
}: ComponentBaseProps) {
|
|
10
|
+
return (
|
|
11
|
+
<StyledCompareSelect.container className={className}>
|
|
12
|
+
{children}
|
|
13
|
+
</StyledCompareSelect.container>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { ComponentBaseProps } from "@uniai-fe/ui-legacy";
|
|
4
|
+
import StyledCompareSelect from "../../styles/styled-components/select-list";
|
|
5
|
+
|
|
6
|
+
export default function CompareSelectHeader({
|
|
7
|
+
className,
|
|
8
|
+
children,
|
|
9
|
+
}: ComponentBaseProps) {
|
|
10
|
+
return (
|
|
11
|
+
<StyledCompareSelect.header className={className}>
|
|
12
|
+
{children}
|
|
13
|
+
</StyledCompareSelect.header>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { ComponentBaseProps } from "@uniai-fe/ui-legacy";
|
|
4
|
+
import StyledCompareSelect from "../../styles/styled-components/select-list";
|
|
5
|
+
|
|
6
|
+
export default function CompareSelectListItem({
|
|
7
|
+
className,
|
|
8
|
+
children,
|
|
9
|
+
}: ComponentBaseProps) {
|
|
10
|
+
return (
|
|
11
|
+
<StyledCompareSelect.item className={className}>
|
|
12
|
+
{children}
|
|
13
|
+
</StyledCompareSelect.item>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { ComponentBaseProps } from "@uniai-fe/ui-legacy";
|
|
4
|
+
import StyledCompareSelect from "../../styles/styled-components/select-list";
|
|
5
|
+
|
|
6
|
+
export default function CompareSelectList({
|
|
7
|
+
className,
|
|
8
|
+
columnLength,
|
|
9
|
+
children,
|
|
10
|
+
}: {
|
|
11
|
+
columnLength: number;
|
|
12
|
+
} & ComponentBaseProps) {
|
|
13
|
+
return (
|
|
14
|
+
<StyledCompareSelect.list className={className} $length={columnLength}>
|
|
15
|
+
{children}
|
|
16
|
+
</StyledCompareSelect.list>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import CompareSelectContainer from "./Container";
|
|
2
|
+
import CompareSelectHeader from "./Header";
|
|
3
|
+
import CompareSelectList from "./List";
|
|
4
|
+
import CompareSelectListItem from "./Item";
|
|
5
|
+
|
|
6
|
+
const CompareSelect = {
|
|
7
|
+
Container: CompareSelectContainer,
|
|
8
|
+
Header: CompareSelectHeader,
|
|
9
|
+
List: CompareSelectList,
|
|
10
|
+
Item: CompareSelectListItem,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default CompareSelect;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import CompareBox from "./components/box";
|
|
2
|
+
import CompareButton from "./components/button";
|
|
3
|
+
import CompareChart from "./components/chart";
|
|
4
|
+
import CompareSelect from "./components/select-list";
|
|
5
|
+
|
|
6
|
+
const Compare = {
|
|
7
|
+
Select: CompareSelect,
|
|
8
|
+
Button: CompareButton,
|
|
9
|
+
Box: CompareBox,
|
|
10
|
+
Chart: CompareChart,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default Compare;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import StyledCompareChartGraph from "./styles/styled-components/chart/graph";
|
|
2
|
+
import StyledCompareChartLayout from "./styles/styled-components/chart/layout";
|
|
3
|
+
import StyledCompareChartLegend from "./styles/styled-components/chart/legend";
|
|
4
|
+
import StyledCompareCommon from "./styles/styled-components/common";
|
|
5
|
+
import StyledCompareSelect from "./styles/styled-components/select-list";
|
|
6
|
+
|
|
7
|
+
const StyledCompare = {
|
|
8
|
+
...StyledCompareCommon,
|
|
9
|
+
select: StyledCompareSelect,
|
|
10
|
+
chart: {
|
|
11
|
+
...StyledCompareChartLayout,
|
|
12
|
+
legend: StyledCompareChartLegend,
|
|
13
|
+
graph: StyledCompareChartGraph,
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export default StyledCompare;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--compare-select-list-item-margin: 3rem;
|
|
3
|
+
|
|
4
|
+
--compare-box-padding: 1.5rem;
|
|
5
|
+
--compare-box-text-margin: 1rem;
|
|
6
|
+
--compare-box-text-strong-size: 1.8rem;
|
|
7
|
+
--compare-box-text-sub-size: 1.6rem;
|
|
8
|
+
|
|
9
|
+
--compare-box-height: 2px + var(--compare-box-padding) * 2 +
|
|
10
|
+
var(--compare-box-text-strong-size) + var(--compare-box-text-sub-size) +
|
|
11
|
+
var(--compare-box-text-margin);
|
|
12
|
+
|
|
13
|
+
--compare-chart-item-padding: 2rem;
|
|
14
|
+
--compare-chart-legend-header-height: 3.5rem;
|
|
15
|
+
--compare-chart-legend-header-margin: 1.5rem;
|
|
16
|
+
--compare-chart-legend-contents-height: 8rem;
|
|
17
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { styled } from "styled-components";
|
|
4
|
+
|
|
5
|
+
const container = styled.div`
|
|
6
|
+
width: 100%;
|
|
7
|
+
height: 100%;
|
|
8
|
+
padding: 0 var(--page-padding-default);
|
|
9
|
+
`;
|
|
10
|
+
const item = styled.div`
|
|
11
|
+
width: 100%;
|
|
12
|
+
height: calc(
|
|
13
|
+
var(--compare-chart-item-padding) +
|
|
14
|
+
var(--compare-chart-legend-header-height) +
|
|
15
|
+
var(--compare-chart-legend-header-margin) +
|
|
16
|
+
var(--compare-chart-legend-contents-height)
|
|
17
|
+
);
|
|
18
|
+
margin-top: var(--compare-chart-item-padding);
|
|
19
|
+
background-color: var(--theme-background-20);
|
|
20
|
+
`;
|
|
21
|
+
const StyledCompareChartGraph = {
|
|
22
|
+
container,
|
|
23
|
+
item,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export default StyledCompareChartGraph;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { styled } from "styled-components";
|
|
4
|
+
import { styleBaseSize } from "@uniai-fe/util-functions";
|
|
5
|
+
|
|
6
|
+
type StyledLayoutProps = Partial<{
|
|
7
|
+
$leftWidth: number | string;
|
|
8
|
+
}>;
|
|
9
|
+
const size = ({ $leftWidth }: StyledLayoutProps) =>
|
|
10
|
+
styleBaseSize("px", $leftWidth, 300);
|
|
11
|
+
const container = styled.div`
|
|
12
|
+
width: 100%;
|
|
13
|
+
height: 100%;
|
|
14
|
+
border: 1px solid var(--color_30);
|
|
15
|
+
background-color: var(--color_0);
|
|
16
|
+
display: flex;
|
|
17
|
+
`;
|
|
18
|
+
const wrapper = styled.div`
|
|
19
|
+
width: 100%;
|
|
20
|
+
height: 100%;
|
|
21
|
+
display: flex;
|
|
22
|
+
`;
|
|
23
|
+
const left = styled.div<StyledLayoutProps>`
|
|
24
|
+
width: ${size};
|
|
25
|
+
`;
|
|
26
|
+
const right = styled.div<StyledLayoutProps>`
|
|
27
|
+
width: calc(100% - ${size});
|
|
28
|
+
border-left: 1px solid var(--color_30);
|
|
29
|
+
`;
|
|
30
|
+
|
|
31
|
+
const StyledCompareChartLayout = {
|
|
32
|
+
container,
|
|
33
|
+
wrapper,
|
|
34
|
+
left,
|
|
35
|
+
right,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export default StyledCompareChartLayout;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { styled } from "styled-components";
|
|
4
|
+
|
|
5
|
+
const container = styled.ul`
|
|
6
|
+
width: 100%;
|
|
7
|
+
height: 100%;
|
|
8
|
+
`;
|
|
9
|
+
|
|
10
|
+
const sectionContainer = styled.li`
|
|
11
|
+
width: 100%;
|
|
12
|
+
padding: var(--compare-chart-item-padding);
|
|
13
|
+
`;
|
|
14
|
+
const sectionHeader = styled.header`
|
|
15
|
+
height: var(--compare-chart-legend-header-height);
|
|
16
|
+
border-bottom: 1px solid var(--color_30);
|
|
17
|
+
margin-bottom: var(--compare-chart-legend-header-margin);
|
|
18
|
+
|
|
19
|
+
h4 {
|
|
20
|
+
font-size: 18px;
|
|
21
|
+
color: var(--color_90);
|
|
22
|
+
font-weight: 600;
|
|
23
|
+
line-height: 1em;
|
|
24
|
+
}
|
|
25
|
+
`;
|
|
26
|
+
type StyledLegendItemProps = Partial<{
|
|
27
|
+
$color: string;
|
|
28
|
+
}>;
|
|
29
|
+
const sectionContents = styled.ul<StyledLegendItemProps>`
|
|
30
|
+
width: 100%;
|
|
31
|
+
height: var(--compare-chart-legend-contents-height);
|
|
32
|
+
`;
|
|
33
|
+
const sectionLegendItem = styled.li<StyledLegendItemProps>`
|
|
34
|
+
margin-top: 10px;
|
|
35
|
+
display: flex;
|
|
36
|
+
align-items: center;
|
|
37
|
+
|
|
38
|
+
figure {
|
|
39
|
+
width: 12px;
|
|
40
|
+
height: 12px;
|
|
41
|
+
margin-right: 8px;
|
|
42
|
+
border-radius: 4px;
|
|
43
|
+
background-color: ${({ $color }) => ($color ? $color : "var(--color_40)")};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
span {
|
|
47
|
+
font-size: 16px;
|
|
48
|
+
color: var(--color_80);
|
|
49
|
+
}
|
|
50
|
+
`;
|
|
51
|
+
|
|
52
|
+
const section = {
|
|
53
|
+
container: sectionContainer,
|
|
54
|
+
header: sectionHeader,
|
|
55
|
+
contents: sectionContents,
|
|
56
|
+
legendItem: sectionLegendItem,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const StyledCompareChartLegend = {
|
|
60
|
+
container,
|
|
61
|
+
section,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export default StyledCompareChartLegend;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { styled } from "styled-components";
|
|
4
|
+
|
|
5
|
+
const box = styled.div<{ $isDashed?: boolean }>`
|
|
6
|
+
width: 100%;
|
|
7
|
+
height: 100%;
|
|
8
|
+
padding: var(--compare-box-padding);
|
|
9
|
+
border-radius: 0.8rem;
|
|
10
|
+
border: 1px ${({ $isDashed }) => ($isDashed ? "dashed" : "solid")}
|
|
11
|
+
var(--color_30);
|
|
12
|
+
position: relative;
|
|
13
|
+
`;
|
|
14
|
+
|
|
15
|
+
const boxText = styled.span`
|
|
16
|
+
display: block;
|
|
17
|
+
`;
|
|
18
|
+
|
|
19
|
+
const textStrong = styled(boxText)`
|
|
20
|
+
font-size: var(--compare-box-text-strong-size);
|
|
21
|
+
color: var(--color_90);
|
|
22
|
+
`;
|
|
23
|
+
|
|
24
|
+
const textSub = styled(boxText)`
|
|
25
|
+
font-size: var(--compare-box-text-sub-size);
|
|
26
|
+
color: var(--color_60);
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
const text = {
|
|
30
|
+
common: boxText,
|
|
31
|
+
strong: textStrong,
|
|
32
|
+
sub: textSub,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const remove = styled.button`
|
|
36
|
+
width: fit-content;
|
|
37
|
+
height: fit-content;
|
|
38
|
+
font-size: 0;
|
|
39
|
+
display: flex;
|
|
40
|
+
align-items: center;
|
|
41
|
+
justify-content: center;
|
|
42
|
+
position: absolute;
|
|
43
|
+
top: 1rem;
|
|
44
|
+
right: 1rem;
|
|
45
|
+
`;
|
|
46
|
+
|
|
47
|
+
const button = {
|
|
48
|
+
remove,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const StyledCompareCommon = {
|
|
52
|
+
box,
|
|
53
|
+
text,
|
|
54
|
+
button,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export default StyledCompareCommon;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { styled } from "styled-components";
|
|
4
|
+
|
|
5
|
+
const container = styled.div`
|
|
6
|
+
width: calc(100% + var(--compare-select-list-item-margin));
|
|
7
|
+
margin: 0 calc(var(--compare-select-list-item-margin) / -2);
|
|
8
|
+
`;
|
|
9
|
+
|
|
10
|
+
const header = styled.header`
|
|
11
|
+
width: 100%;
|
|
12
|
+
margin-bottom: 2rem;
|
|
13
|
+
display: flex;
|
|
14
|
+
align-items: center;
|
|
15
|
+
`;
|
|
16
|
+
|
|
17
|
+
const item = styled.li`
|
|
18
|
+
padding: 0 calc(var(--compare-select-list-item-margin) / 2);
|
|
19
|
+
`;
|
|
20
|
+
|
|
21
|
+
const list = styled.ul<{ $length: number }>`
|
|
22
|
+
width: 100%;
|
|
23
|
+
display: flex;
|
|
24
|
+
${item} {
|
|
25
|
+
width: calc(100% / ${({ $length }) => $length});
|
|
26
|
+
}
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
const StyledCompareSelect = {
|
|
30
|
+
container,
|
|
31
|
+
header,
|
|
32
|
+
list,
|
|
33
|
+
item,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export default StyledCompareSelect;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useCallback, useRef, useState } from "react";
|
|
4
|
+
|
|
5
|
+
import { Dropdown } from "@uniai-fe/ui-legacy";
|
|
6
|
+
import FilterGroup from "./Group";
|
|
7
|
+
import FilterToggleButton from "./ToggleButton";
|
|
8
|
+
|
|
9
|
+
import StyledFilter from "../styles/styled-components/filter";
|
|
10
|
+
import type { FilterGroupDataType } from "../../types";
|
|
11
|
+
import { useClickOutside } from "@uniai-fe/react-hooks";
|
|
12
|
+
|
|
13
|
+
export default function FilterContainer({
|
|
14
|
+
group,
|
|
15
|
+
}: {
|
|
16
|
+
group: FilterGroupDataType[];
|
|
17
|
+
}) {
|
|
18
|
+
const [isOpen, setOpen] = useState<boolean>(false);
|
|
19
|
+
const onToggle = useCallback(() => {
|
|
20
|
+
setOpen(prev => !prev);
|
|
21
|
+
}, []);
|
|
22
|
+
const onClose = useCallback(() => {
|
|
23
|
+
setOpen(false);
|
|
24
|
+
}, []);
|
|
25
|
+
|
|
26
|
+
const dropdownRef = useRef<HTMLDivElement | null>(null);
|
|
27
|
+
useClickOutside(dropdownRef, onClose);
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<StyledFilter.Parent ref={dropdownRef}>
|
|
31
|
+
<FilterToggleButton clickEvent={onToggle} />
|
|
32
|
+
<Dropdown.Container isOpen={isOpen} horizontal="right" gap={7}>
|
|
33
|
+
{group.map(d => (
|
|
34
|
+
<FilterGroup {...d} key={d.groupKey} />
|
|
35
|
+
))}
|
|
36
|
+
</Dropdown.Container>
|
|
37
|
+
</StyledFilter.Parent>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useCallback } from "react";
|
|
4
|
+
import { useFormContext, useWatch } from "react-hook-form";
|
|
5
|
+
|
|
6
|
+
import { Input } from "@uniai-fe/ui-legacy";
|
|
7
|
+
import StyledFilter from "../styles/styled-components/filter";
|
|
8
|
+
|
|
9
|
+
import type { FilterGroupDataType, FilterStateFormType } from "../../types";
|
|
10
|
+
|
|
11
|
+
export default function FilterGroup({
|
|
12
|
+
groupKey,
|
|
13
|
+
categoryKey,
|
|
14
|
+
categoryName,
|
|
15
|
+
items,
|
|
16
|
+
}: FilterGroupDataType) {
|
|
17
|
+
const { register, control, getValues, setValue } =
|
|
18
|
+
useFormContext<FilterStateFormType>();
|
|
19
|
+
|
|
20
|
+
// 현재 그룹 선택 현황
|
|
21
|
+
const checkState = useWatch({ control, name: `filter.${categoryKey}` });
|
|
22
|
+
|
|
23
|
+
// 전체선택 클릭 이벤트
|
|
24
|
+
const onCheckAll = useCallback(
|
|
25
|
+
(e: React.ChangeEvent<HTMLInputElement>) => {
|
|
26
|
+
const { checked } = e.target;
|
|
27
|
+
setValue(`filter.${categoryKey}.all`, checked);
|
|
28
|
+
|
|
29
|
+
// 하위항목에 all check상태 일괄 적용
|
|
30
|
+
const keys = items.map(({ value }) => value);
|
|
31
|
+
|
|
32
|
+
keys.forEach(dataKey => {
|
|
33
|
+
setValue(`filter.${categoryKey}.${dataKey}`, checked);
|
|
34
|
+
});
|
|
35
|
+
},
|
|
36
|
+
[categoryKey, items, setValue],
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
// 하위항목 선택 클릭 이벤트
|
|
40
|
+
const onCheckEach = useCallback(
|
|
41
|
+
(e: React.ChangeEvent<HTMLInputElement>, dataKey: string) => {
|
|
42
|
+
// 클릭한 항목 선택 업데이트
|
|
43
|
+
const { checked: thisChecked } = e.target;
|
|
44
|
+
setValue(`filter.${categoryKey}.${dataKey}`, thisChecked);
|
|
45
|
+
|
|
46
|
+
// 전체 선택상태
|
|
47
|
+
const currentCheckedAll = getValues(`filter.${categoryKey}.all`);
|
|
48
|
+
|
|
49
|
+
// 나머지 하위항목 선택상태
|
|
50
|
+
const others = Object.fromEntries(
|
|
51
|
+
Object.entries(checkState).filter(
|
|
52
|
+
([key]) => !["all", dataKey].includes(key),
|
|
53
|
+
),
|
|
54
|
+
);
|
|
55
|
+
// 현재 항목을 클릭함으로써, 모든 하위항목이 모두 체크되었는지 확인
|
|
56
|
+
const isCheckedAllByEach =
|
|
57
|
+
thisChecked && Object.values(others).every(checked => checked);
|
|
58
|
+
|
|
59
|
+
// 현재 전체선택 체크상태와 다르면 업데이트
|
|
60
|
+
if (currentCheckedAll !== isCheckedAllByEach)
|
|
61
|
+
setValue(`filter.${categoryKey}.all`, isCheckedAllByEach);
|
|
62
|
+
},
|
|
63
|
+
[categoryKey, checkState, getValues, setValue],
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<StyledFilter.Group.Container>
|
|
68
|
+
<StyledFilter.Group.Title>
|
|
69
|
+
<Input.Checkbox
|
|
70
|
+
label={{
|
|
71
|
+
name: <span>{categoryName}</span>,
|
|
72
|
+
position: "right",
|
|
73
|
+
hide: false,
|
|
74
|
+
}}
|
|
75
|
+
selectorId={`${categoryKey}/all`}
|
|
76
|
+
dataKey={`${categoryKey}/all`}
|
|
77
|
+
register={register(`filter.${categoryKey}.all`)}
|
|
78
|
+
state={{ checked: checkState.all }}
|
|
79
|
+
onCustomChange={onCheckAll}
|
|
80
|
+
/>
|
|
81
|
+
</StyledFilter.Group.Title>
|
|
82
|
+
{items.map(({ value: dataKey, name }) => (
|
|
83
|
+
<StyledFilter.Group.Item key={`${groupKey}/${dataKey}`}>
|
|
84
|
+
<Input.Checkbox
|
|
85
|
+
label={{
|
|
86
|
+
name: <span>{name}</span>,
|
|
87
|
+
position: "right",
|
|
88
|
+
hide: false,
|
|
89
|
+
}}
|
|
90
|
+
selectorId={`${categoryKey}/${dataKey}`}
|
|
91
|
+
dataKey={`${categoryKey}/${dataKey}`}
|
|
92
|
+
register={register(`filter.${categoryKey}.${dataKey}`)}
|
|
93
|
+
state={{ checked: checkState[dataKey] }}
|
|
94
|
+
onCustomChange={e => onCheckEach(e, dataKey)}
|
|
95
|
+
/>
|
|
96
|
+
</StyledFilter.Group.Item>
|
|
97
|
+
))}
|
|
98
|
+
</StyledFilter.Group.Container>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Button, Icon } from "@uniai-fe/ui-legacy";
|
|
4
|
+
import StyledFilter from "../styles/styled-components/filter";
|
|
5
|
+
|
|
6
|
+
export default function FilterToggleButton({
|
|
7
|
+
clickEvent,
|
|
8
|
+
}: {
|
|
9
|
+
clickEvent: () => void;
|
|
10
|
+
}) {
|
|
11
|
+
return (
|
|
12
|
+
<StyledFilter.Button
|
|
13
|
+
as={Button.Default}
|
|
14
|
+
className="filter-button"
|
|
15
|
+
styleOptions={{
|
|
16
|
+
priorityType: "pale",
|
|
17
|
+
fillType: "outline",
|
|
18
|
+
sizeType: "small",
|
|
19
|
+
}}
|
|
20
|
+
clickEvent={clickEvent}
|
|
21
|
+
>
|
|
22
|
+
<Icon.Button.Filter />
|
|
23
|
+
<span>필터</span>
|
|
24
|
+
</StyledFilter.Button>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { Button, StyledDropdown } from "@uniai-fe/ui-legacy";
|
|
5
|
+
import { styled } from "styled-components";
|
|
6
|
+
|
|
7
|
+
const FilterButton = styled(Button.Default as React.FC)`
|
|
8
|
+
margin: 0;
|
|
9
|
+
figure {
|
|
10
|
+
margin-right: 4px;
|
|
11
|
+
}
|
|
12
|
+
`;
|
|
13
|
+
const Parent = styled(StyledDropdown.parent)`
|
|
14
|
+
--dropdown-padding-vertical: 15px;
|
|
15
|
+
--dropdown-padding-horizontal: 15px;
|
|
16
|
+
|
|
17
|
+
--dropdown-border-top-radius: 4px;
|
|
18
|
+
--dropdown-border-bottom-radius: 4px;
|
|
19
|
+
|
|
20
|
+
--field-item-width: fit-content;
|
|
21
|
+
--field-item-wrapper-width: fit-content;
|
|
22
|
+
--field-item-flex-wrap: nowrap;
|
|
23
|
+
|
|
24
|
+
${StyledDropdown.wrapper} {
|
|
25
|
+
display: flex;
|
|
26
|
+
}
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
const GroupContainer = styled.dl`
|
|
30
|
+
width: fit-content;
|
|
31
|
+
margin-right: 15px;
|
|
32
|
+
padding-right: 15px;
|
|
33
|
+
border-right: 1px solid var(--color_30);
|
|
34
|
+
|
|
35
|
+
&:last-child {
|
|
36
|
+
margin-right: 0;
|
|
37
|
+
padding-right: 0;
|
|
38
|
+
border-right: 0;
|
|
39
|
+
}
|
|
40
|
+
`;
|
|
41
|
+
|
|
42
|
+
const GroupTitle = styled.dt`
|
|
43
|
+
padding-bottom: 10px;
|
|
44
|
+
border-bottom: 1px solid var(--color_30);
|
|
45
|
+
span {
|
|
46
|
+
font-size: 16px;
|
|
47
|
+
}
|
|
48
|
+
`;
|
|
49
|
+
|
|
50
|
+
const GroupItem = styled.dd`
|
|
51
|
+
margin-top: 5px;
|
|
52
|
+
`;
|
|
53
|
+
|
|
54
|
+
const Group = {
|
|
55
|
+
Container: GroupContainer,
|
|
56
|
+
Title: GroupTitle,
|
|
57
|
+
Item: GroupItem,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const StyledFilter = {
|
|
61
|
+
Parent,
|
|
62
|
+
Button: FilterButton,
|
|
63
|
+
Group,
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export default StyledFilter;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useCallback } from "react";
|
|
4
|
+
import { Chart } from "@uniai-fe/chart-legacy";
|
|
5
|
+
import StyledHealthScore from "../styles/styled/chart";
|
|
6
|
+
import { Button } from "@uniai-fe/ui-legacy";
|
|
7
|
+
// import Button from "@uniai-fe/ui-legacy";
|
|
8
|
+
|
|
9
|
+
export default function HealthScoreChart({
|
|
10
|
+
title,
|
|
11
|
+
score,
|
|
12
|
+
details,
|
|
13
|
+
}: {
|
|
14
|
+
title: string;
|
|
15
|
+
score: number | "";
|
|
16
|
+
details?: React.ReactNode[];
|
|
17
|
+
}) {
|
|
18
|
+
const onOpen = useCallback(() => {
|
|
19
|
+
console.log(details);
|
|
20
|
+
}, [details]);
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<StyledHealthScore.container>
|
|
24
|
+
<Chart.Graph.ArcMeter
|
|
25
|
+
data={{ name: title, value: score || "" }}
|
|
26
|
+
max={100}
|
|
27
|
+
/>
|
|
28
|
+
<StyledHealthScore.title>
|
|
29
|
+
<h5>{title}</h5>
|
|
30
|
+
<Button.Tooltip
|
|
31
|
+
size={18}
|
|
32
|
+
priorityType="primary"
|
|
33
|
+
fillType="solid"
|
|
34
|
+
clickEvent={onOpen}
|
|
35
|
+
/>
|
|
36
|
+
</StyledHealthScore.title>
|
|
37
|
+
</StyledHealthScore.container>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import Image from "next/image";
|
|
4
|
+
import { useMemo } from "react";
|
|
5
|
+
import assetsUrl from "../../asset-url";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 건강도 아이콘 컴포넌트
|
|
9
|
+
* @component
|
|
10
|
+
* @param {object} props
|
|
11
|
+
* @param {boolean} props.isFlockExist 활성화 계군 존재여부
|
|
12
|
+
* @param {number|""} props.healthScore 건강도 값
|
|
13
|
+
*/
|
|
14
|
+
export default function IconHealthScore({
|
|
15
|
+
size = 45,
|
|
16
|
+
isFlockExist,
|
|
17
|
+
healthScore,
|
|
18
|
+
}: {
|
|
19
|
+
size?: number;
|
|
20
|
+
isFlockExist: boolean;
|
|
21
|
+
healthScore: number | "";
|
|
22
|
+
}) {
|
|
23
|
+
const iconName = useMemo(() => {
|
|
24
|
+
if (typeof healthScore === "undefined" || !isFlockExist) return "empty";
|
|
25
|
+
|
|
26
|
+
if (isNaN(Number(healthScore)) || healthScore === "") return "gray";
|
|
27
|
+
|
|
28
|
+
if (healthScore < 25) return "red";
|
|
29
|
+
else if (25 <= healthScore && healthScore < 50) return "yellow";
|
|
30
|
+
else if (50 <= healthScore && healthScore < 75) return "green";
|
|
31
|
+
else if (75 <= healthScore) return "blue";
|
|
32
|
+
|
|
33
|
+
return "gray";
|
|
34
|
+
}, [healthScore, isFlockExist]);
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<figure>
|
|
38
|
+
<Image
|
|
39
|
+
src={`${assetsUrl}/img/health-score/${iconName}.svg`}
|
|
40
|
+
alt="건강"
|
|
41
|
+
width={size}
|
|
42
|
+
height={size}
|
|
43
|
+
/>
|
|
44
|
+
</figure>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { Chart } from "@uniai-fe/chart-legacy";
|
|
5
|
+
import { styled } from "styled-components";
|
|
6
|
+
|
|
7
|
+
const container = styled(Chart.ContentsContainer as React.FC)``;
|
|
8
|
+
|
|
9
|
+
const title = styled.div`
|
|
10
|
+
width: 100%;
|
|
11
|
+
margin-top: 10px;
|
|
12
|
+
display: flex;
|
|
13
|
+
align-items: center;
|
|
14
|
+
justify-content: center;
|
|
15
|
+
h5 {
|
|
16
|
+
font-size: 20px;
|
|
17
|
+
color: var(--color_90);
|
|
18
|
+
font-weight: 600;
|
|
19
|
+
}
|
|
20
|
+
`;
|
|
21
|
+
|
|
22
|
+
const StyledHealthScore = {
|
|
23
|
+
container,
|
|
24
|
+
title,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export default StyledHealthScore;
|
package/src/index.scss
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@use "./compare/compare.scss";
|
package/src/index.tsx
ADDED
package/src/styled.tsx
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 템플릿; 필터 그룹 하위 데이터
|
|
3
|
+
* @property {string} dataKey 데이터 키
|
|
4
|
+
* @property {string} name 이름
|
|
5
|
+
*/
|
|
6
|
+
export type FilterGroupItemDataType = {
|
|
7
|
+
/**
|
|
8
|
+
* register data key
|
|
9
|
+
* categoryKey 프로퍼티의 값
|
|
10
|
+
* - string 값이 아닌 경우, `{categoryKey}_{value}` 형태로 사용
|
|
11
|
+
*/
|
|
12
|
+
value: string;
|
|
13
|
+
name: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 템플릿; 필터 그룹 데이터
|
|
18
|
+
* @property {string} key 렌더링 키
|
|
19
|
+
* @property {string} categoryKey 그룹 카테고리 키 (컬럼 또는 차트 레전드 데이터 키)
|
|
20
|
+
* @property {string} categoryName 그룹 카테고리 이름
|
|
21
|
+
* @property {FilterGroupItemDataType[]} items 필터그룹 하위 아이템 데이터
|
|
22
|
+
*/
|
|
23
|
+
export type FilterGroupDataType = {
|
|
24
|
+
groupKey: string;
|
|
25
|
+
categoryKey: string;
|
|
26
|
+
categoryName: string;
|
|
27
|
+
items: FilterGroupItemDataType[];
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export type FilterStateItemDataType = {
|
|
31
|
+
[itemKey: string]: boolean;
|
|
32
|
+
all: boolean;
|
|
33
|
+
};
|
|
34
|
+
export type FilterGroupStateDataType = {
|
|
35
|
+
[categoryKey: string]: FilterStateItemDataType;
|
|
36
|
+
};
|
|
37
|
+
export type FilterStateFormType = {
|
|
38
|
+
filter: FilterGroupStateDataType;
|
|
39
|
+
};
|