@uniai-fe/uds-primitives 0.0.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.
- package/README.md +63 -0
- package/package.json +85 -0
- package/src/components/alternate/hooks/index.ts +4 -0
- package/src/components/alternate/img/.gitkeep +0 -0
- package/src/components/alternate/index.scss +1 -0
- package/src/components/alternate/index.tsx +4 -0
- package/src/components/alternate/markup/index.tsx +4 -0
- package/src/components/alternate/styles/index.scss +3 -0
- package/src/components/alternate/types/index.ts +4 -0
- package/src/components/alternate/utils/index.ts +4 -0
- package/src/components/badge/hooks/index.ts +4 -0
- package/src/components/badge/img/.gitkeep +0 -0
- package/src/components/badge/index.scss +1 -0
- package/src/components/badge/index.tsx +6 -0
- package/src/components/badge/markup/Badge.tsx +51 -0
- package/src/components/badge/markup/index.tsx +1 -0
- package/src/components/badge/styles/index.scss +189 -0
- package/src/components/badge/types/index.ts +55 -0
- package/src/components/badge/utils/index.ts +21 -0
- package/src/components/button/hooks/index.ts +4 -0
- package/src/components/button/img/.gitkeep +0 -0
- package/src/components/button/index.scss +1 -0
- package/src/components/button/index.tsx +6 -0
- package/src/components/button/markup/Button.tsx +175 -0
- package/src/components/button/markup/index.tsx +1 -0
- package/src/components/button/styles/index.scss +847 -0
- package/src/components/button/types/index.ts +79 -0
- package/src/components/button/utils/index.ts +58 -0
- package/src/components/calendar/hooks/index.ts +4 -0
- package/src/components/calendar/img/.gitkeep +0 -0
- package/src/components/calendar/index.scss +1 -0
- package/src/components/calendar/index.tsx +4 -0
- package/src/components/calendar/markup/index.tsx +4 -0
- package/src/components/calendar/styles/index.scss +3 -0
- package/src/components/calendar/types/index.ts +4 -0
- package/src/components/calendar/utils/index.ts +4 -0
- package/src/components/checkbox/hooks/index.ts +4 -0
- package/src/components/checkbox/img/.gitkeep +0 -0
- package/src/components/checkbox/img/check-large.svg +3 -0
- package/src/components/checkbox/img/check-medium.svg +3 -0
- package/src/components/checkbox/img/check.svg +3 -0
- package/src/components/checkbox/index.scss +1 -0
- package/src/components/checkbox/index.tsx +4 -0
- package/src/components/checkbox/markup/Checkbox.tsx +127 -0
- package/src/components/checkbox/markup/index.ts +1 -0
- package/src/components/checkbox/styles/index.scss +164 -0
- package/src/components/checkbox/types/checkbox.ts +21 -0
- package/src/components/checkbox/types/index.ts +1 -0
- package/src/components/chip/hooks/index.ts +4 -0
- package/src/components/chip/img/.gitkeep +0 -0
- package/src/components/chip/img/remove.svg +3 -0
- package/src/components/chip/index.scss +1 -0
- package/src/components/chip/index.tsx +6 -0
- package/src/components/chip/markup/Chip.tsx +103 -0
- package/src/components/chip/markup/index.tsx +1 -0
- package/src/components/chip/styles/index.scss +140 -0
- package/src/components/chip/types/index.ts +52 -0
- package/src/components/chip/utils/index.ts +36 -0
- package/src/components/dialog/hooks/index.ts +4 -0
- package/src/components/dialog/img/.gitkeep +0 -0
- package/src/components/dialog/index.scss +1 -0
- package/src/components/dialog/index.tsx +3 -0
- package/src/components/dialog/markup/confirm-dialog.tsx +316 -0
- package/src/components/dialog/markup/index.tsx +4 -0
- package/src/components/dialog/markup/notice-dialog.tsx +191 -0
- package/src/components/dialog/styles/base.scss +153 -0
- package/src/components/dialog/styles/confirm.scss +58 -0
- package/src/components/dialog/styles/index.scss +3 -0
- package/src/components/dialog/styles/notice.scss +65 -0
- package/src/components/dialog/types/index.ts +70 -0
- package/src/components/dialog/utils/index.ts +4 -0
- package/src/components/drawer/hooks/index.ts +113 -0
- package/src/components/drawer/img/.gitkeep +0 -0
- package/src/components/drawer/img/close.svg +3 -0
- package/src/components/drawer/index.scss +1 -0
- package/src/components/drawer/index.tsx +3 -0
- package/src/components/drawer/markup/drawer.tsx +421 -0
- package/src/components/drawer/markup/index.tsx +3 -0
- package/src/components/drawer/styles/index.scss +232 -0
- package/src/components/drawer/types/index.ts +51 -0
- package/src/components/drawer/utils/context.ts +15 -0
- package/src/components/drawer/utils/index.tsx +77 -0
- package/src/components/dropdown/hooks/index.ts +4 -0
- package/src/components/dropdown/img/.gitkeep +0 -0
- package/src/components/dropdown/index.scss +1 -0
- package/src/components/dropdown/index.tsx +4 -0
- package/src/components/dropdown/markup/index.tsx +4 -0
- package/src/components/dropdown/styles/index.scss +3 -0
- package/src/components/dropdown/types/index.ts +4 -0
- package/src/components/dropdown/utils/index.ts +4 -0
- package/src/components/input/hooks/index.ts +4 -0
- package/src/components/input/img/.gitkeep +0 -0
- package/src/components/input/img/check-correct.svg +3 -0
- package/src/components/input/img/check-default.svg +3 -0
- package/src/components/input/img/check-incorrect.svg +3 -0
- package/src/components/input/img/error.svg +5 -0
- package/src/components/input/img/hide-off.svg +4 -0
- package/src/components/input/img/hide-on.svg +6 -0
- package/src/components/input/img/reset.svg +3 -0
- package/src/components/input/img/search.svg +4 -0
- package/src/components/input/img/success.svg +3 -0
- package/src/components/input/index.scss +1 -0
- package/src/components/input/index.tsx +6 -0
- package/src/components/input/markup/index.tsx +1 -0
- package/src/components/input/markup/text/Base.tsx +311 -0
- package/src/components/input/markup/text/Identification.tsx +145 -0
- package/src/components/input/markup/text/Password.tsx +71 -0
- package/src/components/input/markup/text/Phone.tsx +115 -0
- package/src/components/input/markup/text/Search.tsx +35 -0
- package/src/components/input/markup/text/index.ts +10 -0
- package/src/components/input/styles/index.scss +375 -0
- package/src/components/input/types/index.ts +56 -0
- package/src/components/input/utils/index.ts +54 -0
- package/src/components/label/hooks/index.ts +4 -0
- package/src/components/label/img/.gitkeep +0 -0
- package/src/components/label/index.scss +1 -0
- package/src/components/label/index.tsx +4 -0
- package/src/components/label/markup/index.tsx +4 -0
- package/src/components/label/styles/index.scss +3 -0
- package/src/components/label/types/index.ts +4 -0
- package/src/components/label/utils/index.ts +4 -0
- package/src/components/navigation/hooks/index.ts +4 -0
- package/src/components/navigation/img/.gitkeep +0 -0
- package/src/components/navigation/index.scss +1 -0
- package/src/components/navigation/index.tsx +8 -0
- package/src/components/navigation/markup/index.tsx +2 -0
- package/src/components/navigation/markup/mobile/BottomNavigation.tsx +127 -0
- package/src/components/navigation/markup/mobile/index.ts +1 -0
- package/src/components/navigation/markup/web/index.ts +4 -0
- package/src/components/navigation/styles/index.scss +133 -0
- package/src/components/navigation/types/index.ts +38 -0
- package/src/components/navigation/utils/index.ts +23 -0
- package/src/components/pagination/hooks/index.ts +4 -0
- package/src/components/pagination/img/.gitkeep +0 -0
- package/src/components/pagination/index.scss +1 -0
- package/src/components/pagination/index.tsx +6 -0
- package/src/components/pagination/markup/Carousel.tsx +76 -0
- package/src/components/pagination/markup/Count.tsx +54 -0
- package/src/components/pagination/markup/Pagination.tsx +83 -0
- package/src/components/pagination/markup/index.tsx +3 -0
- package/src/components/pagination/styles/index.scss +155 -0
- package/src/components/pagination/types/index.ts +68 -0
- package/src/components/pagination/utils/index.ts +58 -0
- package/src/components/radio/hooks/index.ts +4 -0
- package/src/components/radio/img/.gitkeep +0 -0
- package/src/components/radio/index.scss +1 -0
- package/src/components/radio/index.tsx +7 -0
- package/src/components/radio/markup/Radio.tsx +121 -0
- package/src/components/radio/markup/RadioCard.tsx +68 -0
- package/src/components/radio/markup/RadioCardGroup.tsx +75 -0
- package/src/components/radio/markup/index.tsx +3 -0
- package/src/components/radio/styles/index.scss +252 -0
- package/src/components/radio/types/index.ts +1 -0
- package/src/components/radio/types/radio.ts +63 -0
- package/src/components/radio/utils/index.ts +4 -0
- package/src/components/scrollbar/hooks/index.ts +4 -0
- package/src/components/scrollbar/img/.gitkeep +0 -0
- package/src/components/scrollbar/index.scss +1 -0
- package/src/components/scrollbar/index.tsx +4 -0
- package/src/components/scrollbar/markup/index.tsx +4 -0
- package/src/components/scrollbar/styles/index.scss +3 -0
- package/src/components/scrollbar/types/index.ts +4 -0
- package/src/components/scrollbar/utils/index.ts +4 -0
- package/src/components/segmented-control/index.scss +1 -0
- package/src/components/segmented-control/index.tsx +7 -0
- package/src/components/segmented-control/markup/SegmentedControl.tsx +117 -0
- package/src/components/segmented-control/markup/index.ts +1 -0
- package/src/components/segmented-control/styles/index.scss +113 -0
- package/src/components/segmented-control/types/index.ts +22 -0
- package/src/components/select/hooks/index.ts +4 -0
- package/src/components/select/img/.gitkeep +0 -0
- package/src/components/select/index.scss +1 -0
- package/src/components/select/index.tsx +4 -0
- package/src/components/select/markup/index.tsx +4 -0
- package/src/components/select/styles/index.scss +3 -0
- package/src/components/select/types/index.ts +4 -0
- package/src/components/select/utils/index.ts +4 -0
- package/src/components/spinner/hooks/index.ts +4 -0
- package/src/components/spinner/img/.gitkeep +0 -0
- package/src/components/spinner/index.scss +1 -0
- package/src/components/spinner/index.tsx +4 -0
- package/src/components/spinner/markup/index.tsx +4 -0
- package/src/components/spinner/styles/index.scss +3 -0
- package/src/components/spinner/types/index.ts +4 -0
- package/src/components/spinner/utils/index.ts +4 -0
- package/src/components/tab/hooks/index.ts +4 -0
- package/src/components/tab/img/.gitkeep +0 -0
- package/src/components/tab/index.scss +1 -0
- package/src/components/tab/index.tsx +6 -0
- package/src/components/tab/markup/TabContent.tsx +29 -0
- package/src/components/tab/markup/TabList.tsx +60 -0
- package/src/components/tab/markup/TabRoot.tsx +74 -0
- package/src/components/tab/markup/TabTrigger.tsx +47 -0
- package/src/components/tab/markup/index.tsx +4 -0
- package/src/components/tab/styles/index.scss +182 -0
- package/src/components/tab/types/index.ts +46 -0
- package/src/components/tab/utils/index.ts +5 -0
- package/src/components/tab/utils/tab-context.ts +20 -0
- package/src/components/table/hooks/index.ts +4 -0
- package/src/components/table/img/.gitkeep +0 -0
- package/src/components/table/index.scss +1 -0
- package/src/components/table/index.tsx +4 -0
- package/src/components/table/markup/index.tsx +4 -0
- package/src/components/table/styles/index.scss +3 -0
- package/src/components/table/types/index.ts +4 -0
- package/src/components/table/utils/index.ts +4 -0
- package/src/hooks/index.ts +4 -0
- package/src/img/.gitkeep +0 -0
- package/src/index.scss +3 -0
- package/src/index.tsx +26 -0
- package/src/init/dayjs.ts +14 -0
- package/src/theme/ThemeProvider.tsx +25 -0
- package/src/theme/config.ts +29 -0
- package/src/theme/index.ts +3 -0
- package/src/theme/overrides.scss +215 -0
- package/src/types/index.ts +4 -0
- package/src/utils/index.ts +4 -0
package/README.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# @uniai-fe/uds-primitives
|
|
2
|
+
|
|
3
|
+
`@uniai-fe/uds-foundation` 토큰 위에 Radix UI 컴포넌트를 얇게 감싼 **기초 UI 컴포넌트 컬렉션**입니다. Next.js 등 React 런타임에서 바로 import해 버튼·입력·네비게이션 등 공통 요소를 일관된 스타일로 사용할 수 있습니다.
|
|
4
|
+
|
|
5
|
+
## 설치
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @uniai-fe/uds-foundation @uniai-fe/uds-primitives
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
// next.config.ts
|
|
13
|
+
const nextConfig = {
|
|
14
|
+
transpilePackages: ["@uniai-fe/uds-foundation", "@uniai-fe/uds-primitives"],
|
|
15
|
+
};
|
|
16
|
+
export default nextConfig;
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
앱 루트에서 foundation CSS를 1회 import 하면 모든 primitives가 토큰을 공유합니다.
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import "@uniai-fe/uds-foundation/css";
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## 사용 예시
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
import { Button } from "@uniai-fe/uds-primitives";
|
|
29
|
+
|
|
30
|
+
export default function Page() {
|
|
31
|
+
return <Button intent="primary">확인</Button>;
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
모든 컴포넌트는 `className`/`data-*` 기반으로 override가 가능하며, 내부 슬롯(prefix/suffix/iconSlot 등)은 `CONTEXT-*.md`에 정의된 API를 따릅니다.
|
|
36
|
+
|
|
37
|
+
## 구조
|
|
38
|
+
|
|
39
|
+
```plaintext
|
|
40
|
+
src/components/{category}/
|
|
41
|
+
markup/ // 컴포넌트 구현
|
|
42
|
+
types/ // 외부 노출 타입
|
|
43
|
+
styles/ // SCSS (foundation 토큰 기반)
|
|
44
|
+
hooks/ // 카테고리 전용 훅
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
- 배럴(`components/{category}/index.tsx`)은 항상 `import "./index.scss"`를 포함합니다.
|
|
48
|
+
- 스타일은 foundation CSS 변수만 사용하며, Radix 기본 클래스 대신 `.button[data-intent=...]` 형태로 분기합니다.
|
|
49
|
+
|
|
50
|
+
## 스크립트
|
|
51
|
+
|
|
52
|
+
- `pnpm module:lint`
|
|
53
|
+
- `pnpm module:typecheck`
|
|
54
|
+
- `pnpm module:build`
|
|
55
|
+
|
|
56
|
+
루트에서는 `pnpm --filter @uniai-fe/uds-primitives <command>`로 실행할 수 있습니다.
|
|
57
|
+
|
|
58
|
+
## 문서
|
|
59
|
+
|
|
60
|
+
- `CONTEXT.md` 및 `CONTEXT-*.md`: 각 컴포넌트의 상태/진행/디자인 근거
|
|
61
|
+
- `RADIX-SIZE-GUIDE.md`: primitives 사이즈 체계와 Radix 매핑 규칙
|
|
62
|
+
|
|
63
|
+
필요한 컨텍스트를 확인한 뒤 컴포넌트를 import해 사용하면 됩니다.
|
package/package.json
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@uniai-fe/uds-primitives",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "UNIAI Design System; Primitives Components Package",
|
|
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.25.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
|
+
"main": "./src/index.tsx",
|
|
27
|
+
"module": "./src/index.tsx",
|
|
28
|
+
"exports": {
|
|
29
|
+
".": "./src/index.tsx"
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"format": "prettier --write .",
|
|
33
|
+
"format:check": "prettier --check .",
|
|
34
|
+
"lint": "eslint . --max-warnings=0",
|
|
35
|
+
"typecheck": "tsc --project tsconfig.build.json --noEmit",
|
|
36
|
+
"build": "pnpm format && pnpm typecheck",
|
|
37
|
+
"dev": "tsc --project tsconfig.build.json --watch --noEmit",
|
|
38
|
+
"module:lint": "pnpm lint",
|
|
39
|
+
"module:typecheck": "pnpm typecheck",
|
|
40
|
+
"module:build": "pnpm build",
|
|
41
|
+
"design-primitives:build": "pnpm run build",
|
|
42
|
+
"design-primitives:dev": "pnpm run dev"
|
|
43
|
+
},
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"@uniai-fe/uds-foundation": "workspace:^0.0.1",
|
|
46
|
+
"@radix-ui/react-alert-dialog": "^1.1.15",
|
|
47
|
+
"@radix-ui/react-dialog": "^1.1.15",
|
|
48
|
+
"@radix-ui/react-visually-hidden": "^1.2.4",
|
|
49
|
+
"react": ">= 19",
|
|
50
|
+
"react-dom": ">= 19",
|
|
51
|
+
"react-hook-form": ">= 7"
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"@mantine/core": "^8.3.9",
|
|
55
|
+
"@mantine/dates": "^8.3.9",
|
|
56
|
+
"@mantine/hooks": "^8.3.9",
|
|
57
|
+
"@radix-ui/react-checkbox": "^1.3.3",
|
|
58
|
+
"@radix-ui/react-tabs": "^1.1.3",
|
|
59
|
+
"@radix-ui/react-toggle-group": "^1.1.3",
|
|
60
|
+
"@radix-ui/react-primitive": "^2.1.4",
|
|
61
|
+
"@radix-ui/react-radio-group": "^1.3.8",
|
|
62
|
+
"@radix-ui/react-slot": "^1.2.4",
|
|
63
|
+
"@radix-ui/themes": "^3.2.1",
|
|
64
|
+
"@uniai-fe/util-functions": "workspace:*",
|
|
65
|
+
"clsx": "^2.1.1",
|
|
66
|
+
"dayjs": "^1.11.19"
|
|
67
|
+
},
|
|
68
|
+
"devDependencies": {
|
|
69
|
+
"@radix-ui/react-alert-dialog": "^1.1.15",
|
|
70
|
+
"@radix-ui/react-dialog": "^1.1.15",
|
|
71
|
+
"@radix-ui/react-visually-hidden": "^1.2.4",
|
|
72
|
+
"@svgr/webpack": "^8.1.0",
|
|
73
|
+
"@types/node": "^24.10.2",
|
|
74
|
+
"@types/react": "^19.2.7",
|
|
75
|
+
"@types/react-dom": "^19.2.3",
|
|
76
|
+
"@uniai-fe/eslint-config": "workspace:*",
|
|
77
|
+
"@uniai-fe/next-devkit": "workspace:*",
|
|
78
|
+
"@uniai-fe/tsconfig": "workspace:*",
|
|
79
|
+
"eslint": "^9.39.1",
|
|
80
|
+
"prettier": "^3.7.4",
|
|
81
|
+
"react-hook-form": "^7.68.0",
|
|
82
|
+
"sass": "^1.95.0",
|
|
83
|
+
"typescript": "~5.9.3"
|
|
84
|
+
}
|
|
85
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@use "./styles/index.scss";
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@use "./styles/index.scss";
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { forwardRef } from "react";
|
|
2
|
+
import type { BadgeProps } from "../types";
|
|
3
|
+
import { composeBadgeClassName } from "../utils";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Badge 컴포넌트; size/style/intent 축을 data attribute로 노출한다.
|
|
7
|
+
*/
|
|
8
|
+
const Badge = forwardRef<HTMLElementTagNameMap["figure"], BadgeProps>(
|
|
9
|
+
(
|
|
10
|
+
{
|
|
11
|
+
children,
|
|
12
|
+
size = "xsmall",
|
|
13
|
+
style = "fill",
|
|
14
|
+
intent = "primary",
|
|
15
|
+
tone = "primary",
|
|
16
|
+
className,
|
|
17
|
+
inlineStyle,
|
|
18
|
+
...restProps
|
|
19
|
+
},
|
|
20
|
+
ref,
|
|
21
|
+
) => {
|
|
22
|
+
const combinedClassName = composeBadgeClassName({ size, style, className });
|
|
23
|
+
// dot 스타일은 실제 원형 포인트 요소를 별도로 렌더링한다.
|
|
24
|
+
const renderDot =
|
|
25
|
+
style === "dot" ? (
|
|
26
|
+
<span className="badge-dot" aria-hidden="true" />
|
|
27
|
+
) : null;
|
|
28
|
+
const hasLabel = Boolean(children);
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<figure
|
|
32
|
+
{...restProps}
|
|
33
|
+
ref={ref}
|
|
34
|
+
className={combinedClassName}
|
|
35
|
+
style={inlineStyle}
|
|
36
|
+
data-size={size}
|
|
37
|
+
data-style={style}
|
|
38
|
+
data-intent={style === "dot" ? undefined : intent}
|
|
39
|
+
data-tone={style === "dot" ? tone : undefined}
|
|
40
|
+
data-has-label={hasLabel ? "true" : undefined}
|
|
41
|
+
>
|
|
42
|
+
{renderDot}
|
|
43
|
+
{children ? <span className="badge-label">{children}</span> : null}
|
|
44
|
+
</figure>
|
|
45
|
+
);
|
|
46
|
+
},
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
Badge.displayName = "Badge";
|
|
50
|
+
|
|
51
|
+
export { Badge };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Badge } from "./Badge";
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
@use "@uniai-fe/uds-foundation/css";
|
|
2
|
+
|
|
3
|
+
/* Badge 토큰을 foundation 변수에 맞춰 한 번 더 래핑해 override 여지를 둔다. */
|
|
4
|
+
:where(.radix-themes, .theme-root, :root) {
|
|
5
|
+
--theme-badge-height-xsmall: var(--theme-size-small-1, 20px);
|
|
6
|
+
--theme-badge-height-small: var(--theme-size-small-2, 24px);
|
|
7
|
+
--theme-badge-padding-inline-xsmall: var(--spacing-padding-3, 6px);
|
|
8
|
+
--theme-badge-padding-inline-small: var(--spacing-padding-4, 8px);
|
|
9
|
+
--theme-badge-radius: var(--theme-radius-medium-1, 6px);
|
|
10
|
+
--theme-badge-font-family: var(--font-caption-medium-family, inherit);
|
|
11
|
+
--theme-badge-font-size: var(--font-caption-medium-size, 11px);
|
|
12
|
+
--theme-badge-font-weight: var(--font-caption-medium-weight, 400);
|
|
13
|
+
--theme-badge-line-height: var(--font-caption-medium-line-height, 1.5);
|
|
14
|
+
--theme-badge-letter-spacing: var(--font-caption-medium-letter-spacing, 0);
|
|
15
|
+
--theme-badge-dot-size: var(--spacing-gap-3, 8px);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.badge {
|
|
19
|
+
--badge-height: var(--theme-badge-height-xsmall);
|
|
20
|
+
--badge-padding-inline: var(--theme-badge-padding-inline-xsmall);
|
|
21
|
+
--badge-gap: var(--spacing-gap-1, 4px);
|
|
22
|
+
--badge-fill-bg-color: var(--color-primary-default, #1a6aff);
|
|
23
|
+
--badge-fill-label-color: var(--color-common-100, #ffffff);
|
|
24
|
+
--badge-outline-border-color: var(--color-primary-default, #1a6aff);
|
|
25
|
+
--badge-outline-label-color: var(--color-primary-default, #1a6aff);
|
|
26
|
+
--badge-dot-color: var(--color-primary-default, #1a6aff);
|
|
27
|
+
--badge-dot-size: var(--theme-badge-dot-size);
|
|
28
|
+
display: flex;
|
|
29
|
+
align-items: center;
|
|
30
|
+
justify-content: center;
|
|
31
|
+
gap: var(--badge-gap);
|
|
32
|
+
height: var(--badge-height);
|
|
33
|
+
padding-inline: var(--badge-padding-inline);
|
|
34
|
+
padding-block: 0;
|
|
35
|
+
border-radius: var(--theme-badge-radius);
|
|
36
|
+
border: 0;
|
|
37
|
+
font-family: var(--theme-badge-font-family);
|
|
38
|
+
font-size: var(--theme-badge-font-size);
|
|
39
|
+
font-weight: var(--theme-badge-font-weight);
|
|
40
|
+
line-height: 1;
|
|
41
|
+
letter-spacing: var(--theme-badge-letter-spacing);
|
|
42
|
+
white-space: nowrap;
|
|
43
|
+
box-sizing: border-box;
|
|
44
|
+
margin: 0;
|
|
45
|
+
width: fit-content;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.badge:where([data-size="small"]) {
|
|
49
|
+
--badge-height: var(--theme-badge-height-small);
|
|
50
|
+
--badge-padding-inline: var(--theme-badge-padding-inline-small);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.badge:where([data-style="fill"]) {
|
|
54
|
+
background-color: var(--badge-fill-bg-color);
|
|
55
|
+
color: var(--badge-fill-label-color);
|
|
56
|
+
border-color: transparent;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.badge:where([data-style="outlined"]) {
|
|
60
|
+
background-color: var(--color-bg-surface-static-white, #ffffff);
|
|
61
|
+
border-width: 1px;
|
|
62
|
+
border-style: solid;
|
|
63
|
+
border-color: var(--badge-outline-border-color);
|
|
64
|
+
color: var(--badge-outline-label-color);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.badge:where([data-style="dot"]) {
|
|
68
|
+
padding: 0;
|
|
69
|
+
border-width: 0;
|
|
70
|
+
width: var(--badge-dot-size);
|
|
71
|
+
height: var(--badge-dot-size);
|
|
72
|
+
min-width: 0;
|
|
73
|
+
border-radius: var(--badge-dot-size);
|
|
74
|
+
background-color: transparent;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.badge-dot {
|
|
78
|
+
width: var(--badge-dot-size);
|
|
79
|
+
height: var(--badge-dot-size);
|
|
80
|
+
border-radius: 999px;
|
|
81
|
+
background-color: var(--badge-dot-color);
|
|
82
|
+
flex-shrink: 0;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.badge-label {
|
|
86
|
+
display: inline-flex;
|
|
87
|
+
align-items: center;
|
|
88
|
+
color: inherit;
|
|
89
|
+
line-height: 1;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.badge:where([data-style="dot"]) .badge-label {
|
|
93
|
+
font-size: 0;
|
|
94
|
+
line-height: 0;
|
|
95
|
+
height: 0;
|
|
96
|
+
width: 0;
|
|
97
|
+
overflow: hidden;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/* fill intent 별 배경/라벨 컬러 정의 */
|
|
101
|
+
.badge:where([data-style="fill"][data-intent="primary"]) {
|
|
102
|
+
--badge-fill-bg-color: var(--color-primary-default, #1a6aff);
|
|
103
|
+
--badge-fill-label-color: var(--color-common-100, #ffffff);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.badge:where([data-style="fill"][data-intent="secondary"]) {
|
|
107
|
+
--badge-fill-bg-color: var(--color-secondary-default, #e5eeff);
|
|
108
|
+
--badge-fill-label-color: var(--color-primary-default, #1a6aff);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.badge:where([data-style="fill"][data-intent="teritary"]) {
|
|
112
|
+
--badge-fill-bg-color: var(--color-cool-gray-10, #18191b);
|
|
113
|
+
--badge-fill-label-color: var(--color-common-100, #ffffff);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.badge:where([data-style="fill"][data-intent="gray"]) {
|
|
117
|
+
--badge-fill-bg-color: var(--color-bg-surface-neutral, #f2f2f2);
|
|
118
|
+
--badge-fill-label-color: var(--color-label-neutral, #797e86);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.badge:where([data-style="fill"][data-intent="green"]) {
|
|
122
|
+
--badge-fill-bg-color: var(--color-green-95, #e9fcef);
|
|
123
|
+
--badge-fill-label-color: var(--color-green-45, #1dc956);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.badge:where([data-style="fill"][data-intent="yellow"]) {
|
|
127
|
+
--badge-fill-bg-color: var(--color-yellow-95, #fefae7);
|
|
128
|
+
--badge-fill-label-color: var(--color-yellow-45, #dab80b);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.badge:where([data-style="fill"][data-intent="orange"]) {
|
|
132
|
+
--badge-fill-bg-color: var(--color-orange-95, #ffeee5);
|
|
133
|
+
--badge-fill-label-color: var(--color-orange-55, #ff661a);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.badge:where([data-style="fill"][data-intent="red"]) {
|
|
137
|
+
--badge-fill-bg-color: var(--color-red-95, #fde8e7);
|
|
138
|
+
--badge-fill-label-color: var(--color-red-55, #f43625);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/* outlined intent 별 border/label 컬러 정의 */
|
|
142
|
+
.badge:where([data-style="outlined"][data-intent="primary"]) {
|
|
143
|
+
--badge-outline-border-color: var(--color-primary-default, #1a6aff);
|
|
144
|
+
--badge-outline-label-color: var(--color-primary-default, #1a6aff);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.badge:where([data-style="outlined"][data-intent="secondary"]) {
|
|
148
|
+
--badge-outline-border-color: var(--color-border-standard-blue, #ccdeff);
|
|
149
|
+
--badge-outline-label-color: var(--color-primary-strong, #0050e5);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.badge:where([data-style="outlined"][data-intent="teritary"]) {
|
|
153
|
+
--badge-outline-border-color: var(--color-border-standard-heavy, #313235);
|
|
154
|
+
--badge-outline-label-color: var(--color-label-strong, #18191b);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.badge:where([data-style="outlined"][data-intent="gray"]) {
|
|
158
|
+
--badge-outline-border-color: var(--color-border-standard-assistive, #e4e5e7);
|
|
159
|
+
--badge-outline-label-color: var(--color-label-standard, #3d3f43);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.badge:where([data-style="outlined"][data-intent="green"]) {
|
|
163
|
+
--badge-outline-border-color: var(--color-green-45, #1dc956);
|
|
164
|
+
--badge-outline-label-color: var(--color-green-45, #1dc956);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.badge:where([data-style="outlined"][data-intent="yellow"]) {
|
|
168
|
+
--badge-outline-border-color: var(--color-yellow-60, #f5d63d);
|
|
169
|
+
--badge-outline-label-color: var(--color-yellow-45, #dab80b);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.badge:where([data-style="outlined"][data-intent="orange"]) {
|
|
173
|
+
--badge-outline-border-color: var(--color-orange-70, #ff9966);
|
|
174
|
+
--badge-outline-label-color: var(--color-orange-55, #ff661a);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.badge:where([data-style="outlined"][data-intent="red"]) {
|
|
178
|
+
--badge-outline-border-color: var(--color-red-70, #f7796e);
|
|
179
|
+
--badge-outline-label-color: var(--color-red-55, #f43625);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/* dot tone 별 포인트 컬러 */
|
|
183
|
+
.badge:where([data-style="dot"][data-tone="primary"]) {
|
|
184
|
+
--badge-dot-color: var(--color-primary-default, #1a6aff);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.badge:where([data-style="dot"][data-tone="feedback"]) {
|
|
188
|
+
--badge-dot-color: var(--color-red-55, #f43625);
|
|
189
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { ComponentPropsWithoutRef, ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
export const BADGE_SIZES = ["xsmall", "small"] as const;
|
|
4
|
+
export const BADGE_STYLES = ["fill", "outlined", "dot"] as const;
|
|
5
|
+
export const BADGE_INTENTS = [
|
|
6
|
+
"primary",
|
|
7
|
+
"secondary",
|
|
8
|
+
"teritary",
|
|
9
|
+
"gray",
|
|
10
|
+
"green",
|
|
11
|
+
"yellow",
|
|
12
|
+
"orange",
|
|
13
|
+
"red",
|
|
14
|
+
] as const;
|
|
15
|
+
export const BADGE_TONES = ["primary", "feedback"] as const;
|
|
16
|
+
|
|
17
|
+
export type BadgeSize = (typeof BADGE_SIZES)[number];
|
|
18
|
+
export type BadgeStyle = (typeof BADGE_STYLES)[number];
|
|
19
|
+
export type BadgeIntent = (typeof BADGE_INTENTS)[number];
|
|
20
|
+
export type BadgeTone = (typeof BADGE_TONES)[number];
|
|
21
|
+
|
|
22
|
+
type NativeFigureProps = Omit<ComponentPropsWithoutRef<"figure">, "style">;
|
|
23
|
+
|
|
24
|
+
export interface BadgeProps extends Omit<NativeFigureProps, "children"> {
|
|
25
|
+
/**
|
|
26
|
+
* 표시할 라벨. 생략하면 dot 스타일만 렌더링된다.
|
|
27
|
+
*/
|
|
28
|
+
children?: ReactNode;
|
|
29
|
+
/**
|
|
30
|
+
* Figma size 축 (xsmall/small)
|
|
31
|
+
*/
|
|
32
|
+
size?: BadgeSize;
|
|
33
|
+
/**
|
|
34
|
+
* style 축 (fill/outlined/dot)
|
|
35
|
+
*/
|
|
36
|
+
style?: BadgeStyle;
|
|
37
|
+
/**
|
|
38
|
+
* fill/outlined 변형의 intent 축. dot 스타일에는 tone을 사용한다.
|
|
39
|
+
*/
|
|
40
|
+
intent?: BadgeIntent;
|
|
41
|
+
/**
|
|
42
|
+
* dot 변형 전용 tone 축(primary/feedback).
|
|
43
|
+
*/
|
|
44
|
+
tone?: BadgeTone;
|
|
45
|
+
/**
|
|
46
|
+
* 원본 figure 요소에 적용할 인라인 스타일.
|
|
47
|
+
*/
|
|
48
|
+
inlineStyle?: ComponentPropsWithoutRef<"figure">["style"];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface BadgeClassNameOptions {
|
|
52
|
+
size: BadgeSize;
|
|
53
|
+
style: BadgeStyle;
|
|
54
|
+
className?: string;
|
|
55
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import clsx from "clsx";
|
|
2
|
+
import type { BadgeClassNameOptions } from "../types";
|
|
3
|
+
|
|
4
|
+
const BADGE_CLASSNAME = "badge";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Badge 데이터 축을 className 구조로 묶어서 Story/override 시 일관되게 쓴다.
|
|
8
|
+
*/
|
|
9
|
+
const composeBadgeClassName = ({
|
|
10
|
+
size,
|
|
11
|
+
style,
|
|
12
|
+
className,
|
|
13
|
+
}: BadgeClassNameOptions) =>
|
|
14
|
+
clsx(
|
|
15
|
+
BADGE_CLASSNAME,
|
|
16
|
+
`${BADGE_CLASSNAME}--size-${size}`,
|
|
17
|
+
`${BADGE_CLASSNAME}--style-${style}`,
|
|
18
|
+
className,
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
export { BADGE_CLASSNAME, composeBadgeClassName };
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@use "./styles/index.scss";
|