@nurihaus/web-design-system 3.0.1 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. package/README.md +104 -5
  2. package/dist/.storybook/story-kit.d.ts +18 -0
  3. package/dist/ai/components.md +1036 -0
  4. package/dist/ai/tokens.md +344 -0
  5. package/dist/avatar.js +336 -0
  6. package/dist/breadcrumb.js +364 -0
  7. package/dist/check-box.js +360 -0
  8. package/dist/chunk-149.js +57 -0
  9. package/dist/chunk-298.js +34 -0
  10. package/dist/chunk-302.js +105 -0
  11. package/dist/chunk-309.js +36 -0
  12. package/dist/chunk-364.js +126 -0
  13. package/dist/chunk-387.js +56 -0
  14. package/dist/chunk-436.js +33 -0
  15. package/dist/chunk-558.js +200 -0
  16. package/dist/chunk-563.js +181 -0
  17. package/dist/chunk-657.js +143 -0
  18. package/dist/chunk-713.js +38 -0
  19. package/dist/chunk-739.js +68 -0
  20. package/dist/chunk-77.js +34 -0
  21. package/dist/chunk-777.js +117 -0
  22. package/dist/chunk-910.js +64 -0
  23. package/dist/chunk-917.js +35 -0
  24. package/dist/chunk-933.js +70 -0
  25. package/dist/chunk-976.js +29 -0
  26. package/dist/collapsible-menu.js +394 -0
  27. package/dist/components/avatar/avatar-props.d.ts +2 -6
  28. package/dist/components/avatar/avatar-type.d.ts +4 -5
  29. package/dist/components/avatar/avatar.d.ts +1 -0
  30. package/dist/components/avatar/avatar.stories.d.ts +17 -1
  31. package/dist/components/badge/badge-props.d.ts +17 -80
  32. package/dist/components/badge/badge-type.d.ts +38 -2
  33. package/dist/components/badge/badge.stories.d.ts +25 -2
  34. package/dist/components/badge/base-badge.d.ts +2 -1
  35. package/dist/components/badge/icon-text-badge.d.ts +3 -21
  36. package/dist/components/badge/icon-text-option-badge.d.ts +2 -15
  37. package/dist/components/badge/option-badge.d.ts +3 -20
  38. package/dist/components/breadcrumb/base-breadcrumb.d.ts +19 -0
  39. package/dist/components/breadcrumb/breadcrumb-props.d.ts +4 -0
  40. package/dist/components/breadcrumb/breadcrumb-type.d.ts +33 -0
  41. package/dist/components/breadcrumb/breadcrumb.d.ts +4 -0
  42. package/dist/components/breadcrumb/breadcrumb.stories.d.ts +25 -0
  43. package/dist/components/button/button-props.d.ts +2 -7
  44. package/dist/components/button/button-type.d.ts +11 -3
  45. package/dist/components/button/button.stories.d.ts +13 -10
  46. package/dist/components/button/cta-button.d.ts +3 -12
  47. package/dist/components/check-box/base-check-box.d.ts +9 -5
  48. package/dist/components/check-box/check-box-props.d.ts +2 -7
  49. package/dist/components/check-box/check-box-type.d.ts +11 -4
  50. package/dist/components/check-box/check-box.d.ts +3 -10
  51. package/dist/components/check-box/check-box.stories.d.ts +19 -4
  52. package/dist/components/collapsible-menu/base-collapsible-menu.d.ts +9 -5
  53. package/dist/components/collapsible-menu/collapsible-menu-props.d.ts +3 -12
  54. package/dist/components/collapsible-menu/collapsible-menu-type.d.ts +33 -9
  55. package/dist/components/collapsible-menu/collapsible-menu.d.ts +3 -23
  56. package/dist/components/collapsible-menu/collapsible-menu.stories.d.ts +20 -4
  57. package/dist/components/drawer/base-drawer.d.ts +15 -0
  58. package/dist/components/drawer/drawer-props.d.ts +11 -0
  59. package/dist/components/drawer/drawer-type.d.ts +28 -0
  60. package/dist/components/drawer/drawer.d.ts +4 -0
  61. package/dist/components/drawer/drawer.stories.d.ts +34 -0
  62. package/dist/components/drop-down/base-drop-down.d.ts +11 -3
  63. package/dist/components/drop-down/drop-down-props.d.ts +3 -12
  64. package/dist/components/drop-down/drop-down-type.d.ts +34 -15
  65. package/dist/components/drop-down/drop-down.d.ts +3 -32
  66. package/dist/components/drop-down/drop-down.stories.d.ts +23 -4
  67. package/dist/components/elements/text/base-text.d.ts +3 -0
  68. package/dist/components/elements/text/text-props.d.ts +16 -0
  69. package/dist/components/elements/text/text-type.d.ts +6 -2
  70. package/dist/components/elements/text/text.d.ts +2 -1
  71. package/dist/components/elements/text/text.stories.d.ts +43 -3
  72. package/dist/components/loading/index.d.ts +6 -6
  73. package/dist/components/loading/inline-loading/base-inline-loading.d.ts +3 -0
  74. package/dist/components/loading/inline-loading/inline-loading-props.d.ts +9 -0
  75. package/dist/components/loading/inline-loading/inline-loading-type.d.ts +8 -6
  76. package/dist/components/loading/inline-loading/inline-loading.d.ts +3 -2
  77. package/dist/components/loading/inline-loading/inline-loading.stories.d.ts +5 -9
  78. package/dist/components/loading/overlay-loading/base-overlay-loading.d.ts +3 -0
  79. package/dist/components/loading/overlay-loading/overlay-loading-props.d.ts +7 -0
  80. package/dist/components/loading/overlay-loading/overlay-loading-type.d.ts +6 -3
  81. package/dist/components/loading/overlay-loading/overlay-loading.d.ts +3 -3
  82. package/dist/components/loading/overlay-loading/overlay-loading.stories.d.ts +8 -8
  83. package/dist/components/loading/spinner/base-spinner.d.ts +4 -0
  84. package/dist/components/loading/spinner/spinner-props.d.ts +1 -0
  85. package/dist/components/loading/spinner/spinner-type.d.ts +4 -4
  86. package/dist/components/loading/spinner/spinner.d.ts +2 -2
  87. package/dist/components/loading/spinner/spinner.stories.d.ts +5 -12
  88. package/dist/components/modal/base-modal.d.ts +21 -6
  89. package/dist/components/modal/modal-props.d.ts +5 -7
  90. package/dist/components/modal/modal-type.d.ts +34 -10
  91. package/dist/components/modal/modal-with-double-button.d.ts +4 -16
  92. package/dist/components/modal/modal-with-double-button.stories.d.ts +22 -0
  93. package/dist/components/modal/modal.d.ts +4 -0
  94. package/dist/components/modal/modal.stories.d.ts +22 -4
  95. package/dist/components/pagination/base-pagination.d.ts +9 -0
  96. package/dist/components/pagination/pagination-props.d.ts +4 -0
  97. package/dist/components/pagination/pagination-type.d.ts +20 -0
  98. package/dist/components/pagination/pagination.d.ts +4 -0
  99. package/dist/components/pagination/pagination.stories.d.ts +25 -0
  100. package/dist/components/radio/base-radio.d.ts +9 -5
  101. package/dist/components/radio/radio-props.d.ts +1 -0
  102. package/dist/components/radio/radio-type.d.ts +6 -8
  103. package/dist/components/radio/radio.d.ts +2 -2
  104. package/dist/components/radio/radio.stories.d.ts +13 -4
  105. package/dist/components/skeleton/base-skeleton.d.ts +4 -0
  106. package/dist/components/skeleton/skeleton-props.d.ts +15 -0
  107. package/dist/components/skeleton/skeleton-type.d.ts +12 -0
  108. package/dist/components/skeleton/skeleton.d.ts +4 -0
  109. package/dist/components/skeleton/skeleton.stories.d.ts +327 -0
  110. package/dist/components/switch/base-switch.d.ts +11 -0
  111. package/dist/components/switch/switch-props.d.ts +4 -0
  112. package/dist/components/switch/switch-type.d.ts +19 -0
  113. package/dist/components/switch/switch.d.ts +4 -0
  114. package/dist/components/switch/switch.stories.d.ts +24 -0
  115. package/dist/components/table/base-table.d.ts +4 -5
  116. package/dist/components/table/data-table.d.ts +5 -22
  117. package/dist/components/table/table-type.d.ts +22 -1
  118. package/dist/components/table/table.stories.d.ts +19 -3
  119. package/dist/components/tabs/base-tabs.d.ts +7 -7
  120. package/dist/components/tabs/tabs-props.d.ts +1 -1
  121. package/dist/components/tabs/tabs-type.d.ts +9 -13
  122. package/dist/components/tabs/tabs.d.ts +3 -3
  123. package/dist/components/tabs/tabs.stories.d.ts +16 -10
  124. package/dist/components/text-area/base-text-area.d.ts +10 -0
  125. package/dist/components/text-area/text-area-props.d.ts +12 -0
  126. package/dist/components/text-area/text-area-type.d.ts +34 -0
  127. package/dist/components/text-area/text-area.d.ts +4 -0
  128. package/dist/components/text-area/text-area.stories.d.ts +32 -0
  129. package/dist/components/text-field/base-text-field.d.ts +5 -3
  130. package/dist/components/text-field/text-field-props.d.ts +3 -12
  131. package/dist/components/text-field/text-field-type.d.ts +14 -0
  132. package/dist/components/text-field/text-field.d.ts +3 -16
  133. package/dist/components/text-field/text-field.stories.d.ts +24 -0
  134. package/dist/components/toast/base-toast.d.ts +20 -0
  135. package/dist/components/toast/toast-props.d.ts +18 -0
  136. package/dist/components/toast/toast-type.d.ts +45 -0
  137. package/dist/components/toast/toast.d.ts +5 -0
  138. package/dist/components/toast/toast.stories.d.ts +310 -0
  139. package/dist/components/tooltip/base-tooltip.d.ts +13 -3
  140. package/dist/components/tooltip/tooltip-props.d.ts +3 -2
  141. package/dist/components/tooltip/tooltip-type.d.ts +17 -5
  142. package/dist/components/tooltip/tooltip.d.ts +4 -7
  143. package/dist/components/tooltip/tooltip.stories.d.ts +21 -4
  144. package/dist/components.css +3017 -0
  145. package/dist/cta-button.js +237 -0
  146. package/dist/data-table.js +386 -0
  147. package/dist/drawer.js +390 -0
  148. package/dist/drop-down.js +419 -0
  149. package/dist/helpers/hooks/useExternalState.d.ts +5 -0
  150. package/dist/helpers/props/common-props.d.ts +7 -0
  151. package/dist/helpers/types/common-props.d.ts +8 -0
  152. package/dist/icon-text-badge.js +294 -0
  153. package/dist/icon-text-option-badge.js +270 -0
  154. package/dist/index.d.ts +33 -1
  155. package/dist/index.js +4179 -10180
  156. package/dist/loading.js +383 -0
  157. package/dist/modal-with-double-button.js +332 -0
  158. package/dist/modal.js +288 -0
  159. package/dist/option-badge.js +293 -0
  160. package/dist/pagination.js +412 -0
  161. package/dist/radio.js +363 -0
  162. package/dist/reset.css +45 -0
  163. package/dist/skeleton.js +331 -0
  164. package/dist/styles.css +1386 -248
  165. package/dist/switch.js +349 -0
  166. package/dist/tabs.js +421 -0
  167. package/dist/text-area.js +374 -0
  168. package/dist/text-field.js +369 -0
  169. package/dist/text.js +233 -0
  170. package/dist/toast.js +496 -0
  171. package/dist/tokens.css +100 -0
  172. package/dist/tokens.js +4 -6
  173. package/dist/tooltip.js +360 -0
  174. package/package.json +123 -14
  175. package/dist/components/text-field/text-filed.stories.d.ts +0 -8
@@ -0,0 +1,1036 @@
1
+ <!-- scripts/build-ai-docs.mjs 가 만든다. props 표는 타입에서, 설명은 각 컴포넌트 폴더의 usage.md 에서 온다. -->
2
+
3
+ # @nurihaus/web-design-system 컴포넌트
4
+
5
+ 화면을 만들 때 아래 컴포넌트가 있으면 직접 만들지 말고 이것을 쓴다. 색·간격·글자는 같은 폴더의 `tokens.md` 변수만 쓴다.
6
+
7
+ ## CSS 가져오기
8
+
9
+ 앱에서 한 번만 가져온다. 둘 중 하나를 고른다.
10
+
11
+ ```ts
12
+ import '@nurihaus/web-design-system/styles.css'; // 토큰 + 글꼴 스택 + 리셋 + 컴포넌트. 글꼴 파일은 앱이 불러온다
13
+
14
+ import '@nurihaus/web-design-system/tokens.css'; // .nuri-theme 글꼴 스택 없이 쓸 때
15
+ import '@nurihaus/web-design-system/reset.css';
16
+ import '@nurihaus/web-design-system/components.css';
17
+ ```
18
+
19
+ ## 공통 규칙
20
+
21
+ 모든 컴포넌트에 같이 적용된다. 컴포넌트 설명보다 먼저 읽는다.
22
+
23
+ **size 는 전역 이름이다.** `xs · s · m · l · xl` 다섯 개뿐이고, 컴포넌트는 그중 지원하는 것만 받는다(props 표 참고). 같은 글자는 한 줄에 나란히 놓이는 컨트롤(CTAButton · DropDown · TextField)에서 같은 높이다. px 는 `tokens.md` 의 `--control-height-*` 같은 변수에 있고, 컴포넌트에 숫자로 높이를 주지 않는다.
24
+
25
+ **색은 variant 가 정한다.** CTAButton · OptionBadge · IconTextBadge · IconTextOptionBadge 는 `variant` 로 글자·배경·테두리 색이 묶여서 정해진다. 색 prop 은 없다. 색을 받는 컴포넌트는 Text(`color`, 토큰 이름만) 하나다. Spinner · InlineLoading · OverlayLoading 은 CSS `color` 를 따라가므로 `className` 이나 `style` 의 `color` 로 바꾼다.
26
+
27
+ **커스텀은 `className` 과 `style` 로 한다.** 모든 컴포넌트가 둘을 받아 루트 요소에 붙인다(TextField 는 입력 상자, DataTable 은 `<table>`). variant 가 정한 색을 className 으로 덮으려면 `.cta-button.variant-primary` 보다 구체적인 선택자가 필요하다. 색·간격·글자 값은 `tokens.md` 의 변수만 쓴다.
28
+
29
+ **상태는 prop 으로 넘긴다.** `disabled`, `loading`, `isError`, `open`, `checked` 처럼 컴포넌트가 받는 상태 prop 을 쓰고, 클래스를 직접 붙여 상태를 흉내 내지 않는다. 값이 있는 컴포넌트는 `value`/`onValueChange`(제어) 또는 `defaultValue`(비제어) 둘 중 하나로 쓴다.
30
+
31
+ **Tooltip 이 여러 개면 `TooltipProvider` 로 감싼다.** 앱 루트에 한 번 두면 된다. 조건이 맞을 때만 띄우려면 `open` 을 제어하지 말고 `disabled` 를 쓴다.
32
+
33
+ **없는 것은 지어내지 않는다.** 이 문서 끝의 "아직 없는 것" 표에 있는 컴포넌트는 없다. 없는 prop · variant · 토큰 이름도 만들지 않는다. 틀린 variant 나 size 값은 타입 오류가 난다.
34
+
35
+ ## 목록
36
+
37
+ - [Avatar](#avatar)
38
+ - [CTAButton](#ctabutton)
39
+ - [OptionBadge](#optionbadge)
40
+ - [IconTextBadge](#icontextbadge)
41
+ - [IconTextOptionBadge](#icontextoptionbadge)
42
+ - [DataTable](#datatable)
43
+ - [TextField](#textfield)
44
+ - [Radio](#radio)
45
+ - [Checkbox](#checkbox)
46
+ - [Switch](#switch)
47
+ - [TextArea](#textarea)
48
+ - [Toast](#toast)
49
+ - [ToastProvider](#toastprovider)
50
+ - [Pagination](#pagination)
51
+ - [Modal](#modal)
52
+ - [Drawer](#drawer)
53
+ - [Skeleton](#skeleton)
54
+ - [Breadcrumb](#breadcrumb)
55
+ - [DropDown](#dropdown)
56
+ - [CollapsibleMenu](#collapsiblemenu)
57
+ - [Text](#text)
58
+ - [Tooltip](#tooltip)
59
+ - [TooltipProvider](#tooltipprovider)
60
+ - [Tabs](#tabs)
61
+ - [ModalWithDoubleButton](#modalwithdoublebutton)
62
+ - [Spinner](#spinner)
63
+ - [InlineLoading](#inlineloading)
64
+ - [OverlayLoading](#overlayloading)
65
+
66
+ ## Avatar
67
+
68
+ ```ts
69
+ import { Avatar } from '@nurihaus/web-design-system/avatar'; // 이 컴포넌트만 싣는다 (권장)
70
+ import { Avatar } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
71
+ ```
72
+
73
+ **언제 쓴다.** 사용자 프로필 이미지를 원형으로 보여줄 때 쓴다. `src`가 없거나 이미지를 불러오지 못하면 기본 아바타 아이콘이 나오고, `badgeOption`에 넣은 요소는 오른쪽 아래에 겹쳐 표시된다.
74
+
75
+ **쓰지 않는다.** 프로필이 아닌 일반 이미지나 사각형 썸네일에는 쓰지 않는다. 아이콘과 이름을 한 줄로 묶은 라벨이 필요하면 IconTextBadge를 쓴다.
76
+
77
+ ```tsx
78
+ <Avatar size="m" src="https://placehold.co/80x80" alt="프로필 이미지" />
79
+ ```
80
+
81
+ ### props
82
+
83
+ | prop | 타입 | 필수 | 기본값 |
84
+ | --- | --- | --- | --- |
85
+ | `size` | `'s' \| 'm' \| 'l' \| 'xl'` | 예 | `'m'` |
86
+ | `badgeOption` | `ReactNode` | | |
87
+ | `src` | `string` | | |
88
+ | `alt` | `string` | | |
89
+ | `className` | `string` | | |
90
+ | `style` | `CSSProperties` | | |
91
+
92
+ 이 밖에 React·HTML 기본 속성 2개를 받는다.
93
+
94
+ ## CTAButton
95
+
96
+ ```ts
97
+ import { CTAButton } from '@nurihaus/web-design-system/cta-button'; // 이 컴포넌트만 싣는다 (권장)
98
+ import { CTAButton } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
99
+ ```
100
+
101
+ **언제 쓴다.** 저장, 확인, 다음 단계처럼 사용자가 실행하는 동작 버튼에 쓴다. `variant`로 강조 단계를 고르고, `loading`이 true면 `text` 대신 Spinner가 표시된다.
102
+
103
+ **쓰지 않는다.** 확인과 취소 두 버튼을 가진 대화상자는 버튼을 직접 배치하지 말고 ModalWithDoubleButton을 쓴다. 화면 안의 패널 전환은 Tabs, 사이드 메뉴 항목은 CollapsibleMenu를 쓴다.
104
+
105
+ ```tsx
106
+ <CTAButton variant="primary" size="m" text="저장" onClick={() => {}} />
107
+ ```
108
+
109
+ ### props
110
+
111
+ | prop | 타입 | 필수 | 기본값 |
112
+ | --- | --- | --- | --- |
113
+ | `size` | `'xs' \| 's' \| 'm' \| 'l' \| 'xl'` | | `'m'` |
114
+ | `text` | `ReactNode` | 예 | |
115
+ | `variant` | `'neutral' \| 'primary' \| 'outlined' \| 'secondary' \| 'ghost' \| 'confirm'` | | `'primary'` |
116
+ | `leftIcon` | `ReactNode` | | |
117
+ | `rightIcon` | `ReactNode` | | |
118
+ | `loading` | `boolean` | | |
119
+
120
+ 이 밖에 React·HTML 기본 속성 276개를 받는다.
121
+
122
+ ## OptionBadge
123
+
124
+ ```ts
125
+ import { OptionBadge } from '@nurihaus/web-design-system/option-badge'; // 이 컴포넌트만 싣는다 (권장)
126
+ import { OptionBadge } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
127
+ ```
128
+
129
+ **언제 쓴다.** 상태값이나 분류처럼 짧은 값 하나(`option`)를 라벨로 보여줄 때 쓴다. `variant`로 색을 고른다(`neutral`·`primary`·`danger`·`outlined`). 색 prop 은 없다. `tooltip`을 주면 마우스를 올렸을 때 설명이 뜬다.
130
+
131
+ **쓰지 않는다.** 아이콘이 함께 필요하면 IconTextBadge를, 아이콘과 텍스트 오른쪽에 수치 같은 부가 값까지 붙여야 하면 IconTextOptionBadge를 쓴다. 클릭 동작이 필요한 요소에는 CTAButton을 쓴다.
132
+
133
+ ```tsx
134
+ <OptionBadge option="진행 중" variant="primary" size="s" width="fit-content" />
135
+ ```
136
+
137
+ ### props
138
+
139
+ | prop | 타입 | 필수 | 기본값 |
140
+ | --- | --- | --- | --- |
141
+ | `variant` | `'neutral' \| 'primary' \| 'danger' \| 'outlined'` | | `'neutral'` |
142
+ | `width` | `string \| number` | | `'100%'` |
143
+ | `size` | `'xs' \| 's' \| 'm' \| 'l' \| 'xl'` | | `'xl'` |
144
+ | `option` | `ReactNode` | 예 | |
145
+ | `style` | `CSSProperties` | | |
146
+ | `tooltip` | `{ content: ReactNode; position?: 'bottom' \| 'left' \| 'right' \| 'top' \| undefined; delayDuration?: number \| undefined; }` | | |
147
+ | `asChild` | `boolean` | | |
148
+ | `className` | `string` | | |
149
+
150
+ 이 밖에 React·HTML 기본 속성 2개를 받는다.
151
+
152
+ ## IconTextBadge
153
+
154
+ ```ts
155
+ import { IconTextBadge } from '@nurihaus/web-design-system/icon-text-badge'; // 이 컴포넌트만 싣는다 (권장)
156
+ import { IconTextBadge } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
157
+ ```
158
+
159
+ **언제 쓴다.** 아이콘과 텍스트를 한 줄로 묶어 보여줄 때 쓴다. SNS 채널처럼 아이콘으로 구분되는 값에 맞고, `tooltip`을 주면 마우스를 올렸을 때 설명이 뜬다.
160
+
161
+ **쓰지 않는다.** 아이콘 없이 텍스트만 있으면 OptionBadge를 쓴다. 오른쪽에 부가 값을 붙여야 하면 IconTextOptionBadge를 쓴다.
162
+
163
+ ```tsx
164
+ <IconTextBadge
165
+ icon={<img src="https://placehold.co/18x18" alt="" />}
166
+ text="Instagram"
167
+ size="s"
168
+ width="fit-content"
169
+ tooltip={{ content: '연결된 채널', position: 'top' }}
170
+ />
171
+ ```
172
+
173
+ ### props
174
+
175
+ | prop | 타입 | 필수 | 기본값 |
176
+ | --- | --- | --- | --- |
177
+ | `variant` | `'neutral' \| 'primary' \| 'danger' \| 'outlined'` | | `'neutral'` |
178
+ | `width` | `string \| number` | | `'100%'` |
179
+ | `size` | `'xs' \| 's' \| 'm' \| 'l' \| 'xl'` | | `'xl'` |
180
+ | `icon` | `ReactNode` | 예 | |
181
+ | `text` | `ReactNode` | 예 | |
182
+ | `style` | `CSSProperties` | | |
183
+ | `tooltip` | `{ content: ReactNode; position?: 'bottom' \| 'left' \| 'right' \| 'top' \| undefined; delayDuration?: number \| undefined; }` | | |
184
+ | `asChild` | `boolean` | | |
185
+ | `className` | `string` | | |
186
+
187
+ 이 밖에 React·HTML 기본 속성 2개를 받는다.
188
+
189
+ ## IconTextOptionBadge
190
+
191
+ ```ts
192
+ import { IconTextOptionBadge } from '@nurihaus/web-design-system/icon-text-option-badge'; // 이 컴포넌트만 싣는다 (권장)
193
+ import { IconTextOptionBadge } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
194
+ ```
195
+
196
+ **언제 쓴다.** 왼쪽에 아이콘과 텍스트, 오른쪽에 부가 값(`option`)을 함께 보여줄 때 쓴다. 채널 이름과 팔로워 수를 한 배지에 담는 경우가 이에 해당한다.
197
+
198
+ **쓰지 않는다.** 이 배지는 `tooltip`을 받지 않으므로 툴팁이 필요하면 IconTextBadge를 쓴다. 부가 값이 없으면 IconTextBadge나 OptionBadge를 쓴다.
199
+
200
+ ```tsx
201
+ <IconTextOptionBadge
202
+ icon={<img src="https://placehold.co/18x18" alt="" />}
203
+ text="Instagram"
204
+ option="2.4K"
205
+ size="s"
206
+ width="fit-content"
207
+ />
208
+ ```
209
+
210
+ ### props
211
+
212
+ | prop | 타입 | 필수 | 기본값 |
213
+ | --- | --- | --- | --- |
214
+ | `variant` | `'neutral' \| 'primary' \| 'danger' \| 'outlined'` | | `'neutral'` |
215
+ | `width` | `string \| number` | | `'100%'` |
216
+ | `size` | `'xs' \| 's' \| 'm' \| 'l' \| 'xl'` | | `'xl'` |
217
+ | `icon` | `ReactNode` | 예 | |
218
+ | `text` | `ReactNode` | 예 | |
219
+ | `option` | `ReactNode` | 예 | |
220
+ | `style` | `CSSProperties` | | |
221
+ | `className` | `string` | | |
222
+
223
+ 이 밖에 React·HTML 기본 속성 2개를 받는다.
224
+
225
+ ## DataTable
226
+
227
+ ```ts
228
+ import { DataTable } from '@nurihaus/web-design-system/data-table'; // 이 컴포넌트만 싣는다 (권장)
229
+ import { DataTable } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
230
+ ```
231
+
232
+ **언제 쓴다.** 여러 레코드를 열 단위로 나열하는 목록 화면에 쓴다. 이름과 달리 `rows`는 헤더 열 정의(`id`, 헤더 내용)이고 `columns`는 데이터 행 배열이며, 각 데이터 행은 `rows`의 모든 `id`를 키로 가진다. 열 너비는 셀 내용의 너비를 따르므로 같은 열의 헤더와 셀 내용에 같은 너비를 준다.
233
+
234
+ **쓰지 않는다.** 정렬, 페이지네이션, 행 선택, 빈 상태 표시는 내장되어 있지 않으므로 데이터를 바깥에서 가공해 넘기고 빈 목록은 따로 그린다. 헤더는 위에 고정되고 `fixedRowsIndex`로 왼쪽 열을 고정할 수 있지만, 그 외의 표 동작이 필요하면 직접 만든다.
235
+
236
+ ```tsx
237
+ <DataTable
238
+ rows={[
239
+ { id: 'name', value: <div style={{ width: '160px' }}>이름</div> },
240
+ { id: 'status', value: <div style={{ width: '120px' }}>상태</div> },
241
+ ] as const}
242
+ columns={[
243
+ {
244
+ name: { value: <div style={{ width: '160px' }}>홍길동</div> },
245
+ status: { value: <div style={{ width: '120px' }}>대기</div>, onClickCell: () => {} },
246
+ },
247
+ ]}
248
+ fixedRowsIndex={1}
249
+ />
250
+ ```
251
+
252
+ ### props
253
+
254
+ | prop | 타입 | 필수 | 기본값 |
255
+ | --- | --- | --- | --- |
256
+ | `rows` | `T` | 예 | |
257
+ | `columns` | `ColumnsType<T>` | 예 | |
258
+ | `fixedRowsIndex` | `number` | | `0` |
259
+ | `headerHeight` | `number` | | `85` |
260
+ | `rowHeight` | `number` | | `60` |
261
+ | `cellHeight` | `number` | | |
262
+ | `className` | `string` | | |
263
+
264
+ 이 밖에 React·HTML 기본 속성 2개를 받는다.
265
+
266
+ ## TextField
267
+
268
+ ```ts
269
+ import { TextField } from '@nurihaus/web-design-system/text-field'; // 이 컴포넌트만 싣는다 (권장)
270
+ import { TextField } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
271
+ ```
272
+
273
+ **언제 쓴다.** 한 줄 텍스트를 입력받을 때 쓴다. 위 라벨(`title`), 아래 안내 문구(`helperText`), 좌우 아이콘을 함께 붙일 수 있고, `isError`가 true면 `helperText`가 오류 스타일로 바뀐다.
274
+
275
+ **쓰지 않는다.** 여러 줄 입력은 지원하지 않는다(`<input>`만 렌더한다). 정해진 값 중에서 고르는 입력은 DropDown이나 Radio를 쓴다.
276
+
277
+ ```tsx
278
+ <TextField
279
+ size="s"
280
+ width="320px"
281
+ name="title"
282
+ title="제목"
283
+ placeholder="제목을 입력하세요"
284
+ helperText="50자 이내로 입력하세요"
285
+ onChange={() => {}}
286
+ />
287
+ ```
288
+
289
+ ### props
290
+
291
+ | prop | 타입 | 필수 | 기본값 |
292
+ | --- | --- | --- | --- |
293
+ | `className` | `string` | | |
294
+ | `size` | `'xs' \| 's' \| 'm' \| 'l' \| 'xl'` | 예 | `'s'` |
295
+ | `title` | `ReactNode` | | |
296
+ | `name` | `string` | 예 | |
297
+ | `value` | `string` | | |
298
+ | `defaultValue` | `string` | | |
299
+ | `leftIcon` | `ReactNode` | | |
300
+ | `rightIcon` | `ReactNode` | | |
301
+ | `width` | `string \| number` | 예 | `'100%'` |
302
+ | `helperText` | `ReactNode` | | |
303
+ | `isError` | `boolean` | | |
304
+
305
+ 이 밖에 React·HTML 기본 속성 289개를 받는다.
306
+
307
+ ## Radio
308
+
309
+ ```ts
310
+ import { Radio } from '@nurihaus/web-design-system/radio'; // 이 컴포넌트만 싣는다 (권장)
311
+ import { Radio } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
312
+ ```
313
+
314
+ **언제 쓴다.** 몇 개의 선택지를 모두 펼쳐 보여주고 그중 하나만 고르게 할 때 쓴다. 선택 상태는 항상 부모가 가지므로 `onChange`에서 받은 id로 `selected`를 갱신해야 선택이 바뀐다.
315
+
316
+ **쓰지 않는다.** 여러 개를 동시에 고르면 Checkbox를 쓴다. 선택지가 많아 모두 펼치기 어려우면 DropDown을 쓴다.
317
+
318
+ ```tsx
319
+ <Radio
320
+ options={[
321
+ { id: 'all', label: '전체' },
322
+ { id: 'active', label: '활성' },
323
+ { id: 'inactive', label: '비활성', disabled: true },
324
+ ]}
325
+ selected="all"
326
+ onChange={() => {}}
327
+ />
328
+ ```
329
+
330
+ ### props
331
+
332
+ | prop | 타입 | 필수 | 기본값 |
333
+ | --- | --- | --- | --- |
334
+ | `options` | `{ id: string; label: ReactNode; disabled?: boolean \| undefined; }[]` | 예 | |
335
+ | `selected` | `string \| null` | | |
336
+ | `onChange` | `((id: string \| null) => void)` | | |
337
+
338
+ 이 밖에 React·HTML 기본 속성 2개를 받는다.
339
+
340
+ ## Checkbox
341
+
342
+ ```ts
343
+ import { Checkbox } from '@nurihaus/web-design-system/check-box'; // 이 컴포넌트만 싣는다 (권장)
344
+ import { Checkbox } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
345
+ ```
346
+
347
+ **언제 쓴다.** 동의 여부처럼 켜고 끄는 값 하나를 받거나, 목록의 항목을 각각 독립적으로 선택할 때 쓴다. `onChange`는 바뀐 체크 상태(boolean)를 받는다.
348
+
349
+ **쓰지 않는다.** 여러 선택지 중 하나만 골라야 하면 Radio를, 목록에서 값 하나를 고르면 DropDown을 쓴다.
350
+
351
+ ```tsx
352
+ <Checkbox id="agree-terms" label="약관에 동의합니다" checked={false} onChange={() => {}} />
353
+ ```
354
+
355
+ ### props
356
+
357
+ | prop | 타입 | 필수 | 기본값 |
358
+ | --- | --- | --- | --- |
359
+ | `label` | `ReactNode` | 예 | |
360
+ | `size` | `'s' \| 'm' \| 'l'` | | `'m'` |
361
+ | `className` | `string` | | |
362
+ | `onClick` | `((checked: boolean) => void)` | | |
363
+ | `checked` | `boolean` | | |
364
+ | `onChange` | `((checked: boolean) => void)` | | |
365
+ | `id` | `string` | 예 | |
366
+ | `disabled` | `boolean` | | |
367
+
368
+ 이 밖에 React·HTML 기본 속성 2개를 받는다.
369
+
370
+ ## Switch
371
+
372
+ ```ts
373
+ import { Switch } from '@nurihaus/web-design-system/switch'; // 이 컴포넌트만 싣는다 (권장)
374
+ import { Switch } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
375
+ ```
376
+
377
+ **언제 쓴다.** 알림 켜기처럼 켜고 끄는 설정 하나를 바로 반영할 때 쓴다. `onChange`는 바뀐 켜짐 상태(boolean)를 받는다. `className`·`style`은 루트 버튼에 붙는다. `label`을 주지 않으면 `aria-label`로 이름을 준다.
378
+
379
+ **쓰지 않는다.** 제출 버튼을 눌러야 반영되는 폼 항목이나 동의 여부는 Checkbox를, 여러 선택지 중 하나는 Radio를 쓴다.
380
+
381
+ ```tsx
382
+ <Switch id="push-alarm" label="푸시 알림 받기" checked={true} onChange={() => {}} />
383
+ ```
384
+
385
+ ### props
386
+
387
+ | prop | 타입 | 필수 | 기본값 |
388
+ | --- | --- | --- | --- |
389
+ | `className` | `string` | | |
390
+ | `size` | `'s' \| 'm' \| 'l'` | | `'m'` |
391
+ | `label` | `ReactNode` | | |
392
+ | `disabled` | `boolean` | | |
393
+ | `defaultChecked` | `boolean` | | |
394
+ | `id` | `string` | 예 | |
395
+ | `onChange` | `((checked: boolean) => void)` | | |
396
+ | `checked` | `boolean` | | |
397
+
398
+ 이 밖에 React·HTML 기본 속성 270개를 받는다.
399
+
400
+ ## TextArea
401
+
402
+ ```ts
403
+ import { TextArea } from '@nurihaus/web-design-system/text-area'; // 이 컴포넌트만 싣는다 (권장)
404
+ import { TextArea } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
405
+ ```
406
+
407
+ **언제 쓴다.** 메모·설명처럼 여러 줄 텍스트를 입력받을 때 쓴다. 위 라벨(`title`), 아래 안내 문구(`helperText`)를 함께 붙일 수 있고, `isError`가 true면 `helperText`가 오류 스타일로 바뀐다. 높이는 `rows`가 정하고 `size`는 글자 크기와 안쪽 여백만 바꾼다. `resize`는 `'vertical'`(기본)·`'none'` 중 고른다. `className`·`style`은 textarea를 감싸는 테두리 요소에 붙고, `ref`는 textarea 요소를 가리킨다.
408
+
409
+ **쓰지 않는다.** 한 줄 입력은 TextField를 쓴다. 정해진 값 중에서 고르는 입력은 DropDown이나 Radio를 쓴다.
410
+
411
+ ```tsx
412
+ <TextArea name="memo" title="메모" placeholder="내용을 입력하세요" rows={4} />
413
+ ```
414
+
415
+ ### props
416
+
417
+ | prop | 타입 | 필수 | 기본값 |
418
+ | --- | --- | --- | --- |
419
+ | `className` | `string` | | |
420
+ | `size` | `'s' \| 'm'` | | `'s'` |
421
+ | `title` | `ReactNode` | | |
422
+ | `name` | `string` | 예 | |
423
+ | `value` | `string` | | |
424
+ | `defaultValue` | `string` | | |
425
+ | `width` | `string \| number` | | `'100%'` |
426
+ | `resize` | `'none' \| 'vertical'` | | `'vertical'` |
427
+ | `helperText` | `ReactNode` | | |
428
+ | `isError` | `boolean` | | |
429
+
430
+ 이 밖에 React·HTML 기본 속성 275개를 받는다.
431
+
432
+ ## Toast
433
+
434
+ ```ts
435
+ import { Toast } from '@nurihaus/web-design-system/toast'; // 이 컴포넌트만 싣는다 (권장)
436
+ import { Toast } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
437
+ ```
438
+
439
+ **언제 쓴다.** 저장 완료·삭제 완료처럼 사용자의 행동 결과를 짧게 알리고 스스로 사라져야 할 때 쓴다. `title`은 필수, `description`은 한 줄 보충 설명이다. `variant`(`default · secondary · success · warning · error`)가 바탕색을 정한다. 기본은 Tooltip과 같은 진보라, `secondary`는 CTAButton secondary와 같은 연보라(어두운 글자), `success`는 초록, `warning`은 주황, `error`는 confirm 버튼과 같은 빨강이고 이 셋은 앞에 상태 아이콘(체크·느낌표·X)이 붙는다. secondary 외에는 글자가 흰색이다. `action`을 주면 되돌리기 같은 작은 버튼(CTAButton xs·neutral)이 하나 붙고 누르면 토스트가 닫힌다. `showClose={false}`로 닫기 버튼을 뺄 수 있다. `duration`(ms)은 이 토스트만의 표시 시간이다. `className`·`style`은 토스트 루트(`li`)에 붙는다. 반드시 ToastProvider 안에서 쓴다.
440
+
441
+ **쓰지 않는다.** 사용자의 확인이나 선택이 필요한 내용은 ModalWithDoubleButton을 쓴다. 입력 오류처럼 그 자리에서 고쳐야 하는 안내는 TextField의 helper text를 쓴다.
442
+
443
+ ```tsx
444
+ <Toast open title="저장했습니다" description="변경 사항이 반영됐습니다." variant="success" onOpenChange={() => {}} />
445
+ ```
446
+
447
+ ### props
448
+
449
+ | prop | 타입 | 필수 | 기본값 |
450
+ | --- | --- | --- | --- |
451
+ | `className` | `string` | | |
452
+ | `style` | `CSSProperties` | | |
453
+ | `title` | `ReactNode` | 예 | |
454
+ | `onPause` | `(() => void)` | | |
455
+ | `variant` | `'secondary' \| 'default' \| 'success' \| 'warning' \| 'error'` | | `'default'` |
456
+ | `onResume` | `(() => void)` | | |
457
+ | `open` | `boolean` | | |
458
+ | `defaultOpen` | `boolean` | | |
459
+ | `onOpenChange` | `((open: boolean) => void)` | | |
460
+ | `duration` | `number` | | |
461
+ | `description` | `ReactNode` | | |
462
+ | `action` | `ToastAction` | | |
463
+ | `showClose` | `boolean` | | `true` |
464
+
465
+ 이 밖에 React·HTML 기본 속성 262개를 받는다.
466
+
467
+ ## ToastProvider
468
+
469
+ ```ts
470
+ import { ToastProvider } from '@nurihaus/web-design-system/toast'; // 이 컴포넌트만 싣는다 (권장)
471
+ import { ToastProvider } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
472
+ ```
473
+
474
+ **언제 쓴다.** 앱 루트에 한 번 둔다. 토스트가 쌓이는 자리(Viewport)를 그리고 `position`(`top-right · bottom-right · bottom-center`, 기본 `bottom-right`)으로 화면 어느 모서리에 둘지 고른다. `duration`(ms)은 안의 모든 Toast 에 적용되는 기본 표시 시간이다(기본 5000). Toast 가 `duration`을 주면 그 값이 앞선다. `label`은 스크린 리더가 읽는 이름이고 기본은 `알림`이다. 다른 언어 서비스에서만 바꾼다.
475
+
476
+ **쓰지 않는다.** 화면마다 여러 개 두지 않는다. Provider 가 둘이면 토스트 자리가 둘로 갈린다.
477
+
478
+ ```tsx
479
+ <ToastProvider position="bottom-right" duration={5000}>
480
+ <Toast open title="저장했습니다" onOpenChange={() => {}} />
481
+ </ToastProvider>
482
+ ```
483
+
484
+ ### props
485
+
486
+ | prop | 타입 | 필수 | 기본값 |
487
+ | --- | --- | --- | --- |
488
+ | `children` | `ReactNode` | 예 | |
489
+ | `duration` | `number` | | |
490
+ | `position` | `'top-right' \| 'bottom-right' \| 'bottom-center'` | | `'bottom-right'` |
491
+ | `label` | `string` | | `'알림'` |
492
+
493
+ ## Pagination
494
+
495
+ ```ts
496
+ import { Pagination } from '@nurihaus/web-design-system/pagination'; // 이 컴포넌트만 싣는다 (권장)
497
+ import { Pagination } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
498
+ ```
499
+
500
+ **언제 쓴다.** 목록이나 검색 결과가 여러 페이지로 나뉘어 있고 사용자가 번호로 페이지를 고를 때 쓴다. `totalPages`는 필수다. 현재 페이지는 `page`(controlled)·`defaultPage`(uncontrolled)로 주고, `onPageChange`는 새 페이지 번호(1부터)를 받는다. 1과 마지막 페이지는 항상 보이고, 현재 페이지 양옆 `siblingCount`(기본 1)개를 보이며 사이가 벌어지면 `…`를 넣는다. `className`·`style`은 루트 `nav`에 붙는다. `aria-label`을 주면 nav 이름을 바꾼다(기본 "페이지 탐색").
501
+
502
+ **쓰지 않는다.** 스크롤로 계속 불러오는 목록이나 "더 보기" 버튼으로 이어 붙이는 목록에는 쓰지 않는다. 화면 안의 내용 영역을 바꾸는 탭 전환은 Tabs를 쓴다.
503
+
504
+ ```tsx
505
+ <Pagination totalPages={10} page={3} onPageChange={() => {}} />
506
+ ```
507
+
508
+ ### props
509
+
510
+ | prop | 타입 | 필수 | 기본값 |
511
+ | --- | --- | --- | --- |
512
+ | `size` | `'s' \| 'm'` | | `'m'` |
513
+ | `disabled` | `boolean` | | |
514
+ | `page` | `number` | | |
515
+ | `totalPages` | `number` | 예 | |
516
+ | `defaultPage` | `number` | | |
517
+ | `onPageChange` | `((page: number) => void)` | | |
518
+ | `siblingCount` | `number` | | `1` |
519
+
520
+ 이 밖에 React·HTML 기본 속성 266개를 받는다.
521
+
522
+ ## Modal
523
+
524
+ ```ts
525
+ import { Modal } from '@nurihaus/web-design-system/modal'; // 이 컴포넌트만 싣는다 (권장)
526
+ import { Modal } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
527
+ ```
528
+
529
+ **언제 쓴다.** 제목과 본문, 버튼 영역이 자유로운 일반 대화상자에 쓴다. `open · defaultOpen · onOpenChange`로 열고 닫고, `trigger`를 주면 그 요소를 클릭해 열린다. `title`은 필수이고 dialog의 접근성 이름이 된다. `footer`에는 CTAButton 을 넣는다. `className`·`style`은 dialog 요소(Content)에 붙는다. X 아이콘(`showCloseIcon`, 기본 true)·Escape·오버레이 클릭으로 닫힌다.
530
+
531
+ **쓰지 않는다.** 확인·취소 두 버튼으로 끝나는 확인 대화상자는 ModalWithDoubleButton 을 쓴다.
532
+
533
+ ```tsx
534
+ <Modal
535
+ open
536
+ title="필터 설정"
537
+ description="조건을 고르세요."
538
+ footer={<CTAButton text="적용" onClick={() => {}} />}
539
+ onOpenChange={() => {}}
540
+ >
541
+ 본문
542
+ </Modal>
543
+ ```
544
+
545
+ ### props
546
+
547
+ | prop | 타입 | 필수 | 기본값 |
548
+ | --- | --- | --- | --- |
549
+ | `trigger` | `ReactElement<any, string \| JSXElementConstructor<any>>` | | |
550
+ | `title` | `ReactNode` | 예 | |
551
+ | `description` | `ReactNode` | | |
552
+ | `children` | `ReactNode` | | |
553
+ | `footer` | `ReactNode` | | |
554
+ | `showCloseIcon` | `boolean` | | `true` |
555
+ | `size` | `'s' \| 'm' \| 'l'` | | `'m'` |
556
+ | `open` | `boolean` | | |
557
+ | `defaultOpen` | `boolean` | | |
558
+ | `onOpenChange` | `((open: boolean) => void)` | | |
559
+ | `className` | `string` | | |
560
+ | `style` | `CSSProperties` | | |
561
+
562
+ 이 밖에 React·HTML 기본 속성 2개를 받는다.
563
+
564
+ ## Drawer
565
+
566
+ ```ts
567
+ import { Drawer } from '@nurihaus/web-design-system/drawer'; // 이 컴포넌트만 싣는다 (권장)
568
+ import { Drawer } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
569
+ ```
570
+
571
+ **언제 쓴다.** 상세 정보나 필터처럼 현재 화면을 떠나지 않고 옆이나 아래에서 패널을 꺼내 보여줄 때 쓴다. `trigger`에 넘긴 요소를 클릭하면 열리고, `open`·`defaultOpen`·`onOpenChange`로 제어한다. `title`은 필수이며 dialog 의 접근성 이름이 된다. `side`(`right`·`left`·`bottom`, 기본 `right`)와 `size`(`s`·`m`·`l`, 기본 `m`)로 방향과 폭(bottom 은 높이)을 고른다. Escape·바깥 클릭·X 버튼으로 닫히고, `className`·`style`은 패널에 붙는다.
572
+
573
+ **쓰지 않는다.** 삭제 확인처럼 결정 하나만 받는 짧은 대화상자는 ModalWithDoubleButton 을 쓴다. 페이지 자체 내비게이션이나 항상 보이는 사이드바에는 쓰지 않는다.
574
+
575
+ ```tsx
576
+ <Drawer open title="상세 정보" side="right" onOpenChange={() => {}}>
577
+ 본문
578
+ </Drawer>
579
+ ```
580
+
581
+ ### props
582
+
583
+ | prop | 타입 | 필수 | 기본값 |
584
+ | --- | --- | --- | --- |
585
+ | `trigger` | `ReactElement<any, string \| JSXElementConstructor<any>>` | | |
586
+ | `title` | `ReactNode` | 예 | |
587
+ | `description` | `ReactNode` | | |
588
+ | `children` | `ReactNode` | | |
589
+ | `footer` | `ReactNode` | | |
590
+ | `side` | `'bottom' \| 'left' \| 'right'` | | `'right'` |
591
+ | `size` | `'s' \| 'm' \| 'l'` | | `'m'` |
592
+ | `showCloseIcon` | `boolean` | | `true` |
593
+ | `open` | `boolean` | | |
594
+ | `defaultOpen` | `boolean` | | |
595
+ | `onOpenChange` | `((open: boolean) => void)` | | |
596
+ | `className` | `string` | | |
597
+ | `style` | `CSSProperties` | | |
598
+
599
+ 이 밖에 React·HTML 기본 속성 2개를 받는다.
600
+
601
+ ## Skeleton
602
+
603
+ ```ts
604
+ import { Skeleton } from '@nurihaus/web-design-system/skeleton'; // 이 컴포넌트만 싣는다 (권장)
605
+ import { Skeleton } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
606
+ ```
607
+
608
+ **언제 쓴다.** 데이터를 기다리는 동안 들어올 내용의 자리를 같은 모양으로 비워 둘 때 쓴다. 한 줄 글은 `shape="text"`, 여러 줄은 `lines`, 이미지·카드는 `rect`에 `width`·`height`, 아바타는 `circle`에 `width`를 준다. `className`·`style`은 루트 `span`에 붙고 `width`·`height`는 루트 style 에 들어간다. 보조기기에는 숨기므로(`aria-hidden`) 불러오는 중이라는 알림은 감싸는 영역이 `aria-busy`로 알린다.
609
+
610
+ **쓰지 않는다.** 짧은 처리 중 표시는 InlineLoading, 버튼 로딩은 CTAButton의 `loading`, 화면 전체를 막아야 하면 OverlayLoading을 쓴다. 들어올 내용의 모양을 모를 때는 쓰지 않는다.
611
+
612
+ ```tsx
613
+ <Skeleton shape="text" lines={3} />
614
+ ```
615
+
616
+ ### props
617
+
618
+ | prop | 타입 | 필수 | 기본값 |
619
+ | --- | --- | --- | --- |
620
+ | `width` | `string \| number` | | `'100%'` |
621
+ | `height` | `string \| number` | | |
622
+ | `shape` | `'circle' \| 'rect' \| 'text'` | | `'text'` |
623
+ | `lines` | `number` | | `1` |
624
+ | `animated` | `boolean` | | `true` |
625
+
626
+ 이 밖에 React·HTML 기본 속성 266개를 받는다.
627
+
628
+ ## Breadcrumb
629
+
630
+ ```ts
631
+ import { Breadcrumb } from '@nurihaus/web-design-system/breadcrumb'; // 이 컴포넌트만 싣는다 (권장)
632
+ import { Breadcrumb } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
633
+ ```
634
+
635
+ **언제 쓴다.** 계층이 있는 화면에서 현재 위치와 상위 경로를 한 줄로 보여 줄 때 쓴다. `items`의 마지막 항목이 현재 위치라 링크가 아니고 `aria-current="page"`가 붙는다. `href`가 있는 항목은 `<a>` 링크로, `onItemClick`은 클릭한 항목의 `id`를 받는다. Next.js 나 React Router 안에서는 `linkComponent={Link}`를 넘겨 라우터 링크로 그린다. 넘기지 않으면 일반 `<a>`라 페이지가 새로 고쳐진다. `className`·`style`은 `nav` 루트에 붙는다.
636
+
637
+ **쓰지 않는다.** 같은 깊이의 화면을 오가는 데는 Tabs를, 사이드바 메뉴에는 CollapsibleMenu를 쓴다. 계층이 한 단계뿐이면 쓰지 않는다.
638
+
639
+ ```tsx
640
+ <Breadcrumb
641
+ items={[
642
+ { id: 'home', label: '홈', href: '/' },
643
+ { id: 'list', label: '목록', href: '/list' },
644
+ { id: 'detail', label: '상세' },
645
+ ]}
646
+ />
647
+ ```
648
+
649
+ ### props
650
+
651
+ | prop | 타입 | 필수 | 기본값 |
652
+ | --- | --- | --- | --- |
653
+ | `items` | `BreadcrumbItem[]` | 예 | |
654
+ | `size` | `'xs' \| 's' \| 'm' \| 'l' \| 'xl'` | | `'m'` |
655
+ | `onItemClick` | `((id: string) => void)` | | |
656
+ | `separator` | `ReactNode` | | |
657
+ | `linkComponent` | `ElementType` | | |
658
+ | `className` | `string` | | |
659
+ | `style` | `CSSProperties` | | |
660
+
661
+ 이 밖에 React·HTML 기본 속성 2개를 받는다.
662
+
663
+ ## DropDown
664
+
665
+ ```ts
666
+ import { DropDown } from '@nurihaus/web-design-system/drop-down'; // 이 컴포넌트만 싣는다 (권장)
667
+ import { DropDown } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
668
+ ```
669
+
670
+ **언제 쓴다.** 펼쳐지는 목록에서 값 하나를 고를 때 쓴다. 보통 `options`를 넘기고, 목록 영역을 직접 그려야 하면 `renderOption`을 쓰되 이때는 항목 선택 처리와 닫기(`close`)를 직접 해야 한다.
671
+
672
+ **쓰지 않는다.** 여러 값을 동시에 고르는 다중 선택은 지원하지 않는다. 선택지가 적어 모두 펼쳐 보여주는 편이 나으면 Radio를, 페이지 이동 메뉴는 CollapsibleMenu를 쓴다.
673
+
674
+ ```tsx
675
+ <DropDown
676
+ size="s"
677
+ width="240px"
678
+ placeholder="상태 선택"
679
+ options={[
680
+ { label: '전체', value: 'all' },
681
+ { label: '활성', value: 'active' },
682
+ { label: '비활성', value: 'inactive' },
683
+ ]}
684
+ value="all"
685
+ onValueChange={() => {}}
686
+ />
687
+ ```
688
+
689
+ ### props
690
+
691
+ | prop | 타입 | 필수 | 기본값 |
692
+ | --- | --- | --- | --- |
693
+ | `size` | `'xs' \| 's' \| 'm' \| 'l' \| 'xl'` | | `'s'` |
694
+ | `defaultValue` | `string` | | |
695
+ | `value` | `string` | | |
696
+ | `onValueChange` | `((value: string) => void)` | | |
697
+ | `renderValue` | `((value?: string \| undefined) => ReactNode)` | | |
698
+ | `options` | `{ label: ReactNode; value: string; disabled?: boolean \| undefined; }[]` | | |
699
+ | `renderOption` | `((props: { isOpen: boolean; close: () => void; }) => ReactNode)` | | |
700
+ | `placeholder` | `ReactNode` | | |
701
+ | `disabled` | `boolean` | | |
702
+ | `defaultOpen` | `boolean` | | |
703
+ | `open` | `boolean` | | |
704
+ | `onOpenChange` | `((open: boolean) => void)` | | |
705
+ | `leftIcon` | `ReactNode` | | |
706
+ | `rightIcon` | `ReactNode` | | |
707
+ | `width` | `string \| number` | | `'fit-content'` |
708
+ | `side` | `'bottom' \| 'left' \| 'right' \| 'top'` | | |
709
+ | `align` | `'center' \| 'start' \| 'end'` | | |
710
+ | `position` | `'popper' \| 'item-aligned'` | | |
711
+ | `className` | `string` | | |
712
+ | `style` | `CSSProperties` | | |
713
+
714
+ 이 밖에 React·HTML 기본 속성 2개를 받는다.
715
+
716
+ ## CollapsibleMenu
717
+
718
+ ```ts
719
+ import { CollapsibleMenu } from '@nurihaus/web-design-system/collapsible-menu'; // 이 컴포넌트만 싣는다 (권장)
720
+ import { CollapsibleMenu } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
721
+ ```
722
+
723
+ **언제 쓴다.** 사이드바 같은 세로 내비게이션 메뉴에 쓴다. `children`이 있는 항목은 펼치고 접는 그룹이 되고, 없는 항목은 `href`가 있으면 링크, 없으면 버튼으로 렌더되며, `activeKey` 항목과 그 상위 그룹이 강조된다. 링크는 기본으로 `<a>`다. 라우터의 링크로 바꾸려면 `linkComponent`에 넘긴다. 받는 props는 `href`·`className`·`children`·`onClick`·`aria-current`·`aria-disabled`이고, 그대로 링크에 전달하면 된다.
724
+
725
+ **쓰지 않는다.** 같은 화면 안에서 패널을 바꾸는 용도는 Tabs를 쓴다. 본문 콘텐츠를 접고 펼치는 아코디언 용도로는 쓰지 않는다(`<nav>`로 렌더된다). 값을 고르는 폼 입력은 DropDown을 쓴다.
726
+
727
+ Next.js 에서는 `next/link` 의 `Link` 를 그대로 넘긴다.
728
+
729
+ ```ts
730
+ import Link from 'next/link';
731
+
732
+ // <CollapsibleMenu linkComponent={Link} items={…} />
733
+ ```
734
+
735
+ ```tsx
736
+ <CollapsibleMenu
737
+ activeKey="user.search"
738
+ onActiveKeyChange={() => {}}
739
+ linkComponent={(props) => <a {...props} />}
740
+ items={[
741
+ { key: 'dashboard', item: <span>대시보드</span>, href: '/dashboard' },
742
+ {
743
+ key: 'users',
744
+ item: <span>사용자</span>,
745
+ defaultOpen: true,
746
+ children: [
747
+ { key: 'user.search', item: <span>사용자 검색</span> },
748
+ { key: 'user.list', item: <span>사용자 목록</span> },
749
+ ],
750
+ },
751
+ ]}
752
+ />
753
+ ```
754
+
755
+ ### props
756
+
757
+ | prop | 타입 | 필수 | 기본값 |
758
+ | --- | --- | --- | --- |
759
+ | `size` | `'s' \| 'm'` | | `'s'` |
760
+ | `items` | `CollapsibleMenuItem[]` | 예 | |
761
+ | `activeKey` | `string` | | |
762
+ | `onActiveKeyChange` | `((key: string) => void)` | | |
763
+ | `indent` | `number` | | `12` |
764
+ | `chevron` | `ReactNode` | | |
765
+ | `width` | `string \| number` | | `'240px'` |
766
+ | `linkComponent` | `ComponentType<CollapsibleMenuLinkProps>` | | |
767
+ | `className` | `string` | | |
768
+ | `style` | `CSSProperties` | | |
769
+
770
+ 이 밖에 React·HTML 기본 속성 2개를 받는다.
771
+
772
+ ## Text
773
+
774
+ ```ts
775
+ import { Text } from '@nurihaus/web-design-system/text'; // 이 컴포넌트만 싣는다 (권장)
776
+ import { Text } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
777
+ ```
778
+
779
+ **언제 쓴다.** 제목, 본문, 설명처럼 화면에 글자를 표시할 때 쓴다. `size`와 `weight`로 타이포그래피 토큰을, `color`로 색상 토큰을 적용하고, `as`로 태그를 고른다(기본값 `p`).
780
+
781
+ **쓰지 않는다.** 클릭 가능한 텍스트에는 쓰지 않는다. `onClick`, `id` 같은 HTML 속성은 타입상 받지만 요소에 전달되지 않고 `className`과 `style`만 전달된다. 버튼 문구는 CTAButton의 `text`에 문자열로 넘긴다.
782
+
783
+ ```tsx
784
+ <Text as="h2" text="캠페인 목록" size="xl" weight="bold" color="grey-scale-900" />
785
+ ```
786
+
787
+ ### props
788
+
789
+ | prop | 타입 | 필수 | 기본값 |
790
+ | --- | --- | --- | --- |
791
+ | `text` | `ReactNode` | 예 | |
792
+ | `size` | `'base' \| 'xl' \| 'xsm' \| 'sm' \| 'lg' \| '2xl' \| '3xl' \| '4xl' \| '5xl' \| '6xl'` | 예 | |
793
+ | `weight` | `'regular' \| 'medium' \| 'semibold' \| 'bold' \| 'extrabold'` | 예 | |
794
+ | `color` | `'transparent' \| 'purple-50' \| 'purple-100' \| 'purple-200' \| 'purple-300' \| 'purple-400' \| 'purple-500' \| 'purple-600' \| 'purple-700' \| 'purple-800' \| 'purple-900' \| 'grey-scale-50' \| 'grey-scale-100' \| 'grey-scale-200' \| 'grey-scale-300' \| 'grey-scale-400' \| 'grey-scale-500' \| 'grey-scale-600' \| 'grey-scale-700' \| 'grey-scale-800' \| 'grey-scale-900' \| 'accent-red-50' \| 'accent-red-100' \| 'accent-red-200' \| 'accent-red-300' \| 'accent-red-400' \| 'accent-red-500' \| 'accent-red-600' \| 'accent-red-700' \| 'accent-red-800' \| 'accent-red-900' \| 'accent-pink-50' \| 'accent-pink-100' \| 'accent-pink-200' \| 'accent-pink-300' \| 'accent-pink-400' \| 'accent-pink-500' \| 'accent-pink-600' \| 'accent-pink-700' \| 'accent-pink-800' \| 'accent-pink-900' \| 'accent-magenta-50' \| 'accent-magenta-100' \| 'accent-magenta-200' \| 'accent-magenta-300' \| 'accent-magenta-400' \| 'accent-magenta-500' \| 'accent-magenta-600' \| 'accent-magenta-700' \| 'accent-magenta-800' \| 'accent-magenta-900' \| 'accent-blue-50' \| 'accent-blue-100' \| 'accent-blue-200' \| 'accent-blue-300' \| 'accent-blue-400' \| 'accent-blue-500' \| 'accent-blue-600' \| 'accent-blue-700' \| 'accent-blue-800' \| 'accent-blue-900' \| 'accent-mint-50' \| 'accent-mint-100' \| 'accent-mint-200' \| 'accent-mint-300' \| 'accent-mint-400' \| 'accent-mint-500' \| 'accent-mint-600' \| 'accent-mint-700' \| 'accent-mint-800' \| 'accent-mint-900' \| 'accent-green-50' \| 'accent-green-100' \| 'accent-green-200' \| 'accent-green-300' \| 'accent-green-400' \| 'accent-green-500' \| 'accent-green-600' \| 'accent-green-700' \| 'accent-green-800' \| 'accent-green-900' \| 'accent-yellow-50' \| 'accent-yellow-100' \| 'accent-yellow-200' \| 'accent-yellow-300' \| 'accent-yellow-400' \| 'accent-yellow-500' \| 'accent-yellow-600' \| 'accent-yellow-700' \| 'accent-yellow-800' \| 'accent-yellow-900' \| 'accent-orange-50' \| 'accent-orange-100' \| 'accent-orange-200' \| 'accent-orange-300' \| 'accent-orange-400' \| 'accent-orange-500' \| 'accent-orange-600' \| 'accent-orange-700' \| 'accent-orange-800' \| 'accent-orange-900' \| 'black' \| 'white'` | | |
795
+ | `textDecoration` | `TextDecoration<string \| number>` | | |
796
+ | `textAlign` | `TextAlign` | | |
797
+ | `wordBreak` | `WordBreak` | | |
798
+ | `whiteSpace` | `WhiteSpace` | | |
799
+ | `as` | `'h1' \| 'h2' \| 'h3' \| 'h4' \| 'h5' \| 'h6' \| 'p' \| 'span' \| 'strong'` | | |
800
+
801
+ 이 밖에 React·HTML 기본 속성 265개를 받는다.
802
+
803
+ ## Tooltip
804
+
805
+ ```ts
806
+ import { Tooltip } from '@nurihaus/web-design-system/tooltip'; // 이 컴포넌트만 싣는다 (권장)
807
+ import { Tooltip } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
808
+ ```
809
+
810
+ **언제 쓴다.** 아이콘이나 짧은 라벨에 마우스를 올렸을 때 짧은 설명을 띄울 때 쓴다. `trigger`에는 DOM 요소 하나(예: `<span>`)를 넘기고 `direction`으로 뜨는 방향을 고른다. 조건이 맞을 때만 띄우려면 `open`을 제어하지 말고 `disabled`를 쓴다(예: 사이드바가 접혔을 때만 `disabled={!collapsed}`). `disabled`이면 `trigger`만 그려 툴팁이 열린 채 남지 않는다.
811
+
812
+ **쓰지 않는다.** 툴팁 안에 링크나 버튼처럼 클릭할 내용을 넣지 않는다. 툴팁 위로 마우스를 옮기면 닫힌다. 배지에 설명을 붙일 때는 OptionBadge, IconTextBadge의 `tooltip` prop을 쓴다.
813
+
814
+ ```tsx
815
+ <Tooltip trigger={<span>도움말</span>} content="최근 30일 기준입니다." direction="top" disabled={false} />
816
+ ```
817
+
818
+ ### props
819
+
820
+ | prop | 타입 | 필수 | 기본값 |
821
+ | --- | --- | --- | --- |
822
+ | `trigger` | `ReactNode` | 예 | |
823
+ | `content` | `ReactNode` | 예 | |
824
+ | `direction` | `'bottom' \| 'left' \| 'right' \| 'top'` | | `'bottom'` |
825
+ | `disabled` | `boolean` | | `false` |
826
+ | `defaultOpen` | `boolean` | | |
827
+ | `open` | `boolean` | | |
828
+ | `onOpenChange` | `((open: boolean) => void)` | | |
829
+ | `delayDuration` | `number` | | |
830
+ | `disableHoverableContent` | `boolean` | | |
831
+
832
+ 이 밖에 React·HTML 기본 속성 2개를 받는다.
833
+
834
+ ## TooltipProvider
835
+
836
+ ```ts
837
+ import { TooltipProvider } from '@nurihaus/web-design-system/tooltip'; // 이 컴포넌트만 싣는다 (권장)
838
+ import { TooltipProvider } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
839
+ ```
840
+
841
+ **언제 쓴다.** 툴팁이 여러 개 모인 영역(사이드바, 툴바)을 감싼다. 하나를 보고 옆으로 옮기면 `skipDelayDuration` 동안은 지연 없이 바로 뜬다. 앱 루트에 한 번 두어도 된다. 안에 있는 Tooltip과 배지 툴팁은 이 Provider를 같이 쓴다. `delayDuration` 기본값은 100ms다.
842
+
843
+ **쓰지 않는다.** 툴팁이 하나뿐이면 감싸지 않는다. Tooltip은 Provider 없이도 동작한다.
844
+
845
+ ```tsx
846
+ <TooltipProvider skipDelayDuration={300}>
847
+ <Tooltip trigger={<span>대시보드</span>} content="대시보드" direction="right" />
848
+ <Tooltip trigger={<span>사용자</span>} content="사용자" direction="right" />
849
+ </TooltipProvider>
850
+ ```
851
+
852
+ ### props
853
+
854
+ | prop | 타입 | 필수 | 기본값 |
855
+ | --- | --- | --- | --- |
856
+ | `children` | `ReactNode` | 예 | |
857
+ | `delayDuration` | `number` | | `100` |
858
+ | `skipDelayDuration` | `number` | | |
859
+ | `disableHoverableContent` | `boolean` | | |
860
+
861
+ ## Tabs
862
+
863
+ ```ts
864
+ import { Tabs } from '@nurihaus/web-design-system/tabs'; // 이 컴포넌트만 싣는다 (권장)
865
+ import { Tabs } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
866
+ ```
867
+
868
+ **언제 쓴다.** 같은 화면 안에서 여러 패널 중 하나를 골라 보여줄 때 쓴다. `items`의 각 항목이 탭 라벨과 패널 내용을 함께 가지며, `defaultValue`나 `value`를 주지 않으면 처음에 어떤 탭도 선택되지 않는다.
869
+
870
+ **쓰지 않는다.** 다른 페이지로 이동하는 내비게이션은 CollapsibleMenu를 쓴다. 폼에서 값 하나를 고르는 입력은 Radio를 쓴다.
871
+
872
+ ```tsx
873
+ <Tabs
874
+ variant="underline"
875
+ defaultValue="info"
876
+ items={[
877
+ { id: 'info', label: '기본 정보', content: <div>기본 정보 패널</div> },
878
+ { id: 'history', label: '변경 이력', content: <div>변경 이력 패널</div> },
879
+ ]}
880
+ />
881
+ ```
882
+
883
+ ### props
884
+
885
+ | prop | 타입 | 필수 | 기본값 |
886
+ | --- | --- | --- | --- |
887
+ | `items` | `TabsItem[]` | 예 | |
888
+ | `variant` | `'underline' \| 'filled'` | | `'underline'` |
889
+ | `value` | `TabsValue` | | |
890
+ | `defaultValue` | `TabsValue` | | |
891
+ | `onValueChange` | `((id: TabsValue) => void)` | | |
892
+ | `className` | `string` | | |
893
+ | `style` | `CSSProperties` | | |
894
+
895
+ 이 밖에 React·HTML 기본 속성 2개를 받는다.
896
+
897
+ ## ModalWithDoubleButton
898
+
899
+ ```ts
900
+ import { ModalWithDoubleButton } from '@nurihaus/web-design-system/modal-with-double-button'; // 이 컴포넌트만 싣는다 (권장)
901
+ import { ModalWithDoubleButton } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
902
+ ```
903
+
904
+ **언제 쓴다.** 삭제 확인처럼 제목, 설명, 버튼 두 개로 된 확인 대화상자에 쓴다. `ModalWithDoubleButton.Row`는 버튼을 가로로, `ModalWithDoubleButton.Column`은 세로로 배치하며, `left`와 `right`는 CTAButton props를 그대로 받는다.
905
+
906
+ **쓰지 않는다.** `open` prop이 없고 렌더되는 동안 항상 보이므로, 표시 여부는 부모가 조건부 렌더링으로 정하고 버튼 `onClick`에서 언마운트한다. `showCloseIcon`의 X 아이콘은 클릭해도 동작이 없으므로 닫기 수단으로 쓰지 않는다. 일반 모달은 Modal 을 쓴다.
907
+
908
+ ```tsx
909
+ <ModalWithDoubleButton.Row
910
+ title="항목을 삭제할까요?"
911
+ subTitle="삭제하면 되돌릴 수 없습니다."
912
+ left={{ text: '취소', onClick: () => {} }}
913
+ right={{ text: '삭제', variant: 'primary', onClick: () => {} }}
914
+ />
915
+ ```
916
+
917
+ ### ModalWithDoubleButton.Row props
918
+
919
+ | prop | 타입 | 필수 | 기본값 |
920
+ | --- | --- | --- | --- |
921
+ | `title` | `string` | | |
922
+ | `subTitle` | `ReactNode` | | |
923
+ | `titleIcon` | `ReactNode` | | |
924
+ | `content` | `ReactNode` | | |
925
+ | `showCloseIcon` | `boolean` | | |
926
+ | `showTitleLine` | `boolean` | | |
927
+ | `left` | `CTAButtonProps` | 예 | |
928
+ | `right` | `CTAButtonProps` | 예 | |
929
+ | `size` | `'s' \| 'm' \| 'l'` | | `'m'` |
930
+
931
+ 이 밖에 React·HTML 기본 속성 2개를 받는다.
932
+
933
+ ### ModalWithDoubleButton.Column props
934
+
935
+ | prop | 타입 | 필수 | 기본값 |
936
+ | --- | --- | --- | --- |
937
+ | `title` | `string` | | |
938
+ | `subTitle` | `ReactNode` | | |
939
+ | `titleIcon` | `ReactNode` | | |
940
+ | `content` | `ReactNode` | | |
941
+ | `showCloseIcon` | `boolean` | | |
942
+ | `showTitleLine` | `boolean` | | |
943
+ | `left` | `CTAButtonProps` | 예 | |
944
+ | `right` | `CTAButtonProps` | 예 | |
945
+ | `size` | `'s' \| 'm' \| 'l'` | | `'m'` |
946
+
947
+ 이 밖에 React·HTML 기본 속성 2개를 받는다.
948
+
949
+ ## Spinner
950
+
951
+ ```ts
952
+ import { Spinner } from '@nurihaus/web-design-system/loading'; // 이 컴포넌트만 싣는다 (권장)
953
+ import { Spinner } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
954
+ ```
955
+
956
+ **언제 쓴다.** 20px 고정 크기의 회전 표시만 필요할 때 쓴다. 작은 영역이나 다른 요소 옆에 붙인다. 링 색은 CSS `color` 를 따르고 기본은 `grey-scale-200` 이다. 색 prop 은 없다. 다른 색은 `className` 이나 `style` 로 `color` 를 준다(감싸는 요소의 color 는 물려받지 않는다).
957
+
958
+ **쓰지 않는다.** 버튼 로딩은 CTAButton의 `loading`을 쓴다(내부에서 Spinner를 그린다). 크기나 가로 정렬을 정해야 하면 InlineLoading을, 화면 전체를 막아야 하면 OverlayLoading을 쓴다.
959
+
960
+ ```tsx
961
+ <Spinner style={{ color: 'var(--color-grey-scale-400)' }} />
962
+ ```
963
+
964
+ ### props
965
+
966
+ | prop | 타입 | 필수 | 기본값 |
967
+ | --- | --- | --- | --- |
968
+ | `className` | `string` | | |
969
+ | `style` | `CSSProperties` | | |
970
+
971
+ 이 밖에 React·HTML 기본 속성 2개를 받는다.
972
+
973
+ ## InlineLoading
974
+
975
+ ```ts
976
+ import { InlineLoading } from '@nurihaus/web-design-system/loading'; // 이 컴포넌트만 싣는다 (권장)
977
+ import { InlineLoading } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
978
+ ```
979
+
980
+ **언제 쓴다.** 목록이나 카드 영역 안에서 데이터를 불러오는 동안 그 자리에 표시할 때 쓴다. `size`(px)로 크기를, `justifyContent`로 가로 정렬을 정한다. 링 색은 Spinner 와 같이 `className`·`style` 의 `color` 로 바꾼다.
981
+
982
+ **쓰지 않는다.** 로딩 중 다른 조작을 막아야 하면 OverlayLoading을 쓴다. 버튼 안 로딩은 CTAButton의 `loading`을 쓴다.
983
+
984
+ ```tsx
985
+ <InlineLoading size={24} justifyContent="center" />
986
+ ```
987
+
988
+ ### props
989
+
990
+ | prop | 타입 | 필수 | 기본값 |
991
+ | --- | --- | --- | --- |
992
+ | `size` | `number` | 예 | |
993
+ | `justifyContent` | `'center' \| 'flex-start' \| 'flex-end' \| 'space-between'` | | `'flex-start'` |
994
+ | `className` | `string` | | |
995
+ | `style` | `CSSProperties` | | |
996
+
997
+ 이 밖에 React·HTML 기본 속성 2개를 받는다.
998
+
999
+ ## OverlayLoading
1000
+
1001
+ ```ts
1002
+ import { OverlayLoading } from '@nurihaus/web-design-system/loading'; // 이 컴포넌트만 싣는다 (권장)
1003
+ import { OverlayLoading } from '@nurihaus/web-design-system'; // 루트. 첫 로드 JS 가 커진다
1004
+ ```
1005
+
1006
+ **언제 쓴다.** 저장이나 업로드처럼 끝날 때까지 화면 전체 조작을 막아야 할 때 쓴다. 화면 전체를 반투명 배경으로 덮고 가운데에 스피너와 `message`를 표시하며, `open`이 false면 아무것도 렌더하지 않는다. 스피너 색은 `className` 을 주고 `.my-overlay .base-spinner { color: … }` 처럼 바꾼다.
1007
+
1008
+ **쓰지 않는다.** 화면 일부만 로딩 중이면 InlineLoading을, 버튼 하나만 로딩 중이면 CTAButton의 `loading`을 쓴다.
1009
+
1010
+ ```tsx
1011
+ <OverlayLoading open={true} message="저장 중입니다" />
1012
+ ```
1013
+
1014
+ ### props
1015
+
1016
+ | prop | 타입 | 필수 | 기본값 |
1017
+ | --- | --- | --- | --- |
1018
+ | `open` | `boolean` | | `true` |
1019
+ | `message` | `string` | | |
1020
+ | `backdropOpacity` | `number` | | `0.6` |
1021
+ | `zIndex` | `number` | | `1200` |
1022
+ | `className` | `string` | | |
1023
+ | `style` | `CSSProperties` | | |
1024
+
1025
+ 이 밖에 React·HTML 기본 속성 2개를 받는다.
1026
+
1027
+ ## 아직 없는 것
1028
+
1029
+ 아래는 디자인 시스템에 없다. 비슷한 이름의 컴포넌트를 지어내 import 하지 않는다.
1030
+
1031
+ | 필요한 것 | 지금 있는 것 |
1032
+ | --- | --- |
1033
+ | 날짜 선택 | 없음 |
1034
+ | 아이콘 세트 | 없음. 패키지 안 아이콘은 컴포넌트 내부용이라 내보내지 않는다 |
1035
+
1036
+ 없는 것이 필요하면 쓰는 쪽 저장소에서 `tokens.md` 의 변수로 만든다. 그리고 디자인 시스템에 추가할 후보라고 알린다.