@uniai-fe/uds-primitives 0.2.10 → 0.3.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/dist/styles.css +82 -10
- package/package.json +1 -1
- package/src/components/button/index.tsx +1 -1
- package/src/components/button/markup/Base.tsx +1 -1
- package/src/components/button/styles/button.scss +32 -0
- package/src/components/button/styles/variables.scss +16 -0
- package/src/components/button/types/index.ts +3 -179
- package/src/components/button/types/{constants.ts → options.ts} +28 -13
- package/src/components/button/types/props.ts +264 -0
- package/src/components/chip/markup/Chip.tsx +14 -2
- package/src/components/chip/styles/chip.scss +154 -0
- package/src/components/chip/styles/index.scss +2 -138
- package/src/components/chip/styles/variables.scss +26 -0
- package/src/components/chip/types/index.ts +2 -52
- package/src/components/chip/types/options.ts +38 -0
- package/src/components/chip/types/props.ts +115 -0
- package/src/components/chip/utils/class-name.ts +36 -0
- package/src/components/chip/utils/index.ts +1 -36
- package/src/components/form/index.tsx +3 -16
- package/src/components/form/markup/form-field/index.tsx +1 -0
- package/src/components/form/markup/index.tsx +11 -0
- package/src/components/form/styles/index.scss +2 -2
- package/src/components/form/utils/index.ts +1 -0
- package/src/components/info-box/img/ban.svg +5 -0
- package/src/components/info-box/img/caution.svg +5 -0
- package/src/components/info-box/img/check.svg +4 -0
- package/src/components/info-box/img/info.svg +6 -0
- package/src/components/info-box/index.scss +1 -0
- package/src/components/info-box/index.tsx +4 -0
- package/src/components/info-box/markup/Icon.tsx +14 -0
- package/src/components/info-box/markup/InfoBox.tsx +65 -0
- package/src/components/info-box/markup/index.ts +2 -0
- package/src/components/info-box/styles/index.scss +2 -0
- package/src/components/info-box/styles/info-box.scss +61 -0
- package/src/components/info-box/styles/variables.scss +33 -0
- package/src/components/info-box/types/index.ts +1 -0
- package/src/components/info-box/types/props.ts +34 -0
- package/src/components/table/markup/Container.tsx +30 -66
- package/src/components/table/types/foundation.ts +10 -45
- package/src/index.tsx +19 -21
- package/src/components/button/types/templates.ts +0 -84
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import type { ComponentPropsWithoutRef, ElementType, ReactNode } from "react";
|
|
2
|
+
import type { FormFieldWidth } from "../../form/types";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Button; priority 옵션
|
|
6
|
+
* @typedef {"primary" | "secondary" | "tertiary"} ButtonPriority
|
|
7
|
+
* @desc
|
|
8
|
+
* - primary: 브랜드 컬러를 사용하는 기본 버튼.
|
|
9
|
+
* - secondary: 보조 강조 색상.
|
|
10
|
+
* - tertiary: 중립/회색 계열 보조 버튼.
|
|
11
|
+
*/
|
|
12
|
+
export type ButtonPriority = "primary" | "secondary" | "tertiary";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Button; fill 옵션
|
|
16
|
+
* @typedef {"solid" | "outlined"} ButtonFill
|
|
17
|
+
* @desc
|
|
18
|
+
* - solid: 배경을 채우는 스타일.
|
|
19
|
+
* - outlined: 배경을 비우고 선만 두르는 스타일.
|
|
20
|
+
*/
|
|
21
|
+
export type ButtonFill = "solid" | "outlined";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Button; size 옵션
|
|
25
|
+
* @typedef {"xlarge" | "large" | "medium" | "small" | "table"} ButtonSize
|
|
26
|
+
* @desc
|
|
27
|
+
* - xlarge: 데스크톱 주요 CTA.
|
|
28
|
+
* - large: 일반 CTA.
|
|
29
|
+
* - medium: 기본/폼 버튼.
|
|
30
|
+
* - small: 보조/툴바 버튼.
|
|
31
|
+
* - table: Table 셀 내 필드/액션 버튼.
|
|
32
|
+
*/
|
|
33
|
+
export type ButtonSize = "xlarge" | "large" | "medium" | "small" | "table";
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* fill/size 조합으로 구성된 scale 식별자.
|
|
37
|
+
* @typedef {
|
|
38
|
+
* "solid-xlarge" | "solid-large" | "solid-medium" | "solid-small" |
|
|
39
|
+
* "outlined-xlarge" | "outlined-large" | "outlined-medium" | "outlined-small"
|
|
40
|
+
* } ButtonScale
|
|
41
|
+
* @deprecated
|
|
42
|
+
* @desc
|
|
43
|
+
* - solid-xlarge~solid-small: Solid fill + size 조합.
|
|
44
|
+
* - outlined-xlarge~outlined-small: Outline fill + size 조합.
|
|
45
|
+
*/
|
|
46
|
+
export type ButtonScale =
|
|
47
|
+
| "solid-xlarge"
|
|
48
|
+
| "solid-large"
|
|
49
|
+
| "solid-medium"
|
|
50
|
+
| "solid-small"
|
|
51
|
+
| "outlined-xlarge"
|
|
52
|
+
| "outlined-large"
|
|
53
|
+
| "outlined-medium"
|
|
54
|
+
| "outlined-small";
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Button; state 옵션
|
|
58
|
+
* @typedef {"default" | "readonly" | "disabled"} ButtonState
|
|
59
|
+
* @desc
|
|
60
|
+
* - default: 일반 상태.
|
|
61
|
+
* - readonly: 조작은 막고 비주얼만 유지.
|
|
62
|
+
* - disabled: 비활성화.
|
|
63
|
+
*/
|
|
64
|
+
export type ButtonState = "default" | "readonly" | "disabled";
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Button; 인터랙션 상태 고정을 위한 data-user-action 옵션
|
|
68
|
+
* @typedef {"hover" | "pressed"} ButtonUserAction
|
|
69
|
+
* @optional
|
|
70
|
+
* @desc
|
|
71
|
+
* - hover: hover 비주얼 강제 노출.
|
|
72
|
+
* - pressed: pressed 비주얼 강제 노출.
|
|
73
|
+
*/
|
|
74
|
+
export type ButtonUserAction = "hover" | "pressed";
|
|
75
|
+
/**
|
|
76
|
+
* @deprecated
|
|
77
|
+
*/
|
|
78
|
+
export type ButtonScaleGroup = ButtonFill;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Button; <button /> native props 타입
|
|
82
|
+
*/
|
|
83
|
+
type NativeButtonProps = ComponentPropsWithoutRef<"button">;
|
|
84
|
+
/**
|
|
85
|
+
* Button; <a /> native props 타입
|
|
86
|
+
*/
|
|
87
|
+
type AnchorProps = ComponentPropsWithoutRef<"a">;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Button; 컴포넌트 공통 props 타입
|
|
91
|
+
*/
|
|
92
|
+
type SharedElementProps = Omit<NativeButtonProps, "children"> &
|
|
93
|
+
Omit<AnchorProps, "children">;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Button 컴포넌트의 공통 props.
|
|
97
|
+
* @property {ElementType} [as] 렌더링할 요소. 기본값은 button.
|
|
98
|
+
* @property {ReactNode} [children] 주 내용. 문자열이면 자동으로 .button-label로 감싼다.
|
|
99
|
+
* @property {"solid-xlarge" | "solid-large" | "solid-medium" | "solid-small" | "outlined-xlarge" | "outlined-large" | "outlined-medium" | "outlined-small"} [scale] 레이아웃/spacing 집합. 추후 fill/size 조합으로 대체 예정이다.
|
|
100
|
+
* @property {"solid" | "outlined"} fill 채움 스타일. scale 조합보다 우선한다.
|
|
101
|
+
* @property {"xlarge" | "large" | "medium" | "small" | "table"} size 높이/타이포 스케일. scale 조합보다 우선한다.
|
|
102
|
+
* @property {"primary" | "secondary" | "tertiary"} priority semantic color priority.
|
|
103
|
+
* @property {"default" | "readonly" | "disabled"} [state] UI 상태. disabled prop과 조합된다.
|
|
104
|
+
* @property {boolean} [block] width:100% 확장 여부.
|
|
105
|
+
* @property {FormFieldWidth} [width] width preset. block보다 우선 적용된다.
|
|
106
|
+
* @property {boolean} [loading] true면 readonly 처리 + aria-busy.
|
|
107
|
+
* @property {ReactNode} [left] 라벨 왼쪽 커스텀 슬롯.
|
|
108
|
+
* @property {ReactNode} [right] 라벨 오른쪽 커스텀 슬롯.
|
|
109
|
+
* @property {"hover" | "pressed"} ["data-user-action"] Interaction 상태 강제 표현용 data attr.
|
|
110
|
+
*/
|
|
111
|
+
export interface ButtonProps extends SharedElementProps {
|
|
112
|
+
/**
|
|
113
|
+
* 렌더링할 요소 타입.
|
|
114
|
+
* 기본값은 button.
|
|
115
|
+
*/
|
|
116
|
+
as?: ElementType;
|
|
117
|
+
/**
|
|
118
|
+
* 버튼 라벨 또는 컨텐츠.
|
|
119
|
+
* 문자열/숫자는 .button-label span으로 감싼다.
|
|
120
|
+
*/
|
|
121
|
+
children?: ReactNode;
|
|
122
|
+
/**
|
|
123
|
+
* @deprecated
|
|
124
|
+
* "solid-xlarge" | "solid-large" | "solid-medium" | "solid-small" |
|
|
125
|
+
* "outlined-xlarge" | "outlined-large" | "outlined-medium" | "outlined-small"
|
|
126
|
+
* fill/size를 제공하지 않을 때만 fallback으로 사용한다.
|
|
127
|
+
*/
|
|
128
|
+
scale?: ButtonScale;
|
|
129
|
+
/**
|
|
130
|
+
* @required
|
|
131
|
+
* "primary" | "secondary" | "tertiary"
|
|
132
|
+
*/
|
|
133
|
+
priority: ButtonPriority;
|
|
134
|
+
/**
|
|
135
|
+
* @required
|
|
136
|
+
* "solid" | "outlined"
|
|
137
|
+
*/
|
|
138
|
+
fill: ButtonFill;
|
|
139
|
+
/**
|
|
140
|
+
* @required
|
|
141
|
+
* "xlarge" | "large" | "medium" | "small" | "table"
|
|
142
|
+
*/
|
|
143
|
+
size: ButtonSize;
|
|
144
|
+
/**
|
|
145
|
+
* 컴포넌트 내부 state.
|
|
146
|
+
* disabled prop과 병합되어 최종 상태를 결정한다.
|
|
147
|
+
* "default" | "readonly" | "disabled"
|
|
148
|
+
*/
|
|
149
|
+
state?: ButtonState;
|
|
150
|
+
/**
|
|
151
|
+
* true면 버튼 폭을 100%로 확장한다.
|
|
152
|
+
*/
|
|
153
|
+
block?: boolean;
|
|
154
|
+
/**
|
|
155
|
+
* width preset.
|
|
156
|
+
* block보다 우선 적용된다.
|
|
157
|
+
*/
|
|
158
|
+
width?: FormFieldWidth;
|
|
159
|
+
/**
|
|
160
|
+
* true면 readonly 상태로 전환하고 aria-busy를 설정한다.
|
|
161
|
+
*/
|
|
162
|
+
loading?: boolean;
|
|
163
|
+
/**
|
|
164
|
+
* 라벨 왼쪽 커스텀 슬롯.
|
|
165
|
+
*/
|
|
166
|
+
left?: ReactNode;
|
|
167
|
+
/**
|
|
168
|
+
* 라벨 오른쪽 커스텀 슬롯.
|
|
169
|
+
*/
|
|
170
|
+
right?: ReactNode;
|
|
171
|
+
/**
|
|
172
|
+
* Storybook 등에서 hover/pressed 상태를 강제로 표현하기 위한 data attribute.
|
|
173
|
+
* "hover" | "pressed"
|
|
174
|
+
*/
|
|
175
|
+
"data-user-action"?: ButtonUserAction;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* 변경 사항: preset 타입 파일을 제거하고 props.ts로 통합한다.
|
|
180
|
+
*/
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Button Types; Text preset size 옵션
|
|
184
|
+
* @typedef {"small" | "medium" | "large"} TextButtonSize
|
|
185
|
+
* @desc
|
|
186
|
+
* - `small`
|
|
187
|
+
* - `medium`
|
|
188
|
+
* - `large`
|
|
189
|
+
*/
|
|
190
|
+
export type TextButtonSize = "small" | "medium" | "large";
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Button Types; Text preset priority 옵션
|
|
194
|
+
* @typedef {"secondary" | "tertiary"} TextButtonPriority
|
|
195
|
+
* @desc
|
|
196
|
+
* - `secondary`
|
|
197
|
+
* - `tertiary`
|
|
198
|
+
*/
|
|
199
|
+
export type TextButtonPriority = Exclude<ButtonPriority, "primary">;
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Button Types; Text preset props
|
|
203
|
+
* @property {ElementType} [as] 렌더링할 요소. 기본값은 button.
|
|
204
|
+
* @property {ReactNode} [children] 주 내용. 문자열이면 자동으로 .button-label로 감싼다.
|
|
205
|
+
* @property {"small" | "medium" | "large"} size 높이/타이포 스케일. scale 조합보다 우선한다.
|
|
206
|
+
* @property {"secondary" | "tertiary"} priority semantic color priority.
|
|
207
|
+
* @property {"default" | "readonly" | "disabled"} [state] UI 상태. disabled prop과 조합된다.
|
|
208
|
+
* @property {boolean} [block] width:100% 확장 여부.
|
|
209
|
+
* @property {boolean} [loading] true면 readonly 처리 + aria-busy.
|
|
210
|
+
* @property {ReactNode} [left] 라벨 왼쪽 커스텀 슬롯.
|
|
211
|
+
* @property {ReactNode} [right] 라벨 오른쪽 커스텀 슬롯.
|
|
212
|
+
* @property {"hover" | "pressed"} ["data-user-action"] Interaction 상태 강제 표현용 data attr.
|
|
213
|
+
*/
|
|
214
|
+
export interface TextButtonProps extends Omit<
|
|
215
|
+
ButtonProps,
|
|
216
|
+
"scale" | "priority" | "fill" | "size"
|
|
217
|
+
> {
|
|
218
|
+
/**
|
|
219
|
+
* @required
|
|
220
|
+
* "secondary" | "tertiary"
|
|
221
|
+
*/
|
|
222
|
+
priority: TextButtonPriority;
|
|
223
|
+
/**
|
|
224
|
+
* Text template은 table size를 지원하지 않는다.
|
|
225
|
+
* @required
|
|
226
|
+
* "small" | "medium" | "large"
|
|
227
|
+
*/
|
|
228
|
+
size: TextButtonSize;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Button Types; Rounded preset size 옵션
|
|
233
|
+
* @typedef {"small" | "medium" | "large"} RoundButtonSize
|
|
234
|
+
* @desc
|
|
235
|
+
* - `small`
|
|
236
|
+
* - `medium`
|
|
237
|
+
* - `large`
|
|
238
|
+
*/
|
|
239
|
+
export type RoundButtonSize = "small" | "medium" | "large";
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Button Types; Rounded preset props
|
|
243
|
+
* @property {ElementType} [as] 렌더링할 요소. 기본값은 button.
|
|
244
|
+
* @property {ReactNode} [children] 주 내용. 문자열이면 자동으로 .button-label로 감싼다.
|
|
245
|
+
* @property {"small" | "medium" | "large"} size 높이/타이포 스케일. 템플릿에서 fill/size를 자동 매핑한다.
|
|
246
|
+
* @property {"primary" | "secondary" | "tertiary"} priority semantic color priority.
|
|
247
|
+
* @property {"default" | "readonly" | "disabled"} [state] UI 상태. disabled prop과 조합된다.
|
|
248
|
+
* @property {boolean} [block] width:100% 확장 여부.
|
|
249
|
+
* @property {boolean} [loading] true면 readonly 처리 + aria-busy.
|
|
250
|
+
* @property {ReactNode} [left] 라벨 왼쪽 커스텀 슬롯.
|
|
251
|
+
* @property {ReactNode} [right] 라벨 오른쪽 커스텀 슬롯.
|
|
252
|
+
* @property {"hover" | "pressed"} ["data-user-action"] Interaction 상태 강제 표현용 data attr.
|
|
253
|
+
*/
|
|
254
|
+
export interface RoundButtonProps extends Omit<
|
|
255
|
+
ButtonProps,
|
|
256
|
+
"scale" | "size" | "fill"
|
|
257
|
+
> {
|
|
258
|
+
/**
|
|
259
|
+
* 라운드 버튼 전용 size 축.
|
|
260
|
+
* "small" | "medium" | "large"
|
|
261
|
+
* 템플릿에서 fill/size를 자동 매핑한다.
|
|
262
|
+
*/
|
|
263
|
+
size: RoundButtonSize;
|
|
264
|
+
}
|
|
@@ -19,6 +19,7 @@ const isChipInputProps = (props: ChipProps): props is ChipInputProps =>
|
|
|
19
19
|
* @param {"filter" | "filter-rounded" | "assist" | "input"} [props.kind="filter"] chip kind.
|
|
20
20
|
* @param {boolean} [props.selected] 선택 상태. filter 계열에서 aria-pressed로 노출.
|
|
21
21
|
* @param {React.ReactNode} [props.leading] assist kind 전용 leading slot.
|
|
22
|
+
* @param {"default" | "table"} [props.size="default"] chip size 축. table은 셀 콘텐츠용 compact 규격이다.
|
|
22
23
|
* @param {string} [props.removeButtonLabel="선택 항목 삭제"] input kind 제거 버튼 라벨.
|
|
23
24
|
* @param {(event: MouseEvent<HTMLButtonElement>) => void} [props.onRemove] input kind 제거 핸들러.
|
|
24
25
|
* @param {string} [props.className] root className.
|
|
@@ -33,6 +34,7 @@ const Chip = forwardRef<HTMLElement, ChipProps>((props, ref) => {
|
|
|
33
34
|
onRemove,
|
|
34
35
|
selected,
|
|
35
36
|
leading,
|
|
37
|
+
size = "default",
|
|
36
38
|
...restProps
|
|
37
39
|
} = props;
|
|
38
40
|
const hasLeading = Boolean(leading);
|
|
@@ -51,6 +53,7 @@ const Chip = forwardRef<HTMLElement, ChipProps>((props, ref) => {
|
|
|
51
53
|
className={combinedClassName}
|
|
52
54
|
data-kind="input"
|
|
53
55
|
data-removable="true"
|
|
56
|
+
data-size={size}
|
|
54
57
|
data-selected={selected ? "true" : undefined}
|
|
55
58
|
data-has-leading={hasLeading ? "true" : undefined}
|
|
56
59
|
>
|
|
@@ -67,8 +70,16 @@ const Chip = forwardRef<HTMLElement, ChipProps>((props, ref) => {
|
|
|
67
70
|
);
|
|
68
71
|
}
|
|
69
72
|
|
|
70
|
-
const {
|
|
71
|
-
|
|
73
|
+
const {
|
|
74
|
+
children,
|
|
75
|
+
selected,
|
|
76
|
+
leading,
|
|
77
|
+
className,
|
|
78
|
+
kind,
|
|
79
|
+
size = "default",
|
|
80
|
+
type,
|
|
81
|
+
...restProps
|
|
82
|
+
} = props;
|
|
72
83
|
const resolvedKind = kind ?? "filter";
|
|
73
84
|
const isAssist = resolvedKind === "assist";
|
|
74
85
|
const hasLeading = isAssist && Boolean(leading);
|
|
@@ -87,6 +98,7 @@ const Chip = forwardRef<HTMLElement, ChipProps>((props, ref) => {
|
|
|
87
98
|
type={type ?? "button"}
|
|
88
99
|
className={combinedClassName}
|
|
89
100
|
data-kind={resolvedKind}
|
|
101
|
+
data-size={size}
|
|
90
102
|
data-selected={selected ? "true" : undefined}
|
|
91
103
|
data-has-leading={hasLeading ? "true" : undefined}
|
|
92
104
|
aria-pressed={
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
.chip {
|
|
2
|
+
--chip-gap: var(--theme-chip-gap);
|
|
3
|
+
--chip-bg: transparent;
|
|
4
|
+
--chip-border-color: transparent;
|
|
5
|
+
--chip-label-color: var(--color-label-standard, #3d3f43);
|
|
6
|
+
display: flex;
|
|
7
|
+
align-items: center;
|
|
8
|
+
justify-content: center;
|
|
9
|
+
gap: var(--chip-gap);
|
|
10
|
+
height: var(--theme-chip-height);
|
|
11
|
+
padding-left: var(--theme-chip-padding-horizontal);
|
|
12
|
+
padding-right: var(--theme-chip-padding-horizontal);
|
|
13
|
+
padding-block: 0;
|
|
14
|
+
border-radius: var(--theme-chip-radius);
|
|
15
|
+
border: 1px solid var(--chip-border-color);
|
|
16
|
+
background-color: var(--chip-bg);
|
|
17
|
+
color: var(--chip-label-color);
|
|
18
|
+
font-family: var(--theme-chip-font-family);
|
|
19
|
+
font-size: var(--theme-chip-font-size);
|
|
20
|
+
font-weight: var(--theme-chip-font-weight);
|
|
21
|
+
line-height: 1;
|
|
22
|
+
letter-spacing: 0.2px;
|
|
23
|
+
box-sizing: border-box;
|
|
24
|
+
margin: 0;
|
|
25
|
+
width: fit-content;
|
|
26
|
+
transition:
|
|
27
|
+
background-color 0.16s ease,
|
|
28
|
+
color 0.16s ease,
|
|
29
|
+
border-color 0.16s ease,
|
|
30
|
+
opacity 0.16s ease;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
button.chip {
|
|
34
|
+
appearance: none;
|
|
35
|
+
cursor: pointer;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
figure.chip {
|
|
39
|
+
margin: 0;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.chip:focus-visible {
|
|
43
|
+
outline: 2px solid var(--color-primary-default, #1a6aff);
|
|
44
|
+
outline-offset: 2px;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.chip:where([data-kind="filter"]),
|
|
48
|
+
.chip:where([data-kind="filter-rounded"]) {
|
|
49
|
+
--chip-bg: var(--color-surface-standard, #f2f2f3);
|
|
50
|
+
--chip-label-color: var(--color-label-neutral, #797e86);
|
|
51
|
+
--chip-border-color: transparent;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.chip:where([data-kind="filter"][data-selected="true"]),
|
|
55
|
+
.chip:where([data-kind="filter-rounded"][data-selected="true"]) {
|
|
56
|
+
--chip-bg: var(--color-surface-heavy, #313235);
|
|
57
|
+
--chip-label-color: var(--color-common-100, #ffffff);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.chip:where([data-kind="filter-rounded"]) {
|
|
61
|
+
border-radius: var(--theme-chip-rounded-radius);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.chip:where([data-kind="assist"]) {
|
|
65
|
+
--chip-bg: var(--color-surface-static-neutral, #f2f2f2);
|
|
66
|
+
--chip-label-color: var(--color-label-strong, #18191b);
|
|
67
|
+
--chip-gap: var(--spacing-gap-2, 8px);
|
|
68
|
+
--chip-border-color: transparent;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.chip:where([data-kind="assist"][data-selected="true"]) {
|
|
72
|
+
--chip-label-color: var(--color-primary-default, #1a6aff);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.chip:where([data-kind="input"]) {
|
|
76
|
+
--chip-gap: var(--spacing-gap-1, 4px);
|
|
77
|
+
--chip-bg: var(--color-common-100, #ffffff);
|
|
78
|
+
--chip-label-color: var(--color-label-standard, #3d3f43);
|
|
79
|
+
--chip-border-color: var(--color-border-assistive, #e4e5e7);
|
|
80
|
+
padding-left: var(--spacing-padding-4, 8px);
|
|
81
|
+
padding-right: var(--spacing-padding-2, 4px);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.chip:where([data-size="table"]) {
|
|
85
|
+
height: var(--theme-chip-height-table);
|
|
86
|
+
padding-left: var(--theme-chip-padding-horizontal-table);
|
|
87
|
+
padding-right: var(--theme-chip-padding-horizontal-table);
|
|
88
|
+
padding-block: var(--theme-chip-padding-block-table);
|
|
89
|
+
--chip-gap: var(--theme-chip-gap-table);
|
|
90
|
+
font-size: var(--theme-chip-font-size-table);
|
|
91
|
+
font-weight: var(--theme-chip-font-weight-table);
|
|
92
|
+
line-height: var(--theme-chip-line-height-table);
|
|
93
|
+
border-radius: var(--theme-chip-radius-table);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.chip:where([data-size="table"]) .chip-label {
|
|
97
|
+
line-height: var(--theme-chip-line-height-table);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.chip:where([data-size="table"][data-kind="input"]) {
|
|
101
|
+
padding-left: var(--theme-chip-padding-left-table);
|
|
102
|
+
padding-right: var(--theme-chip-padding-right-table);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.chip:where([data-size="table"][data-kind="input"]) .chip-remove-button {
|
|
106
|
+
width: fit-content;
|
|
107
|
+
height: fit-content;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.chip:where([data-kind="input"][data-selected="true"]) {
|
|
111
|
+
--chip-border-color: var(--color-border-standard-blue, #ccdeff);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.chip-leading {
|
|
115
|
+
display: flex;
|
|
116
|
+
align-items: center;
|
|
117
|
+
justify-content: center;
|
|
118
|
+
color: inherit;
|
|
119
|
+
flex-shrink: 0;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.chip-label {
|
|
123
|
+
display: flex;
|
|
124
|
+
align-items: center;
|
|
125
|
+
gap: var(--spacing-gap-1, 4px);
|
|
126
|
+
color: inherit;
|
|
127
|
+
line-height: 1;
|
|
128
|
+
white-space: nowrap;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.chip-remove-button {
|
|
132
|
+
display: flex;
|
|
133
|
+
align-items: center;
|
|
134
|
+
justify-content: center;
|
|
135
|
+
width: fit-content;
|
|
136
|
+
height: fit-content;
|
|
137
|
+
border: none;
|
|
138
|
+
background: transparent;
|
|
139
|
+
color: var(--color-label-neutral, #797e86);
|
|
140
|
+
cursor: pointer;
|
|
141
|
+
padding: 0;
|
|
142
|
+
transition: color 0.16s ease;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.chip-remove-button:hover,
|
|
146
|
+
.chip-remove-button:focus-visible {
|
|
147
|
+
color: var(--color-label-strong, #18191b);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.chip-remove-button svg {
|
|
151
|
+
display: block;
|
|
152
|
+
width: 100%;
|
|
153
|
+
height: 100%;
|
|
154
|
+
}
|
|
@@ -1,138 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
--theme-chip-height: var(--theme-size-small-3, 32px);
|
|
4
|
-
--theme-chip-padding-inline: var(--spacing-padding-5, 12px);
|
|
5
|
-
--theme-chip-radius: var(--theme-radius-medium-3, 8px);
|
|
6
|
-
--theme-chip-rounded-radius: var(--theme-radius-large-2, 16px);
|
|
7
|
-
--theme-chip-font-family: var(--font-body-medium-family, "Pretendard");
|
|
8
|
-
--theme-chip-font-size: var(--font-body-xsmall-size, 13px);
|
|
9
|
-
--theme-chip-font-weight: var(--font-body-xsmall-weight, 400);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
.chip {
|
|
13
|
-
--chip-gap: var(--spacing-gap-1, 4px);
|
|
14
|
-
--chip-bg: transparent;
|
|
15
|
-
--chip-border-color: transparent;
|
|
16
|
-
--chip-label-color: var(--color-label-standard, #3d3f43);
|
|
17
|
-
display: flex;
|
|
18
|
-
align-items: center;
|
|
19
|
-
justify-content: center;
|
|
20
|
-
gap: var(--chip-gap);
|
|
21
|
-
height: var(--theme-chip-height);
|
|
22
|
-
padding-inline: var(--theme-chip-padding-inline);
|
|
23
|
-
padding-block: 0;
|
|
24
|
-
border-radius: var(--theme-chip-radius);
|
|
25
|
-
border: 1px solid var(--chip-border-color);
|
|
26
|
-
background-color: var(--chip-bg);
|
|
27
|
-
color: var(--chip-label-color);
|
|
28
|
-
font-family: var(--theme-chip-font-family);
|
|
29
|
-
font-size: var(--theme-chip-font-size);
|
|
30
|
-
font-weight: var(--theme-chip-font-weight);
|
|
31
|
-
line-height: 1;
|
|
32
|
-
letter-spacing: 0.2px;
|
|
33
|
-
box-sizing: border-box;
|
|
34
|
-
margin: 0;
|
|
35
|
-
width: fit-content;
|
|
36
|
-
transition:
|
|
37
|
-
background-color 0.16s ease,
|
|
38
|
-
color 0.16s ease,
|
|
39
|
-
border-color 0.16s ease,
|
|
40
|
-
opacity 0.16s ease;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
button.chip {
|
|
44
|
-
appearance: none;
|
|
45
|
-
cursor: pointer;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
figure.chip {
|
|
49
|
-
margin: 0;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
.chip:focus-visible {
|
|
53
|
-
outline: 2px solid var(--color-primary-default, #1a6aff);
|
|
54
|
-
outline-offset: 2px;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
.chip:where([data-kind="filter"]),
|
|
58
|
-
.chip:where([data-kind="filter-rounded"]) {
|
|
59
|
-
--chip-bg: var(--color-surface-standard, #f2f2f3);
|
|
60
|
-
--chip-label-color: var(--color-label-neutral, #797e86);
|
|
61
|
-
--chip-border-color: transparent;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
.chip:where([data-kind="filter"][data-selected="true"]),
|
|
65
|
-
.chip:where([data-kind="filter-rounded"][data-selected="true"]) {
|
|
66
|
-
--chip-bg: var(--color-surface-heavy, #313235);
|
|
67
|
-
--chip-label-color: var(--color-common-100, #ffffff);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
.chip:where([data-kind="filter-rounded"]) {
|
|
71
|
-
border-radius: var(--theme-chip-rounded-radius);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
.chip:where([data-kind="assist"]) {
|
|
75
|
-
--chip-bg: var(--color-surface-static-neutral, #f2f2f2);
|
|
76
|
-
--chip-label-color: var(--color-label-strong, #18191b);
|
|
77
|
-
--chip-gap: var(--spacing-gap-2, 8px);
|
|
78
|
-
--chip-border-color: transparent;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
.chip:where([data-kind="assist"][data-selected="true"]) {
|
|
82
|
-
--chip-label-color: var(--color-primary-default, #1a6aff);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
.chip:where([data-kind="input"]) {
|
|
86
|
-
--chip-gap: var(--spacing-gap-1, 4px);
|
|
87
|
-
--chip-bg: var(--color-common-100, #ffffff);
|
|
88
|
-
--chip-label-color: var(--color-label-standard, #3d3f43);
|
|
89
|
-
--chip-border-color: var(--color-border-assistive, #e4e5e7);
|
|
90
|
-
padding-inline-start: var(--spacing-padding-4, 8px);
|
|
91
|
-
padding-inline-end: var(--spacing-padding-2, 4px);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
.chip:where([data-kind="input"][data-selected="true"]) {
|
|
95
|
-
--chip-border-color: var(--color-border-standard-blue, #ccdeff);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
.chip-leading {
|
|
99
|
-
display: inline-flex;
|
|
100
|
-
align-items: center;
|
|
101
|
-
justify-content: center;
|
|
102
|
-
color: inherit;
|
|
103
|
-
flex-shrink: 0;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
.chip-label {
|
|
107
|
-
display: inline-flex;
|
|
108
|
-
align-items: center;
|
|
109
|
-
gap: var(--spacing-gap-1, 4px);
|
|
110
|
-
color: inherit;
|
|
111
|
-
line-height: 1;
|
|
112
|
-
white-space: nowrap;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
.chip-remove-button {
|
|
116
|
-
display: inline-flex;
|
|
117
|
-
align-items: center;
|
|
118
|
-
justify-content: center;
|
|
119
|
-
width: 16px;
|
|
120
|
-
height: 16px;
|
|
121
|
-
border: none;
|
|
122
|
-
background: transparent;
|
|
123
|
-
color: var(--color-label-neutral, #797e86);
|
|
124
|
-
cursor: pointer;
|
|
125
|
-
padding: 0;
|
|
126
|
-
transition: color 0.16s ease;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
.chip-remove-button:hover,
|
|
130
|
-
.chip-remove-button:focus-visible {
|
|
131
|
-
color: var(--color-label-strong, #18191b);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
.chip-remove-button svg {
|
|
135
|
-
display: block;
|
|
136
|
-
width: 100%;
|
|
137
|
-
height: 100%;
|
|
138
|
-
}
|
|
1
|
+
@use "./variables.scss";
|
|
2
|
+
@use "./chip.scss";
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/* Chip 기본 토큰 래핑 */
|
|
2
|
+
:root {
|
|
3
|
+
--theme-chip-height: var(--theme-size-small-3, 32px);
|
|
4
|
+
--theme-chip-padding-horizontal: var(--spacing-padding-5, 12px);
|
|
5
|
+
--theme-chip-gap: var(--spacing-gap-1, 4px);
|
|
6
|
+
/* 변경: table chip 수치를 Figma table 액션 셀 기준으로 보정한다. */
|
|
7
|
+
/* 변경: table chip 높이는 button table과 동일하게 24px로 맞춘다. */
|
|
8
|
+
--theme-chip-height-table: 24px;
|
|
9
|
+
--theme-chip-padding-horizontal-table: var(--spacing-padding-5, 12px);
|
|
10
|
+
--theme-chip-padding-left-table: var(--spacing-padding-5, 12px);
|
|
11
|
+
--theme-chip-padding-right-table: var(--spacing-padding-4, 8px);
|
|
12
|
+
/* 변경: table chip은 높이 24px 고정을 위해 수직 padding을 사용하지 않는다. */
|
|
13
|
+
--theme-chip-padding-block-table: 0px;
|
|
14
|
+
--theme-chip-gap-table: var(--spacing-gap-1, 4px);
|
|
15
|
+
--theme-chip-font-size-table: var(--font-caption-large-size, 12px);
|
|
16
|
+
/* 변경: table chip 텍스트는 font-weight를 400으로 고정한다. */
|
|
17
|
+
--theme-chip-font-weight-table: 400;
|
|
18
|
+
--theme-chip-line-height-table: var(--font-caption-large-line-height, 1.5);
|
|
19
|
+
/* 변경: Figma 705:13702 기준으로 table chip radius를 6px로 고정한다. */
|
|
20
|
+
--theme-chip-radius-table: var(--theme-radius-medium-2, 6px);
|
|
21
|
+
--theme-chip-radius: var(--theme-radius-medium-3, 8px);
|
|
22
|
+
--theme-chip-rounded-radius: var(--theme-radius-large-2, 16px);
|
|
23
|
+
--theme-chip-font-family: var(--font-body-medium-family, "Pretendard");
|
|
24
|
+
--theme-chip-font-size: var(--font-body-xsmall-size, 13px);
|
|
25
|
+
--theme-chip-font-weight: var(--font-body-xsmall-weight, 400);
|
|
26
|
+
}
|
|
@@ -1,52 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export const CHIP_KINDS = [
|
|
4
|
-
"filter",
|
|
5
|
-
"filter-rounded",
|
|
6
|
-
"assist",
|
|
7
|
-
"input",
|
|
8
|
-
] as const;
|
|
9
|
-
|
|
10
|
-
export type ChipKind = (typeof CHIP_KINDS)[number];
|
|
11
|
-
export type InteractiveChipKind = Exclude<ChipKind, "input">;
|
|
12
|
-
|
|
13
|
-
type ButtonProps = Omit<ComponentPropsWithoutRef<"button">, "children">;
|
|
14
|
-
type FigureProps = Omit<ComponentPropsWithoutRef<"figure">, "children">;
|
|
15
|
-
|
|
16
|
-
export interface ChipCommonProps {
|
|
17
|
-
/**
|
|
18
|
-
* 표시할 라벨.
|
|
19
|
-
*/
|
|
20
|
-
children?: ReactNode;
|
|
21
|
-
/**
|
|
22
|
-
* 선택 상태. filter 계열에서 외부 상태로 제어한다.
|
|
23
|
-
*/
|
|
24
|
-
selected?: boolean;
|
|
25
|
-
/**
|
|
26
|
-
* assist kind 전용 leading slot.
|
|
27
|
-
*/
|
|
28
|
-
leading?: ReactNode;
|
|
29
|
-
/**
|
|
30
|
-
* input kind 전용 제거 버튼 aria-label.
|
|
31
|
-
*/
|
|
32
|
-
removeButtonLabel?: string;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export interface ChipInteractiveProps extends ChipCommonProps, ButtonProps {
|
|
36
|
-
kind?: InteractiveChipKind;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface ChipInputProps extends ChipCommonProps, FigureProps {
|
|
40
|
-
kind: "input";
|
|
41
|
-
onRemove?: (event: MouseEvent<HTMLButtonElement>) => void;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export type ChipProps = ChipInteractiveProps | ChipInputProps;
|
|
45
|
-
|
|
46
|
-
export interface ChipClassNameOptions {
|
|
47
|
-
kind: ChipKind;
|
|
48
|
-
selected?: boolean;
|
|
49
|
-
hasLeading?: boolean;
|
|
50
|
-
removable?: boolean;
|
|
51
|
-
className?: string;
|
|
52
|
-
}
|
|
1
|
+
export * from "./options";
|
|
2
|
+
export type * from "./props";
|