@idbrnd/design-system 1.2.2 → 1.3.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @idbrnd/design-system
2
2
 
3
- ![Version](https://img.shields.io/badge/version-1.2.1-4B5FE1?style=flat-square)
3
+ ![Version](https://img.shields.io/badge/version-1.3.1-4B5FE1?style=flat-square)
4
4
  ![React](https://img.shields.io/badge/React-18.3.1-61DAFB?style=flat-square&logo=react&logoColor=white)
5
5
  ![Vite](https://img.shields.io/badge/Vite-7.3.0-646CFF?style=flat-square&logo=vite&logoColor=white)
6
6
  ![TypeScript](https://img.shields.io/badge/TypeScript-5.9.3-3178C6?style=flat-square&logo=typescript&logoColor=white)
@@ -55,6 +55,8 @@ export default function App() {
55
55
  - [Toast](#toast)
56
56
  - [Snackbar](#snackbar)
57
57
  - [PushBadge](#pushbadge)
58
+ - [Select](#select)
59
+ - [Dropdown](#dropdown)
58
60
  - [Content](#content)
59
61
  - [ContentBadge](#contentbadge)
60
62
  - [StateBadge](#statebadge)
@@ -75,14 +77,14 @@ import { FillButton } from "@idbrnd/design-system";
75
77
  <FillButton variant="primary" disabled>비활성화</FillButton>
76
78
  ```
77
79
 
78
- | Prop | 타입 | 기본값 | 설명 |
79
- |------|------|--------|------|
80
- | `variant` | `"primary"` \| `"assistive"` \| `"error"` | `"primary"` | 색상 변형 |
81
- | `size` | `"xsmall"` \| `"small"` \| `"medium"` \| `"large"` | `"medium"` | 크기 |
82
- | `widthType` | `"hug"` \| `"fixed"` \| `"fill"` | `"hug"` | 너비 방식 |
83
- | `loading` | `boolean` | `false` | 로딩 스피너 표시 |
84
- | `disabled` | `boolean` | `false` | 비활성화 |
85
- | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
80
+ | Prop | 타입 | 기본값 | 설명 |
81
+ | ------------- | -------------------------------------------------- | ----------- | ------------------ |
82
+ | `variant` | `"primary"` \| `"assistive"` \| `"error"` | `"primary"` | 색상 변형 |
83
+ | `size` | `"xsmall"` \| `"small"` \| `"medium"` \| `"large"` | `"medium"` | 크기 |
84
+ | `widthType` | `"hug"` \| `"fixed"` \| `"fill"` | `"hug"` | 너비 방식 |
85
+ | `loading` | `boolean` | `false` | 로딩 스피너 표시 |
86
+ | `disabled` | `boolean` | `false` | 비활성화 |
87
+ | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
86
88
 
87
89
  ### OutlineButton
88
90
 
@@ -94,14 +96,14 @@ import { OutlineButton } from "@idbrnd/design-system";
94
96
  <OutlineButton variant="assistive" loading>처리 중</OutlineButton>
95
97
  ```
96
98
 
97
- | Prop | 타입 | 기본값 | 설명 |
98
- |------|------|--------|------|
99
- | `variant` | `"primary"` \| `"secondary"` \| `"assistive"` \| `"error"` | `"assistive"` | 색상 변형 |
100
- | `size` | `"xsmall"` \| `"small"` \| `"medium"` \| `"large"` | `"medium"` | 크기 |
101
- | `widthType` | `"hug"` \| `"fixed"` \| `"fill"` | `"hug"` | 너비 방식 |
102
- | `loading` | `boolean` | `false` | 로딩 스피너 표시 |
103
- | `disabled` | `boolean` | `false` | 비활성화 |
104
- | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
99
+ | Prop | 타입 | 기본값 | 설명 |
100
+ | ------------- | ---------------------------------------------------------- | ------------- | ------------------ |
101
+ | `variant` | `"primary"` \| `"secondary"` \| `"assistive"` \| `"error"` | `"assistive"` | 색상 변형 |
102
+ | `size` | `"xsmall"` \| `"small"` \| `"medium"` \| `"large"` | `"medium"` | 크기 |
103
+ | `widthType` | `"hug"` \| `"fixed"` \| `"fill"` | `"hug"` | 너비 방식 |
104
+ | `loading` | `boolean` | `false` | 로딩 스피너 표시 |
105
+ | `disabled` | `boolean` | `false` | 비활성화 |
106
+ | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
105
107
 
106
108
  ### TextButton
107
109
 
@@ -112,14 +114,14 @@ import { TextButton } from "@idbrnd/design-system";
112
114
  <TextButton variant="assistive" size="small">작은 버튼</TextButton>
113
115
  ```
114
116
 
115
- | Prop | 타입 | 기본값 | 설명 |
116
- |------|------|--------|------|
117
- | `variant` | `"primary"` \| `"assistive"` \| `"error"` | `"assistive"` | 색상 변형 |
118
- | `size` | `"small"` \| `"medium"` \| `"large"` | `"medium"` | 크기 |
119
- | `widthType` | `"hug"` \| `"fixed"` \| `"fill"` | `"hug"` | 너비 방식 |
120
- | `loading` | `boolean` | `false` | 로딩 스피너 표시 |
121
- | `disabled` | `boolean` | `false` | 비활성화 |
122
- | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
117
+ | Prop | 타입 | 기본값 | 설명 |
118
+ | ------------- | ----------------------------------------- | ------------- | ------------------ |
119
+ | `variant` | `"primary"` \| `"assistive"` \| `"error"` | `"assistive"` | 색상 변형 |
120
+ | `size` | `"small"` \| `"medium"` \| `"large"` | `"medium"` | 크기 |
121
+ | `widthType` | `"hug"` \| `"fixed"` \| `"fill"` | `"hug"` | 너비 방식 |
122
+ | `loading` | `boolean` | `false` | 로딩 스피너 표시 |
123
+ | `disabled` | `boolean` | `false` | 비활성화 |
124
+ | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
123
125
 
124
126
  ### WeakButton
125
127
 
@@ -130,14 +132,14 @@ import { WeakButton } from "@idbrnd/design-system";
130
132
  <WeakButton variant="error" size="xsmall">삭제</WeakButton>
131
133
  ```
132
134
 
133
- | Prop | 타입 | 기본값 | 설명 |
134
- |------|------|--------|------|
135
- | `variant` | `"primary"` \| `"assistive"` \| `"error"` | `"assistive"` | 색상 변형 |
136
- | `size` | `"xsmall"` \| `"small"` \| `"medium"` \| `"large"` | `"medium"` | 크기 |
137
- | `widthType` | `"hug"` \| `"fixed"` \| `"fill"` | `"hug"` | 너비 방식 |
138
- | `loading` | `boolean` | `false` | 로딩 스피너 표시 |
139
- | `disabled` | `boolean` | `false` | 비활성화 |
140
- | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
135
+ | Prop | 타입 | 기본값 | 설명 |
136
+ | ------------- | -------------------------------------------------- | ------------- | ------------------ |
137
+ | `variant` | `"primary"` \| `"assistive"` \| `"error"` | `"assistive"` | 색상 변형 |
138
+ | `size` | `"xsmall"` \| `"small"` \| `"medium"` \| `"large"` | `"medium"` | 크기 |
139
+ | `widthType` | `"hug"` \| `"fixed"` \| `"fill"` | `"hug"` | 너비 방식 |
140
+ | `loading` | `boolean` | `false` | 로딩 스피너 표시 |
141
+ | `disabled` | `boolean` | `false` | 비활성화 |
142
+ | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
141
143
 
142
144
  ---
143
145
 
@@ -152,11 +154,11 @@ import { BasicIconButton } from "@idbrnd/design-system";
152
154
  <BasicIconButton size="small" disabled><SearchIcon /></BasicIconButton>
153
155
  ```
154
156
 
155
- | Prop | 타입 | 기본값 | 설명 |
156
- |------|------|--------|------|
157
- | `size` | `"small"` \| `"medium"` \| `"large"` | `"medium"` | 크기 |
158
- | `disabled` | `boolean` | `false` | 비활성화 |
159
- | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
157
+ | Prop | 타입 | 기본값 | 설명 |
158
+ | ------------- | ------------------------------------ | ---------- | ------------------ |
159
+ | `size` | `"small"` \| `"medium"` \| `"large"` | `"medium"` | 크기 |
160
+ | `disabled` | `boolean` | `false` | 비활성화 |
161
+ | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
160
162
 
161
163
  ### FillIconButton
162
164
 
@@ -167,12 +169,12 @@ import { FillIconButton } from "@idbrnd/design-system";
167
169
  <FillIconButton variant="overlay"><PlusIcon /></FillIconButton>
168
170
  ```
169
171
 
170
- | Prop | 타입 | 기본값 | 설명 |
171
- |------|------|--------|------|
172
- | `variant` | `"primary"` \| `"basic"` \| `"overlay"` | `"primary"` | 색상 변형 |
173
- | `size` | `"small"` \| `"medium"` \| `"large"` | `"medium"` | 크기 |
174
- | `disabled` | `boolean` | `false` | 비활성화 |
175
- | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
172
+ | Prop | 타입 | 기본값 | 설명 |
173
+ | ------------- | --------------------------------------- | ----------- | ------------------ |
174
+ | `variant` | `"primary"` \| `"basic"` \| `"overlay"` | `"primary"` | 색상 변형 |
175
+ | `size` | `"small"` \| `"medium"` \| `"large"` | `"medium"` | 크기 |
176
+ | `disabled` | `boolean` | `false` | 비활성화 |
177
+ | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
176
178
 
177
179
  ### OutlineIconButton
178
180
 
@@ -183,11 +185,11 @@ import { OutlineIconButton } from "@idbrnd/design-system";
183
185
  <OutlineIconButton disabled><EditIcon /></OutlineIconButton>
184
186
  ```
185
187
 
186
- | Prop | 타입 | 기본값 | 설명 |
187
- |------|------|--------|------|
188
- | `size` | `"small"` \| `"medium"` \| `"large"` | `"medium"` | 크기 |
189
- | `disabled` | `boolean` | `false` | 비활성화 |
190
- | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
188
+ | Prop | 타입 | 기본값 | 설명 |
189
+ | ------------- | ------------------------------------ | ---------- | ------------------ |
190
+ | `size` | `"small"` \| `"medium"` \| `"large"` | `"medium"` | 크기 |
191
+ | `disabled` | `boolean` | `false` | 비활성화 |
192
+ | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
191
193
 
192
194
  ---
193
195
 
@@ -216,23 +218,24 @@ function InputExample() {
216
218
  }
217
219
  ```
218
220
 
219
- | Prop | 타입 | 기본값 | 설명 |
220
- |------|------|--------|------|
221
- | `value` | `string` | — | **(필수)** 입력값 |
222
- | `onChange` | `ChangeEventHandler` | — | **(필수)** 변경 핸들러 |
223
- | `variant` | `"error"` \| `"success"` | — | 수동 상태 지정. 미지정 시 값 유무로 자동 결정 |
224
- | `headingContent` | `ReactNode` | — | 상단 라벨 |
225
- | `description` | `ReactNode \| boolean` | — | 하단 설명 텍스트 |
226
- | `errorMessage` | `ReactNode` | — | `variant="error"` 시 표시되는 메시지 |
227
- | `leadingIcon` | `ReactNode` | — | 입력 필드 좌측 아이콘 |
228
- | `trailingContent` | `ReactNode` | — | 입력 필드 우측 콘텐츠 |
229
- | `size` | `"small"` \| `"medium"` \| `"large"` | `"medium"` | 크기 |
230
- | `width` | `number \| string` | `"100%"` | 너비 |
231
- | `readOnly` | `boolean` | `false` | 읽기 전용 |
232
- | `disabled` | `boolean` | `false` | 비활성화 |
233
- | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
221
+ | Prop | 타입 | 기본값 | 설명 |
222
+ | ----------------- | ------------------------------------ | ---------- | --------------------------------------------- |
223
+ | `value` | `string` | — | **(필수)** 입력값 |
224
+ | `onChange` | `ChangeEventHandler` | — | **(필수)** 변경 핸들러 |
225
+ | `variant` | `"error"` \| `"success"` | — | 수동 상태 지정. 미지정 시 값 유무로 자동 결정 |
226
+ | `headingContent` | `ReactNode` | — | 상단 라벨 |
227
+ | `description` | `ReactNode \| boolean` | — | 하단 설명 텍스트 |
228
+ | `errorMessage` | `ReactNode` | — | `variant="error"` 시 표시되는 메시지 |
229
+ | `leadingIcon` | `ReactNode` | — | 입력 필드 좌측 아이콘 |
230
+ | `trailingContent` | `ReactNode` | — | 입력 필드 우측 콘텐츠 |
231
+ | `size` | `"small"` \| `"medium"` \| `"large"` | `"medium"` | 크기 |
232
+ | `width` | `number \| string` | `"100%"` | 너비 |
233
+ | `readOnly` | `boolean` | `false` | 읽기 전용 |
234
+ | `disabled` | `boolean` | `false` | 비활성화 |
235
+ | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
234
236
 
235
237
  유의사항:
238
+
236
239
  - `variant`를 지정하지 않으면 값 존재 여부에 따라 내부적으로 `basic` 또는 `typed` 상태가 자동 적용됩니다.
237
240
  - `variant="error"`일 때는 `description` 대신 `errorMessage`가 우선 노출됩니다.
238
241
  - `defaultValue`는 지원하지 않습니다. 반드시 `value` + `onChange`로 제어해야 합니다.
@@ -259,16 +262,16 @@ function SearchExample() {
259
262
  }
260
263
  ```
261
264
 
262
- | Prop | 타입 | 기본값 | 설명 |
263
- |------|------|--------|------|
264
- | `value` | `string` | — | **(필수)** 입력값 |
265
- | `onChange` | `ChangeEventHandler` | — | **(필수)** 변경 핸들러 |
266
- | `onSearch` | `(value: string) => void` | — | 검색 실행 콜백 |
267
- | `onClear` | `() => void` | — | 삭제 버튼 클릭 콜백. 미지정 시 기본 동작 사용 |
268
- | `size` | `"small"` \| `"medium"` \| `"large"` | `"medium"` | 크기 |
269
- | `variant` | `"default"` \| `"onTyping"` \| `"typed"` \| `"error"` | — | 상태 변형 |
270
- | `disabled` | `boolean` | `false` | 비활성화 |
271
- | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
265
+ | Prop | 타입 | 기본값 | 설명 |
266
+ | ------------- | ----------------------------------------------------- | ---------- | --------------------------------------------- |
267
+ | `value` | `string` | — | **(필수)** 입력값 |
268
+ | `onChange` | `ChangeEventHandler` | — | **(필수)** 변경 핸들러 |
269
+ | `onSearch` | `(value: string) => void` | — | 검색 실행 콜백 |
270
+ | `onClear` | `() => void` | — | 삭제 버튼 클릭 콜백. 미지정 시 기본 동작 사용 |
271
+ | `size` | `"small"` \| `"medium"` \| `"large"` | `"medium"` | 크기 |
272
+ | `variant` | `"default"` \| `"onTyping"` \| `"typed"` \| `"error"` | — | 상태 변형 |
273
+ | `disabled` | `boolean` | `false` | 비활성화 |
274
+ | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
272
275
 
273
276
  ---
274
277
 
@@ -293,7 +296,13 @@ function CheckBoxExample() {
293
296
  <CheckBox checked={true} indeterminate onChange={() => {}} />
294
297
 
295
298
  {/* variant / size / density */}
296
- <CheckBox variant="assistive" size="small" density="compact" checked onChange={() => {}} />
299
+ <CheckBox
300
+ variant="assistive"
301
+ size="small"
302
+ density="compact"
303
+ checked
304
+ onChange={() => {}}
305
+ />
297
306
 
298
307
  <CheckBox checked disabled />
299
308
  </>
@@ -301,18 +310,19 @@ function CheckBoxExample() {
301
310
  }
302
311
  ```
303
312
 
304
- | Prop | 타입 | 기본값 | 설명 |
305
- |------|------|--------|------|
306
- | `checked` | `boolean` | — | **(필수)** 체크 상태 |
307
- | `onChange` | `(checked: boolean) => void` | — | 변경 핸들러 |
308
- | `indeterminate` | `boolean` | `false` | `checked={true}`와 함께 사용 시 대시(–) 아이콘 표시 |
309
- | `variant` | `"primary"` \| `"assistive"` | `"primary"` | 색상 변형 |
310
- | `size` | `"medium"` \| `"small"` | `"medium"` | 크기 |
311
- | `density` | `"default"` \| `"compact"` | `"default"` | wrapper 밀도 |
312
- | `disabled` | `boolean` | `false` | 비활성화 |
313
- | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
313
+ | Prop | 타입 | 기본값 | 설명 |
314
+ | --------------- | ---------------------------- | ----------- | --------------------------------------------------- |
315
+ | `checked` | `boolean` | — | **(필수)** 체크 상태 |
316
+ | `onChange` | `(checked: boolean) => void` | — | 변경 핸들러 |
317
+ | `indeterminate` | `boolean` | `false` | `checked={true}`와 함께 사용 시 대시(–) 아이콘 표시 |
318
+ | `variant` | `"primary"` \| `"assistive"` | `"primary"` | 색상 변형 |
319
+ | `size` | `"medium"` \| `"small"` | `"medium"` | 크기 |
320
+ | `density` | `"default"` \| `"compact"` | `"default"` | wrapper 밀도 |
321
+ | `disabled` | `boolean` | `false` | 비활성화 |
322
+ | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
314
323
 
315
324
  유의사항:
325
+
316
326
  - `indeterminate={true}`는 `checked={true}`일 때만 대시 아이콘을 표시합니다.
317
327
  - `density="compact"`은 wrapper 크기만 줄이며, 아이콘은 `size` 기준으로 그대로 표시됩니다.
318
328
 
@@ -330,7 +340,10 @@ function RadioGroupExample() {
330
340
  return (
331
341
  <>
332
342
  {["A", "B", "C"].map((v) => (
333
- <label key={v} style={{ display: "flex", alignItems: "center", gap: 6 }}>
343
+ <label
344
+ key={v}
345
+ style={{ display: "flex", alignItems: "center", gap: 6 }}
346
+ >
334
347
  <Radio
335
348
  name="group"
336
349
  value={v}
@@ -345,19 +358,20 @@ function RadioGroupExample() {
345
358
  }
346
359
  ```
347
360
 
348
- | Prop | 타입 | 기본값 | 설명 |
349
- |------|------|--------|------|
350
- | `checked` | `boolean` | — | **(필수)** 선택 상태 |
351
- | `onChange` | `(checked: boolean) => void` | — | 변경 핸들러 |
352
- | `name` | `string` | — | input name 속성 |
353
- | `value` | `string` | — | input value 속성 |
354
- | `variant` | `"primary"` \| `"assistive"` | `"primary"` | 색상 변형 |
355
- | `size` | `"medium"` \| `"small"` | `"medium"` | 크기 |
356
- | `density` | `"default"` \| `"compact"` | `"default"` | wrapper 밀도 |
357
- | `disabled` | `boolean` | `false` | 비활성화 |
358
- | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
361
+ | Prop | 타입 | 기본값 | 설명 |
362
+ | ------------- | ---------------------------- | ----------- | -------------------- |
363
+ | `checked` | `boolean` | — | **(필수)** 선택 상태 |
364
+ | `onChange` | `(checked: boolean) => void` | — | 변경 핸들러 |
365
+ | `name` | `string` | — | input name 속성 |
366
+ | `value` | `string` | — | input value 속성 |
367
+ | `variant` | `"primary"` \| `"assistive"` | `"primary"` | 색상 변형 |
368
+ | `size` | `"medium"` \| `"small"` | `"medium"` | 크기 |
369
+ | `density` | `"default"` \| `"compact"` | `"default"` | wrapper 밀도 |
370
+ | `disabled` | `boolean` | `false` | 비활성화 |
371
+ | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
359
372
 
360
373
  유의사항:
374
+
361
375
  - `name`이 같아도 자동으로 하나만 선택되지 않습니다. `checked={selected === value}` 비교로 직접 제어해야 합니다.
362
376
  - `onChange`는 클릭 시 `true`가 전달됩니다. `isChecked && setSelected(v)` 패턴으로 true일 때만 state를 변경합니다.
363
377
 
@@ -382,15 +396,16 @@ function ToggleSwitchExample() {
382
396
  }
383
397
  ```
384
398
 
385
- | Prop | 타입 | 기본값 | 설명 |
386
- |------|------|--------|------|
387
- | `active` | `boolean` | — | **(필수)** 활성 상태 |
388
- | `onChange` | `(active: boolean) => void` | — | 변경 핸들러 |
389
- | `size` | `"small"` \| `"medium"` \| `"large"` | `"medium"` | 크기 |
390
- | `disabled` | `boolean` | `false` | 비활성화 |
391
- | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
399
+ | Prop | 타입 | 기본값 | 설명 |
400
+ | ------------- | ------------------------------------ | ---------- | -------------------- |
401
+ | `active` | `boolean` | — | **(필수)** 활성 상태 |
402
+ | `onChange` | `(active: boolean) => void` | — | 변경 핸들러 |
403
+ | `size` | `"small"` \| `"medium"` \| `"large"` | `"medium"` | 크기 |
404
+ | `disabled` | `boolean` | `false` | 비활성화 |
405
+ | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
392
406
 
393
407
  유의사항:
408
+
394
409
  - 체크 상태를 나타내는 prop 이름이 `checked`가 아닌 `active`입니다.
395
410
 
396
411
  ---
@@ -411,11 +426,11 @@ showToast({ variant: "basic", message: "알림 메시지입니다." });
411
426
  dismissToast();
412
427
  ```
413
428
 
414
- | 옵션 | 타입 | 설명 |
415
- |------|------|------|
416
- | `message` | `string` | **(필수)** 표시할 메시지 |
417
- | `variant` | `"basic"` \| `"positive"` \| `"negative"` | 색상 변형 |
418
- | `duration` | `number` | 자동 닫힘 시간(ms) |
429
+ | 옵션 | 타입 | 설명 |
430
+ | ---------- | ----------------------------------------- | ------------------------ |
431
+ | `message` | `string` | **(필수)** 표시할 메시지 |
432
+ | `variant` | `"basic"` \| `"positive"` \| `"negative"` | 색상 변형 |
433
+ | `duration` | `number` | 자동 닫힘 시간(ms) |
419
434
 
420
435
  ### Snackbar
421
436
 
@@ -438,14 +453,14 @@ showSnackbar({ variant: "loading", heading: "처리 중..." });
438
453
  dismissSnackbar();
439
454
  ```
440
455
 
441
- | 옵션 | 타입 | 설명 |
442
- |------|------|------|
443
- | `heading` | `string` | **(필수)** 제목 텍스트 |
444
- | `variant` | `"basic"` \| `"loading"` | 색상 변형 |
445
- | `description` | `string` | 본문 텍스트 |
446
- | `actionLabel` | `string` | 액션 버튼 레이블 |
447
- | `onActionClick` | `() => void` | 액션 버튼 클릭 콜백 |
448
- | `closeButton` | `boolean` | 닫기 버튼 표시 여부 |
456
+ | 옵션 | 타입 | 설명 |
457
+ | --------------- | ------------------------ | ---------------------- |
458
+ | `heading` | `string` | **(필수)** 제목 텍스트 |
459
+ | `variant` | `"basic"` \| `"loading"` | 색상 변형 |
460
+ | `description` | `string` | 본문 텍스트 |
461
+ | `actionLabel` | `string` | 액션 버튼 레이블 |
462
+ | `onActionClick` | `() => void` | 액션 버튼 클릭 콜백 |
463
+ | `closeButton` | `boolean` | 닫기 버튼 표시 여부 |
449
464
 
450
465
  ### PushBadge
451
466
 
@@ -459,11 +474,197 @@ import { PushBadge } from "@idbrnd/design-system";
459
474
  <PushBadge variant="info" />
460
475
  ```
461
476
 
462
- | Prop | 타입 | 기본값 | 설명 |
463
- |------|------|--------|------|
464
- | `variant` | `"dot"` \| `"number"` \| `"info"` | — | **(필수)** 표시 형태 |
465
- | `count` | `number` | — | `variant="number"` 시 표시할 숫자 |
466
- | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
477
+ | Prop | 타입 | 기본값 | 설명 |
478
+ | ------------- | --------------------------------- | ------ | --------------------------------- |
479
+ | `variant` | `"dot"` \| `"number"` \| `"info"` | — | **(필수)** 표시 형태 |
480
+ | `count` | `number` | — | `variant="number"` 시 표시할 숫자 |
481
+ | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
482
+
483
+ ---
484
+
485
+ ## Select
486
+
487
+ 라벨, 트리거 버튼, 드롭다운 목록을 포함하는 완성형 셀렉트 컴포넌트입니다.
488
+ 트리거 클릭 시 포탈 기반 드롭다운이 열리며, 외부 클릭·Escape·스크롤/리사이즈를 자동으로 처리합니다.
489
+
490
+ ```tsx
491
+ import { useState } from "react";
492
+ import { Select } from "@idbrnd/design-system";
493
+ import type { DropdownOption } from "@idbrnd/design-system";
494
+
495
+ const options: DropdownOption[] = [
496
+ { label: "사과", value: "apple", icon: <AppleIcon /> },
497
+ { label: "바나나", value: "banana", icon: <BananaIcon /> },
498
+ { label: "포도", value: "grape", icon: <GrapeIcon /> },
499
+ ];
500
+
501
+ function SelectExample() {
502
+ const [selected, setSelected] = useState<DropdownOption | null>(null);
503
+
504
+ return (
505
+ <Select
506
+ heading="과일 선택"
507
+ placeholder="선택해주세요."
508
+ options={options}
509
+ value={selected}
510
+ onSelect={setSelected}
511
+ />
512
+ );
513
+ }
514
+ ```
515
+
516
+ **검색 기능 + 아이콘 표시**
517
+
518
+ ```tsx
519
+ <Select
520
+ heading="검색 가능한 셀렉트"
521
+ required
522
+ type="search"
523
+ content={true}
524
+ align="center"
525
+ options={optionsWithIcons}
526
+ value={selected}
527
+ onSelect={setSelected}
528
+ />
529
+ ```
530
+
531
+ **에러 상태**
532
+
533
+ ```tsx
534
+ <Select
535
+ heading="필수 항목"
536
+ variant="error"
537
+ description="항목을 선택해주세요."
538
+ options={options}
539
+ value={selected}
540
+ onSelect={setSelected}
541
+ />
542
+ ```
543
+
544
+ | Prop | 타입 | 기본값 | 설명 |
545
+ | ---------------- | -------------------------------------------------------- | ----------------- | ------------------------------------------------------------- |
546
+ | `options` | `DropdownOption[]` | `[]` | 드롭다운에 표시할 옵션 배열. `{ label, value, icon? }` 형태 |
547
+ | `value` | `DropdownOption \| null` | — | 현재 선택된 옵션 |
548
+ | `onSelect` | `(option: DropdownOption) => void` | — | 옵션 선택 시 호출되는 콜백 |
549
+ | `variant` | `"default"` \| `"choosing"` \| `"error"` \| `"selected"` | `"default"` | 트리거 상태. `choosing`/`selected`는 자동 적용 |
550
+ | `size` | `"large"` \| `"medium"` \| `"small"` | `"medium"` | 트리거 높이. large: 44px, medium: 40px, small: 36px |
551
+ | `heading` | `string` | — | 상단 라벨 텍스트. 미지정 시 라벨 미표시 |
552
+ | `required` | `boolean` | `false` | 라벨 우측 `*` 표시 여부 |
553
+ | `leadingContent` | `ReactNode` | — | 트리거 텍스트 좌측에 표시할 아이콘 등 |
554
+ | `description` | `ReactNode` | — | 하단 안내 문구 |
555
+ | `placeholder` | `string` | `"선택해주세요."` | 미선택 시 표시할 텍스트 |
556
+ | `disabled` | `boolean` | `false` | 비활성화 |
557
+ | `type` | `"basic"` \| `"search"` | `"basic"` | `"search"` 시 드롭다운 상단에 검색 입력란 표시, 실시간 필터링 |
558
+ | `align` | `"left"` \| `"center"` | `"left"` | 옵션 텍스트 정렬 |
559
+ | `content` | `boolean` | `false` | `true` 시 옵션의 `icon`이 라벨 좌측에 표시 |
560
+ | `width` | `number` | — | 컴포넌트 너비(px). 미지정 시 부모 너비 100% |
561
+ | `textColor` | `string` | — | 옵션 라벨 텍스트 색상 커스터마이징 (예: `"#6366f1"`) |
562
+ | `customStyle` | `CSSProperties` | — | 루트 엘리먼트 인라인 스타일 |
563
+
564
+ 유의사항:
565
+
566
+ - `variant`를 직접 지정하지 않아도 열림/선택 상태에 따라 `choosing` → `selected`로 자동 전환됩니다.
567
+ - `variant="error"`는 자동 전환되지 않으므로 직접 지정해야 합니다.
568
+ - 드롭다운 목록은 `document.body`에 포탈로 렌더링됩니다.
569
+ - 긴 옵션 라벨은 말줄임 처리되며, hover 시 툴팁으로 전체 텍스트가 표시됩니다.
570
+
571
+ ---
572
+
573
+ ## Dropdown
574
+
575
+ `children`을 트리거로 사용하는 범용 드롭다운 컴포넌트입니다.
576
+ 버튼, 텍스트, 아이콘 등 원하는 엘리먼트를 트리거로 자유롭게 사용할 수 있습니다.
577
+
578
+ ```tsx
579
+ import { useState } from "react";
580
+ import { Dropdown, OutlineButton } from "@idbrnd/design-system";
581
+ import type { DropdownOption } from "@idbrnd/design-system";
582
+
583
+ const options: DropdownOption[] = [
584
+ { label: "수정", value: "edit", icon: <EditIcon /> },
585
+ { label: "복제", value: "duplicate", icon: <CopyIcon /> },
586
+ { label: "삭제", value: "delete", icon: <TrashIcon /> },
587
+ ];
588
+
589
+ function DropdownExample() {
590
+ const handleSelect = (option: DropdownOption) => {
591
+ console.log("선택:", option.label);
592
+ };
593
+
594
+ return (
595
+ <Dropdown options={options} onSelect={handleSelect} width={200}>
596
+ <OutlineButton variant="assistive">메뉴 열기</OutlineButton>
597
+ </Dropdown>
598
+ );
599
+ }
600
+ ```
601
+
602
+ **검색 기능 + 선택 상태 표시**
603
+
604
+ ```tsx
605
+ function SearchDropdown() {
606
+ const [selected, setSelected] = useState<DropdownOption | null>(null);
607
+
608
+ return (
609
+ <Dropdown
610
+ type="search"
611
+ options={options}
612
+ onSelect={setSelected}
613
+ selectedValue={selected?.value}
614
+ width={240}
615
+ >
616
+ <button>{selected?.label ?? "선택하세요"}</button>
617
+ </Dropdown>
618
+ );
619
+ }
620
+ ```
621
+
622
+ **아이콘 포함 + 커스텀 색상**
623
+
624
+ ```tsx
625
+ <Dropdown
626
+ content={true}
627
+ align="center"
628
+ options={optionsWithIcons}
629
+ onSelect={handleSelect}
630
+ textColor="#6366f1"
631
+ width={240}
632
+ >
633
+ <FillButton variant="primary">아이콘 메뉴</FillButton>
634
+ </Dropdown>
635
+ ```
636
+
637
+ | Prop | 타입 | 기본값 | 설명 |
638
+ | --------------- | ---------------------------------- | --------- | --------------------------------------------------------------- |
639
+ | `children` | `ReactNode` | — | **(필수)** 드롭다운 트리거 엘리먼트 |
640
+ | `options` | `DropdownOption[]` | `[]` | 드롭다운에 표시할 옵션 배열. `{ label, value, icon? }` 형태 |
641
+ | `onSelect` | `(option: DropdownOption) => void` | — | **(필수)** 옵션 선택 시 호출되는 콜백 |
642
+ | `selectedValue` | `string` | — | 현재 선택된 옵션의 `value`. 선택 스타일 및 `aria-selected` 적용 |
643
+ | `type` | `"basic"` \| `"search"` | `"basic"` | `"search"` 시 드롭다운 상단에 검색 입력란 표시, 실시간 필터링 |
644
+ | `align` | `"left"` \| `"center"` | `"left"` | 옵션 텍스트 정렬 |
645
+ | `content` | `boolean` | `false` | `true` 시 옵션의 `icon`이 라벨 좌측에 표시 |
646
+ | `width` | `number` | — | 드롭다운 목록 너비(px). 미지정 시 트리거 너비에 맞춤 |
647
+ | `textColor` | `string` | — | 옵션 라벨 텍스트 색상 커스터마이징 (예: `"#6366f1"`) |
648
+ | `disabled` | `boolean` | `false` | `true` 시 트리거 클릭이 무시됨 |
649
+ | `customStyle` | `CSSProperties` | — | 루트 엘리먼트 인라인 스타일 |
650
+
651
+ 유의사항:
652
+
653
+ - Select와 달리 트리거 UI를 직접 제공해야 합니다 (`children`).
654
+ - Select는 선택 상태를 `value` 객체로 관리하지만, Dropdown은 `selectedValue` 문자열로 관리합니다.
655
+ - 드롭다운 목록은 `document.body`에 포탈로 렌더링됩니다.
656
+ - 긴 옵션 라벨은 말줄임 처리되며, hover 시 툴팁으로 전체 텍스트가 표시됩니다.
657
+ - 열림/닫힘 시 fade + scale 애니메이션이 적용됩니다.
658
+
659
+ ### Select vs Dropdown 비교
660
+
661
+ | 기능 | Select | Dropdown |
662
+ | -------------- | ------------------------------- | ------------------------------- |
663
+ | 트리거 | 내장 (라벨 + 버튼 + chevron) | `children`으로 직접 제공 |
664
+ | 선택 값 관리 | `value: DropdownOption \| null` | `selectedValue: string` |
665
+ | 라벨/설명/에러 | 지원 | 미지원 (직접 구현) |
666
+ | 사이즈 변형 | large / medium / small | 트리거에 따라 자유 |
667
+ | 사용 시나리오 | 폼 내 셀렉트 필드 | 컨텍스트 메뉴, 액션 드롭다운 등 |
467
668
 
468
669
  ---
469
670
 
@@ -487,16 +688,16 @@ import { ContentBadge } from "@idbrnd/design-system";
487
688
  <ContentBadge contentColor="#6366f1" borderColor="#6366f1">커스텀</ContentBadge>
488
689
  ```
489
690
 
490
- | Prop | 타입 | 기본값 | 설명 |
491
- |------|------|--------|------|
492
- | `variant` | `"basic"` \| `"primary"` | `"basic"` | 색상 변형 |
493
- | `size` | `"default"` \| `"compact"` \| `"compact-small"` | `"default"` | 크기 |
494
- | `contentColor` | `string` | — | 텍스트/아이콘 색상 오버라이드 |
495
- | `backgroundColor` | `string` | — | 배경색 오버라이드 |
496
- | `borderColor` | `string` | — | 테두리색 오버라이드 |
497
- | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
498
- | `className` | `string` | — | 추가 클래스명 |
499
- | `children` | `ReactNode` | — | 배지 내용 |
691
+ | Prop | 타입 | 기본값 | 설명 |
692
+ | ----------------- | ----------------------------------------------- | ----------- | ----------------------------- |
693
+ | `variant` | `"basic"` \| `"primary"` | `"basic"` | 색상 변형 |
694
+ | `size` | `"default"` \| `"compact"` \| `"compact-small"` | `"default"` | 크기 |
695
+ | `contentColor` | `string` | — | 텍스트/아이콘 색상 오버라이드 |
696
+ | `backgroundColor` | `string` | — | 배경색 오버라이드 |
697
+ | `borderColor` | `string` | — | 테두리색 오버라이드 |
698
+ | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
699
+ | `className` | `string` | — | 추가 클래스명 |
700
+ | `children` | `ReactNode` | — | 배지 내용 |
500
701
 
501
702
  ### StateBadge
502
703
 
@@ -521,14 +722,14 @@ import { StateBadge } from "@idbrnd/design-system";
521
722
  <StateBadge variant="success" stateIcon>성공</StateBadge>
522
723
  ```
523
724
 
524
- | Prop | 타입 | 기본값 | 설명 |
525
- |------|------|--------|------|
526
- | `variant` | `"basic"` \| `"error"` \| `"success"` \| `"info"` \| `"warning"` | `"basic"` | 상태 색상 변형 |
527
- | `size` | `"default"` \| `"compact"` | `"default"` | 크기 |
528
- | `stateIcon` | `boolean` | `false` | `true`이면 variant 색상의 원형 dot 표시 |
529
- | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
530
- | `className` | `string` | — | 추가 클래스명 |
531
- | `children` | `ReactNode` | — | 배지 내용 |
725
+ | Prop | 타입 | 기본값 | 설명 |
726
+ | ------------- | ---------------------------------------------------------------- | ----------- | --------------------------------------- |
727
+ | `variant` | `"basic"` \| `"error"` \| `"success"` \| `"info"` \| `"warning"` | `"basic"` | 상태 색상 변형 |
728
+ | `size` | `"default"` \| `"compact"` | `"default"` | 크기 |
729
+ | `stateIcon` | `boolean` | `false` | `true`이면 variant 색상의 원형 dot 표시 |
730
+ | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
731
+ | `className` | `string` | — | 추가 클래스명 |
732
+ | `children` | `ReactNode` | — | 배지 내용 |
532
733
 
533
734
  ---
534
735
 
@@ -545,12 +746,12 @@ import { Spinner, CustomSpinner, Clip, FadeSpinner } from "@idbrnd/design-system
545
746
  <FadeSpinner width={4} height={12} />
546
747
  ```
547
748
 
548
- | 컴포넌트 | 주요 Props | 설명 |
549
- |----------|-----------|------|
550
- | `Spinner` | — | 기본 스피너 |
551
- | `CustomSpinner` | `size` | 크기 조정 가능한 스피너 |
552
- | `Clip` | `size`, `color` | 원형 클립 스피너 |
553
- | `FadeSpinner` | `width`, `height`, `color` | 페이드 인/아웃 스피너 |
749
+ | 컴포넌트 | 주요 Props | 설명 |
750
+ | --------------- | -------------------------- | ----------------------- |
751
+ | `Spinner` | — | 기본 스피너 |
752
+ | `CustomSpinner` | `size` | 크기 조정 가능한 스피너 |
753
+ | `Clip` | `size`, `color` | 원형 클립 스피너 |
754
+ | `FadeSpinner` | `width`, `height`, `color` | 페이드 인/아웃 스피너 |
554
755
 
555
756
  ---
556
757
 
@@ -580,6 +781,15 @@ import type {
580
781
  StateBadgeProps,
581
782
  StateBadgeVariant,
582
783
  StateBadgeSize,
784
+ SelectProps,
785
+ SelectVariant,
786
+ SelectSize,
787
+ SelectType,
788
+ SelectAlign,
789
+ DropdownProps,
790
+ DropdownOption,
791
+ DropdownType,
792
+ DropdownAlign,
583
793
  } from "@idbrnd/design-system";
584
794
  ```
585
795
 
@@ -591,4 +801,6 @@ import type {
591
801
  - `Radio`는 `name` prop만으로 단일 선택이 보장되지 않습니다. `checked={selected === value}` 비교로 직접 제어해야 합니다.
592
802
  - `CheckBox`의 `indeterminate={true}`는 `checked={true}`와 함께 사용해야 대시 아이콘이 표시됩니다.
593
803
  - `showToast`, `showSnackbar`는 내부적으로 `document.body`에 포털을 생성하므로 브라우저 환경에서 호출해야 합니다.
804
+ - `Select`와 `Dropdown`의 드롭다운 목록은 `document.body`에 포탈로 렌더링되므로 브라우저 환경에서만 동작합니다.
805
+ - `Select`는 폼 필드용, `Dropdown`은 컨텍스트 메뉴/액션 드롭다운용으로 사용을 권장합니다.
594
806
  - CSS 변수/타이포그래피를 포함한 스타일을 위해 `@idbrnd/design-system/style.css` import를 권장합니다.