@idbrnd/design-system 3.0.4 → 3.2.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @idbrnd/design-system
2
2
 
3
- ![Version](https://img.shields.io/badge/version-3.0.4-4B5FE1?style=flat-square)
3
+ ![Version](https://img.shields.io/badge/version-3.2.0-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)
@@ -47,6 +47,7 @@ export default function App() {
47
47
  - [입력](#입력)
48
48
  - [Input](#input)
49
49
  - [SearchBar](#searchbar)
50
+ - [Textarea](#textarea)
50
51
  - [컨트롤](#컨트롤)
51
52
  - [CheckBox](#checkbox)
52
53
  - [Radio](#radio)
@@ -92,15 +93,15 @@ import { FillButton } from "@idbrnd/design-system";
92
93
  <FillButton variant="primary" disabled>비활성화</FillButton>
93
94
  ```
94
95
 
95
- | Prop | 타입 | 기본값 | 설명 |
96
- | ------------- | -------------------------------------------------- | ----------- | ------------------ |
97
- | `variant` | `"primary"` \| `"assistive"` \| `"error"` | `"primary"` | 색상 변형 |
98
- | `size` | `"xsmall"` \| `"small"` \| `"medium"` \| `"large"` | `"medium"` | 크기 |
99
- | `widthType` | `"fixed"` \| `"flexible"` | `"flexible"` | 너비 방식 |
100
- | `loading` | `boolean` | `false` | 로딩 스피너 표시 |
101
- | `disabled` | `boolean` | `false` | 비활성화 |
96
+ | Prop | 타입 | 기본값 | 설명 |
97
+ | ------------------ | -------------------------------------------------- | ------------ | ------------------- |
98
+ | `variant` | `"primary"` \| `"assistive"` \| `"error"` | `"primary"` | 색상 변형 |
99
+ | `size` | `"xsmall"` \| `"small"` \| `"medium"` \| `"large"` | `"medium"` | 크기 |
100
+ | `widthType` | `"fixed"` \| `"flexible"` | `"flexible"` | 너비 방식 |
101
+ | `loading` | `boolean` | `false` | 로딩 스피너 표시 |
102
+ | `disabled` | `boolean` | `false` | 비활성화 |
102
103
  | `keepFocusOnClick` | `boolean` | `false` | 클릭 후 포커스 유지 |
103
- | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
104
+ | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
104
105
 
105
106
  ### OutlineButton
106
107
 
@@ -112,15 +113,15 @@ import { OutlineButton } from "@idbrnd/design-system";
112
113
  <OutlineButton variant="assistive" loading>처리 중</OutlineButton>
113
114
  ```
114
115
 
115
- | Prop | 타입 | 기본값 | 설명 |
116
- | ------------- | ---------------------------------------------------------- | ------------- | ------------------ |
117
- | `variant` | `"primary"` \| `"assistive"` \| `"secondary"` | `"primary"` | 색상 변형 |
118
- | `size` | `"xsmall"` \| `"small"` \| `"medium"` \| `"large"` | `"medium"` | 크기 |
119
- | `widthType` | `"fixed"` \| `"flexible"` | `"flexible"` | 너비 방식 |
120
- | `loading` | `boolean` | `false` | 로딩 스피너 표시 |
121
- | `disabled` | `boolean` | `false` | 비활성화 |
122
- | `keepFocusOnClick` | `boolean` | `false` | 클릭 후 포커스 유지 |
123
- | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
116
+ | Prop | 타입 | 기본값 | 설명 |
117
+ | ------------------ | -------------------------------------------------- | ------------ | ------------------- |
118
+ | `variant` | `"primary"` \| `"assistive"` \| `"secondary"` | `"primary"` | 색상 변형 |
119
+ | `size` | `"xsmall"` \| `"small"` \| `"medium"` \| `"large"` | `"medium"` | 크기 |
120
+ | `widthType` | `"fixed"` \| `"flexible"` | `"flexible"` | 너비 방식 |
121
+ | `loading` | `boolean` | `false` | 로딩 스피너 표시 |
122
+ | `disabled` | `boolean` | `false` | 비활성화 |
123
+ | `keepFocusOnClick` | `boolean` | `false` | 클릭 후 포커스 유지 |
124
+ | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
124
125
 
125
126
  ### TextButton
126
127
 
@@ -131,15 +132,15 @@ import { TextButton } from "@idbrnd/design-system";
131
132
  <TextButton variant="assistive" size="small">작은 버튼</TextButton>
132
133
  ```
133
134
 
134
- | Prop | 타입 | 기본값 | 설명 |
135
- | ------------- | ----------------------------------------- | ------------- | ------------------ |
136
- | `variant` | `"primary"` \| `"assistive"` \| `"secondary"` | `"primary"` | 색상 변형 |
137
- | `size` | `"xsmall"` \| `"small"` \| `"medium"` \| `"large"` | `"medium"` | 크기 |
138
- | `widthType` | `"fixed"` \| `"flexible"` | `"flexible"` | 너비 방식 |
139
- | `loading` | `boolean` | `false` | 로딩 스피너 표시 |
140
- | `disabled` | `boolean` | `false` | 비활성화 |
141
- | `keepFocusOnClick` | `boolean` | `false` | 클릭 후 포커스 유지 |
142
- | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
135
+ | Prop | 타입 | 기본값 | 설명 |
136
+ | ------------------ | -------------------------------------------------- | ------------ | ------------------- |
137
+ | `variant` | `"primary"` \| `"assistive"` \| `"secondary"` | `"primary"` | 색상 변형 |
138
+ | `size` | `"xsmall"` \| `"small"` \| `"medium"` \| `"large"` | `"medium"` | 크기 |
139
+ | `widthType` | `"fixed"` \| `"flexible"` | `"flexible"` | 너비 방식 |
140
+ | `loading` | `boolean` | `false` | 로딩 스피너 표시 |
141
+ | `disabled` | `boolean` | `false` | 비활성화 |
142
+ | `keepFocusOnClick` | `boolean` | `false` | 클릭 후 포커스 유지 |
143
+ | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
143
144
 
144
145
  ### WeakButton
145
146
 
@@ -150,15 +151,15 @@ import { WeakButton } from "@idbrnd/design-system";
150
151
  <WeakButton variant="error" size="xsmall">삭제</WeakButton>
151
152
  ```
152
153
 
153
- | Prop | 타입 | 기본값 | 설명 |
154
- | ------------- | -------------------------------------------------- | ------------- | ------------------ |
155
- | `variant` | `"primary"` \| `"assistive"` \| `"error"` | `"primary"` | 색상 변형 |
156
- | `size` | `"xsmall"` \| `"small"` \| `"medium"` \| `"large"` | `"medium"` | 크기 |
157
- | `widthType` | `"fixed"` \| `"flexible"` | `"flexible"` | 너비 방식 |
158
- | `loading` | `boolean` | `false` | 로딩 스피너 표시 |
159
- | `disabled` | `boolean` | `false` | 비활성화 |
160
- | `keepFocusOnClick` | `boolean` | `false` | 클릭 후 포커스 유지 |
161
- | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
154
+ | Prop | 타입 | 기본값 | 설명 |
155
+ | ------------------ | -------------------------------------------------- | ------------ | ------------------- |
156
+ | `variant` | `"primary"` \| `"assistive"` \| `"error"` | `"primary"` | 색상 변형 |
157
+ | `size` | `"xsmall"` \| `"small"` \| `"medium"` \| `"large"` | `"medium"` | 크기 |
158
+ | `widthType` | `"fixed"` \| `"flexible"` | `"flexible"` | 너비 방식 |
159
+ | `loading` | `boolean` | `false` | 로딩 스피너 표시 |
160
+ | `disabled` | `boolean` | `false` | 비활성화 |
161
+ | `keepFocusOnClick` | `boolean` | `false` | 클릭 후 포커스 유지 |
162
+ | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
162
163
 
163
164
  ---
164
165
 
@@ -174,13 +175,13 @@ import { BasicIconButton } from "@idbrnd/design-system";
174
175
  <BasicIconButton size="medium" disabled><SearchIcon /></BasicIconButton>
175
176
  ```
176
177
 
177
- | Prop | 타입 | 기본값 | 설명 |
178
- | ------------- | -------------------------- | ----------- | --------------------------------------------------------------------------------------------------- |
179
- | `size` | `"medium"` \| `"large"` | `"medium"` | 아이콘 크기 (large: 24×24, medium: 20×20) |
180
- | `touchArea` | `"default"` \| `"compact"` | `"default"` | 인터랙션 적용 범위. large는 default(44×44)/compact(36×36) 모두 지원, medium은 default(32×32)만 지원 |
181
- | `disabled` | `boolean` | `false` | 비활성화 |
182
- | `keepFocusOnClick` | `boolean` | `false` | 클릭 후 포커스 유지 |
183
- | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
178
+ | Prop | 타입 | 기본값 | 설명 |
179
+ | ------------------ | -------------------------- | ----------- | --------------------------------------------------------------------------------------------------- |
180
+ | `size` | `"medium"` \| `"large"` | `"medium"` | 아이콘 크기 (large: 24×24, medium: 20×20) |
181
+ | `touchArea` | `"default"` \| `"compact"` | `"default"` | 인터랙션 적용 범위. large는 default(44×44)/compact(36×36) 모두 지원, medium은 default(32×32)만 지원 |
182
+ | `disabled` | `boolean` | `false` | 비활성화 |
183
+ | `keepFocusOnClick` | `boolean` | `false` | 클릭 후 포커스 유지 |
184
+ | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
184
185
 
185
186
  ### FillIconButton
186
187
 
@@ -191,13 +192,13 @@ import { FillIconButton } from "@idbrnd/design-system";
191
192
  <FillIconButton variant="overlay"><PlusIcon /></FillIconButton>
192
193
  ```
193
194
 
194
- | Prop | 타입 | 기본값 | 설명 |
195
- | ------------- | --------------------------------------- | ----------- | ------------------ |
196
- | `variant` | `"basic"` \| `"overlay"` \| `"primary"` | `"basic"` | 색상 변형 |
197
- | `size` | `"medium"` \| `"large"` | `"medium"` | 크기 |
198
- | `disabled` | `boolean` | `false` | 비활성화 |
199
- | `keepFocusOnClick` | `boolean` | `false` | 클릭 후 포커스 유지 |
200
- | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
195
+ | Prop | 타입 | 기본값 | 설명 |
196
+ | ------------------ | --------------------------------------- | ---------- | ------------------- |
197
+ | `variant` | `"basic"` \| `"overlay"` \| `"primary"` | `"basic"` | 색상 변형 |
198
+ | `size` | `"medium"` \| `"large"` | `"medium"` | 크기 |
199
+ | `disabled` | `boolean` | `false` | 비활성화 |
200
+ | `keepFocusOnClick` | `boolean` | `false` | 클릭 후 포커스 유지 |
201
+ | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
201
202
 
202
203
  ### OutlineIconButton
203
204
 
@@ -208,12 +209,12 @@ import { OutlineIconButton } from "@idbrnd/design-system";
208
209
  <OutlineIconButton disabled><WriteIcon /></OutlineIconButton>
209
210
  ```
210
211
 
211
- | Prop | 타입 | 기본값 | 설명 |
212
- | ------------- | ------------------------------------ | ---------- | ------------------ |
213
- | `size` | `"medium"` \| `"large"` | `"medium"` | 크기 |
214
- | `disabled` | `boolean` | `false` | 비활성화 |
212
+ | Prop | 타입 | 기본값 | 설명 |
213
+ | ------------------ | ----------------------- | ---------- | ------------------- |
214
+ | `size` | `"medium"` \| `"large"` | `"medium"` | 크기 |
215
+ | `disabled` | `boolean` | `false` | 비활성화 |
215
216
  | `keepFocusOnClick` | `boolean` | `false` | 클릭 후 포커스 유지 |
216
- | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
217
+ | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
217
218
 
218
219
  ---
219
220
 
@@ -348,6 +349,82 @@ function SearchExample() {
348
349
  - `description`, `fixedDescriptionHeight` 등 나머지 보조 속성은 `Input`과 동일하게 사용할 수 있습니다.
349
350
  - helper 문구를 일부 케이스에서만 줄바꿈하고 싶다면 `errorMessage` 또는 `description`에 래퍼 노드를 넘겨 `maxWidth`나 `<br />`를 적용하면 됩니다.
350
351
 
352
+ ### Textarea
353
+
354
+ 두 줄 이상의 긴 텍스트를 입력받는 controlled 컴포넌트입니다. 한 줄 입력에는 [Input](#input)을 사용하세요.
355
+
356
+ 높이는 `size`와 `minRows`로 결정되며 내용이 길어져도 늘어나지 않습니다. 넘치는 내용은 입력 영역 내부에서 스크롤됩니다.
357
+
358
+ ```tsx
359
+ import { useState } from "react";
360
+ import { Textarea, TextButton, OutlineButton } from "@idbrnd/design-system";
361
+
362
+ function TextareaExample() {
363
+ const [content, setContent] = useState("");
364
+
365
+ return (
366
+ <Textarea
367
+ headingContent="문의 내용"
368
+ required
369
+ value={content}
370
+ onChange={(e) => setContent(e.target.value)}
371
+ description="접수 후 영업일 기준 2일 내에 답변드립니다."
372
+ bottom
373
+ leadingContent
374
+ maxLength={500}
375
+ trailingContent={
376
+ <>
377
+ <TextButton size="medium" variant="assistive">
378
+ 취소
379
+ </TextButton>
380
+ <OutlineButton size="xsmall" variant="secondary">
381
+ 등록
382
+ </OutlineButton>
383
+ </>
384
+ }
385
+ />
386
+ );
387
+ }
388
+ ```
389
+
390
+ | Prop | 타입 | 기본값 | 설명 |
391
+ | ----------------- | ------------------------------------------------------------------- | --------------------- | ---------------------------------------------------------------- |
392
+ | `designType` | `"outline"` \| `"fill"` | `"outline"` | 입력 영역 외형 |
393
+ | `size` | `"default"` \| `"large"` | `"default"` | 텍스트 크기와 컨테이너 높이 (108px / 112px) |
394
+ | `variant` | `"basic"` \| `"onFocus"` \| `"typed"` \| `"error"` \| `"success"` | 자동 계산 | 수동 상태 지정. `errorMessage`가 있으면 `error`가 우선 적용 |
395
+ | `minRows` | `number` | `2` | 노출할 줄 수 = 고정 높이. 넘치면 내부 스크롤 |
396
+ | `width` | `number \| string` | `"100%"` | 루트 너비 |
397
+ | `value` | `string` | — | **(필수)** 입력값 |
398
+ | `onChange` | `ChangeEventHandler<HTMLTextAreaElement>` | — | 값 변경 핸들러 |
399
+ | `heading` | `boolean` | `true` | 상단 라벨 표시 여부 |
400
+ | `headingContent` | `ReactNode` | `"Label"` | 상단 라벨 내용 |
401
+ | `required` | `boolean` | `false` | 필수 입력 표시 및 native `required` 속성 적용 |
402
+ | `description` | `ReactNode \| boolean` | `false` | 하단 안내 문구. `true`면 기본 안내 문구를 사용 |
403
+ | `errorMessage` | `ReactNode` | 기본 에러 문구 | 의미 있는 값이 전달되면 에러 상태가 자동 적용되고 해당 문구 표시 |
404
+ | `successMessage` | `ReactNode` | — | `variant="success"`일 때만 노출 |
405
+ | `bottom` | `boolean` | `false` | 입력 영역 내부 하단 영역(글자 수 · 보조 액션) 노출 여부 |
406
+ | `leadingContent` | `boolean` | `false` | 하단 좌측 글자 수 카운터 **상시** 노출 여부 |
407
+ | `maxLength` | `number` | `100` 또는 `1500` | 최대 입력 글자 수. 기본값은 `leadingContent`에 따라 달라짐 |
408
+ | `trailingContent` | `ReactNode` | — | 하단 우측 보조 액션. 전달한 노드를 그대로 렌더링 |
409
+ | `placeholder` | `string` | `"텍스트를 입력해 주세요."` | 플레이스홀더 |
410
+ | `readOnly` | `boolean` | `false` | 읽기 전용 |
411
+ | `disabled` | `boolean` | `false` | 비활성화 |
412
+ | `customStyle` | `CSSProperties` | `{}` | 루트 컨테이너에 적용할 추가 인라인 스타일 |
413
+
414
+ 유의사항:
415
+
416
+ - `defaultValue`는 지원하지 않습니다. 반드시 `value` + `onChange`로 제어해야 합니다.
417
+ - 컨테이너 높이는 `size`가 결정합니다(`default` 108px / `large` 112px). `trailingContent`나 카운터 유무로 높이가 달라지지 않습니다.
418
+ - 카운터와 보조 액션은 `bottom={true}`일 때만 노출됩니다. `bottom={false}`면 하단 영역 자체가 없습니다.
419
+ - **`leadingContent={false}`여도 한도까지 100자 이하로 남으면 카운터가 자동으로 나타납니다.** 곧 입력이 막힌다는 것을 사용자가 미리 알 수 있게 하기 위한 동작이며, 기본값 기준 1400자부터 `1400/1500`이 표시됩니다.
420
+ - `maxLength`를 지정하지 않으면 `leadingContent={true}`일 때 `100`, `false`일 때 `1500`이 적용됩니다. 즉 **입력 길이 제한은 항상 걸립니다.**
421
+ - `trailingContent`는 전달한 노드를 그대로 렌더링합니다. 버튼을 하나 넘기면 하나만, 둘 넘기면 둘 다 노출됩니다. 버튼이 두 개일 때는 위계가 높은 버튼을 우측에 둡니다.
422
+ - 하단 영역은 높이 32px을 차지합니다. 보조 액션 버튼은 이 높이에 맞춰 `TextButton size="medium"`, `OutlineButton size="xsmall"` 등을 사용하세요.
423
+ - `errorMessage`·`description`의 빈 문자열, 공백 문자열, 내용 없는 JSX 래퍼는 문구가 없는 것으로 처리합니다. 노출할 문구가 없으면 문구 영역 자체를 렌더링하지 않습니다.
424
+ - `variant="error"`면 적색 보더와 아이콘이, `variant="success"`면 녹색 체크 아이콘이 함께 표시됩니다.
425
+ - 사용자가 모서리를 드래그해 크기를 바꾸는 native resize는 비활성화되어 있습니다.
426
+ - `id`, `name`, `onFocus`, `onBlur`, `autoFocus` 등 대부분의 native `<textarea>` props도 함께 사용할 수 있습니다.
427
+
351
428
  ---
352
429
 
353
430
  ## 컨트롤
@@ -510,12 +587,12 @@ showToast({ variant: "basic", message: "알림 메시지입니다." });
510
587
  dismissToast();
511
588
  ```
512
589
 
513
- | 옵션 | 타입 | 설명 |
514
- | ------------- | ----------------------------------------- | ------------------------------- |
515
- | `message` | `ReactNode` | **(필수)** 표시할 메시지 |
516
- | `variant` | `"basic"` \| `"positive"` \| `"negative"` | 색상 변형 (기본값 `"basic"`) |
590
+ | 옵션 | 타입 | 설명 |
591
+ | ------------- | ----------------------------------------- | ------------------------------------------------------ |
592
+ | `message` | `ReactNode` | **(필수)** 표시할 메시지 |
593
+ | `variant` | `"basic"` \| `"positive"` \| `"negative"` | 색상 변형 (기본값 `"basic"`) |
517
594
  | `icon` | `ReactNode` | 사용자 지정 아이콘. 미지정 시 variant 기본 아이콘 사용 |
518
- | `customStyle` | `CSSProperties` | 추가 인라인 스타일 |
595
+ | `customStyle` | `CSSProperties` | 추가 인라인 스타일 |
519
596
 
520
597
  ### Snackbar
521
598
 
@@ -538,16 +615,16 @@ showSnackbar({ variant: "loading", heading: "처리 중..." });
538
615
  dismissSnackbar();
539
616
  ```
540
617
 
541
- | 옵션 | 타입 | 설명 |
542
- | --------------- | ------------------------ | --------------------------------------------- |
543
- | `variant` | `"basic"` \| `"loading"` | 색상 변형 (기본값 `"basic"`) |
544
- | `heading` | `ReactNode` | 제목 텍스트 |
545
- | `description` | `ReactNode` | 본문 텍스트 |
618
+ | 옵션 | 타입 | 설명 |
619
+ | --------------- | ------------------------ | --------------------------------------------------- |
620
+ | `variant` | `"basic"` \| `"loading"` | 색상 변형 (기본값 `"basic"`) |
621
+ | `heading` | `ReactNode` | 제목 텍스트 |
622
+ | `description` | `ReactNode` | 본문 텍스트 |
546
623
  | `icon` | `ReactNode` | 사용자 지정 아이콘. 전달 시 loading 스피너보다 우선 |
547
- | `actionLabel` | `ReactNode` | 액션 버튼 레이블 |
548
- | `onActionClick` | `() => void` | 액션 버튼 클릭 콜백 |
549
- | `closeButton` | `boolean` | 닫기 버튼 표시 여부 |
550
- | `customStyle` | `CSSProperties` | 추가 인라인 스타일 |
624
+ | `actionLabel` | `ReactNode` | 액션 버튼 레이블 |
625
+ | `onActionClick` | `() => void` | 액션 버튼 클릭 콜백 |
626
+ | `closeButton` | `boolean` | 닫기 버튼 표시 여부 |
627
+ | `customStyle` | `CSSProperties` | 추가 인라인 스타일 |
551
628
 
552
629
  ### PushBadge
553
630
 
@@ -561,12 +638,12 @@ import { PushBadge } from "@idbrnd/design-system";
561
638
  <PushBadge variant="info" />
562
639
  ```
563
640
 
564
- | Prop | 타입 | 기본값 | 설명 |
565
- | ------------- | --------------------------------- | ---------- | ------------------------------------------- |
566
- | `variant` | `"dot"` \| `"number"` \| `"info"` | `"number"` | 표시 형태 |
567
- | `count` | `number` | `1` | `variant="number"` 시 표시할 숫자 |
568
- | `maxCount` | `number` | `999` | `count`가 초과하면 `{maxCount}+`로 표시 |
569
- | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
641
+ | Prop | 타입 | 기본값 | 설명 |
642
+ | ------------- | --------------------------------- | ---------- | --------------------------------------- |
643
+ | `variant` | `"dot"` \| `"number"` \| `"info"` | `"number"` | 표시 형태 |
644
+ | `count` | `number` | `1` | `variant="number"` 시 표시할 숫자 |
645
+ | `maxCount` | `number` | `999` | `count`가 초과하면 `{maxCount}+`로 표시 |
646
+ | `customStyle` | `CSSProperties` | — | 추가 인라인 스타일 |
570
647
 
571
648
  ### Tooltip
572
649
 
@@ -0,0 +1,15 @@
1
+ import { type ButtonHTMLAttributes, type CSSProperties, type ReactNode } from 'react';
2
+ export type TertiaryButtonVariant = 'fill' | 'tinted' | 'gray' | 'primary';
3
+ export type TertiaryButtonSize = 'large' | 'medium' | 'small';
4
+ export interface TertiaryButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'style'> {
5
+ variant?: TertiaryButtonVariant;
6
+ size?: TertiaryButtonSize;
7
+ leadingIcon?: ReactNode;
8
+ trailingIcon?: ReactNode;
9
+ loading?: boolean;
10
+ keepFocusOnClick?: boolean;
11
+ customStyle?: CSSProperties;
12
+ children?: ReactNode;
13
+ }
14
+ declare function TertiaryButton({ variant, size, leadingIcon, trailingIcon, disabled, loading, keepFocusOnClick, customStyle, onClick, className, children, type, ...rest }: TertiaryButtonProps): import("react/jsx-runtime").JSX.Element;
15
+ export default TertiaryButton;
@@ -0,0 +1,21 @@
1
+ import type { TextareaProps } from './Textarea.types';
2
+ export type { TextareaProps, TextareaSize, TextareaVariant, DesignType } from './Textarea.types';
3
+ declare function Textarea({ designType, // 입력 영역 외형 타입
4
+ size, // 텍스트 크기
5
+ variant, // 상태 variant
6
+ minRows, // 노출할 줄 수 (고정 높이)
7
+ width, // 루트 너비
8
+ value, // 현재 값
9
+ heading, // 라벨 표시 여부
10
+ headingContent, // 라벨 내용
11
+ required, // 라벨 우측 *
12
+ description, // 하단 안내 문구
13
+ errorMessage, // 에러 상태 문구
14
+ successMessage, // 성공 상태 문구
15
+ bottom, // 입력 영역 내부 하단 영역 표시 여부
16
+ leadingContent, // 하단 좌측 글자 수 카운터 표시 여부
17
+ maxLength, // 최대 입력 글자 수 (카운터 분모)
18
+ trailingContent, // 하단 우측 슬롯
19
+ customStyle, // 루트 커스텀 스타일
20
+ placeholder, disabled, readOnly, id, ...rest }: TextareaProps): import("react/jsx-runtime").JSX.Element;
21
+ export default Textarea;
@@ -0,0 +1,133 @@
1
+ import type { CSSProperties, ReactNode, TextareaHTMLAttributes } from 'react';
2
+ import type { DesignType } from '../Input/Input.types';
3
+ export type { DesignType };
4
+ /**
5
+ * Textarea 높이·텍스트 크기 타입.
6
+ * @defaultValue `'default'`
7
+ */
8
+ export type TextareaSize = 'default' | 'large';
9
+ /**
10
+ * Textarea 상태 variant 타입.
11
+ * - `basic`: 기본 상태
12
+ * - `onFocus`: 포커스 진입 상태(보더 강조)
13
+ * - `typed`: 값이 입력된 상태
14
+ * - `error`: 유효성 오류 상태
15
+ * - `success`: 유효성 통과 상태
16
+ */
17
+ export type TextareaVariant = 'basic' | 'onFocus' | 'typed' | 'error' | 'success';
18
+ /** `Textarea` 컴포넌트 public props. */
19
+ export interface TextareaProps extends Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'style' | 'className' | 'cols' | 'rows' | 'required' | 'defaultValue' | 'value'> {
20
+ /**
21
+ * 입력 영역 외형 타입.
22
+ * - `outline`: 테두리로 영역 구분
23
+ * - `fill`: 배경 채움 + 테두리로 영역 구분
24
+ * @defaultValue `'outline'`
25
+ */
26
+ designType?: DesignType;
27
+ /**
28
+ * 입력 영역 텍스트 크기.
29
+ * - `default`: 14px
30
+ * - `large`: 16px
31
+ * @defaultValue `'default'`
32
+ */
33
+ size?: TextareaSize;
34
+ /**
35
+ * 상태 variant.
36
+ * `errorMessage`가 있으면 `error`가 우선 적용된다.
37
+ * 미지정 시 값 존재 여부에 따라 `basic`/`typed`를 자동 계산한다.
38
+ * 실제 포커스 시각 상태는 CSS `:focus-within`으로 처리하므로,
39
+ * `onFocus`는 포커스 상태를 강제로 보여줘야 할 때만 사용한다.
40
+ */
41
+ variant?: TextareaVariant;
42
+ /**
43
+ * 입력 영역에 노출할 줄 수.
44
+ * 이 값이 곧 고정 높이가 되며, 내용이 넘치면 내부 스크롤로 처리한다.
45
+ * Text Field와 구분하기 위해 2줄 미만은 권장하지 않는다.
46
+ * @defaultValue `2`
47
+ */
48
+ minRows?: number;
49
+ /**
50
+ * 루트 너비.
51
+ * 미지정 시 `100%`가 적용된다.
52
+ */
53
+ width?: number | string;
54
+ /**
55
+ * Textarea 값.
56
+ * 이 컴포넌트는 controlled 방식만 지원하므로 필수로 전달해야 한다.
57
+ */
58
+ value: string;
59
+ /**
60
+ * 상단 heading(라벨) 표시 여부.
61
+ * @defaultValue `true`
62
+ */
63
+ heading?: boolean;
64
+ /**
65
+ * 상단 heading(라벨) 텍스트/노드.
66
+ * @defaultValue `'Label'`
67
+ */
68
+ headingContent?: ReactNode;
69
+ /**
70
+ * 필수 입력 여부.
71
+ * `true`면 textarea의 `required` 속성을 적용하고, heading 노출 시 `*`를 함께 표시한다.
72
+ * @defaultValue `false`
73
+ */
74
+ required?: boolean;
75
+ /**
76
+ * 하단 안내 문구
77
+ * - `false`: 안내 문구 없음
78
+ * - `true`: 기본 안내 문구 사용
79
+ * - `ReactNode`: 커스텀 안내 문구 사용
80
+ *
81
+ * 주의: `error`/`success` 상태에서는 `errorMessage`/`successMessage`가 우선 노출된다.
82
+ * 노출할 문구가 없으면 문구 영역(`<p>`) 자체를 렌더링하지 않는다.
83
+ * @defaultValue `false`
84
+ */
85
+ description?: ReactNode | boolean;
86
+ /**
87
+ * 에러 문구.
88
+ * 전달되면 `variant` 지정 여부와 관계없이 `error` 상태가 우선 적용된다.
89
+ * 빈 문자열, 공백 문자열, 내용 없는 JSX 래퍼는 에러 문구로 간주하지 않는다.
90
+ */
91
+ errorMessage?: ReactNode;
92
+ /**
93
+ * 성공 문구.
94
+ * `variant === 'success'`일 때만 노출되며, 상태를 바꾸지는 않는다.
95
+ * (성공 판정 시점은 소비처의 유효성 검사 결과에 맡긴다)
96
+ */
97
+ successMessage?: ReactNode;
98
+ /**
99
+ * 입력 영역 내부 하단 영역(글자 수 · 보조 액션) 노출 여부.
100
+ * @defaultValue `false`
101
+ */
102
+ bottom?: boolean;
103
+ /**
104
+ * 하단 영역 좌측 글자 수 카운터(`현재/최대`)를 **상시** 노출할지 여부.
105
+ * (피그마 `Leading Content`에 대응)
106
+ *
107
+ * `false`여도 한도까지 남은 글자 수가 100자 이하가 되면 카운터가 나타난다.
108
+ * 곧 입력이 막힌다는 것을 사용자가 미리 인지할 수 있게 하기 위함이다.
109
+ * (기본값 기준 1400자부터 `1400/1500`)
110
+ *
111
+ * 주의:
112
+ * - `bottom`이 `false`면 하단 영역 자체가 없으므로 표시되지 않는다.
113
+ * - 분모가 필요하므로 `maxLength`가 항상 적용된다. 즉 입력 길이 제한이 늘 걸린다.
114
+ * @defaultValue `false`
115
+ */
116
+ leadingContent?: boolean;
117
+ /**
118
+ * 최대 입력 글자 수. (textarea 네이티브 속성)
119
+ * 도달하면 추가 입력이 차단되고, 카운터가 노출될 때 분모로 표시된다.
120
+ *
121
+ * 미지정 시 기본값은 `leadingContent`에 따라 달라진다.
122
+ * - `true`: `100` (짧은 입력을 전제)
123
+ * - `false`: `1500` (긴 글 입력을 전제)
124
+ */
125
+ maxLength?: number;
126
+ /**
127
+ * 하단 영역 우측 콘텐츠(보조 액션 버튼 등).
128
+ * 버튼이 두 개일 경우 위계가 높은 버튼을 우측에 둔다.
129
+ */
130
+ trailingContent?: ReactNode;
131
+ /** 루트 컨테이너에 적용할 추가 style */
132
+ customStyle?: CSSProperties;
133
+ }
package/dist/index.d.ts CHANGED
@@ -2,10 +2,12 @@ import "./styles/variables.css";
2
2
  import "./styles/font-metrics.css";
3
3
  export { default as FillButton } from "./components/Button/Fill/FillButton";
4
4
  export { default as OutlineButton } from "./components/Button/Outline/OutlineButton";
5
+ export { default as TertiaryButton } from "./components/Button/Tertiary/TertiaryButton";
5
6
  export { default as TextButton } from "./components/Button/Text/TextButton";
6
7
  export { default as WeakButton } from "./components/Button/Weak/WeakButton";
7
8
  export type { FillButtonProps, FillButtonSize, FillButtonVariant, FillButtonWidthType, } from "./components/Button/Fill/FillButton";
8
9
  export type { OutlineButtonProps, OutlineButtonSize, OutlineButtonVariant, OutlineButtonWidthType, } from "./components/Button/Outline/OutlineButton";
10
+ export type { TertiaryButtonProps, TertiaryButtonSize, TertiaryButtonVariant, } from "./components/Button/Tertiary/TertiaryButton";
9
11
  export type { TextButtonProps, TextButtonSize, TextButtonVariant, TextButtonWidthType, } from "./components/Button/Text/TextButton";
10
12
  export type { WeakButtonProps, WeakButtonSize, WeakButtonVariant, WeakButtonWidthType, } from "./components/Button/Weak/WeakButton";
11
13
  export { default as BasicIconButton } from "./components/IconButton/BasicIcon/BasicIconButton";
@@ -18,6 +20,8 @@ export { default as Input } from "./components/Input/Input";
18
20
  export { default as SearchBar } from "./components/SearchBar/SearchBar";
19
21
  export type { InputProps, InputSize, DesignType, InputVariant, } from "./components/Input/Input";
20
22
  export type { SearchBarProps, SearchBarSize, SearchBarVariant, } from "./components/SearchBar/SearchBar";
23
+ export { default as Textarea } from "./components/Textarea/Textarea";
24
+ export type { TextareaProps, TextareaSize, TextareaVariant, } from "./components/Textarea/Textarea";
21
25
  export { default as CheckBox } from "./components/Control/CheckBox/CheckBox";
22
26
  export { default as Radio } from "./components/Control/Radio/Radio";
23
27
  export { default as ToggleSwitch } from "./components/Control/ToggleSwitch/ToggleSwitch";