sellmate-design-system-react 2.2.0 → 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 (113) hide show
  1. package/AGENTS.md +661 -0
  2. package/README.md +85 -0
  3. package/dist/components/SActionModal/README.md +41 -0
  4. package/dist/components/SBadge/README.md +25 -0
  5. package/dist/components/SBarcodeInput/README.md +64 -0
  6. package/dist/components/SButton/README.md +49 -0
  7. package/dist/components/SCalendar/README.md +42 -0
  8. package/dist/components/SCallout/README.md +29 -0
  9. package/dist/components/SCallout/SCallout.cjs +76 -1
  10. package/dist/components/SCallout/SCallout.cjs.map +1 -1
  11. package/dist/components/SCallout/SCallout.d.ts +6 -2
  12. package/dist/components/SCallout/SCallout.js +76 -1
  13. package/dist/components/SCallout/SCallout.js.map +1 -1
  14. package/dist/components/SCard/README.md +12 -0
  15. package/dist/components/SCheckbox/README.md +44 -0
  16. package/dist/components/SChip/README.md +57 -0
  17. package/dist/components/SChipInput/README.md +77 -0
  18. package/dist/components/SCircleProgress/README.md +35 -0
  19. package/dist/components/SConfirmModal/README.md +61 -0
  20. package/dist/components/SDatePicker/README.md +62 -0
  21. package/dist/components/SDateRangePicker/README.md +64 -0
  22. package/dist/components/SDivider/README.md +25 -0
  23. package/dist/components/SDraggableItem/README.md +40 -0
  24. package/dist/components/SDraggableItem/SDraggableItem.cjs +3079 -0
  25. package/dist/components/SDraggableItem/SDraggableItem.cjs.map +1 -0
  26. package/dist/components/SDraggableItem/SDraggableItem.d.ts +33 -0
  27. package/dist/components/SDraggableItem/SDraggableItem.js +3077 -0
  28. package/dist/components/SDraggableItem/SDraggableItem.js.map +1 -0
  29. package/dist/components/SDraggableItem/index.d.ts +1 -0
  30. package/dist/components/SDropdownButton/README.md +50 -0
  31. package/dist/components/SExpansionItem/README.md +40 -0
  32. package/dist/components/SExpansionItem/SExpansionItem.cjs +6 -15
  33. package/dist/components/SExpansionItem/SExpansionItem.cjs.map +1 -1
  34. package/dist/components/SExpansionItem/SExpansionItem.js +6 -15
  35. package/dist/components/SExpansionItem/SExpansionItem.js.map +1 -1
  36. package/dist/components/SField/README.md +91 -0
  37. package/dist/components/SFilePicker/README.md +72 -0
  38. package/dist/components/SForm/README.md +31 -0
  39. package/dist/components/SGhostButton/README.md +76 -0
  40. package/dist/components/SGnb/README.md +56 -0
  41. package/dist/components/SGuide/README.md +34 -0
  42. package/dist/components/SIcon/README.md +91 -0
  43. package/dist/components/SInput/README.md +66 -0
  44. package/dist/components/SKeyValueTable/README.md +59 -0
  45. package/dist/components/SLayout/README.md +42 -0
  46. package/dist/components/SLinearProgress/README.md +17 -0
  47. package/dist/components/SList/README.md +14 -0
  48. package/dist/components/SList/SList.cjs +38 -0
  49. package/dist/components/SList/SList.cjs.map +1 -0
  50. package/dist/components/SList/SList.d.ts +10 -0
  51. package/dist/components/SList/SList.js +36 -0
  52. package/dist/components/SList/SList.js.map +1 -0
  53. package/dist/components/SList/index.d.ts +1 -0
  54. package/dist/components/SListItem/README.md +19 -0
  55. package/dist/components/SListItem/SListItem.cjs +83 -0
  56. package/dist/components/SListItem/SListItem.cjs.map +1 -0
  57. package/dist/components/SListItem/SListItem.d.ts +26 -0
  58. package/dist/components/SListItem/SListItem.js +81 -0
  59. package/dist/components/SListItem/SListItem.js.map +1 -0
  60. package/dist/components/SListItem/index.d.ts +1 -0
  61. package/dist/components/SLoadingContainer/README.md +29 -0
  62. package/dist/components/SLoadingModal/README.md +52 -0
  63. package/dist/components/SModal/README.md +192 -0
  64. package/dist/components/SModalContainer/README.md +49 -0
  65. package/dist/components/SNumberInput/README.md +74 -0
  66. package/dist/components/SPage/README.md +25 -0
  67. package/dist/components/SPagination/README.md +40 -0
  68. package/dist/components/SPopover/README.md +54 -0
  69. package/dist/components/SPopup/README.md +38 -0
  70. package/dist/components/SPortal/README.md +51 -0
  71. package/dist/components/SRadio/README.md +57 -0
  72. package/dist/components/SRadioButton/README.md +24 -0
  73. package/dist/components/SScrollArea/README.md +16 -0
  74. package/dist/components/SSectionHeaderCard/README.md +51 -0
  75. package/dist/components/SSectionHeaderCard/SSectionHeaderCard.cjs +3414 -0
  76. package/dist/components/SSectionHeaderCard/SSectionHeaderCard.cjs.map +1 -0
  77. package/dist/components/SSectionHeaderCard/SSectionHeaderCard.d.ts +32 -0
  78. package/dist/components/SSectionHeaderCard/SSectionHeaderCard.js +3392 -0
  79. package/dist/components/SSectionHeaderCard/SSectionHeaderCard.js.map +1 -0
  80. package/dist/components/SSectionHeaderCard/index.d.ts +1 -0
  81. package/dist/components/SSelect/README.md +79 -0
  82. package/dist/components/SStepper/README.md +30 -0
  83. package/dist/components/SSwitch/README.md +24 -0
  84. package/dist/components/STable/README.md +82 -0
  85. package/dist/components/STableBar/README.md +28 -0
  86. package/dist/components/STabs/README.md +37 -0
  87. package/dist/components/STag/README.md +44 -0
  88. package/dist/components/STextLink/README.md +48 -0
  89. package/dist/components/STextarea/README.md +60 -0
  90. package/dist/components/STimePicker/README.md +65 -0
  91. package/dist/components/STimeRangePicker/README.md +63 -0
  92. package/dist/components/SToast/README.md +70 -0
  93. package/dist/components/SToggle/README.md +22 -0
  94. package/dist/components/STooltip/README.md +57 -0
  95. package/dist/index.cjs +815 -419
  96. package/dist/index.cjs.map +1 -1
  97. package/dist/index.d.ts +4 -0
  98. package/dist/index.js +815 -423
  99. package/dist/index.js.map +1 -1
  100. package/dist/llms.txt +3496 -0
  101. package/dist/styles.css +545 -19
  102. package/dist/theme.css +22 -0
  103. package/eslint/index.mjs +66 -0
  104. package/eslint/lib/class-names.mjs +119 -0
  105. package/eslint/lib/table-column.mjs +53 -0
  106. package/eslint/rules/no-arbitrary-class.mjs +91 -0
  107. package/eslint/rules/no-off-scale-spacing.mjs +67 -0
  108. package/eslint/rules/no-raw-html-control.mjs +117 -0
  109. package/eslint/rules/prefer-typo-preset.mjs +73 -0
  110. package/eslint/rules/require-locale-number.mjs +78 -0
  111. package/eslint/rules/table-numeric-align.mjs +93 -0
  112. package/eslint/scale.gen.mjs +16 -0
  113. package/package.json +9 -4
package/dist/llms.txt ADDED
@@ -0,0 +1,3496 @@
1
+ <!-- 자동 생성: npm run build (scripts/gen-llms.mjs). 직접 수정 금지. -->
2
+
3
+ # sellmate-design-system-react — AI 에이전트 참조 문서
4
+
5
+ 이 파일 하나에 사용 규칙(§1)·토큰 어휘(§2)·전체 컴포넌트 Props(§3)가 들어 있다.
6
+
7
+ ## 1. 사용 규칙 (AGENTS.md 전문)
8
+
9
+ # sellmate-design-system-react — 사용 규칙 (AGENTS.md)
10
+
11
+ > **대상**: 이 패키지로 화면을 만드는 소비 앱의 개발자와 AI 코딩 에이전트(Claude 등).
12
+ > 이 문서는 "무엇을 언제 쓰고, 무엇을 쓰면 안 되는지"의 단일 기준이다.
13
+ > 개별 컴포넌트의 상세 Props/Events는 `node_modules/sellmate-design-system-react/dist/components/<이름>/README.md` 를 참조한다.
14
+
15
+ ## 0. 최우선 원칙 — 디자인 시스템 컴포넌트가 먼저다
16
+
17
+ **화면 요소를 만들기 전에, 그 역할을 하는 컴포넌트가 이미 있는지 먼저 확인한다.**
18
+ 있으면 반드시 그것을 쓴다. 직접 만드는 것은 대응 컴포넌트가 **없다는 것을 확인한 뒤**의 최후 수단이다.
19
+
20
+ ```tsx
21
+ ❌ <button onClick={save}>저장</button> ✅ <SButton label="저장" onClick={save} />
22
+ ❌ <table>…</table> ✅ <STable columns={columns} rows={rows} />
23
+ ❌ <div className="rounded border p-16">…</div> ✅ <SSectionHeaderCard>…</SSectionHeaderCard>
24
+ ❌ <ul><li>…</li></ul> ✅ <SList><SListItem title="…" /></SList>
25
+ ```
26
+
27
+ "비슷하게 생긴 것을 직접 만드는 것"이 어색함의 가장 큰 원인이다. 대응 컴포넌트를 쓰면 색·간격·상태·접근성이 전부 따라온다.
28
+
29
+ ### 0-1. 전체 컴포넌트 인덱스
30
+
31
+ 무엇을 만들지 정했으면 **이 표에서 먼저 찾는다.** 상세 Props 는 `dist/components/<이름>/README.md` 참조.
32
+
33
+ | 분류 | 컴포넌트 |
34
+ | --- | --- |
35
+ | **버튼·링크** | `SButton` `SGhostButton` `SDropdownButton` `STextLink` `SSwitch` `SToggle` |
36
+ | **입력 (폼)** | `SForm` `SField` `SInput` `SNumberInput` `STextarea` `SSelect` `SCheckbox` `SRadio` `SRadioButton` `SChip` `SChipInput` `SBarcodeInput` `SFilePicker` |
37
+ | **날짜·시간** | `SCalendar` `SDatePicker` `SDateRangePicker` `STimePicker` `STimeRangePicker` |
38
+ | **표·목록** | `STable` `STableBar` `SKeyValueTable` `SList` `SListItem` `SDraggableItem` |
39
+ | **레이아웃** | `SLayout` `SGnb` `SPage` `SSectionHeaderCard` `SCard` `SDivider` `SScrollArea` `SExpansionItem` |
40
+ | **내비게이션** | `STabs` `SPagination` `SStepper` |
41
+ | **표시·상태** | `STag` `SBadge` `SIcon` `SCallout` `SGuide` |
42
+ | **진행·로딩** | `SLinearProgress` `SCircleProgress` `SLoadingContainer` `SLoadingModal` |
43
+ | **오버레이** | `STooltip` `SPopover` `SPopup` `SPortal` |
44
+ | **모달** | `SModal.confirm()` `SModal.create()` + `SActionModal` `SConfirmModal` |
45
+ | **알림** | `SToast` `SToastContainer` |
46
+
47
+ 표에 없는 UI 를 만들어야 할 때만 `div` 로 직접 조립하고, 그때도 §1-2 · §2 의 토큰 규칙을 지킨다.
48
+
49
+ ### 0-2. 프로젝트 설정
50
+
51
+ 설정(Tailwind v4 `theme.css` import, `@source` 지정, Next.js 주의사항)은 패키지 [README.md](./README.md)를 따른다. 이 문서는 설정이 끝난 상태에서의 **화면 작성 규칙**만 다룬다.
52
+
53
+ ---
54
+
55
+ ## 1. 절대 규칙 (금지 목록)
56
+
57
+ AI 에이전트는 코드를 생성하기 전에 이 목록을 반드시 지킨다.
58
+
59
+ ### 1-1. 생 HTML 컨트롤 금지
60
+
61
+ §0 원칙의 구체적 목록이다. 아래 요소는 **어떤 경우에도** 생 HTML 로 만들지 않는다.
62
+
63
+ | 금지 | 대신 사용 |
64
+ | --- | --- |
65
+ | `<button>` | `SButton`, `SGhostButton`, `SDropdownButton`, `STextLink` |
66
+ | `<input type="text/password/...">` | `SInput` |
67
+ | `<input type="number">` | `SNumberInput` |
68
+ | `<input type="checkbox">` | `SCheckbox`, `SToggle`, `SSwitch` |
69
+ | `<input type="radio">` | `SRadio`, `SRadioButton` |
70
+ | `<input type="file">` | `SFilePicker` |
71
+ | `<select>` | `SSelect` |
72
+ | `<textarea>` | `STextarea` |
73
+ | `<table>` | `STable`, `SKeyValueTable` |
74
+ | `<form>` | `SForm` |
75
+ | `<dialog>`, 직접 만든 오버레이 | `SModal.confirm(...)`, `SModal.create(...)`, `SPopup` |
76
+ | `alert()`, `confirm()` | `SToast`, `SModal.confirm(...)` |
77
+ | 직접 만든 탭/페이지네이션/스텝퍼 | `STabs`, `SPagination`, `SStepper` |
78
+ | `<ul>`/`<li>` 로 만든 목록 UI | `SList` + `SListItem` (드래그 정렬은 `SDraggableItem`) |
79
+ | 직접 만든 섹션 카드(제목 바 + 본문 박스) | `SSectionHeaderCard` + `.Header` / `.Body` |
80
+ | `<svg>` 직접 삽입, 이모지 아이콘 | `SIcon` |
81
+ | `<hr>` | `SDivider` |
82
+ | `<details>` / `<summary>` | `SExpansionItem` |
83
+ | `<progress>` | `SLinearProgress`, `SCircleProgress` |
84
+ | `<label>` (폼 레이블) | `SField` 의 `label` prop |
85
+ | 직접 만든 카드·패널 박스 | `SCard`, `SSectionHeaderCard` |
86
+ | 직접 만든 스크롤 영역 | `SScrollArea` |
87
+
88
+ 예외: 순수 레이아웃 요소(`div`, `section`, 시맨틱 `h1~h6`, `p`, `span`, `a`)는 허용. 단 스타일은 아래 규칙을 따른다.
89
+
90
+ ### 1-2. 임의 값(arbitrary literal) 금지
91
+
92
+ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
93
+
94
+ ```text
95
+ ❌ text-[14px] font-[600] bg-[#eee] gap-[13px] p-[10px] rounded-[5px] text-[#333]
96
+ ✅ typo-body-sm-default text-fg-tertiary bg-bg-frame gap-12 p-8 rounded-md
97
+ ✅ w-[var(--sys-size-control-md-height)] ← 토큰을 var()로 참조하는 것은 허용
98
+ ```
99
+
100
+ - 하드코딩 hex/px/rem 리터럴은 어디에도 쓰지 않는다 (인라인 `style` 포함).
101
+ - 인라인 `style`은 런타임 계산값(동적 width 등)에만 허용한다.
102
+
103
+ **단, 디자인 토큰이 없는 속성의 임의 값은 정당하다.** 화면 고유의 레이아웃 치수가 여기 해당한다.
104
+
105
+ ```tsx
106
+ ✅ <div className="w-[280px] max-w-[1200px] grid-cols-[200px_1fr]" /> // 앱 고유 치수
107
+ ❌ <div className="text-[13px] bg-[#eee] gap-[13px] rounded-[5px]" /> // 토큰이 있는 속성
108
+ ```
109
+
110
+ 규칙은 **색 · 타이포 · 간격 · 모서리** 네 계열에만 적용된다 — 이 넷은 토큰이 이미 있으므로 임의 값은 곧 디자인 시스템 우회다.
111
+
112
+ ### 1-3. 타이포그래피는 프리셋으로만
113
+
114
+ `text-14 font-bold` 같은 조합을 즉흥으로 만들지 않는다. §2-1의 `typo-*` 프리셋 클래스를 쓴다.
115
+
116
+ ### 1-4. 숫자는 무조건 `toLocaleString()`
117
+
118
+ **숫자를 화면에 표시할 때는 예외 없이 `toLocaleString()` 을 거쳐 세 자리마다 콤마를 넣는다.**
119
+ 금액·수량·건수·재고 무엇이든, 테이블·상세·요약 문구 어디에 놓이든 같다.
120
+
121
+ ```tsx
122
+ ❌ <span>{price}원</span> ❌ {`${qty}개`} ❌ {String(count)}
123
+ ✅ <span>{price.toLocaleString()}원</span>
124
+ ✅ format: (v: number) => `${Number(v).toLocaleString()}개`
125
+ ```
126
+
127
+ 콤마 없는 `39000` 은 자릿수를 세어야 읽히지만 `39,000` 은 한눈에 읽힌다. 숫자를 그대로 출력하는 코드는 미완성으로 본다.
128
+
129
+ 전화번호·사업자번호처럼 하이픈이 들어가는 값은 애초에 숫자가 아니라 **서식이 정해진 문자열**이므로 이 규칙의 대상이 아니다.
130
+
131
+ ---
132
+
133
+ ## 2. 조합 어휘 — 컴포넌트 "사이"를 채울 때 쓰는 것들
134
+
135
+ ### 2-1. 타이포그래피 프리셋
136
+
137
+ 역할(role) → 크기 → 굵기 순으로 조합된 클래스가 이미 준비되어 있다.
138
+
139
+ | 용도 | 클래스 |
140
+ | --- | --- |
141
+ | 페이지/섹션 제목 | `typo-heading-lg`(18px) · `typo-heading-md`(16px) · `typo-heading-sm`(14px) · `typo-heading-xs`(12px) |
142
+ | 본문 | `typo-body-lg-*` (16px) · `typo-body-md-*`(14px) · `typo-body-sm-*`(12px) · `typo-body-xs-default`(11px) — `*` = `default`/`medium`/`bold` |
143
+ | 테이블 | `typo-table-header` · `typo-table-body` · `typo-table-accent` |
144
+ | 컨트롤·필드·피드백·내비 | `typo-control-*` `typo-field-*` `typo-feedback-*` `typo-navigation-*` (컴포넌트 내부용 — 직접 쓸 일은 드묾) |
145
+
146
+ **기본 선택 — 이 조합을 쓴다.** 이 서비스는 정보 밀도가 높아 본문이 12px 이다. 14px 를 본문 기본으로 쓰지 않는다.
147
+
148
+ | 역할 | 클래스 | 크기 |
149
+ | --- | --- | --- |
150
+ | 페이지 헤더 제목 (h1) | `typo-heading-lg` | 18px |
151
+ | 섹션 제목 | `typo-heading-sm` | 14px |
152
+ | 본문 | `typo-body-sm-default` | 12px |
153
+ | 보조 설명 | `typo-body-sm-default` + `text-fg-tertiary` | 12px / `grey_65` |
154
+
155
+ 페이지 제목만 18px 로 크게 두고 그 아래는 14 / 12 로 촘촘하게 간다. 중간 크기(16px)는 기본 골격에서 쓰지 않는다.
156
+
157
+ ### 2-2. 간격 (spacing)
158
+
159
+ - 유틸리티 숫자 = px. 스케일: `2 4 6 8 10 12 16 19 20 22 24 28 32 36 40 48 60`
160
+ - 형제 요소 간격은 margin 대신 부모의 `flex`/`grid` + `gap-*`으로 잡는다.
161
+ - 시맨틱 간격 토큰 (텍스트 덩어리·요소 사이 기본 리듬):
162
+
163
+ | 상황 | 값 |
164
+ | --- | --- |
165
+ | **페이지 콘텐츠 패딩** | **`p-20`** |
166
+ | **섹션 ↔ 섹션** | **`gap-12`** |
167
+ | **페이지 내 블록 ↔ 블록** (헤더·필터·툴바·테이블 사이) | **`gap-12`** |
168
+ | 요소 ↔ 요소 (기본) | `gap-8` (`--sys-space-stack-gap-element-normal`) |
169
+ | 제목 ↔ 설명 텍스트 | `gap-4` (`--sys-space-stack-gap-text-normal`) |
170
+
171
+ 정보 밀도가 높은 서비스라 블록 간격을 넓게 벌리지 않는다. `gap-16` / `gap-24` 를 페이지 골격의 기본값으로 쓰지 않는다.
172
+
173
+ ### 2-3. 색상
174
+
175
+ **시맨틱 유틸리티를 우선 사용한다** — 의미가 이름에 담긴 토큰이 이미 유틸리티로 존재한다.
176
+
177
+ #### 텍스트 색은 두 단계뿐이다
178
+
179
+ | 용도 | 유틸리티 | 값 |
180
+ | --- | --- | --- |
181
+ | **본문·제목 (기본값)** | 지정하지 않는다 — 전역 기본색이 이미 적용된다 | `grey_95` `#222222` |
182
+ | **보조 설명 · 비활성** | **`text-fg-tertiary`** | `grey_65` `#888888` |
183
+
184
+ - `theme.css` 가 `body` 에 `grey_95` 를 깔아두므로 **본문에 텍스트 색 클래스를 붙이지 않는다.** `text-fg-primary` 를 매번 쓰는 것은 불필요하다.
185
+ - 흐리게 보여야 하는 것은 보조 설명이든 비활성이든 **전부 `text-fg-tertiary`** 다.
186
+ - 중간 톤(`text-fg-secondary` `#555`)은 텍스트 위계에 쓰지 않는다. 두 단계로만 간다.
187
+
188
+ ```tsx
189
+ ✅ <p>주문이 접수되었습니다.</p> // 색 지정 없음 = grey_95
190
+ ✅ <p className="text-fg-tertiary">최근 30일 기준</p> // 보조 설명
191
+ ✅ <span className="text-fg-tertiary">-</span> // 빈 값 (§3-4)
192
+ ❌ <p className="text-fg-primary">주문이 접수되었습니다.</p> // 불필요
193
+ ❌ <p className="text-fg-secondary">최근 30일 기준</p> // 중간 톤 사용
194
+ ```
195
+
196
+ #### 그 밖의 색
197
+
198
+ | 용도 | 유틸리티 |
199
+ | --- | --- |
200
+ | 상태 텍스트 | `text-fg-danger` `text-fg-success` `text-fg-warning` `text-fg-accent` `text-fg-inverse` |
201
+ | 배경 | `bg-bg-frame`(흰 콘텐츠 면) · `bg-bg-neutralLight`/`bg-bg-neutralBright`(옅은 회색 면) · `bg-bg-screen`(앱 바탕) |
202
+ | 보더/구분선 | `border-border-default` · `border-border-strong` · `border-divider-default` |
203
+ | 비활성 배경·보더 | `bg-disabled-bg` `border-disabled-border` |
204
+ | 링크 | `text-link-accent` |
205
+
206
+ - 시맨틱 토큰에 맞는 항목이 없을 때만 원색 스케일(`bg-blue-subtle` 등)을 쓴다.
207
+ - 어느 쪽이든 **토큰 유틸리티만** 사용 — 리터럴 hex 금지.
208
+
209
+ ---
210
+
211
+ ## 3. 컴포넌트 선택 규칙 — "언제 뭘 쓰나"
212
+
213
+ > ⚠️ 초안(개발 작성). <!-- TODO(디자인): 전체 검수·확정 --> 표시가 있는 행은 디자인 확정 전까지 초안 기준으로 사용.
214
+
215
+ ### 3-1. 라벨/표시류 — STag vs SBadge vs SChip
216
+
217
+ | 상황 | 사용 |
218
+ | --- | --- |
219
+ | **상태·분류를 라벨로 표시 (기본값)** | **`STag`** — `<STag size="sm" color="..." label="판매중" />`. 목록의 상태 컬럼, 상세의 분류 태그 등 대부분이 여기 해당한다 |
220
+ | 라벨 없이 **색 점만**으로 상태를 찍을 때 | `SBadge` — 점(dot)만 그리는 인디케이터. 텍스트가 이미 있고 앞에 점만 붙이는 좁은 경우에만 |
221
+ | 사용자가 입력·삭제·편집하는 **토큰** | `SChip` (단독) / `SChipInput` (입력 필드 안에서) |
222
+
223
+ > 상태 표시는 **`STag size="sm"` 이 기본**이다. 색 점 + 텍스트 조합(`SBadge`)을 기본으로 쓰지 않는다.
224
+
225
+ ### 3-2. 알림/안내류
226
+
227
+ | 상황 | 사용 |
228
+ | --- | --- |
229
+ | 화면에 **상시 노출**되는 안내·경고 문구 | `SCallout` — `type` + `message` 배열(중첩 = 들여쓰기) |
230
+ | 작업 결과를 **일시적으로** 알림 | `SToast` (+ 루트에 `SToastContainer`) |
231
+ | 진행 전 **확인/취소**를 받아야 할 때 | `SModal.confirm({...}).onOk(...)` |
232
+ | 특정 UI 요소에 대한 **온보딩·기능 안내** | `SGuide` |
233
+ | 로딩 중 화면 잠금 | `SLoadingModal` / 영역 로딩은 `SLoadingContainer` |
234
+
235
+ ### 3-3. 플로팅/오버레이류
236
+
237
+ | 상황 | 사용 |
238
+ | --- | --- |
239
+ | hover 시 **짧은 보조 설명** (상호작용 없음) | `STooltip` |
240
+ | 클릭 시 **상호작용 가능한** 콘텐츠 (메뉴·미니 폼) | `SPopover` |
241
+ | **팝업** (별도 창 성격의 가벼운 표시) | `SPopup` |
242
+ | 페이지 흐름을 끊고 **작업을 완료**시키는 창 | **`SActionModal` 로 컴포넌트를 만들고 `SModal.create` 로 띄운다** (아래 참조) |
243
+ | 확인/취소만 받는 창 | `SModal.confirm(...)` |
244
+ | 로딩 중 화면 잠금 | `SModal.loading(...)` / `SLoadingModal` |
245
+
246
+ **모달 만드는 법** — 디자인 시스템이 띄우는 모달은 `SActionModal` · `SConfirmModal` · `SLoadingModal` 3종뿐이고 `create` · `confirm` · `loading` 이 1:1로 대응한다.
247
+ 직접 오버레이를 만들지 않는다. 작업용 모달은 **`SActionModal` 을 루트로 하는 컴포넌트를 만들어 `SModal.create` 에 넘긴다.**
248
+
249
+ ```tsx
250
+ // 1) SActionModal 을 루트로 하는 컴포넌트를 만든다
251
+ // create 가 주입하는 open / onOpenChange / onClose 를 그대로 SActionModal 에 전달해야 한다
252
+ function OrderModal({ orderId, open, onOpenChange, onClose, modalRef }: OrderModalProps) {
253
+ return (
254
+ <SActionModal
255
+ open={open}
256
+ onOpenChange={onOpenChange}
257
+ onClose={onClose}
258
+ modalTitle="주문 상세"
259
+ width={720}
260
+ // 주 액션은 button(단수), 보조 버튼은 footerLeft — 하단 버튼 양끝 분리 규칙과 같다
261
+ button={{ label: '접수', onClick: () => modalRef.ok() }}
262
+ footerLeft={<SButton color="secondary" label="취소" onClick={() => modalRef.cancel()} />}
263
+ >
264
+ <SKeyValueTable fields={orderFields} values={order} />
265
+ </SActionModal>
266
+ );
267
+ }
268
+
269
+ // 2) SModal.create 로 띄운다
270
+ SModal.create({ component: OrderModal, componentProps: { orderId } })
271
+ .onOk(() => refetch())
272
+ .onDismissed(() => {});
273
+ ```
274
+
275
+ ### 3-4. 테이블 컬럼 정렬
276
+
277
+ **숫자가 들어가는 컬럼은 예외 없이 오른쪽 정렬한다** (`align: 'right'`).
278
+ 자릿수가 세로로 맞아야 값의 크기를 눈으로 비교할 수 있기 때문이다.
279
+
280
+ | 컬럼 내용 | 정렬 | 예 |
281
+ | --- | --- | --- |
282
+ | **금액·수량·개수·비율 등 단위가 붙은 숫자** | **`'right'`** | `39,000원` · `12개` · `3건` · `15%` |
283
+ | **숫자만 있는 값** | **`'right'`** | `1024` · `39000` |
284
+ | 텍스트 | 생략(기본 `left`) | 상품명, 카테고리 |
285
+ | 상태 태그·아이콘·체크박스 등 고정폭 요소 | `'center'` | `STag`, `SIcon` |
286
+
287
+ ```tsx
288
+ const columns: STableColumn[] = [
289
+ { name: 'id', label: 'ID', field: 'id', width: '80px', align: 'right' },
290
+ { name: 'name', label: '상품명', field: 'name' }, // 텍스트 → 생략
291
+ { name: 'qty', label: '수량', field: 'qty', width: '80px', align: 'right',
292
+ format: (v: number) => `${Number(v).toLocaleString()}개` },
293
+ { name: 'price', label: '판매가', field: 'price', width: '120px', align: 'right',
294
+ format: (v: number) => `${Number(v).toLocaleString()}원` },
295
+ { name: 'status', label: '상태', field: 'status', width: '100px', align: 'center',
296
+ render: () => <STag size="sm" color="green" label="판매중" /> },
297
+ ];
298
+ ```
299
+
300
+ - `format` 으로 단위를 붙이더라도 **숫자 기반이면 오른쪽 정렬**이다. 단위 때문에 문자열이 되는 것은 정렬 판단과 무관하다.
301
+ - 숫자는 §1-4 대로 **`toLocaleString()` 이 필수**다. 세 자리 콤마 없이 출력하지 않는다.
302
+ - `SKeyValueTable` 의 값 셀도 같은 기준을 따른다.
303
+
304
+ #### 값이 없는 셀은 회색 하이픈
305
+
306
+ 셀을 **빈칸으로 두지 않는다.** 값이 `null` · `undefined` · 빈 문자열이면 `-` 를 `text-fg-tertiary`(`grey_65`)로 표시한다.
307
+ 빈칸은 "데이터가 없음"인지 "렌더가 깨졌는지" 구분되지 않지만, 회색 하이픈은 없다는 사실을 명시한다.
308
+
309
+ ```tsx
310
+ // 재사용 헬퍼를 하나 두고 모든 컬럼에서 쓴다
311
+ const emptyCell = <span className="text-fg-tertiary">-</span>;
312
+ const hasValue = (v: unknown) => v !== null && v !== undefined && v !== '';
313
+
314
+ const columns: STableColumn[] = [
315
+ { name: 'memo', label: '메모', field: 'memo',
316
+ render: (row: SRow) => (hasValue(row.memo) ? row.memo : emptyCell) },
317
+ { name: 'price', label: '판매가', field: 'price', width: '120px', align: 'right',
318
+ render: (row: SRow) =>
319
+ hasValue(row.price) ? `${Number(row.price).toLocaleString()}원` : emptyCell },
320
+ ];
321
+ ```
322
+
323
+ `0` 은 값이 있는 것이므로 하이픈으로 바꾸지 않는다 — `0원` 그대로 표시한다.
324
+
325
+ ### 3-5. 버튼류
326
+
327
+ | 상황 | 사용 |
328
+ | --- | --- |
329
+ | 일반 액션 | `SButton` (`color`: `primary` / `secondary` / `neutral` / `danger`, `size`: xs~lg) |
330
+ | **삭제 등 파괴적 액션** | **`SButton color="danger"`** 또는 **`color="danger" outline`** |
331
+ | 텍스트만 있는 저강조 액션 | `SGhostButton` |
332
+ | 본문 속 이동 링크 | `STextLink` |
333
+ | 메인 액션 + 부가 메뉴 | `SDropdownButton` |
334
+
335
+ 한 화면의 primary 버튼 개수에 대한 제약은 없다. 다만 **삭제·초기화 등 되돌릴 수 없는 액션은 반드시 `danger`** 로 표시한다.
336
+
337
+ **하단 버튼 배치 (모든 화면 공통)** — 그룹을 모아 정렬하지 않고 **양끝으로 벌린다**(`justify-between`).
338
+
339
+ - **왼쪽 끝**: 취소·닫기·목록 등 되돌리는 액션
340
+ - **오른쪽 끝**: 저장·등록·수정·삭제 등 실행 액션
341
+ - 체크박스·안내 문구 등 **부가 요소는 오른쪽 그룹 안, 실행 버튼 바로 왼쪽**에 둔다
342
+
343
+ ```tsx
344
+ <div className="flex items-center justify-between">
345
+ <SButton color="secondary" label="취소" />
346
+ <div className="flex items-center gap-8">
347
+ <SCheckbox label="계속 등록하기" value={keep} onValueChange={setKeep} /> {/* 부가 요소 */}
348
+ <SButton label="저장" />
349
+ </div>
350
+ </div>
351
+ ```
352
+
353
+ ---
354
+
355
+ ## 4. 페이지 레시피 — 표준 골격
356
+
357
+ > 새 페이지는 반드시 아래 골격에서 시작한다. 임의 골격을 발명하지 않는다.
358
+ >
359
+ > **핵심 원칙 — 표 형태의 정보는 `SKeyValueTable` 로 만든다.** 필터·등록/수정 폼·상세 정보가 모두 여기 해당한다.
360
+ > `SField` 컨트롤을 `div` 로 직접 나열해 폼을 만들지 않는다.
361
+
362
+ ### 4-1. 앱 셸 (모든 페이지 공통)
363
+
364
+ ```tsx
365
+ import { SLayout, SGnb, SPage, type SGnbMenuItem } from 'sellmate-design-system-react';
366
+
367
+ const MENU: SGnbMenuItem[] = [
368
+ { label: '주문', value: 'orders', icon: 'bill' },
369
+ { label: '상품', value: 'products', icon: 'box', children: [{ label: '목록', value: 'product-list' }] },
370
+ ];
371
+
372
+ export default function AppShell({ children }: { children: React.ReactNode }) {
373
+ return (
374
+ <SLayout type="box" header="fix">
375
+ {/* type/header/folded 는 SLayout 에만 준다 — SGnb 는 context 에서 읽는다 */}
376
+ <SGnb items={MENU} value={current} onValueChange={navigate} logo={<Logo />} />
377
+ <SPage background="frame" className="p-20">{children}</SPage>
378
+ </SLayout>
379
+ );
380
+ }
381
+ ```
382
+
383
+ ### 4-2. 목록 페이지 (필터 + 테이블)
384
+
385
+ 구조: **페이지 헤더(제목 + 가이드 링크) → 필터(`SKeyValueTable`) → `STableBar` → `STable`**
386
+
387
+ 액션 버튼의 위치가 핵심이다:
388
+
389
+ - **페이지 제목 줄에는 액션 버튼을 두지 않는다.** 가이드·매뉴얼 링크 등 부가 정보만 온다.
390
+ - **주요 액션(등록 등)은 `STableBar` 의 `rightActions`** 에 둔다.
391
+ - **선택 상태 액션(선택 삭제 등)은 `STableBar` 의 `actions`** 에 둔다. `actions` 슬롯은 건수 요약이 있으면 앞에 구분선(`SDivider`)을 **자동으로** 넣으므로 직접 구분선을 만들지 않는다.
392
+
393
+ ```tsx
394
+ import {
395
+ SButton, STextLink, SKeyValueTable, STableBar, STable, STag,
396
+ type STableColumn, type SRow, type SKeyValueField,
397
+ } from 'sellmate-design-system-react';
398
+
399
+ /** 필터도 표다 — SKeyValueTable 로 만든다 (서비스 전용 전역 필터가 따로 있는 경우 제외) */
400
+ const filterFields: SKeyValueField[][] = [
401
+ [
402
+ { name: 'status', label: '상태', type: 'select',
403
+ options: { options: STATUS_OPTIONS, emitValue: true } },
404
+ { name: 'keyword', label: '검색어', type: 'input',
405
+ options: { placeholder: '상품명 / 상품코드' } },
406
+ ],
407
+ [
408
+ { name: 'period', label: '등록일', type: 'date-range-picker', tdColSpan: 3 },
409
+ ],
410
+ ];
411
+
412
+ const columns: STableColumn[] = [
413
+ // 숫자 컬럼은 전부 align: 'right' — §3-4
414
+ { name: 'id', label: 'ID', field: 'id', width: '80px', align: 'right' },
415
+ { name: 'name', label: '상품명', field: 'name' },
416
+ { name: 'stock', label: '재고', field: 'stock', width: '90px', align: 'right',
417
+ format: (v: number) => `${Number(v).toLocaleString()}개` },
418
+ { name: 'price', label: '판매가', field: 'price', width: '120px', align: 'right',
419
+ format: (v: number) => `${Number(v).toLocaleString()}원` },
420
+ // 상태 컬럼은 STag size="sm" — SBadge 색 점을 기본으로 쓰지 않는다
421
+ { name: 'status', label: '상태', field: 'status', width: '100px', align: 'center',
422
+ render: (row: SRow) =>
423
+ row.status === 'selling'
424
+ ? <STag size="sm" color="green" label="판매중" />
425
+ : <STag size="sm" color="grey" label="판매중지" /> },
426
+ ];
427
+
428
+ export default function ProductListPage() {
429
+ const [filters, setFilters] = useState<Record<string, unknown>>({});
430
+ const [selected, setSelected] = useState<SRow[]>([]);
431
+
432
+ return (
433
+ <div className="flex flex-col gap-12">
434
+ {/* 페이지 헤더 — 액션 버튼 없음. 가이드/매뉴얼 링크 자리 */}
435
+ <div className="flex items-center justify-between">
436
+ <h1 className="typo-heading-lg m-0">상품 목록</h1>
437
+ <STextLink label="이용 가이드" rightArrow="chevron" onClick={openGuide} />
438
+ </div>
439
+
440
+ {/* 필터 — search 를 켜면 우측에 검색 패널이 붙는다 */}
441
+ <SKeyValueTable
442
+ fields={filterFields}
443
+ values={filters}
444
+ search
445
+ onChange={({ values }) => setFilters(values)}
446
+ onSearch={fetchList}
447
+ />
448
+
449
+ {/* 툴바 — 좌: 건수 + (구분선 자동) + 선택 액션 / 우: 주요 액션 */}
450
+ <STableBar
451
+ total={total}
452
+ selected={selected.length}
453
+ actions={
454
+ <SButton size="sm" color="secondary" label="선택 삭제"
455
+ disabled={!selected.length} onClick={removeSelected} />
456
+ }
457
+ rightActions={<SButton size="sm" label="상품 등록" onClick={goCreate} />}
458
+ />
459
+
460
+ <STable
461
+ columns={columns}
462
+ rows={rows}
463
+ rowKey="id"
464
+ selectable
465
+ selected={selected}
466
+ onSelectedChange={setSelected}
467
+ pagination={{ currentPage, lastPage }}
468
+ isLoading={isLoading}
469
+ />
470
+ </div>
471
+ );
472
+ }
473
+ ```
474
+
475
+ ### 4-3. 폼 페이지 (등록/수정)
476
+
477
+ 구조: **페이지 제목 → `SForm` + `SKeyValueTable` → 하단 버튼**
478
+
479
+ - 필드를 `div` 로 나열하지 않고 **`SKeyValueTable` 의 행으로 구성**한다.
480
+ - 검증 규칙은 각 field 의 `options.rules` 로 넘긴다. `SForm` 이 하위 컨트롤을 자동 수집해 submit 시 일괄 검증하고, 실패 시 첫 실패 필드로 포커스를 옮긴다.
481
+ - **버튼 순서: 취소·닫기가 왼쪽, 저장·등록·수정·삭제가 오른쪽.** 이 순서는 모든 화면에서 동일하다.
482
+
483
+ ```tsx
484
+ import {
485
+ SForm, SKeyValueTable, SButton, SCheckbox,
486
+ type SFormHandle, type SKeyValueField, type Rule,
487
+ } from 'sellmate-design-system-react';
488
+
489
+ const required = (msg: string): Rule => v =>
490
+ v != null && String(v).trim() !== '' ? true : msg;
491
+
492
+ const fields: SKeyValueField[][] = [
493
+ [
494
+ { name: 'name', label: '상품명', required: true, type: 'input',
495
+ options: { placeholder: '상품명 입력', rules: [required('상품명을 입력해 주세요.')] } },
496
+ { name: 'code', label: '상품코드', type: 'input', options: { placeholder: '자동 생성' } },
497
+ ],
498
+ [
499
+ { name: 'category', label: '카테고리', required: true, type: 'select',
500
+ options: { options: CATEGORY_OPTIONS, emitValue: true,
501
+ rules: [required('카테고리를 선택해 주세요.')] } },
502
+ { name: 'price', label: '판매가', type: 'number-input' },
503
+ ],
504
+ [
505
+ { name: 'memo', label: '메모', type: 'textarea', tdColSpan: 3,
506
+ helpText: ['내부 관리용 메모입니다.'] },
507
+ ],
508
+ ];
509
+
510
+ export default function ProductCreatePage() {
511
+ const formRef = useRef<SFormHandle>(null);
512
+ const [values, setValues] = useState<Record<string, unknown>>({});
513
+
514
+ return (
515
+ <div className="flex flex-col gap-12">
516
+ <h1 className="typo-heading-lg m-0">상품 등록</h1>
517
+
518
+ <SForm ref={formRef} formClass="flex flex-col gap-12" onSubmit={save}>
519
+ <SKeyValueTable
520
+ fields={fields}
521
+ values={values}
522
+ onChange={({ values }) => setValues(values)}
523
+ />
524
+
525
+ {/* 하단 버튼은 양끝으로 벌린다. 부가 요소(체크박스 등)는 저장 바로 왼쪽 */}
526
+ <div className="flex items-center justify-between">
527
+ <SButton type="button" color="secondary" label="취소" onClick={goBack} />
528
+ <div className="flex items-center gap-8">
529
+ <SCheckbox label="계속 등록하기" value={keepOpen} onValueChange={v => setKeepOpen(v as boolean)} />
530
+ <SButton type="submit" label="저장" />
531
+ </div>
532
+ </div>
533
+ </SForm>
534
+ </div>
535
+ );
536
+ }
537
+ ```
538
+
539
+ ### 4-4. 상세(조회) 페이지
540
+
541
+ 구조: **페이지 헤더(제목) → 섹션별 `SSectionHeaderCard` + `SKeyValueTable` → 하단 버튼**
542
+
543
+ - 조회 값은 `type: 'text'` 행으로 표시한다. **상태·분류 태그도 별도 영역이 아니라 표의 한 행**으로 넣는다 (`render` 에 `STag`).
544
+ - 행이 많아지면 **유형별로 섹션을 나누고, 각 섹션을 `SSectionHeaderCard` 로 감싼다.**
545
+ 합성 컴포넌트라 `SSectionHeaderCard.Header` / `SSectionHeaderCard.Body` 를 자식으로 쓴다.
546
+ - **수정·삭제 버튼은 하단에 둔다.** 내용이 짧아 우측 상단에 두는 변형도 있으나 기본은 하단이다.
547
+
548
+ ```tsx
549
+ import {
550
+ SSectionHeaderCard, SKeyValueTable, SButton, STag, type SKeyValueField,
551
+ } from 'sellmate-design-system-react';
552
+
553
+ const basicFields: SKeyValueField[][] = [
554
+ [
555
+ { name: 'code', label: '상품코드', type: 'text' },
556
+ { name: 'createdAt', label: '등록일', type: 'text' },
557
+ ],
558
+ [
559
+ { name: 'category', label: '카테고리', type: 'text' },
560
+ // 상태 태그도 표의 한 행으로 표현한다
561
+ { name: 'status', label: '상태',
562
+ render: <STag size="sm" color="green" label="판매중" /> },
563
+ ],
564
+ ];
565
+
566
+ const priceFields: SKeyValueField[][] = [
567
+ [
568
+ { name: 'price', label: '판매가', type: 'text' },
569
+ { name: 'cost', label: '원가', type: 'text' },
570
+ ],
571
+ ];
572
+
573
+ export default function ProductDetailPage() {
574
+ return (
575
+ <div className="flex flex-col gap-12">
576
+ <h1 className="typo-heading-lg m-0">클래식 셔츠</h1>
577
+
578
+ <SSectionHeaderCard>
579
+ <SSectionHeaderCard.Header title="기본 정보" marker thickness="accent" />
580
+ <SSectionHeaderCard.Body>
581
+ <SKeyValueTable fields={basicFields} values={product} />
582
+ </SSectionHeaderCard.Body>
583
+ </SSectionHeaderCard>
584
+
585
+ <SSectionHeaderCard>
586
+ {/* 헤더 우측에 액션이 필요하면 slot 을 쓴다 */}
587
+ <SSectionHeaderCard.Header
588
+ title="가격 정보"
589
+ marker
590
+ helpText={['부가세 포함 금액입니다.']}
591
+ slot={<SButton size="sm" color="secondary" label="이력" onClick={openHistory} />}
592
+ />
593
+ <SSectionHeaderCard.Body>
594
+ <SKeyValueTable fields={priceFields} values={product} />
595
+ </SSectionHeaderCard.Body>
596
+ </SSectionHeaderCard>
597
+
598
+ {/* 액션은 하단 — 목록(되돌리기)은 왼쪽 끝, 실행 액션은 오른쪽 끝 */}
599
+ <div className="flex items-center justify-between">
600
+ <SButton color="secondary" label="목록" onClick={goList} />
601
+ <div className="flex items-center gap-8">
602
+ <SButton color="danger" outline label="삭제" onClick={confirmDelete} />
603
+ <SButton label="수정" onClick={goEdit} />
604
+ </div>
605
+ </div>
606
+ </div>
607
+ );
608
+ }
609
+ ```
610
+
611
+ ### 4-5. 섹션 카드 — SSectionHeaderCard
612
+
613
+ 폼 페이지에서도 입력 항목이 많으면 유형별로 `SSectionHeaderCard` 로 나눈다. 주요 옵션:
614
+
615
+ | Prop (Header) | 용도 |
616
+ | --- | --- |
617
+ | `title` | 섹션 제목 (필수) |
618
+ | `marker` | 제목 앞 점 표시 |
619
+ | `required` | 제목 뒤 필수(\*) 표시 — 필수 입력 섹션에 |
620
+ | `helpText` | 도움말 툴팁 (`string[]`) |
621
+ | `subtitle` | 부제 |
622
+ | `slot` | 헤더 우측 영역 (버튼 등) |
623
+ | `thickness` | 상단 강조선 — `false`(기본) / `'default'` / `'accent'` |
624
+
625
+ ---
626
+
627
+ ## 5. 자가 점검 체크리스트
628
+
629
+ 페이지를 완성하면 다음을 확인한다. 하나라도 어기면 수정 후 완료를 보고한다.
630
+
631
+ - [ ] 생 HTML 컨트롤(`<button>` `<input>` `<select>` `<table>` …)이 없는가
632
+ - [ ] `text-[14px]`, `bg-[#...]` 같은 리터럴 임의 값이 없는가 (`var(--sys-*)` 참조는 허용)
633
+ - [ ] 텍스트에 `typo-*` 프리셋을 썼는가
634
+ - [ ] 간격이 전부 토큰 스케일 값인가 (`gap-13` ❌ → `gap-12` ✅)
635
+ - [ ] 본문이 12px(`typo-body-sm-default`)이고 보조 설명이 `text-fg-tertiary` 인가 (14px 본문 ❌)
636
+ - [ ] 페이지 패딩이 `p-20`, 블록·섹션 간격이 `gap-12` 인가 (`gap-16`/`gap-24` ❌)
637
+ - [ ] 페이지가 §4의 표준 골격에서 시작했는가
638
+ - [ ] 필터·폼·상세 정보를 `SKeyValueTable` 로 만들었는가 (컨트롤을 `div` 로 나열하지 않았는가)
639
+ - [ ] 섹션 구분에 `SSectionHeaderCard` 를 썼는가 (직접 만든 카드가 아니라)
640
+ - [ ] 목록의 주요 액션이 `STableBar` 의 `rightActions` 에 있는가 (페이지 제목 줄이 아니라)
641
+ - [ ] 상태 표시에 `STag size="sm"` 을 썼는가
642
+ - [ ] 테이블의 숫자 컬럼(금액·수량·개수 등)이 전부 `align: 'right'` 인가
643
+ - [ ] 화면에 표시하는 모든 숫자에 `toLocaleString()` 을 써서 세 자리 콤마를 넣었는가
644
+ - [ ] 금액·수량 등 양을 나타내는 숫자에 `toLocaleString()` 을 썼는가 (번호·코드는 제외)
645
+ - [ ] 하단 버튼이 양끝 분리(`justify-between`)이고, 되돌리기가 왼쪽 끝 · 실행이 오른쪽 끝인가
646
+ - [ ] 삭제 등 파괴적 액션에 `color="danger"` 를 썼는가
647
+ - [ ] 작업용 모달을 `SActionModal` + `SModal.create` 로 만들었는가 (직접 오버레이 ❌)
648
+ - [ ] 상태 표시·알림·확인 다이얼로그가 §3의 선택 규칙을 따르는가
649
+
650
+ ---
651
+
652
+ ## 6. 린트로 강제되는 규칙
653
+
654
+ 위 규칙 중 일부는 소비 앱의 ESLint 로 검출된다 (`sellmate-design-system-react/eslint`).
655
+ **코드를 넘기기 전에 린트를 통과시킨다.**
656
+
657
+ | 규칙 | 잡히는 것 |
658
+ | --- | --- |
659
+ | `sellmate/no-raw-html-control` | §1-1 생 HTML 컨트롤, `alert()`/`confirm()` |
660
+ | `sellmate/no-arbitrary-class` | §1-2 토큰 있는 속성의 임의 값 (`text-[14px]`, `bg-[#eee]`) |
661
+ | `sellmate/no-off-scale-spacing` | §2-2 스케일 밖 간격 (`gap-13`) |
662
+ | `sellmate/table-numeric-align` | §3-4 숫자 컬럼의 `align: 'right'` 누락 (`--fix` 지원) |
663
+ | `sellmate/require-locale-number` | §1-4 숫자 컬럼의 `toLocaleString()` 누락 |
664
+ | `sellmate/require-locale-number` | §1-4 금액·수량 등의 `toLocaleString()` 누락 |
665
+ | `sellmate/prefer-typo-preset` | §1-3 낱개 폰트 조합 (`text-14 font-bold`) |
666
+
667
+ `gap-13` 처럼 스케일 밖 값은 Tailwind v4 에서 **에러 없이 조용히 무시된다**. "간격이 왜 안 먹지" 로만 보이므로 반드시 스케일 값을 쓴다.
668
+
669
+ 린트가 잡지 못하는 것(§3 컴포넌트 선택, §4 페이지 골격, 버튼 배치)은 §5 체크리스트로 직접 확인한다.
670
+
671
+
672
+ ## 2. 디자인 토큰 어휘 (theme.css 자동 추출)
673
+
674
+ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터럴 임의 값(`text-[14px]`, `bg-[#eee]`)은 금지.
675
+
676
+ ### 간격 (px 단위 — gap-N, p-N, m-N 등)
677
+ 2 4 6 8 10 12 16 19 20 22 24 28 32 36 40 44 48 56 60 62 80
678
+
679
+ ### 폰트 크기 (text-N — 단, 본문 텍스트는 아래 typo-* 프리셋 우선)
680
+ 10 11 12 13 14 16 18 20 24 28 32
681
+
682
+ ### radius (rounded-*)
683
+ 2 4 6 8 full control-sm control-md feedback-xs-square feedback-sm-square feedback-pill field-sm field-md navigation item-bordered
684
+
685
+ ### 색상 팔레트 (text-{family}-{step}, bg-…, border-…)
686
+ - white
687
+ - black
688
+ - grey: 10 20 25 30 35 45 50 55 60 65 70 80 85 90 95 05 strong subtle
689
+ - red: 15 20 30 45 60 70 75 80 85 90 95 99 strong subtle
690
+ - orange: 10 20 35 45 55 60 65 75 85 90 95 99 strong subtle
691
+ - yellow: 10 20 25 30 40 45 50 60 70 80 90 95 strong moderate subtle
692
+ - olive: 10 15 20 30 45 55 65 70 80 90 95 05
693
+ - green: 15 25 45 55 65 70 75 80 85 90 95 99 strong subtle
694
+ - steelblue: 10 25 45 60 65 70 75 80 85 90 95 99
695
+ - oceanblue: 15 25 50 60 65 70 75 80 85 90 95 99
696
+ - brilliantblue: 10 20 25 40 50 60 70 75 80 85 90 95 99 05
697
+ - bg: screen frame brand subtle accent accentLight accentBright accentFaint deep danger dangerLight warning caution progress success successLight neutralLight neutralBright tableHeader
698
+ - fg: primary secondary tertiary quaternary brand subtleWarm subtleCool deep success dangerLight accent accentLight danger warning inverse
699
+ - border: default strong accent accentLight danger success
700
+ - disabled: bg text icon border
701
+ - link: accent
702
+ - divider: default
703
+ - lightblue: strong subtle
704
+ - blue: strong subtle
705
+ - darkblue: strong subtle
706
+ - indigo: strong subtle
707
+ - primary: 10 20 25 40 50 60 70 75 80 85 90 95 99 05
708
+ - danger: 15 20 30 45 60 70 75 80 85 90 95 99 strong subtle
709
+ - success: 15 25 45 55 65 70 75 80 85 90 95 99 strong subtle
710
+ - warning: 10 20 35 45 55 60 65 75 85 90 95 99 strong subtle
711
+
712
+ ### 타이포그래피 프리셋 (클래스 하나로 size/weight/line-height 적용)
713
+ | 클래스 | size | weight | line-height |
714
+ | --- | --- | --- | --- |
715
+ | typo-heading-sm | 14px | 700 | 24px |
716
+ | typo-heading-xs | 12px | 700 | 20px |
717
+ | typo-heading-md | 16px | 700 | 26px |
718
+ | typo-heading-lg | 18px | 700 | 30px |
719
+ | typo-body-xs-default | 11px | 400 | 18px |
720
+ | typo-body-sm-default | 12px | 400 | 20px |
721
+ | typo-body-sm-medium | 12px | 500 | 20px |
722
+ | typo-body-sm-bold | 12px | 700 | 20px |
723
+ | typo-body-md-default | 14px | 400 | 24px |
724
+ | typo-body-md-medium | 14px | 500 | 24px |
725
+ | typo-body-md-bold | 14px | 700 | 24px |
726
+ | typo-body-lg-default | 16px | 400 | 26px |
727
+ | typo-body-lg-medium | 16px | 500 | 26px |
728
+ | typo-body-lg-bold | 16px | 700 | 26px |
729
+ | typo-control-xs-default | 12px | 400 | 20px |
730
+ | typo-control-xs-medium | 12px | 500 | 20px |
731
+ | typo-control-xs-underline | 12px | 500 | 20px |
732
+ | typo-control-xs-bold | 12px | 700 | 20px |
733
+ | typo-control-sm-default | 14px | 500 | 24px |
734
+ | typo-control-sm-underline | 14px | 500 | 24px |
735
+ | typo-control-md-default | 16px | 500 | 26px |
736
+ | typo-control-md-underline | 16px | 500 | 26px |
737
+ | typo-control-lg | 18px | 500 | 30px |
738
+ | typo-feedback-xs | 11px | 500 | 18px |
739
+ | typo-feedback-sm-regular | 12px | 400 | 20px |
740
+ | typo-feedback-sm-medium | 12px | 500 | 20px |
741
+ | typo-feedback-sm-bold | 12px | 700 | 20px |
742
+ | typo-feedback-sm-underline | 12px | 500 | 20px |
743
+ | typo-feedback-md-bold | 14px | 700 | 24px |
744
+ | typo-feedback-md-medium | 14px | 500 | 24px |
745
+ | typo-feedback-md-underline | 14px | 700 | 24px |
746
+ | typo-field-xs-default | 11px | 400 | 18px |
747
+ | typo-field-sm-default | 12px | 400 | 20px |
748
+ | typo-field-sm-medium | 12px | 500 | 20px |
749
+ | typo-field-sm-bold | 12px | 700 | 20px |
750
+ | typo-field-md-default | 14px | 400 | 24px |
751
+ | typo-field-md-medium | 14px | 500 | 24px |
752
+ | typo-field-md-bold | 14px | 700 | 24px |
753
+ | typo-field-lg-default | 16px | 400 | 26px |
754
+ | typo-field-lg-medium | 16px | 500 | 26px |
755
+ | typo-field-lg-bold | 16px | 700 | 26px |
756
+ | typo-navigation-xs-medium | 11px | 500 | 18px |
757
+ | typo-navigation-xs-bold | 11px | 700 | 18px |
758
+ | typo-navigation-sm-default | 12px | 400 | 20px |
759
+ | typo-navigation-sm-medium | 12px | 500 | 20px |
760
+ | typo-navigation-sm-bold | 12px | 700 | 20px |
761
+ | typo-navigation-md-bold | 13px | 700 | 22px |
762
+ | typo-navigation-lg-medium | 14px | 500 | 24px |
763
+ | typo-navigation-lg-bold | 14px | 700 | 24px |
764
+ | typo-table-header | 12px | 500 | 20px |
765
+ | typo-table-body | 12px | 400 | 20px |
766
+ | typo-table-accent | 12px | 700 | 20px |
767
+
768
+ ### 시맨틱 색 토큰 (var() 참조 전용 — 예: bg-[var(--sys-color-bg-frame)])
769
+ --sys-color-bg-accent
770
+ --sys-color-bg-accentBright
771
+ --sys-color-bg-accentFaint
772
+ --sys-color-bg-accentLight
773
+ --sys-color-bg-brand
774
+ --sys-color-bg-caution
775
+ --sys-color-bg-danger
776
+ --sys-color-bg-dangerLight
777
+ --sys-color-bg-deep
778
+ --sys-color-bg-frame
779
+ --sys-color-bg-neutralBright
780
+ --sys-color-bg-neutralLight
781
+ --sys-color-bg-progress
782
+ --sys-color-bg-screen
783
+ --sys-color-bg-subtle
784
+ --sys-color-bg-success
785
+ --sys-color-bg-successLight
786
+ --sys-color-bg-tableHeader
787
+ --sys-color-bg-warning
788
+ --sys-color-blue-strong
789
+ --sys-color-blue-subtle
790
+ --sys-color-border-accent
791
+ --sys-color-border-accentLight
792
+ --sys-color-border-danger
793
+ --sys-color-border-default
794
+ --sys-color-border-strong
795
+ --sys-color-border-success
796
+ --sys-color-control-bg-inverseHover
797
+ --sys-color-darkblue-strong
798
+ --sys-color-darkblue-subtle
799
+ --sys-color-disabled-bg
800
+ --sys-color-disabled-border
801
+ --sys-color-disabled-icon
802
+ --sys-color-disabled-text
803
+ --sys-color-divider-default
804
+ --sys-color-fg-accent
805
+ --sys-color-fg-accentLight
806
+ --sys-color-fg-brand
807
+ --sys-color-fg-danger
808
+ --sys-color-fg-dangerLight
809
+ --sys-color-fg-deep
810
+ --sys-color-fg-inverse
811
+ --sys-color-fg-primary
812
+ --sys-color-fg-quaternary
813
+ --sys-color-fg-secondary
814
+ --sys-color-fg-subtleCool
815
+ --sys-color-fg-subtleWarm
816
+ --sys-color-fg-success
817
+ --sys-color-fg-tertiary
818
+ --sys-color-fg-warning
819
+ --sys-color-field-bg-default
820
+ --sys-color-field-bg-hover
821
+ --sys-color-field-bg-readOnly
822
+ --sys-color-field-border-danger
823
+ --sys-color-field-border-default
824
+ --sys-color-field-border-focus
825
+ --sys-color-field-border-hover
826
+ --sys-color-field-border-success
827
+ --sys-color-field-icon-default
828
+ --sys-color-field-text-default
829
+ --sys-color-field-text-placeholder
830
+ --sys-color-field-text-readOnly
831
+ --sys-color-indigo-strong
832
+ --sys-color-indigo-subtle
833
+ --sys-color-lightblue-strong
834
+ --sys-color-lightblue-subtle
835
+ --sys-color-link-accent
836
+ --sys-color-navigation-gnb-bg-dark
837
+
838
+ ## 3. 컴포넌트 카탈로그 (Props / Events / Methods)
839
+
840
+ # SActionModal
841
+
842
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
843
+
844
+ ### SActionModal
845
+
846
+ #### Props
847
+
848
+ | Prop | Type | Default | Description |
849
+ |------|------|---------|-------------|
850
+ | `open?` | `boolean` | — | |
851
+ | `persistent?` | `boolean` | — | true면 백드롭·ESC로 안 닫히고 흔들림 (sd-modal-container persistent) |
852
+ | `modalTitle?` | `string` | `''` | |
853
+ | `button?` | `SActionModalButton` | — | 하단 액션 버튼 (주 액션 1개). 의도적으로 단수다 — 보조 버튼(취소·삭제 등)은 `footerLeft` 슬롯에 직접 배치한다. |
854
+ | `footerLeft?` | `ReactNode` | — | footer 좌측 영역 (sd-action-modal 의 bottom-sub-content 슬롯). 보조 버튼이나 안내 문구를 넣는다. 버튼을 하나 더 쓰고 싶을 때 여기에 SButton 을 넣는다. |
855
+ | `width?` | `number \| string` | — | |
856
+ | `height?` | `number \| string` | — | |
857
+ | `children?` | `ReactNode` | — | |
858
+
859
+ #### Events
860
+
861
+ | Event | Type | Description |
862
+ |-------|------|-------------|
863
+ | `onOpenChange` | `(open: boolean) => void` | |
864
+ | `onClose` | `() => void` | 닫기(X) 버튼 클릭 (sdClose) |
865
+
866
+ ## Dependencies
867
+
868
+ ### Depends on
869
+
870
+ - [SButton](../SButton)
871
+ - [SModalContainer](../SModalContainer)
872
+
873
+ ### Graph
874
+
875
+ ---
876
+
877
+ # SBadge
878
+
879
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
880
+
881
+ ### SBadge
882
+
883
+ #### Props
884
+
885
+ | Prop | Type | Default | Description |
886
+ |------|------|---------|-------------|
887
+ | `color?` | `SBadgeColor` | `'blue'` | 뱃지 색상 |
888
+
889
+ ## Dependencies
890
+
891
+ ### Used by
892
+
893
+ - [SList](../SList)
894
+
895
+ ### Graph
896
+
897
+ ---
898
+
899
+ # SBarcodeInput
900
+
901
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
902
+
903
+ ### SBarcodeInput
904
+
905
+ #### Props
906
+
907
+ | Prop | Type | Default | Description |
908
+ |------|------|---------|-------------|
909
+ | `value?` | `string \| number \| null` | — | |
910
+ | `size?` | `SBarcodeInputSize` | `'sm'` | |
911
+ | `clearable?` | `boolean` | `false` | |
912
+ | `autoFocus?` | `boolean` | — | |
913
+ | `name?` | `string` | — | |
914
+ | `placeholder?` | `string` | `'입력해 주세요.'` | |
915
+ | `disabled?` | `boolean` | `false` | |
916
+ | `readOnly?` | `boolean` | `false` | |
917
+ | `rules?` | `Rule[]` | — | |
918
+ | `status?` | `SFieldStatus` | — | |
919
+ | `focused?` | `boolean` | — | |
920
+ | `hovered?` | `boolean` | — | |
921
+ | `prefix?` | `ReactNode` | — | 입력 앞 슬롯 |
922
+ | `suffix?` | `ReactNode` | — | 입력 뒤 슬롯 |
923
+ | `inputClass?` | `string` | — | |
924
+ | `inputStyle?` | `CSSProperties` | — | |
925
+ | `label?` | `string` | — | |
926
+ | `labelWidth?` | `number \| string` | — | |
927
+ | `icon?` | `SIconName` | — | |
928
+ | `iconColor?` | `SColor` | — | |
929
+ | `labelTooltip?` | `string` | — | |
930
+ | `labelTooltipProps?` | `Partial<STooltipProps>` | — | |
931
+ | `addonLabel?` | `string` | — | |
932
+ | `addonAlign?` | `SFieldAddonAlign` | — | |
933
+ | `hint?` | `string` | — | |
934
+ | `error?` | `boolean` | — | |
935
+ | `errorMessage?` | `string` | — | |
936
+ | `width?` | `number \| string` | — | |
937
+ | `className?` | `string` | — | |
938
+ | `style?` | `CSSProperties` | — | |
939
+
940
+ #### Events
941
+
942
+ | Event | Type | Description |
943
+ |-------|------|-------------|
944
+ | `onValueChange` | `(value: string) => void` | 값 변경 (sdUpdate) |
945
+ | `onFocus` | `() => void` | |
946
+ | `onBlur` | `() => void` | |
947
+
948
+ ## Dependencies
949
+
950
+ ### Depends on
951
+
952
+ - [SField](../SField)
953
+ - [SGhostButton](../SGhostButton)
954
+
955
+ ### Graph
956
+
957
+ ---
958
+
959
+ # SButton
960
+
961
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
962
+
963
+ ### SButton
964
+
965
+ #### Props
966
+
967
+ | Prop | Type | Default | Description |
968
+ |------|------|---------|-------------|
969
+ | `color?` | `SButtonColor` | `'primary'` | 색상 — sd-button preset의 색 계열 |
970
+ | `outline?` | `boolean` | `false` | 아웃라인(테두리) 스타일. primary·neutral·danger 만 지원 (secondary 는 무시) |
971
+ | `size?` | `SButtonSize` | `'sm'` | 크기 |
972
+ | `icon?` | `SIconName` | — | 레이블 왼쪽 아이콘 |
973
+ | `rightIcon?` | `SIconName` | — | 레이블 오른쪽 아이콘 |
974
+ | `label?` | `string` | — | 버튼 텍스트 (문자열만 — 아이콘은 icon/rightIcon 사용) |
975
+
976
+ ## Dependencies
977
+
978
+ ### Used by
979
+
980
+ - [SActionModal](../SActionModal)
981
+ - [SConfirmModal](../SConfirmModal)
982
+ - [SDropdownButton](../SDropdownButton)
983
+ - [SKeyValueTable](../SKeyValueTable)
984
+ - [SLoadingModal](../SLoadingModal)
985
+ - [SPopover](../SPopover)
986
+ - [SPopup](../SPopup)
987
+ - [SToast](../SToast)
988
+
989
+ ### Depends on
990
+
991
+ - [SIcon](../SIcon)
992
+
993
+ ### Graph
994
+
995
+ ---
996
+
997
+ # SCalendar
998
+
999
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
1000
+
1001
+ ### SCalendar
1002
+
1003
+ #### Props
1004
+
1005
+ | Prop | Type | Default | Description |
1006
+ |------|------|---------|-------------|
1007
+ | `value?` | `string \| null` | — | 선택 날짜 (YYYY-MM-DD) |
1008
+ | `selectable?` | `[string, string]` | — | 선택 가능 범위 [시작, 종료] (빈 문자열이면 제한 없음) |
1009
+ | `events?` | `SCalendarEventGroup[]` | — | 이벤트 그룹 (날짜별 점) |
1010
+ | `elevated?` | `boolean` | `false` | 카드 그림자 |
1011
+ | `className?` | `string` | — | |
1012
+ | `style?` | `CSSProperties` | — | |
1013
+
1014
+ #### Events
1015
+
1016
+ | Event | Type | Description |
1017
+ |-------|------|-------------|
1018
+ | `onValueChange` | `(date: string) => void` | 선택 변경 (sdUpdate) |
1019
+ | `onViewChange` | `(v: { year: number; month: number }) => void` | 보이는 연·월 변경 (sdViewChange) |
1020
+
1021
+ ## Dependencies
1022
+
1023
+ ### Used by
1024
+
1025
+ - [SDatePicker](../SDatePicker)
1026
+
1027
+ ### Depends on
1028
+
1029
+ - [SGhostButton](../SGhostButton)
1030
+
1031
+ ### Graph
1032
+
1033
+ ---
1034
+
1035
+ # SCallout
1036
+
1037
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
1038
+
1039
+ ### SCallout
1040
+
1041
+ #### Props
1042
+
1043
+ | Prop | Type | Default | Description |
1044
+ |------|------|---------|-------------|
1045
+ | `type?` | `SCalloutType` | `'default'` | 콜아웃 타입 (색상·아이콘·타이틀 결정) |
1046
+ | `message?` | `SCalloutMessage[]` | `[]` | 표시할 메시지 목록 (배열 중첩으로 depth 표현). 각 문자열은 인라인 HTML(`<b>`, `<a>` 등)을 허용하며 sanitizeInlineHtml 로 살균 후 렌더된다. |
1047
+ | `children?` | `ReactNode` | — | message 대신 직접 본문을 넣을 때 |
1048
+ | `className?` | `string` | — | |
1049
+ | `style?` | `CSSProperties` | — | |
1050
+
1051
+ ## Dependencies
1052
+
1053
+ ### Depends on
1054
+
1055
+ - [SIcon](../SIcon)
1056
+
1057
+ ### Graph
1058
+
1059
+ ---
1060
+
1061
+ # SCard
1062
+
1063
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
1064
+
1065
+ ### SCard
1066
+
1067
+ #### Props
1068
+
1069
+ | Prop | Type | Default | Description |
1070
+ |------|------|---------|-------------|
1071
+ | `bordered?` | `boolean` | `false` | 테두리 표시 여부 |
1072
+
1073
+ ---
1074
+
1075
+ # SCheckbox
1076
+
1077
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
1078
+
1079
+ ### SCheckbox
1080
+
1081
+ #### Props
1082
+
1083
+ | Prop | Type | Default | Description |
1084
+ |------|------|---------|-------------|
1085
+ | `value?` | `SCheckboxValue` | `false` | 선택 상태. boolean(단일) / 배열(그룹) / null(indeterminate) |
1086
+ | `val?` | `unknown` | — | 배열(그룹) 모드에서 이 체크박스의 값 |
1087
+ | `label?` | `string` | `''` | 라벨 |
1088
+ | `disabled?` | `boolean` | `false` | 비활성 |
1089
+ | `inverse?` | `boolean` | `false` | 어두운 배경용 흰색 스타일 |
1090
+ | `className?` | `string` | — | |
1091
+ | `style?` | `CSSProperties` | — | |
1092
+
1093
+ #### Events
1094
+
1095
+ | Event | Type | Description |
1096
+ |-------|------|-------------|
1097
+ | `onValueChange` | `(value: boolean \| unknown[]) => void` | 값 변경 (sdUpdate) |
1098
+
1099
+ ## Dependencies
1100
+
1101
+ ### Used by
1102
+
1103
+ - [SKeyValueTable](../SKeyValueTable)
1104
+ - [STable](../STable)
1105
+
1106
+ ### Depends on
1107
+
1108
+ - [SIcon](../SIcon)
1109
+
1110
+ ### Graph
1111
+
1112
+ ---
1113
+
1114
+ # SChip
1115
+
1116
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
1117
+
1118
+ ### SChip
1119
+
1120
+ #### Props
1121
+
1122
+ | Prop | Type | Default | Description |
1123
+ |------|------|---------|-------------|
1124
+ | `name?` | `string` | — | 폼 연동용 name |
1125
+ | `value?` | `string` | `''` | 칩 텍스트 값 |
1126
+ | `error?` | `boolean` | — | 에러 상태 |
1127
+ | `disabled?` | `boolean` | `false` | 비활성 |
1128
+ | `editable?` | `boolean` | `false` | 인라인 편집 가능 (기본값 false) |
1129
+ | `showRemove?` | `boolean` | `true` | 닫기 버튼 표시 |
1130
+ | `placeholder?` | `string` | `''` | 편집 중 placeholder |
1131
+ | `rules?` | `Rule[]` | — | 유효성 규칙 |
1132
+ | `className?` | `string` | — | |
1133
+ | `style?` | `CSSProperties` | — | |
1134
+
1135
+ #### Events
1136
+
1137
+ | Event | Type | Description |
1138
+ |-------|------|-------------|
1139
+ | `onValueChange` | `(value: string) => void` | 편집 완료 (sdUpdate) |
1140
+ | `onRemove` | `() => void` | 삭제 (sdRemove) |
1141
+ | `onFocus` | `() => void` | 편집 진입 (sdFocus) |
1142
+ | `onBlur` | `() => void` | 편집 이탈 (sdBlur) |
1143
+
1144
+ #### Methods (ref)
1145
+
1146
+ | Method | Type | Description |
1147
+ |--------|------|-------------|
1148
+ | `focus` | `() => void` | 편집 input에 포커스합니다. |
1149
+ | `getNativeElement` | `() => HTMLElement \| null` | 네이티브 편집 엘리먼트를 반환합니다. |
1150
+
1151
+ ## Dependencies
1152
+
1153
+ ### Used by
1154
+
1155
+ - [SChipInput](../SChipInput)
1156
+ - [SFilePicker](../SFilePicker)
1157
+
1158
+ ### Depends on
1159
+
1160
+ - [SGhostButton](../SGhostButton)
1161
+
1162
+ ### Graph
1163
+
1164
+ ---
1165
+
1166
+ # SChipInput
1167
+
1168
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
1169
+
1170
+ ### SChipInput
1171
+
1172
+ #### Props
1173
+
1174
+ | Prop | Type | Default | Description |
1175
+ |------|------|---------|-------------|
1176
+ | `values?` | `string[]` | `[]` | 칩 값 목록 |
1177
+ | `errors?` | `boolean[] \| ((value: string) => boolean)` | `[]` | 칩별 에러 (배열 또는 판별 함수) |
1178
+ | `disabledChips?` | `boolean[] \| ((value: string) => boolean)` | `[]` | 칩별 비활성 (배열 또는 판별 함수) |
1179
+ | `size?` | `SChipInputSize` | `'sm'` | |
1180
+ | `disabled?` | `boolean` | `false` | |
1181
+ | `placeholder?` | `string` | `'태그 입력 (Enter로 등록 / 콤마로 구분 / 띄어쓰기 불가)'` | |
1182
+ | `name?` | `string` | — | |
1183
+ | `rules?` | `Rule[]` | — | |
1184
+ | `error?` | `boolean` | — | |
1185
+ | `useReset?` | `boolean` | `false` | 입력 초기화 버튼 표시 |
1186
+ | `maxCount?` | `number` | — | 최대 칩 개수 |
1187
+ | `metaPlacement?` | `SChipInputMetaPlacement` | `'end'` | 최대 개수·초기화 meta 위치 |
1188
+ | `duplicateLabel?` | `string` | — | 중복 에러 메시지 항목명 |
1189
+ | `suggestions?` | `string[]` | `[]` | 자동완성 후보 |
1190
+ | `loadingSuggestions?` | `boolean` | `false` | |
1191
+ | `recommendedItems?` | `string[]` | `[]` | 추천 항목 (입력값 없을 때) |
1192
+ | `loadingRecommendedItems?` | `boolean` | `false` | |
1193
+ | `dropdownMinWidth?` | `number \| string` | `200` | 드롭다운 최소 너비 (숫자=px) |
1194
+ | `label?` | `string` | — | |
1195
+ | `labelWidth?` | `number \| string` | — | |
1196
+ | `hint?` | `string` | — | |
1197
+ | `errorMessage?` | `string` | — | |
1198
+ | `width?` | `number \| string` | — | |
1199
+ | `status?` | `SFieldStatus` | — | |
1200
+ | `icon?` | `SIconName` | — | |
1201
+ | `iconColor?` | `SColor` | — | |
1202
+ | `labelTooltip?` | `string` | — | |
1203
+ | `labelTooltipProps?` | `Partial<STooltipProps>` | — | |
1204
+ | `className?` | `string` | — | |
1205
+ | `style?` | `CSSProperties` | — | |
1206
+
1207
+ #### Events
1208
+
1209
+ | Event | Type | Description |
1210
+ |-------|------|-------------|
1211
+ | `onValueChange` | `(values: string[]) => void` | 값 변경 (sdUpdate) — 전체 배열 |
1212
+ | `onInput` | `(value: string) => void` | 입력 중 문자열 변경 (sdInput) |
1213
+ | `onFocus` | `() => void` | |
1214
+ | `onBlur` | `() => void` | |
1215
+
1216
+ #### Methods (ref)
1217
+
1218
+ | Method | Type | Description |
1219
+ |--------|------|-------------|
1220
+ | `focus` | `() => void` | 입력 필드에 포커스를 이동합니다. |
1221
+
1222
+ ## Dependencies
1223
+
1224
+ ### Depends on
1225
+
1226
+ - [SChip](../SChip)
1227
+ - [SField](../SField)
1228
+ - [SIcon](../SIcon)
1229
+ - [SPortal](../SPortal)
1230
+ - [STextLink](../STextLink)
1231
+
1232
+ ### Graph
1233
+
1234
+ ---
1235
+
1236
+ # SCircleProgress
1237
+
1238
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
1239
+
1240
+ ### SCircleProgress
1241
+
1242
+ #### Props
1243
+
1244
+ | Prop | Type | Default | Description |
1245
+ |------|------|---------|-------------|
1246
+ | `value?` | `number` | `0` | 진행률 (0–100) |
1247
+ | `type?` | `SCircleProgressType` | `'primary'` | 색상 테마 |
1248
+ | `indeterminate?` | `boolean` | `false` | 불확정(스피너) 모드 — value 무시 |
1249
+ | `label?` | `string` | — | 하단 레이블 |
1250
+ | `innerValue?` | `boolean` | `false` | true면 퍼센트를 원 아래가 아닌 원 가운데에 표시 |
1251
+ | `className?` | `string` | — | |
1252
+ | `style?` | `CSSProperties` | — | |
1253
+
1254
+ ## Dependencies
1255
+
1256
+ ### Used by
1257
+
1258
+ - [SLoadingContainer](../SLoadingContainer)
1259
+ - [SLoadingModal](../SLoadingModal)
1260
+ - [STable](../STable)
1261
+
1262
+ ### Graph
1263
+
1264
+ ---
1265
+
1266
+ # SConfirmModal
1267
+
1268
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
1269
+
1270
+ ### SConfirmModal
1271
+
1272
+ #### Props
1273
+
1274
+ | Prop | Type | Default | Description |
1275
+ |------|------|---------|-------------|
1276
+ | `open?` | `boolean` | — | |
1277
+ | `persistent?` | `boolean` | — | true면 백드롭·ESC로 안 닫히고 흔들림 (sd-modal-container persistent) |
1278
+ | `type?` | `SConfirmModalType` | `'positive'` | 타입 (아이콘·메인버튼 색 결정) |
1279
+ | `modalTitle?` | `string` | `''` | |
1280
+ | `titleClass?` | `string` | — | 제목 엘리먼트에 추가할 클래스 (sd-confirm-modal titleClass) |
1281
+ | `topMessage?` | `string[]` | `[]` | 콘텐츠 박스 상단 메시지 목록 (각 항목이 한 줄, HTML 문자열 허용 — innerHTML 렌더링) |
1282
+ | `bottomMessage?` | `string[]` | `[]` | 콘텐츠 박스 하단 메시지 목록 (각 항목이 한 줄, HTML 문자열 허용 — innerHTML 렌더링) |
1283
+ | `tagLabel?` | `string` | — | tag 슬롯 기본 태그 레이블 (tagSlot 미지정 시) |
1284
+ | `tagShape?` | `STagShape` | `'square'` | 태그 프리셋 (shape/size/color) — sd-confirm-modal tagPreset 대응 |
1285
+ | `tagSize?` | `STagSize` | `'sm'` | |
1286
+ | `tagColor?` | `STagColor` | `'grey'` | |
1287
+ | `slotLabel?` | `string` | — | 콘텐츠 박스에서 태그 오른쪽에 오는 텍스트 (optionSlot 미지정 시) |
1288
+ | `tagSlot?` | `ReactNode` | — | 콘텐츠 박스 tag 위치 커스텀 콘텐츠 (미지정 시 tagLabel 기반 STag) |
1289
+ | `optionSlot?` | `ReactNode` | — | 콘텐츠 박스에서 태그 오른쪽 커스텀 콘텐츠 (미지정 시 slotLabel 텍스트) |
1290
+ | `mainButtonLabel?` | `string` | `'확인'` | 메인 버튼 |
1291
+ | `mainButtonName?` | `ConfirmModalMainButton` | — | 확인 버튼 프리셋 (없으면 type에 따라 자동 결정) |
1292
+ | `subButtonLabel?` | `string` | — | 서브(취소) 버튼 |
1293
+
1294
+ #### Events
1295
+
1296
+ | Event | Type | Description |
1297
+ |-------|------|-------------|
1298
+ | `onOpenChange` | `(open: boolean) => void` | |
1299
+ | `onOk` | `() => void` | 확인 버튼 클릭 (sdOk) |
1300
+ | `onCancel` | `() => void` | |
1301
+ | `onClose` | `() => void` | 닫기(X) 버튼 클릭 (sdClose) |
1302
+
1303
+ ## Dependencies
1304
+
1305
+ ### Used by
1306
+
1307
+ - [SModal](../SModal)
1308
+
1309
+ ### Depends on
1310
+
1311
+ - [SButton](../SButton)
1312
+ - [SIcon](../SIcon)
1313
+ - [SModalContainer](../SModalContainer)
1314
+ - [STag](../STag)
1315
+
1316
+ ### Graph
1317
+
1318
+ ---
1319
+
1320
+ # SDatePicker
1321
+
1322
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
1323
+
1324
+ ### SDatePicker
1325
+
1326
+ #### Props
1327
+
1328
+ | Prop | Type | Default | Description |
1329
+ |------|------|---------|-------------|
1330
+ | `value?` | `string \| null` | — | 선택 날짜 (YYYY-MM-DD) |
1331
+ | `size?` | `SDatePickerSize` | `'sm'` | |
1332
+ | `placeholder?` | `string` | `'YYYY-MM-DD'` | |
1333
+ | `selectable?` | `[string, string]` | — | 선택 가능 범위 [시작, 종료] |
1334
+ | `disabled?` | `boolean` | `false` | |
1335
+ | `width?` | `number \| string` | — | |
1336
+ | `name?` | `string` | — | |
1337
+ | `rules?` | `Rule[]` | — | |
1338
+ | `status?` | `SFieldStatus` | — | |
1339
+ | `label?` | `string` | — | |
1340
+ | `labelWidth?` | `number \| string` | — | |
1341
+ | `icon?` | `SIconName` | — | |
1342
+ | `iconColor?` | `SColor` | — | |
1343
+ | `labelTooltip?` | `string` | — | |
1344
+ | `labelTooltipProps?` | `Partial<STooltipProps>` | — | |
1345
+ | `addonLabel?` | `string` | — | |
1346
+ | `addonAlign?` | `SFieldAddonAlign` | — | |
1347
+ | `hint?` | `string` | — | |
1348
+ | `error?` | `boolean` | — | |
1349
+ | `errorMessage?` | `string` | — | |
1350
+ | `className?` | `string` | — | |
1351
+ | `style?` | `CSSProperties` | — | |
1352
+
1353
+ #### Events
1354
+
1355
+ | Event | Type | Description |
1356
+ |-------|------|-------------|
1357
+ | `onValueChange` | `(date: string) => void` | 선택 변경 (sdUpdate) |
1358
+ | `onViewChange` | `(view: { year: number; month: number }) => void` | 보이는 연·월 변경 (sdViewChange) |
1359
+
1360
+ ## Dependencies
1361
+
1362
+ ### Used by
1363
+
1364
+ - [SKeyValueTable](../SKeyValueTable)
1365
+
1366
+ ### Depends on
1367
+
1368
+ - [SCalendar](../SCalendar)
1369
+ - [SField](../SField)
1370
+ - [SIcon](../SIcon)
1371
+
1372
+ ### Graph
1373
+
1374
+ ---
1375
+
1376
+ # SDateRangePicker
1377
+
1378
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
1379
+
1380
+ ### SDateRangePicker
1381
+
1382
+ #### Props
1383
+
1384
+ | Prop | Type | Default | Description |
1385
+ |------|------|---------|-------------|
1386
+ | `value?` | `SDateRangeValue` | — | |
1387
+ | `size?` | `SDateRangePickerSize` | `'sm'` | |
1388
+ | `placeholder?` | `string` | `'YYYY-MM-DD ~ YYYY-MM-DD'` | |
1389
+ | `selectable?` | `[string, string]` | — | |
1390
+ | `maxRange?` | `number` | — | 최대 선택 일수 |
1391
+ | `useTimePicker?` | `boolean` | `false` | 시간(시:분) 선택 푸터 사용 여부. true이면 value는 "YYYY-MM-DD HH:mm" 형식이 되고, 캘린더 하단에 시작·종료 시간 입력이 표시된다. |
1392
+ | `disabled?` | `boolean` | `false` | |
1393
+ | `width?` | `number \| string` | — | |
1394
+ | `name?` | `string` | — | |
1395
+ | `rules?` | `Rule[]` | — | |
1396
+ | `status?` | `SFieldStatus` | — | |
1397
+ | `label?` | `string` | — | |
1398
+ | `labelWidth?` | `number \| string` | — | |
1399
+ | `icon?` | `SIconName` | — | |
1400
+ | `iconColor?` | `SColor` | — | |
1401
+ | `labelTooltip?` | `string` | — | |
1402
+ | `labelTooltipProps?` | `Partial<STooltipProps>` | — | |
1403
+ | `addonLabel?` | `string` | — | |
1404
+ | `addonAlign?` | `SFieldAddonAlign` | — | |
1405
+ | `hint?` | `string` | — | |
1406
+ | `error?` | `boolean` | — | |
1407
+ | `errorMessage?` | `string` | — | |
1408
+ | `className?` | `string` | — | |
1409
+ | `style?` | `CSSProperties` | — | |
1410
+
1411
+ #### Events
1412
+
1413
+ | Event | Type | Description |
1414
+ |-------|------|-------------|
1415
+ | `onValueChange` | `(range: SDateRangeValue) => void` | 선택 변경 (sdUpdate) — [start, end] |
1416
+ | `onViewChange` | `(view: { year: number; month: number }) => void` | 보이는 연·월 변경 (sdViewChange) |
1417
+
1418
+ ## Dependencies
1419
+
1420
+ ### Used by
1421
+
1422
+ - [SKeyValueTable](../SKeyValueTable)
1423
+
1424
+ ### Depends on
1425
+
1426
+ - [SField](../SField)
1427
+ - [SGhostButton](../SGhostButton)
1428
+ - [SIcon](../SIcon)
1429
+
1430
+ ### Graph
1431
+
1432
+ ---
1433
+
1434
+ # SDivider
1435
+
1436
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
1437
+
1438
+ ### SDivider
1439
+
1440
+ #### Props
1441
+
1442
+ | Prop | Type | Default | Description |
1443
+ |------|------|---------|-------------|
1444
+ | `vertical?` | `boolean` | `false` | true면 수직 분할선, false면 수평 분할선 |
1445
+
1446
+ ## Dependencies
1447
+
1448
+ ### Used by
1449
+
1450
+ - [STableBar](../STableBar)
1451
+
1452
+ ### Graph
1453
+
1454
+ ---
1455
+
1456
+ # SDraggableItem
1457
+
1458
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
1459
+
1460
+ ### SDraggableItem
1461
+
1462
+ #### Props
1463
+
1464
+ | Prop | Type | Default | Description |
1465
+ |------|------|---------|-------------|
1466
+ | `title` | `SDraggableItemSlot` | — | 드래그 아이템 제목 |
1467
+ | `trailing?` | `SDraggableItemSlot` | — | 타이틀 뒤에 표시할 태그/콘텐츠 |
1468
+ | `accentStripe?` | `boolean` | `false` | 아이템 왼쪽 accent stripe 표시 여부 |
1469
+ | `bordered?` | `boolean` | `false` | 외곽 테두리 사용 여부 |
1470
+ | `selected?` | `boolean` | `false` | 선택 상태 여부 |
1471
+ | `dense?` | `boolean` | `false` | 조밀한 높이 사용 여부 |
1472
+ | `disabled?` | `boolean` | `false` | 비활성 상태 여부 |
1473
+ | `dragOverlay?` | `boolean` | `true` | 드래그 시 마우스를 따라가는 overlay 표시 여부 |
1474
+ | `dragOverlayOpacity?` | `number` | `0.75` | 드래그 overlay 투명도 |
1475
+ | `dragHandleProps?` | `HTMLAttributes<HTMLDivElement>` | — | 드래그 핸들에 전달할 속성 |
1476
+
1477
+ #### Events
1478
+
1479
+ | Event | Type | Description |
1480
+ |-------|------|-------------|
1481
+ | `onDragHandleMouseDown` | `(event: MouseEvent<HTMLDivElement>) => void` | 드래그 핸들 mouse down 이벤트 |
1482
+
1483
+ ## Dependencies
1484
+
1485
+ ### Depends on
1486
+
1487
+ - [SIcon](../SIcon)
1488
+
1489
+ ### Graph
1490
+
1491
+ ---
1492
+
1493
+ # SDropdownButton
1494
+
1495
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
1496
+
1497
+ ### SDropdownButton
1498
+
1499
+ #### Props
1500
+
1501
+ | Prop | Type | Default | Description |
1502
+ |------|------|---------|-------------|
1503
+ | `color?` | `SButtonColor` | `'primary'` | 버튼 색상 |
1504
+ | `outline?` | `boolean` | `false` | 아웃라인(테두리) 스타일 |
1505
+ | `size?` | `SDropdownButtonSize` | `'sm'` | 크기 (lg 미지원) |
1506
+ | `label?` | `string` | `''` | 버튼 레이블 |
1507
+ | `items?` | `SDropdownButtonItem[]` | `[]` | 메뉴 아이템 목록 |
1508
+ | `split?` | `boolean` | `false` | split 모드 — 레이블 버튼과 토글 분리 |
1509
+ | `disabled?` | `boolean` | `false` | 비활성 |
1510
+ | `className?` | `string` | — | |
1511
+ | `style?` | `CSSProperties` | — | |
1512
+
1513
+ #### Events
1514
+
1515
+ | Event | Type | Description |
1516
+ |-------|------|-------------|
1517
+ | `onItemClick` | `(value: string \| number) => void` | 메뉴 아이템 선택 (sdClick) |
1518
+ | `onLabelClick` | `() => void` | split 레이블 버튼 클릭 (sdButtonClick) |
1519
+ | `onOpenChange` | `(open: boolean) => void` | 열림/닫힘 변경 (sdDropDownShow) |
1520
+
1521
+ #### Methods (ref)
1522
+
1523
+ | Method | Type | Description |
1524
+ |--------|------|-------------|
1525
+ | `open` | `() => void` | 드롭다운 열기 (sdOpen) |
1526
+ | `close` | `() => void` | 드롭다운 닫기 (sdClose) |
1527
+
1528
+ ## Dependencies
1529
+
1530
+ ### Depends on
1531
+
1532
+ - [SButton](../SButton)
1533
+ - [SIcon](../SIcon)
1534
+
1535
+ ### Graph
1536
+
1537
+ ---
1538
+
1539
+ # SExpansionItem
1540
+
1541
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
1542
+
1543
+ ### SExpansionItem
1544
+
1545
+ #### Props
1546
+
1547
+ | Prop | Type | Default | Description |
1548
+ |------|------|---------|-------------|
1549
+ | `title` | `ReactNode` | — | |
1550
+ | `depth?` | `1 \| 2 \| 3` | `1` | 중첩 단계. Figma 기준 1~3단계 |
1551
+ | `leading?` | `ReactNode \| SExpansionItemRenderProp` | — | 타이틀 앞에 표시할 아이콘/콘텐츠 |
1552
+ | `trailing?` | `ReactNode` | — | 타이틀 뒤에 표시할 태그/콘텐츠 |
1553
+ | `bodyPadding?` | `boolean` | `true` | body 내부 패딩 사용 여부 |
1554
+ | `bordered?` | `boolean` | `false` | 외곽 테두리 사용 여부 |
1555
+ | `expanded?` | `boolean` | — | |
1556
+ | `defaultExpanded?` | `boolean` | `false` | |
1557
+ | `dense?` | `boolean` | `false` | |
1558
+ | `disabled?` | `boolean` | `false` | |
1559
+
1560
+ #### Events
1561
+
1562
+ | Event | Type | Description |
1563
+ |-------|------|-------------|
1564
+ | `onToggle` | `(expanded: boolean, event: MouseEvent<HTMLButtonElement>) => void` | |
1565
+
1566
+ ## Dependencies
1567
+
1568
+ ### Depends on
1569
+
1570
+ - [SIcon](../SIcon)
1571
+
1572
+ ### Graph
1573
+
1574
+ ---
1575
+
1576
+ # SField
1577
+
1578
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
1579
+
1580
+ ### SField
1581
+
1582
+ #### Props
1583
+
1584
+ | Prop | Type | Default | Description |
1585
+ |------|------|---------|-------------|
1586
+ | `name?` | `string` | — | 폼 연동용 name 속성 |
1587
+ | `label?` | `string` | `''` | 레이블 텍스트 |
1588
+ | `icon?` | `SIconName` | — | 레이블 영역 아이콘명 (크기는 size 토큰 16px 고정) |
1589
+ | `iconColor?` | `SColor` | — | 레이블 영역 아이콘 색상. 미지정 시 currentColor 상속 |
1590
+ | `labelTooltip?` | `string` | `''` | 레이블 툴팁 텍스트 |
1591
+ | `labelTooltipProps?` | `Partial<STooltipProps>` | — | 레이블 툴팁 상세 옵션 (SdTooltipProps 대응) |
1592
+ | `labelWidth?` | `number \| string` | — | 레이블/어드온 레이블 너비 (숫자=px). label·addonLabel 폭에 공통 적용 |
1593
+ | `labelHeight?` | `number \| string` | — | 레이블 박스 높이 (숫자=px). 미지정 시 size별 기본값 사용. 컨트롤 박스가 기본 필드 높이보다 큰 경우(multiline 등) 첫 행에 세로 중앙 정렬을 맞추기 위해 컨트롤 박스 높이를 넘겨받는다. |
1594
+ | `size?` | `SFieldSize` | `'sm'` | 필드 크기 |
1595
+ | `addonLabel?` | `string` | `''` | 우측 어드온 레이블 |
1596
+ | `addonAlign?` | `SFieldAddonAlign` | `'start'` | 어드온 정렬 |
1597
+ | `error?` | `boolean` | `false` | 에러 상태 |
1598
+ | `errorMessage?` | `string` | `''` | 에러 메시지 |
1599
+ | `status?` | `SFieldStatus` | — | 필드 상태 — 'error' | 'pass' | 'default'. 'pass'면 초록 테두리 |
1600
+ | `focused?` | `boolean` | `false` | 포커스 상태 (제어/반영) |
1601
+ | `hovered?` | `boolean` | `false` | 호버 상태 (제어/반영) |
1602
+ | `rules?` | `Rule[]` | — | 유효성 규칙 (검증은 컨트롤에서 수행, API 패리티) |
1603
+ | `hint?` | `string` | `''` | 하단 힌트 |
1604
+ | `disabled?` | `boolean` | `false` | 비활성 |
1605
+ | `readOnly?` | `boolean` | `false` | 읽기 전용 (회색 배경) |
1606
+ | `width?` | `number \| string` | — | 컨트롤 너비 (숫자=px). 지정하면 필드가 부모 폭을 다 먹지 않고 (레이블 + width) 만큼만 차지한다. 다른 요소와 나란히 놓으려면 부모를 flex 로 두면 된다. |
1607
+ | `minWidth?` | `number \| string` | — | 컨트롤 최소 너비 (숫자=px). 하한선만 지정하며 필드는 계속 부모 폭을 채운다 |
1608
+ | `multiline?` | `boolean` | `false` | 멀티라인(textarea) — 컨트롤 높이를 고정하지 않고 min-height만 적용 |
1609
+ | `borderless?` | `boolean` | `false` | 테두리 박스 제거 (inline 컨트롤용) — border/배경/hover·focus 강조만 사라지고 label·hint·errorMessage 등 나머지 필드 구성은 그대로 동작한다. |
1610
+ | `children?` | `ReactNode` | — | 실제 컨트롤 (input/select 등) — 테두리 없이 렌더, 테두리는 SField가 제공 |
1611
+ | `className?` | `string` | — | |
1612
+ | `style?` | `CSSProperties` | — | |
1613
+
1614
+ #### Events
1615
+
1616
+ | Event | Type | Description |
1617
+ |-------|------|-------------|
1618
+ | `onMouseEnter` | `() => void` | |
1619
+ | `onMouseLeave` | `() => void` | |
1620
+
1621
+ #### Methods (ref)
1622
+
1623
+ | Method | Type | Description |
1624
+ |--------|------|-------------|
1625
+ | `focus` | `() => void` | 내부 컨트롤에 포커스하고 필드를 화면에 스크롤합니다. |
1626
+
1627
+ ## Dependencies
1628
+
1629
+ ### Used by
1630
+
1631
+ - [SBarcodeInput](../SBarcodeInput)
1632
+ - [SChipInput](../SChipInput)
1633
+ - [SDatePicker](../SDatePicker)
1634
+ - [SDateRangePicker](../SDateRangePicker)
1635
+ - [SFilePicker](../SFilePicker)
1636
+ - [SInput](../SInput)
1637
+ - [SNumberInput](../SNumberInput)
1638
+ - [SSelect](../SSelect)
1639
+ - [STextarea](../STextarea)
1640
+ - [STimePicker](../STimePicker)
1641
+ - [STimeRangePicker](../STimeRangePicker)
1642
+
1643
+ ### Depends on
1644
+
1645
+ - [SIcon](../SIcon)
1646
+ - [STooltip](../STooltip)
1647
+
1648
+ ### Graph
1649
+
1650
+ ---
1651
+
1652
+ # SFilePicker
1653
+
1654
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
1655
+
1656
+ ### SFilePicker
1657
+
1658
+ #### Props
1659
+
1660
+ | Prop | Type | Default | Description |
1661
+ |------|------|---------|-------------|
1662
+ | `value?` | `SFilePickerValue` | `null` | |
1663
+ | `placeholder?` | `string` | `'Click to upload'` | |
1664
+ | `disabled?` | `boolean` | `false` | |
1665
+ | `focused?` | `boolean` | `false` | 포커스 상태 여부 |
1666
+ | `hovered?` | `boolean` | `false` | 호버 상태 여부 |
1667
+ | `inline?` | `boolean` | `false` | 인라인 표시 — SField 테두리 박스만 사라지고 label/rules/hint 는 그대로 동작 |
1668
+ | `multiple?` | `boolean` | `false` | |
1669
+ | `useMultipleListBox?` | `boolean` | `false` | multiple 파일 목록을 필드 아래 칩 대신, caret(▼) 클릭 시 열리는 리스트박스 카드로 표시 (바깥 클릭 시 닫힘) |
1670
+ | `accept?` | `string` | — | |
1671
+ | `maxFileSize?` | `number \| string` | — | |
1672
+ | `maxTotalSize?` | `number \| string` | — | |
1673
+ | `maxFiles?` | `number \| string` | — | |
1674
+ | `name?` | `string` | — | |
1675
+ | `rules?` | `Rule[]` | — | |
1676
+ | `status?` | `SFieldStatus` | — | |
1677
+ | `size?` | `SFieldSize` | `'sm'` | |
1678
+ | `label?` | `string` | — | |
1679
+ | `labelWidth?` | `number \| string` | — | |
1680
+ | `icon?` | `SIconName` | — | |
1681
+ | `iconColor?` | `SColor` | — | |
1682
+ | `labelTooltip?` | `string` | — | |
1683
+ | `labelTooltipProps?` | `Partial<STooltipProps>` | — | |
1684
+ | `addonLabel?` | `string` | — | |
1685
+ | `addonAlign?` | `SFieldAddonAlign` | `'start'` | |
1686
+ | `hint?` | `string` | — | |
1687
+ | `error?` | `boolean` | — | |
1688
+ | `errorMessage?` | `string` | — | |
1689
+ | `width?` | `number \| string` | — | |
1690
+ | `className?` | `string` | — | |
1691
+ | `style?` | `CSSProperties` | — | |
1692
+
1693
+ #### Events
1694
+
1695
+ | Event | Type | Description |
1696
+ |-------|------|-------------|
1697
+ | `onValueChange` | `(value: SFilePickerValue) => void` | 파일 변경 (sdUpdate) |
1698
+ | `onReject` | `(detail: { files: File[]; reason: SFilePickerRejectReason }) => void` | 제한 초과 거부 (sdReject) |
1699
+
1700
+ ## Dependencies
1701
+
1702
+ ### Used by
1703
+
1704
+ - [SKeyValueTable](../SKeyValueTable)
1705
+
1706
+ ### Depends on
1707
+
1708
+ - [SChip](../SChip)
1709
+ - [SField](../SField)
1710
+ - [SGhostButton](../SGhostButton)
1711
+ - [SIcon](../SIcon)
1712
+
1713
+ ### Graph
1714
+
1715
+ ---
1716
+
1717
+ # SForm
1718
+
1719
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
1720
+
1721
+ ### SForm
1722
+
1723
+ #### Props
1724
+
1725
+ | Prop | Type | Default | Description |
1726
+ |------|------|---------|-------------|
1727
+ | `formClass?` | `string` | — | form 엘리먼트에 추가할 클래스 (sd-form formClass 대응) |
1728
+ | `children?` | `ReactNode` | — | |
1729
+ | `className?` | `string` | — | |
1730
+ | `style?` | `CSSProperties` | — | |
1731
+
1732
+ #### Events
1733
+
1734
+ | Event | Type | Description |
1735
+ |-------|------|-------------|
1736
+ | `onSubmit` | `() => void` | 모든 필드 검증 통과 후 발생 (sd-form sdSubmit 대응) |
1737
+ | `onValidationError` | `(error: SFormValidationError) => void` | 검증 실패 시 실패한 필드 name 목록 전달 (sd-form sdValidationError 대응) |
1738
+
1739
+ #### Methods (ref)
1740
+
1741
+ | Method | Type | Description |
1742
+ |--------|------|-------------|
1743
+ | `validate` | `() => boolean` | 등록된 모든 필드를 검증하고 통과 여부를 반환 |
1744
+ | `resetValidation` | `() => void` | 모든 필드의 검증 상태만 초기화 (값은 소비자 소유) |
1745
+ | `reset` | `() => void` | resetValidation 별칭 — sd-form sdReset 대응. 값 리셋은 소비자(value/onChange) 책임 |
1746
+ | `focusFirstInvalid` | `() => void` | 첫 번째 실패 필드로 포커스 이동 |
1747
+
1748
+ ---
1749
+
1750
+ # SGhostButton
1751
+
1752
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
1753
+
1754
+ ### SGhostButton
1755
+
1756
+ #### Props
1757
+
1758
+ | Prop | Type | Default | Description |
1759
+ |------|------|---------|-------------|
1760
+ | `icon` | `SIconName` | — | 표시할 아이콘명 (필수) |
1761
+ | `size?` | `SGhostButtonSize` | `'sm'` | 버튼 크기 |
1762
+ | `intent?` | `SGhostButtonIntent` | `'default'` | 색상 의도 |
1763
+ | `ariaLabel?` | `string` | `''` | 스크린리더용 접근성 레이블 |
1764
+ | `ariaPressed?` | `boolean` | — | 토글 버튼일 때 눌림 상태 (aria-pressed). 미지정 시 속성 자체를 내보내지 않는다. |
1765
+ | `tooltipText?` | `string` | — | 호버 시 표시할 툴팁 텍스트 |
1766
+ | `disabled?` | `boolean` | `false` | 비활성 |
1767
+ | `anchorClassName?` | `string` | — | tooltip anchor wrapper 클래스. tooltipText가 있을 때 위치 조정용으로 사용합니다. |
1768
+ | `anchorStyle?` | `CSSProperties` | — | tooltip anchor wrapper 스타일. tooltipText가 있을 때 위치 조정용으로 사용합니다. |
1769
+ | `className?` | `string` | — | |
1770
+ | `style?` | `CSSProperties` | — | |
1771
+
1772
+ #### Events
1773
+
1774
+ | Event | Type | Description |
1775
+ |-------|------|-------------|
1776
+ | `onClick` | `(e: MouseEvent<HTMLButtonElement>) => void` | 클릭 (sdClick) |
1777
+
1778
+ ## Dependencies
1779
+
1780
+ ### Used by
1781
+
1782
+ - [SBarcodeInput](../SBarcodeInput)
1783
+ - [SCalendar](../SCalendar)
1784
+ - [SChip](../SChip)
1785
+ - [SDateRangePicker](../SDateRangePicker)
1786
+ - [SFilePicker](../SFilePicker)
1787
+ - [SGnb](../SGnb)
1788
+ - [SGuide](../SGuide)
1789
+ - [SInput](../SInput)
1790
+ - [SModalContainer](../SModalContainer)
1791
+ - [SPopover](../SPopover)
1792
+ - [SSelect](../SSelect)
1793
+ - [STimePicker](../STimePicker)
1794
+ - [STimeRangePicker](../STimeRangePicker)
1795
+ - [SToast](../SToast)
1796
+ - [STooltip](../STooltip)
1797
+
1798
+ ### Depends on
1799
+
1800
+ - [SIcon](../SIcon)
1801
+ - [STag](../STag)
1802
+
1803
+ ### Graph
1804
+
1805
+ ---
1806
+
1807
+ # SGnb
1808
+
1809
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
1810
+
1811
+ ### SGnb
1812
+
1813
+ #### Props
1814
+
1815
+ | Prop | Type | Default | Description |
1816
+ |------|------|---------|-------------|
1817
+ | `type?` | `SGnbType` | — | 메뉴 스타일: box(라운드) / belt(풀폭 행). 미지정 시 SLayout 의 type 을 따른다. |
1818
+ | `header?` | `SGnbHeader` | — | 상단바 구조: fix(GNB 폭 고정) / full(레이아웃 전폭). 미지정 시 SLayout 의 header 를 따른다. 단, useRail 이고 레일에 children 없는 아이템이 있으면 메뉴 폭이 출렁이므로 full 로 강제된다. |
1819
+ | `color?` | `SGnbColor` | `'light'` | 색상 테마: light / dark |
1820
+ | `items` | `SGnbMenuItem[]` | — | 메뉴 트리 (depth1 → depth2 → depth3). useRail 이면 첫 depth 가 레일이 되고 그 children 이 depth1 이 된다. |
1821
+ | `useRail?` | `boolean` | — | 좌측 레일 사용 여부. 켜면 items 의 첫 depth 가 아이콘+라벨 버튼의 세로 레일로 서고, 선택된 레일 아이템의 children 이 오른쪽 메뉴 패널에 깔린다. 미지정 시 SLayout 의 useRail 을 따른다. children 없는 레일 아이템이 활성이면 메뉴 패널은 렌더되지 않고 레일만 남는다. |
1822
+ | `value?` | `string` | `''` | 현재 선택된 아이템 value |
1823
+ | `folded?` | `boolean` | — | 접힘(레일) 상태. 미지정 시 SLayout 의 folded 를 따른다. |
1824
+ | `logo?` | `ReactNode` | — | 상단바 로고 영역 (slot) |
1825
+ | `railFooter?` | `ReactNode` | — | 레일 하단 고정 슬롯. 레일 아이템이 많아 넘치면 아이템 목록(ul)만 스크롤되고 이 슬롯은 레일 하단에 붙어 고정된다. useRail 일 때만 렌더된다. |
1826
+ | `menuFooter?` | `ReactNode` | — | 메뉴 하단 고정 슬롯. 메뉴 아이템이 많아 넘치면 메뉴 목록(ul)만 스크롤되고 이 슬롯은 메뉴 하단에 붙어 고정된다. 메뉴가 렌더될 때만(showMenu) 나타난다. |
1827
+ | `foldedFooter?` | `ReactNode` | — | 접힘(fix 레일) 하단 고정 슬롯. 접으면 본문이 빠져나가며 rail/menu footer 도 사라지므로, 48px 폴드 레일 바닥에 붙는 별도 슬롯이다. header="fix" 로 접혔을 때만 나타난다. |
1828
+ | `ariaLabel?` | `string` | `'global navigation'` | 메뉴 landmark(nav) 의 접근성 레이블. 한 화면에 nav 가 여럿일 때 구분한다. |
1829
+
1830
+ #### Events
1831
+
1832
+ | Event | Type | Description |
1833
+ |-------|------|-------------|
1834
+ | `onValueChange` | `(value: string) => void` | 선택 변경 (sdUpdate) |
1835
+ | `onRailChange` | `(value: string) => void` | 레일 선택 변경. 레일 아이템을 눌러 패널이 바뀔 때 알린다(선택 상태는 SGnb 가 자체 관리). |
1836
+ | `onFoldChange` | `(folded: boolean) => void` | 접힘 토글 (sdFoldChange) |
1837
+ | `onLauncherClick` | `() => void` | 앱런처(그리드) 버튼 클릭. 미지정 시 런처 버튼을 렌더하지 않는다. 접힘 레일(fix)에는 폴드 버튼만 남으므로 표시되지 않는다. |
1838
+
1839
+ ## Dependencies
1840
+
1841
+ ### Used by
1842
+
1843
+ - [SLayout](../SLayout)
1844
+
1845
+ ### Depends on
1846
+
1847
+ - [SGhostButton](../SGhostButton)
1848
+ - [SIcon](../SIcon)
1849
+ - [SLayout](../SLayout)
1850
+ - [STag](../STag)
1851
+
1852
+ ### Graph
1853
+
1854
+ ---
1855
+
1856
+ # SGuide
1857
+
1858
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
1859
+
1860
+ ### SGuide
1861
+
1862
+ #### Props
1863
+
1864
+ | Prop | Type | Default | Description |
1865
+ |------|------|---------|-------------|
1866
+ | `type?` | `SGuideType` | `'tip'` | 가이드 타입 (tip: 팝업, notion: 외부 링크) |
1867
+ | `label?` | `string` | `''` | 버튼 레이블 (없으면 type 기본값) |
1868
+ | `message?` | `SGuideMessage` | `''` | 팝업 메시지 (배열이면 리스트, 중첩 배열이면 depth 표현) |
1869
+ | `url?` | `string` | `''` | notion 타입 클릭 시 이동 URL |
1870
+ | `popupTitle?` | `string` | `''` | 팝업 제목 (없으면 type 기본값) |
1871
+ | `popupWidth?` | `number` | — | 팝업 너비 (px, 기본 426) |
1872
+ | `className?` | `string` | — | |
1873
+ | `style?` | `CSSProperties` | — | |
1874
+
1875
+ ## Dependencies
1876
+
1877
+ ### Depends on
1878
+
1879
+ - [SGhostButton](../SGhostButton)
1880
+ - [SIcon](../SIcon)
1881
+
1882
+ ### Graph
1883
+
1884
+ ---
1885
+
1886
+ # SIcon
1887
+
1888
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
1889
+
1890
+ ### SIcon
1891
+
1892
+ #### Props
1893
+
1894
+ | Prop | Type | Default | Description |
1895
+ |------|------|---------|-------------|
1896
+ | `name` | `SIconName` | — | 아이콘명 |
1897
+ | `size?` | `number \| string` | `24` | 아이콘 크기(px 또는 CSS 길이) |
1898
+ | `color?` | `SColor` | — | 아이콘 색상. 팔레트 키(`grey_65`, `red_95` …) 또는 임의 CSS 색상. 미지정 시 currentColor 상속 |
1899
+ | `rotate?` | `0 \| 90 \| 180 \| 270` | — | 회전 각도 |
1900
+ | `label?` | `string` | — | 접근성 라벨. 없으면 aria-hidden 처리 |
1901
+ | `className?` | `string` | — | |
1902
+ | `style?` | `CSSProperties` | — | |
1903
+
1904
+ ## Dependencies
1905
+
1906
+ ### Used by
1907
+
1908
+ - [SButton](../SButton)
1909
+ - [SCallout](../SCallout)
1910
+ - [SCheckbox](../SCheckbox)
1911
+ - [SChipInput](../SChipInput)
1912
+ - [SConfirmModal](../SConfirmModal)
1913
+ - [SDatePicker](../SDatePicker)
1914
+ - [SDateRangePicker](../SDateRangePicker)
1915
+ - [SDraggableItem](../SDraggableItem)
1916
+ - [SDropdownButton](../SDropdownButton)
1917
+ - [SExpansionItem](../SExpansionItem)
1918
+ - [SField](../SField)
1919
+ - [SFilePicker](../SFilePicker)
1920
+ - [SGhostButton](../SGhostButton)
1921
+ - [SGnb](../SGnb)
1922
+ - [SGuide](../SGuide)
1923
+ - [SKeyValueTable](../SKeyValueTable)
1924
+ - [SList](../SList)
1925
+ - [SLoadingModal](../SLoadingModal)
1926
+ - [SNumberInput](../SNumberInput)
1927
+ - [SPagination](../SPagination)
1928
+ - [SPopover](../SPopover)
1929
+ - [SSectionHeaderCard](../SSectionHeaderCard)
1930
+ - [SSelect](../SSelect)
1931
+ - [SStepper](../SStepper)
1932
+ - [STable](../STable)
1933
+ - [STag](../STag)
1934
+ - [STextLink](../STextLink)
1935
+ - [STimePicker](../STimePicker)
1936
+ - [STimeRangePicker](../STimeRangePicker)
1937
+ - [SToast](../SToast)
1938
+ - [STooltip](../STooltip)
1939
+
1940
+ ### Graph
1941
+
1942
+ ---
1943
+
1944
+ # SInput
1945
+
1946
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
1947
+
1948
+ ### SInput
1949
+
1950
+ #### Props
1951
+
1952
+ | Prop | Type | Default | Description |
1953
+ |------|------|---------|-------------|
1954
+ | `value?` | `string \| number` | — | 값 (제어) |
1955
+ | `type?` | `'text' \| 'password' \| 'email' \| 'number' \| 'tel' \| 'search' \| 'url'` | `'text'` | 입력 타입 |
1956
+ | `size?` | `SFieldSize` | `'sm'` | 크기 |
1957
+ | `icon?` | `SIconName` | — | 좌측 아이콘명 |
1958
+ | `iconColor?` | `SColor` | — | 좌측 아이콘 색상 |
1959
+ | `prefix?` | `ReactNode` | — | 입력 앞(좌측) 커스텀 노드 — 원본 slot="prefix" |
1960
+ | `suffix?` | `ReactNode` | — | 입력 뒤(우측) 커스텀 노드 — 원본 slot="suffix" |
1961
+ | `clearable?` | `boolean` | `false` | 지우기 버튼 |
1962
+ | `rules?` | `Rule[]` | — | 유효성 규칙 — blur 시 자동 검증 |
1963
+ | `status?` | `SFieldStatus` | — | 필드 상태 ('default' | 'pass' | 'error') |
1964
+ | `focused?` | `boolean` | — | 포커스 상태 (제어/반영) |
1965
+ | `hovered?` | `boolean` | — | 호버 상태 (제어/반영) |
1966
+ | `inputClass?` | `string` | — | 내부 input 요소 className |
1967
+ | `inputStyle?` | `CSSProperties` | — | 내부 input 요소 style |
1968
+ | `label?` | `string` | — | |
1969
+ | `labelWidth?` | `number \| string` | — | |
1970
+ | `labelTooltip?` | `string` | — | 레이블 툴팁 텍스트 |
1971
+ | `labelTooltipProps?` | `Partial<STooltipProps>` | — | 레이블 툴팁 상세 옵션 |
1972
+ | `hint?` | `string` | — | |
1973
+ | `error?` | `boolean` | — | |
1974
+ | `errorMessage?` | `string` | — | |
1975
+ | `addonLabel?` | `string` | — | |
1976
+ | `addonAlign?` | `SFieldAddonAlign` | — | 어드온 정렬 |
1977
+ | `width?` | `number \| string` | — | |
1978
+ | `disabled?` | `boolean` | `false` | |
1979
+ | `readOnly?` | `boolean` | `false` | |
1980
+ | `className?` | `string` | — | |
1981
+ | `style?` | `CSSProperties` | — | |
1982
+
1983
+ #### Events
1984
+
1985
+ | Event | Type | Description |
1986
+ |-------|------|-------------|
1987
+ | `onValueChange` | `(value: string) => void` | 값 변경 (sdUpdate) — 문자열 전달 |
1988
+ | `onChange` | `InputHTMLAttributes<HTMLInputElement>['onChange']` | 네이티브 onChange (form-agnostic 연동용, RHF 등) |
1989
+
1990
+ ## Dependencies
1991
+
1992
+ ### Used by
1993
+
1994
+ - [SKeyValueTable](../SKeyValueTable)
1995
+
1996
+ ### Depends on
1997
+
1998
+ - [SField](../SField)
1999
+ - [SGhostButton](../SGhostButton)
2000
+
2001
+ ### Graph
2002
+
2003
+ ---
2004
+
2005
+ # SKeyValueTable
2006
+
2007
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
2008
+
2009
+ ### SKeyValueTable
2010
+
2011
+ #### Props
2012
+
2013
+ | Prop | Type | Default | Description |
2014
+ |------|------|---------|-------------|
2015
+ | `fields?` | `SKeyValueField[][]` | `[]` | 2차원 배열. row → [th|td] 필드들 |
2016
+ | `values?` | `Record<string, unknown>` | `{}` | field name을 key로 하는 값 객체 (`{ [name]: value }`). 지정 시 해당 field의 값으로 사용되며, field별 `options.value`보다 우선합니다. `onChange`의 `detail.values`와 함께 controlled 패턴으로 사용합니다. |
2017
+ | `search?` | `boolean` | `false` | 우측 검색 패널 |
2018
+ | `radius?` | `'default' \| 'useTop' \| 'full'` | `'default'` | border-radius 제어 |
2019
+ | `className?` | `string` | — | |
2020
+ | `style?` | `CSSProperties` | — | |
2021
+
2022
+ #### Events
2023
+
2024
+ | Event | Type | Description |
2025
+ |-------|------|-------------|
2026
+ | `onChange` | `(detail: SKeyValueChangeDetail) => void` | 값 변경 (sdChange) |
2027
+ | `onSearch` | `() => void` | 검색 클릭 (sdSearch) |
2028
+
2029
+ ## Dependencies
2030
+
2031
+ ### Depends on
2032
+
2033
+ - [SButton](../SButton)
2034
+ - [SCheckbox](../SCheckbox)
2035
+ - [SDatePicker](../SDatePicker)
2036
+ - [SDateRangePicker](../SDateRangePicker)
2037
+ - [SFilePicker](../SFilePicker)
2038
+ - [SIcon](../SIcon)
2039
+ - [SInput](../SInput)
2040
+ - [SNumberInput](../SNumberInput)
2041
+ - [SRadio](../SRadio)
2042
+ - [SSelect](../SSelect)
2043
+ - [STextarea](../STextarea)
2044
+ - [STooltip](../STooltip)
2045
+
2046
+ ### Graph
2047
+
2048
+ ---
2049
+
2050
+ # SLayout
2051
+
2052
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
2053
+
2054
+ ### SLayout
2055
+
2056
+ #### Props
2057
+
2058
+ | Prop | Type | Default | Description |
2059
+ |------|------|---------|-------------|
2060
+ | `type?` | `SLayoutType` | `'box'` | 메뉴 스타일: box(라운드) / belt(풀폭 행). 자식 SGnb 가 이 값을 따른다. |
2061
+ | `header?` | `SLayoutHeader` | `'fix'` | 레이아웃 구조: fix(가로 분할) / full(풀폭 상단바). 자식 SGnb·SPage 가 이 값을 따른다. |
2062
+ | `useRail?` | `boolean` | `false` | GNB 좌측 레일 사용 여부. 자식 SGnb 가 이 값을 따르고, full 의 메뉴 열 폭도 레일만큼 넓어진다. 레일에 children 없는 아이템이 있으면 SGnb 가 header 를 full 로 강제하고, 이 레이아웃도 그에 맞춰 full 그리드로 선다. |
2063
+ | `folded?` | `boolean` | — | GNB 접힘 상태 (controlled) |
2064
+ | `defaultFolded?` | `boolean` | `false` | GNB 초기 접힘 상태 (uncontrolled) |
2065
+
2066
+ #### Events
2067
+
2068
+ | Event | Type | Description |
2069
+ |-------|------|-------------|
2070
+ | `onFoldedChange` | `(folded: boolean) => void` | 접힘 상태 변경 |
2071
+
2072
+ ## Dependencies
2073
+
2074
+ ### Used by
2075
+
2076
+ - [SGnb](../SGnb)
2077
+ - [SPage](../SPage)
2078
+
2079
+ ### Depends on
2080
+
2081
+ - [SGnb](../SGnb)
2082
+
2083
+ ### Graph
2084
+
2085
+ ---
2086
+
2087
+ # SLinearProgress
2088
+
2089
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
2090
+
2091
+ ### SLinearProgress
2092
+
2093
+ #### Props
2094
+
2095
+ | Prop | Type | Default | Description |
2096
+ |------|------|---------|-------------|
2097
+ | `value?` | `number` | `0` | 진행률 (0–100) |
2098
+ | `type?` | `SLinearProgressType` | `'primary'` | 색상 타입 |
2099
+ | `indeterminate?` | `boolean` | `false` | 진행률 없이 무한 애니메이션 |
2100
+ | `label?` | `string` | — | 하단 레이블 |
2101
+ | `className?` | `string` | — | |
2102
+ | `style?` | `CSSProperties` | — | |
2103
+
2104
+ ---
2105
+
2106
+ # SList
2107
+
2108
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
2109
+
2110
+ ### SList
2111
+
2112
+ #### Props
2113
+
2114
+ | Prop | Type | Default | Description |
2115
+ |------|------|---------|-------------|
2116
+ | `children?` | `ReactNode` | — | 리스트 컨테이너 내부에 렌더링할 내용 |
2117
+ | `useGap?` | `boolean` | `false` | 리스트 아이템 사이 gap 토큰 적용 여부 |
2118
+ | `usePadding?` | `boolean` | `false` | 리스트 컨테이너 padding 토큰 적용 여부 |
2119
+
2120
+ ---
2121
+
2122
+ # SListItem
2123
+
2124
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
2125
+
2126
+ ### SListItem
2127
+
2128
+ #### Props
2129
+
2130
+ | Prop | Type | Default | Description |
2131
+ |------|------|---------|-------------|
2132
+ | `title` | `SListItemSlot` | — | 리스트 아이템 제목 |
2133
+ | `depth?` | `1 \| 2 \| 3` | `1` | 중첩 단계. Figma 기준 1~3단계 |
2134
+ | `leading?` | `SListItemSlot` | — | 타이틀 앞에 표시할 아이콘/콘텐츠 |
2135
+ | `trailing?` | `SListItemSlot` | — | 타이틀 뒤에 표시할 태그/콘텐츠 |
2136
+ | `bordered?` | `boolean` | `false` | 외곽 테두리 사용 여부 |
2137
+ | `accentStripe?` | `boolean` | `false` | 아이템 왼쪽 accent stripe 표시 여부 |
2138
+ | `dense?` | `boolean` | `false` | 조밀한 높이 사용 여부 |
2139
+ | `disabled?` | `boolean` | `false` | 비활성 상태 여부 |
2140
+
2141
+ ---
2142
+
2143
+ # SLoadingContainer
2144
+
2145
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
2146
+
2147
+ ### SLoadingContainer
2148
+
2149
+ #### Props
2150
+
2151
+ | Prop | Type | Default | Description |
2152
+ |------|------|---------|-------------|
2153
+ | `open?` | `boolean` | `false` | 오버레이 표시 여부 (Stencil의 show()/hide() 명령형 API → 제어 prop으로 대체) |
2154
+ | `message?` | `string` | — | 스피너 하단 메시지 |
2155
+ | `zIndex?` | `number` | `1200` | z-index. 기본값(1200)은 토스트(1100)·모달(1000/1001) 위 — 전체 화면을 막는 최상단 블로킹 오버레이 |
2156
+ | `className?` | `string` | — | |
2157
+ | `style?` | `CSSProperties` | — | |
2158
+
2159
+ ## Dependencies
2160
+
2161
+ ### Depends on
2162
+
2163
+ - [SCircleProgress](../SCircleProgress)
2164
+
2165
+ ### Graph
2166
+
2167
+ ---
2168
+
2169
+ # SLoadingModal
2170
+
2171
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
2172
+
2173
+ ### SLoadingModal
2174
+
2175
+ #### Props
2176
+
2177
+ | Prop | Type | Default | Description |
2178
+ |------|------|---------|-------------|
2179
+ | `open?` | `boolean` | — | |
2180
+ | `persistent?` | `boolean` | — | true면 백드롭·ESC로 안 닫히고 흔들림 (sd-modal-container persistent). 로딩 중 닫힘 방지에 유용 |
2181
+ | `state?` | `LoadingModalState` | `'loading'` | 상태 (loading: 스피너, error: 경고 아이콘) |
2182
+ | `progress?` | `number` | — | 진행률 (0–100) — 지정 시 determinate 스피너 |
2183
+ | `message?` | `string \| string[]` | — | 메시지 (없으면 state 기본값) |
2184
+ | `useButton?` | `boolean` | `false` | 하단 버튼 표시 |
2185
+ | `buttonLabel?` | `string` | — | 버튼 레이블 (없으면 state 기본값) |
2186
+ | `width?` | `number \| string` | `520` | |
2187
+ | `height?` | `number \| string` | `320` | |
2188
+
2189
+ #### Events
2190
+
2191
+ | Event | Type | Description |
2192
+ |-------|------|-------------|
2193
+ | `onOpenChange` | `(open: boolean) => void` | |
2194
+ | `onClose` | `() => void` | 닫기(X) 버튼 클릭 (sdClose) — error 상태에서만 노출 |
2195
+ | `onButtonClick` | `() => void` | 버튼 클릭 (sdClick) |
2196
+
2197
+ ## Dependencies
2198
+
2199
+ ### Used by
2200
+
2201
+ - [SModal](../SModal)
2202
+
2203
+ ### Depends on
2204
+
2205
+ - [SButton](../SButton)
2206
+ - [SCircleProgress](../SCircleProgress)
2207
+ - [SIcon](../SIcon)
2208
+ - [SModalContainer](../SModalContainer)
2209
+
2210
+ ### Graph
2211
+
2212
+ ---
2213
+
2214
+ # SModal
2215
+
2216
+ > 수작성 문서 — SModal 은 컴포넌트가 아니라 **명령형 모달 서비스**라 `docs:gen`(Props/Events 표) 대상이 아니다.
2217
+
2218
+ 버튼 핸들러에서 바로 호출해 모달을 띄우는 명령형 API. 선언형 `<SConfirmModal open>` / `<SLoadingModal open>` 을 **대체하지 않고 추가로** 제공한다. (원본 디자인 시스템 `sdModal` 파리티)
2219
+
2220
+ 호출 시마다 `document.body` 에 컨테이너를 만들어 모달을 렌더하고, 닫힘 애니메이션이 끝나면 자동으로 언마운트한다. 모든 메서드는 체이닝 핸들 [`SModalRef`](#smodalref) 를 반환한다.
2221
+
2222
+ | 메서드 | 띄우는 모달 | 용도 | 주요 콜백/제어 |
2223
+ |---|---|---|---|
2224
+ | [`SModal.confirm(options)`](#smodalconfirm) | `SConfirmModal` | 확인/취소 | `onOk` / `onCancel` / `onClose` |
2225
+ | [`SModal.loading(options?)`](#smodalloading) | `SLoadingModal` | 로딩·에러 (persistent 기본 true) | `onClick` / `update` |
2226
+ | [`SModal.create({ component })`](#smodalcreate) | `SActionModal` | 액션 모달 | `modalRef` 주입 → `ok/cancel/close/submit` |
2227
+
2228
+ > **띄울 수 있는 모달은 `SActionModal` / `SConfirmModal` / `SLoadingModal` 세 가지뿐이고, 위 메서드가 1:1 로 대응한다.** 그 밖의 스타일로 모달을 띄우는 경로는 제공하지 않는다. `create` 는 컨테이너를 덧씌우지 않고 `component` 를 **그대로** 렌더하므로, `component` 는 루트에 `SActionModal` 을 렌더해야 한다 — 그러지 않으면 딤·카드 없이 콘텐츠만 뜨며, 개발 모드에서 `console.warn` 으로 경고한다.
2229
+
2230
+ ```ts
2231
+ import { SModal } from 'sellmate-design-system-react';
2232
+ ```
2233
+
2234
+ ---
2235
+
2236
+ ## SModal.confirm
2237
+
2238
+ 아이콘 + 제목 + 메시지 + 확인/취소 버튼. `type` 에 따라 아이콘·메인 버튼 색이 결정된다.
2239
+
2240
+ ```tsx
2241
+ SModal.confirm({
2242
+ type: 'negative', // 'positive' | 'negative' | 'default'
2243
+ modalTitle: '삭제하시겠습니까?',
2244
+ topMessage: ['이 작업은 되돌릴 수 없습니다.'],
2245
+ mainButtonLabel: '삭제',
2246
+ subButtonLabel: '취소',
2247
+ })
2248
+ .onOk(() => deleteItem())
2249
+ .onCancel(() => {});
2250
+ ```
2251
+
2252
+ **옵션 (`SConfirmOptions`)** — 선언형 `SConfirmModalProps` 에서 제어 흐름 props(`open`/`onOpenChange`/`onOk`/`onCancel`/`onClose`)를 제외한 전부. 주요 키: `type`, `modalTitle`, `topMessage`/`bottomMessage`, `mainButtonLabel`/`mainButtonName`, `subButtonLabel`, `tagSlot`/`optionSlot`, `persistent`.
2253
+
2254
+ ---
2255
+
2256
+ ## SModal.loading
2257
+
2258
+ 스피너/에러 모달. 로딩 중 임의 닫힘을 막기 위해 **`persistent` 기본값이 `true`** 다(백드롭·ESC로 안 닫힘). "띄우고 → 작업 → 결과 반영" 흐름을 `update()` / `close()` 로 제어한다.
2259
+
2260
+ ```tsx
2261
+ const ref = SModal.loading({ message: '업로드 중...' });
2262
+ try {
2263
+ await upload();
2264
+ ref.close();
2265
+ } catch {
2266
+ ref.update({ state: 'error', message: '업로드 실패' }) // 표시 중 상태 갱신
2267
+ .onClick(() => retry()); // 「다시 시도」 버튼
2268
+ }
2269
+ ```
2270
+
2271
+ 진행률:
2272
+
2273
+ ```tsx
2274
+ const ref = SModal.loading({ progress: 0, message: '파일 업로드 중...' });
2275
+ ref.update({ progress: 60 }); // 0–100
2276
+ ref.update({ progress: 100 });
2277
+ ref.close();
2278
+ ```
2279
+
2280
+ > **버튼 클릭은 자동 닫힘이 아니다.** error 상태의 버튼(기본 "다시 시도")은 `onClick` 만 발화하고 모달은 유지된다. consumer 가 `update()`(다시 로딩) / `close()` 로 후속 동작을 결정한다.
2281
+
2282
+ **옵션 (`SLoadingOptions`)** — `state`('loading' | 'error'), `progress`(0–100), `message`(string | string[]), `useButton`, `buttonLabel`, `width`/`height`, `persistent`.
2283
+
2284
+ ---
2285
+
2286
+ ## SModal.create
2287
+
2288
+ **`SActionModal` 을 루트로 렌더하는 컴포넌트**를 띄운다. `create` 는 컨테이너를 덧씌우지 않고 `component` 를 **그대로** 렌더하며, 표시 제어(`open` / `onOpenChange` / `onClose`)와 [`modalRef`](#smodalref) 를 주입하고 닫힘 애니메이션 종료 후 언마운트를 담당한다.
2289
+
2290
+ 컴포넌트는 주입받은 `open` / `onOpenChange` / `onClose` 를 **SActionModal 에 그대로 전달**해야 한다. 전달하지 않으면 모달이 열리지도, 닫히지도 않는다.
2291
+
2292
+ ```tsx
2293
+ import { SModal, SActionModal, type SModalCreateComponentProps } from 'sellmate-design-system-react';
2294
+
2295
+ // componentProps 로 넘긴 값 + 주입 prop 을 함께 받는다
2296
+ interface OrderModalProps extends SModalCreateComponentProps {
2297
+ orderId: string;
2298
+ }
2299
+
2300
+ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderModalProps) {
2301
+ return (
2302
+ <SActionModal
2303
+ open={open}
2304
+ onOpenChange={onOpenChange}
2305
+ onClose={onClose}
2306
+ modalTitle="주문 처리"
2307
+ button={{ label: '처리', onClick: () => modalRef.ok() }}
2308
+ >
2309
+ <p>주문번호 {orderId} 를 접수합니다.</p>
2310
+ </SActionModal>
2311
+ );
2312
+ }
2313
+
2314
+ SModal.create({ component: OrderModal, componentProps: { orderId: 'ORD-001' } })
2315
+ .onOk(() => toast('저장 완료'))
2316
+ .onDismissed(() => cleanup());
2317
+ ```
2318
+
2319
+ **옵션 (`SCreateOptions<P>`)**
2320
+
2321
+ | 키 | 타입 | 설명 |
2322
+ |---|---|---|
2323
+ | `component` | `ComponentType<P & SModalCreateComponentProps>` | 루트에 `SActionModal` 을 렌더하는 컴포넌트 |
2324
+ | `componentProps?` | `P` | 컴포넌트에 전달할 추가 props |
2325
+
2326
+ **주입되는 prop (`SModalCreateComponentProps`)**
2327
+
2328
+ | 키 | 타입 | 설명 |
2329
+ |---|---|---|
2330
+ | `open` | `boolean` | SActionModal 의 `open` 에 그대로 전달 |
2331
+ | `onOpenChange` | `(open: boolean) => void` | SActionModal 의 `onOpenChange` 에 그대로 전달 |
2332
+ | `onClose` | `() => void` | SActionModal 의 `onClose` 에 그대로 전달 |
2333
+ | `modalRef` | `SModalRef` | `ok()`/`cancel()`/`close()`/`submit()` 으로 자기 모달 제어 |
2334
+
2335
+ ### 비동기 제출 — 응답 보고 닫기
2336
+
2337
+ SActionModal `button` 의 푸터 버튼은 클릭 시 **즉시 닫힌다**. 저장 API 응답에 따라 닫힘 여부를 정해야 하면 푸터 버튼 대신 **본문에 버튼을 두고** `modalRef` 로 닫힘 시점을 직접 제어한다.
2338
+
2339
+ ```tsx
2340
+ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderModalProps) {
2341
+ const [error, setError] = useState('');
2342
+ const handleSubmit = async () => {
2343
+ try {
2344
+ await save(orderId);
2345
+ modalRef.ok(); // 성공 → onOk + 닫기
2346
+ } catch {
2347
+ setError('저장 실패'); // 실패 → 모달 유지
2348
+ }
2349
+ };
2350
+ return (
2351
+ // button 을 주지 않으면 푸터가 렌더되지 않는다
2352
+ <SActionModal open={open} onOpenChange={onOpenChange} onClose={onClose} persistent modalTitle="주문 처리">
2353
+ {error && <p>{error}</p>}
2354
+ <SButton label="저장" onClick={handleSubmit} />
2355
+ <SButton label="취소" onClick={() => modalRef.cancel()} />
2356
+ </SActionModal>
2357
+ );
2358
+ }
2359
+ ```
2360
+
2361
+ ---
2362
+
2363
+ ## SModalRef
2364
+
2365
+ 모든 `SModal.*` 호출이 반환하고, `create` 에서는 컴포넌트 prop 으로도 주입되는 제어 핸들. 콜백 등록(체이닝)과 트리거/갱신 메서드를 함께 제공한다. 모달 종류에 따라 관련 있는 메서드만 실제로 발화한다.
2366
+
2367
+ ### 콜백 등록 (체이닝)
2368
+
2369
+ | 메서드 | 발화 시점 |
2370
+ |---|---|
2371
+ | `onOk(fn)` | 확인 버튼(confirm) 또는 `ok()` |
2372
+ | `onCancel(fn)` | 취소 버튼(confirm) 또는 `cancel()` |
2373
+ | `onClose(fn)` | 닫기(X) 버튼 또는 `close()` |
2374
+ | `onClick(fn)` | 단일 버튼 모달(loading error)의 버튼 클릭 — **닫힘 없음** |
2375
+ | `onSubmit(fn)` | create 커스텀 모달의 `submit()` — **닫힘 없음** |
2376
+ | `onDismissed(fn)` | 사유 무관 완전히 닫혀 언마운트된 뒤(백드롭·ESC 포함) |
2377
+
2378
+ ### 트리거 / 제어
2379
+
2380
+ | 메서드 | 동작 |
2381
+ |---|---|
2382
+ | `ok()` | `onOk` 발화 + 닫기 (저장/처리 성공) |
2383
+ | `cancel()` | `onCancel` 발화 + 닫기 (작업 취소) |
2384
+ | `close()` | `onClose` 발화 + 닫기 (중립적 닫기) |
2385
+ | `submit()` | `onSubmit` 발화 (닫힘 없음) |
2386
+ | `update(patch)` | 표시 중 옵션 갱신 (loading→error, progress 등). create 는 미지원. |
2387
+
2388
+ 모든 닫힘은 애니메이션 종료 후 `onDismissed` 로 수렴한다. 이미 닫힘이 시작된 뒤의 중복 트리거는 무시된다.
2389
+
2390
+ ---
2391
+
2392
+ ## 주의사항
2393
+
2394
+ - **선언형과 공존**: 서비스는 추가 API다. open 상태가 앱 상태/라우트에 묶인 경우엔 선언형 `<SConfirmModal open>` / `<SLoadingModal open>` 이 더 적합하다.
2395
+ - **백드롭·ESC = 중립적 닫힘**: 특정 콜백(onClose 등) 없이 `onDismissed` 만 발화한다. 명시적 버튼·메서드만 onOk/onCancel/onClose 를 발화한다.
2396
+ - **Context 미상속**: `create` 의 커스텀 컴포넌트는 새 React 트리(createRoot)에서 렌더되어 부모의 Context Provider(Theme·Store 등)를 상속하지 않는다. 필요하면 컴포넌트 내부에서 직접 Provider 로 감싸라. (confirm/loading 은 토큰이 `:root` CSS 변수라 무관)
2397
+ - **`create` 의 `component` 는 SActionModal 을 루트로**: `create` 는 컨테이너를 덧씌우지 않으므로, 본문만 렌더하는 컴포넌트를 넘기면 딤·카드 없이 콘텐츠가 그대로 화면에 붙는다. TypeScript 는 이를 막지 못한다(`component` 타입이 아무 컴포넌트나 허용). 개발 모드에서는 마운트 직후 렌더 결과로 이를 감지해 `console.warn` 으로 경고한다 — 세 모달은 모두 Portal 로 `body` 에 렌더되므로 `create` 가 만든 host 는 비어 있어야 하는데, host 에 엘리먼트가 남아 있으면 모달이 아닌 것으로 판정한다.
2398
+
2399
+ ## Dependencies
2400
+
2401
+ ### Depends on
2402
+
2403
+ - [SActionModal](../SActionModal)
2404
+ - [SConfirmModal](../SConfirmModal)
2405
+ - [SLoadingModal](../SLoadingModal)
2406
+
2407
+ ---
2408
+
2409
+ # SModalContainer
2410
+
2411
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
2412
+
2413
+ ### SModalContainer
2414
+
2415
+ #### Props
2416
+
2417
+ | Prop | Type | Default | Description |
2418
+ |------|------|---------|-------------|
2419
+ | `open?` | `boolean` | — | 표시 여부 (제어) |
2420
+ | `ariaTitle?` | `string` | `'모달'` | 접근성 제목 (스크린리더용). 시각 제목은 children이 담당 |
2421
+ | `showClose?` | `boolean` | `false` | 우측 상단 닫기 버튼 |
2422
+ | `persistent?` | `boolean` | `false` | true면 백드롭 클릭·ESC로 닫히지 않고 흔들림(shake) 애니메이션 (sd-modal-container persistent) |
2423
+ | `width?` | `number \| string` | — | 너비/높이 |
2424
+ | `height?` | `number \| string` | — | |
2425
+ | `children?` | `ReactNode` | — | |
2426
+ | `className?` | `string` | — | |
2427
+ | `style?` | `CSSProperties` | — | |
2428
+
2429
+ #### Events
2430
+
2431
+ | Event | Type | Description |
2432
+ |-------|------|-------------|
2433
+ | `onOpenChange` | `(open: boolean) => void` | 표시 상태 변경 |
2434
+ | `onClose` | `() => void` | 닫기(X) 버튼 클릭 시 발생 (sd-modal-container 닫기 버튼 대응) |
2435
+
2436
+ ## Dependencies
2437
+
2438
+ ### Used by
2439
+
2440
+ - [SActionModal](../SActionModal)
2441
+ - [SConfirmModal](../SConfirmModal)
2442
+ - [SLoadingModal](../SLoadingModal)
2443
+
2444
+ ### Depends on
2445
+
2446
+ - [SGhostButton](../SGhostButton)
2447
+
2448
+ ### Graph
2449
+
2450
+ ---
2451
+
2452
+ # SNumberInput
2453
+
2454
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
2455
+
2456
+ ### SNumberInput
2457
+
2458
+ #### Props
2459
+
2460
+ | Prop | Type | Default | Description |
2461
+ |------|------|---------|-------------|
2462
+ | `value?` | `string \| number \| null` | — | 입력 값 |
2463
+ | `size?` | `SNumberInputSize` | `'sm'` | 크기 |
2464
+ | `min?` | `number` | `Number.NEGATIVE_INFINITY` | 최솟값 |
2465
+ | `max?` | `number` | `Number.POSITIVE_INFINITY` | 최댓값 |
2466
+ | `step?` | `number` | `1` | 스텝 단위 |
2467
+ | `useButton?` | `boolean` | `false` | 증감 버튼 표시 |
2468
+ | `useDecimal?` | `boolean` | `false` | 소수점 입력 허용 |
2469
+ | `maxDecimalLength?` | `number` | — | 소수점 이하 최대 자리수 |
2470
+ | `inputPrefix?` | `string` | `''` | 입력 앞 접두 텍스트 |
2471
+ | `inputSuffix?` | `string` | `''` | 입력 뒤 접미 텍스트 |
2472
+ | `inputClass?` | `string` | — | 네이티브 input className |
2473
+ | `inputStyle?` | `CSSProperties` | — | 네이티브 input style |
2474
+ | `autoFocus?` | `boolean` | — | 마운트 시 자동 포커스 |
2475
+ | `name?` | `string` | — | 폼 연동 name |
2476
+ | `placeholder?` | `string` | `'입력해 주세요.'` | |
2477
+ | `disabled?` | `boolean` | `false` | |
2478
+ | `readOnly?` | `boolean` | `false` | |
2479
+ | `rules?` | `Rule[]` | — | 유효성 규칙 — blur 시 검증 |
2480
+ | `status?` | `SFieldStatus` | — | |
2481
+ | `focused?` | `boolean` | — | |
2482
+ | `hovered?` | `boolean` | — | |
2483
+ | `label?` | `string` | — | |
2484
+ | `labelWidth?` | `number \| string` | — | |
2485
+ | `icon?` | `SIconName` | — | |
2486
+ | `iconColor?` | `SColor` | — | |
2487
+ | `labelTooltip?` | `string` | — | |
2488
+ | `labelTooltipProps?` | `Partial<STooltipProps>` | — | |
2489
+ | `addonLabel?` | `string` | — | |
2490
+ | `addonAlign?` | `SFieldAddonAlign` | — | |
2491
+ | `hint?` | `string` | — | |
2492
+ | `error?` | `boolean` | — | |
2493
+ | `errorMessage?` | `string` | — | |
2494
+ | `width?` | `number \| string` | — | |
2495
+ | `className?` | `string` | — | |
2496
+ | `style?` | `CSSProperties` | — | |
2497
+
2498
+ #### Events
2499
+
2500
+ | Event | Type | Description |
2501
+ |-------|------|-------------|
2502
+ | `onValueChange` | `(value: number \| null) => void` | 값 변경 (sdUpdate) — number|null 전달 |
2503
+ | `onFocus` | `(e: React.FocusEvent<HTMLInputElement>) => void` | 포커스 이벤트 (sdFocus) |
2504
+ | `onBlur` | `(e: React.FocusEvent<HTMLInputElement>) => void` | 블러 이벤트 (sdBlur) |
2505
+
2506
+ ## Dependencies
2507
+
2508
+ ### Used by
2509
+
2510
+ - [SKeyValueTable](../SKeyValueTable)
2511
+
2512
+ ### Depends on
2513
+
2514
+ - [SField](../SField)
2515
+ - [SIcon](../SIcon)
2516
+
2517
+ ### Graph
2518
+
2519
+ ---
2520
+
2521
+ # SPage
2522
+
2523
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
2524
+
2525
+ ### SPage
2526
+
2527
+ #### Props
2528
+
2529
+ | Prop | Type | Default | Description |
2530
+ |------|------|---------|-------------|
2531
+ | `background?` | `SPageBackground` | `'frame'` | 페이지 배경 표면. frame=흰 콘텐츠 면, neutral=옅은 회색 면, screen=앱 바탕. 스크롤바 처리도 여기 묶여 있다 — 셋 다 구분선+트랙 배경이고, 트랙 색만 neutral 에서 흰색이 된다. |
2532
+
2533
+ ## Dependencies
2534
+
2535
+ ### Depends on
2536
+
2537
+ - [SLayout](../SLayout)
2538
+
2539
+ ### Graph
2540
+
2541
+ ---
2542
+
2543
+ # SPagination
2544
+
2545
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
2546
+
2547
+ ### SPagination
2548
+
2549
+ #### Props
2550
+
2551
+ | Prop | Type | Default | Description |
2552
+ |------|------|---------|-------------|
2553
+ | `currentPage?` | `number` | `1` | 현재 페이지 |
2554
+ | `lastPage?` | `number` | `1` | 마지막 페이지 |
2555
+ | `simple?` | `boolean` | `false` | 단순 모드 (현재/마지막만 표시) |
2556
+ | `className?` | `string` | — | |
2557
+ | `style?` | `CSSProperties` | — | |
2558
+
2559
+ #### Events
2560
+
2561
+ | Event | Type | Description |
2562
+ |-------|------|-------------|
2563
+ | `onPageChange` | `(page: number) => void` | 페이지 변경 (sdPageChange) |
2564
+
2565
+ ## Dependencies
2566
+
2567
+ ### Used by
2568
+
2569
+ - [STable](../STable)
2570
+
2571
+ ### Depends on
2572
+
2573
+ - [SIcon](../SIcon)
2574
+
2575
+ ### Graph
2576
+
2577
+ ---
2578
+
2579
+ # SPopover
2580
+
2581
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
2582
+
2583
+ ### SPopover
2584
+
2585
+ #### Props
2586
+
2587
+ | Prop | Type | Default | Description |
2588
+ |------|------|---------|-------------|
2589
+ | `open?` | `boolean` | — | 표시 여부 (제어). onOpenChange 와 함께 사용 |
2590
+ | `defaultOpen?` | `boolean` | — | 기본 표시 여부 (비제어) |
2591
+ | `placement?` | `SPopoverPlacement` | `'bottom'` | 표시 방향 |
2592
+ | `type?` | `SPopoverType` | `'default'` | 색상 타입 (default: 다크 / danger·warning·accent: 라이트 배경) |
2593
+ | `menuTitle` | `string` | — | 메뉴 제목 (필수) |
2594
+ | `titleIcon?` | `SIconName` | — | 제목 좌측 아이콘 (선택) |
2595
+ | `children?` | `ReactNode` | — | 본문 내용 |
2596
+ | `trigger?` | `ReactNode` | — | 커스텀 트리거. 없으면 아이콘 트리거 렌더 |
2597
+ | `icon?` | `SIconName` | `'helpOutline'` | 기본(아이콘) 트리거 설정 |
2598
+ | `iconSize?` | `number` | `12` | |
2599
+ | `color?` | `SColor` | `'#01BB4B'` | 트리거 아이콘 색상. 팔레트 키(`grey_65`, `red_95` …) 또는 임의 CSS 색상 |
2600
+ | `leftLinkLabel?` | `string` | — | 하단 좌측 링크 레이블 (선택). 스타일은 chevron 링크로 고정 |
2601
+ | `buttonLabel` | `string` | — | 하단 우측 버튼 레이블 (필수). 색상/스타일은 type 별로 고정 |
2602
+ | `ariaLabel?` | `string` | — | |
2603
+ | `menuClassName?` | `string` | — | |
2604
+ | `style?` | `CSSProperties` | — | |
2605
+
2606
+ #### Events
2607
+
2608
+ | Event | Type | Description |
2609
+ |-------|------|-------------|
2610
+ | `onOpenChange` | `(open: boolean) => void` | 표시 상태 변경 (sdShowChange) |
2611
+ | `onLeftLinkClick` | `() => void` | 하단 좌측 링크 클릭 |
2612
+ | `onButtonClick` | `() => void` | 하단 우측 버튼 클릭 |
2613
+
2614
+ ## Dependencies
2615
+
2616
+ ### Depends on
2617
+
2618
+ - [SButton](../SButton)
2619
+ - [SGhostButton](../SGhostButton)
2620
+ - [SIcon](../SIcon)
2621
+ - [STextLink](../STextLink)
2622
+
2623
+ ### Graph
2624
+
2625
+ ---
2626
+
2627
+ # SPopup
2628
+
2629
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
2630
+
2631
+ ### SPopup
2632
+
2633
+ #### Props
2634
+
2635
+ | Prop | Type | Default | Description |
2636
+ |------|------|---------|-------------|
2637
+ | `popupTitle?` | `string` | `''` | 헤더 제목 |
2638
+ | `type?` | `SPopupType` | `'default'` | 타입 (헤더 색상) |
2639
+ | `useFooter?` | `boolean` | `true` | 하단 footer 표시 여부 |
2640
+ | `submitButton?` | `SPopupSubmitButton` | — | 확인 버튼 설정 |
2641
+ | `footerLeft?` | `ReactNode` | — | footer 좌측 영역 |
2642
+ | `children?` | `ReactNode` | — | 본문 |
2643
+ | `className?` | `string` | — | |
2644
+ | `style?` | `CSSProperties` | — | |
2645
+
2646
+ #### Events
2647
+
2648
+ | Event | Type | Description |
2649
+ |-------|------|-------------|
2650
+ | `onSubmit` | `() => void` | 확인 버튼 클릭 (sdSubmit) |
2651
+
2652
+ ## Dependencies
2653
+
2654
+ ### Depends on
2655
+
2656
+ - [SButton](../SButton)
2657
+
2658
+ ### Graph
2659
+
2660
+ ---
2661
+
2662
+ # SPortal
2663
+
2664
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
2665
+
2666
+ ### SPortal
2667
+
2668
+ #### Props
2669
+
2670
+ | Prop | Type | Default | Description |
2671
+ |------|------|---------|-------------|
2672
+ | `open` | `boolean` | — | 표시 여부 (제어). onClose 와 함께 사용한다. |
2673
+ | `anchorRef` | `RefObject<HTMLElement \| null>` | — | 위치 기준이 되는 앵커 엘리먼트 ref (sd-portal `parentRef` 대응). 트리거 버튼 등에 그대로 걸면 그 엘리먼트에 붙어서 뜬다. |
2674
+ | `placement?` | `SPortalPlacement` | `'bottom'` | 표시 방향 (공간이 부족하면 자동으로 반대 방향으로 전환). |
2675
+ | `align?` | `SPortalAlign` | `'start'` | 교차축 정렬 (start·center·end). |
2676
+ | `offset?` | `number` | `4` | 앵커로부터의 간격 px (sd-portal offset[1]). |
2677
+ | `alignOffset?` | `number` | `0` | 정렬축 오프셋 px (sd-portal offset[0]). |
2678
+ | `fit?` | `boolean` | `false` | 앵커 너비에 맞춰 최소 너비를 잡는다. |
2679
+ | `maxHeight?` | `number` | — | 콘텐츠 최대 높이 px. 넘치면 세로 스크롤. |
2680
+ | `autoClose?` | `boolean` | `false` | 콘텐츠 내부를 클릭하면 자동으로 닫는다. |
2681
+ | `persistent?` | `boolean` | `false` | 외부 클릭 · ESC 로 닫히지 않게 한다. |
2682
+ | `viewportPadding?` | `number` | `0` | 뷰포트 경계 여백 px (충돌 회피 패딩). |
2683
+ | `children?` | `ReactNode` | — | 본문 내용 (콘텐츠 자유). |
2684
+ | `className?` | `string` | — | |
2685
+ | `style?` | `CSSProperties` | — | |
2686
+ | `ariaLabel?` | `string` | — | |
2687
+
2688
+ #### Events
2689
+
2690
+ | Event | Type | Description |
2691
+ |-------|------|-------------|
2692
+ | `onClose` | `() => void` | 외부 클릭 · ESC 등으로 닫힘이 요청될 때 (sd-portal `sdClose` 대응). |
2693
+ | `onOpenChange` | `(open: boolean) => void` | 열림/닫힘 변경 전체 콜백 (선택). |
2694
+ | `onOpenAutoFocus` | `(event: Event) => void` | 열릴 때 포커스 이동 처리 (기본은 Radix가 콘텐츠로 포커스를 옮김). e.preventDefault()로 막을 수 있다. |
2695
+ | `onCloseAutoFocus` | `(event: Event) => void` | 닫힐 때 포커스 복귀 처리 (기본은 Radix가 앵커로 포커스를 되돌림). e.preventDefault()로 막을 수 있다. |
2696
+ | `onPointerDownOutside` | `(event: Event) => void` | 바깥 영역 pointerdown 으로 닫힘이 시작될 때 (앵커 위 클릭·persistent 는 제외). |
2697
+
2698
+ ## Dependencies
2699
+
2700
+ ### Used by
2701
+
2702
+ - [SChipInput](../SChipInput)
2703
+ - [SSelect](../SSelect)
2704
+
2705
+ ### Graph
2706
+
2707
+ ---
2708
+
2709
+ # SRadio
2710
+
2711
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
2712
+
2713
+ ### SRadioGroup
2714
+
2715
+ #### Props
2716
+
2717
+ | Prop | Type | Default | Description |
2718
+ |------|------|---------|-------------|
2719
+ | `options?` | `SRadioOption[]` | `[]` | 옵션 목록 |
2720
+ | `value?` | `SRadioValue \| null` | — | 현재 선택 값 |
2721
+ | `direction?` | `'vertical' \| 'horizontal'` | `'vertical'` | 배치 방향 |
2722
+ | `disabled?` | `boolean` | `false` | 전체 비활성 |
2723
+ | `groupName?` | `string` | — | 라디오 name 속성 (form-agnostic 그룹핑) |
2724
+ | `className?` | `string` | — | |
2725
+ | `style?` | `CSSProperties` | — | |
2726
+
2727
+ #### Events
2728
+
2729
+ | Event | Type | Description |
2730
+ |-------|------|-------------|
2731
+ | `onValueChange` | `(val: SRadioValue) => void` | 선택 변경 (sdUpdate) |
2732
+
2733
+ ### SRadio
2734
+
2735
+ #### Props
2736
+
2737
+ | Prop | Type | Default | Description |
2738
+ |------|------|---------|-------------|
2739
+ | `val` | `SRadioValue` | — | 이 라디오의 값 |
2740
+ | `value?` | `SRadioValue \| null` | — | 그룹의 현재 선택 값 |
2741
+ | `label?` | `string` | — | 라벨 |
2742
+ | `disabled?` | `boolean` | `false` | 비활성 |
2743
+ | `name?` | `string` | — | native radio name (form-agnostic 그룹핑) |
2744
+ | `className?` | `string` | — | |
2745
+ | `style?` | `CSSProperties` | — | |
2746
+
2747
+ #### Events
2748
+
2749
+ | Event | Type | Description |
2750
+ |-------|------|-------------|
2751
+ | `onValueChange` | `(val: SRadioValue) => void` | 선택 변경 (sdUpdate) — 선택된 val 전달 |
2752
+
2753
+ ## Dependencies
2754
+
2755
+ ### Used by
2756
+
2757
+ - [SKeyValueTable](../SKeyValueTable)
2758
+
2759
+ ### Graph
2760
+
2761
+ ---
2762
+
2763
+ # SRadioButton
2764
+
2765
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
2766
+
2767
+ ### SRadioButton
2768
+
2769
+ #### Props
2770
+
2771
+ | Prop | Type | Default | Description |
2772
+ |------|------|---------|-------------|
2773
+ | `options?` | `SRadioButtonOption[]` | `[]` | 옵션 목록 |
2774
+ | `value?` | `string \| number \| null` | — | 현재 선택 값 |
2775
+ | `size?` | `SRadioButtonSize` | `'sm'` | 크기 |
2776
+ | `disabled?` | `boolean` | `false` | 전체 비활성 |
2777
+ | `name?` | `string` | — | input name 속성 |
2778
+ | `className?` | `string` | — | |
2779
+ | `style?` | `CSSProperties` | — | |
2780
+
2781
+ #### Events
2782
+
2783
+ | Event | Type | Description |
2784
+ |-------|------|-------------|
2785
+ | `onValueChange` | `(value: string \| number) => void` | 변경 (sdUpdate) |
2786
+
2787
+ ---
2788
+
2789
+ # SScrollArea
2790
+
2791
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
2792
+
2793
+ ### SScrollArea
2794
+
2795
+ #### Props
2796
+
2797
+ | Prop | Type | Default | Description |
2798
+ |------|------|---------|-------------|
2799
+ | `axis?` | `SScrollAreaAxis` | `'both'` | 스크롤 방향. vertical=세로만, horizontal=가로만, both=양방향 |
2800
+ | `background?` | `boolean` | `false` | true면 스크롤바 트랙 배경을 채운다. 콘텐츠 영역 배경은 바뀌지 않는다. |
2801
+ | `bordered?` | `boolean` | `false` | true면 스크롤바와 콘텐츠 사이에 1px 구분선을 그린다. 컨테이너 테두리가 아니다. |
2802
+ | `maxHeight?` | `string` | — | viewport 최대 높이 (예: '400px'). 비우면 부모 크기를 따른다. |
2803
+ | `maxWidth?` | `string` | — | viewport 최대 너비 (예: '480px'). 비우면 부모 크기를 따른다. |
2804
+
2805
+ ---
2806
+
2807
+ # SSectionHeaderCard
2808
+
2809
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
2810
+
2811
+ ### SSectionHeaderCardBody
2812
+
2813
+ #### Props
2814
+
2815
+ | Prop | Type | Default | Description |
2816
+ |------|------|---------|-------------|
2817
+ | `children?` | `SSectionHeaderCardBodyChildren` | — | 바디 슬롯. 특정 컴포넌트 타입으로 제한하지 않습니다. |
2818
+
2819
+ ### SSectionHeaderCardHeader
2820
+
2821
+ #### Props
2822
+
2823
+ | Prop | Type | Default | Description |
2824
+ |------|------|---------|-------------|
2825
+ | `title` | `ReactNode` | — | 헤더 제목 |
2826
+ | `marker?` | `boolean` | `false` | 제목 앞 점 표시 여부 |
2827
+ | `required?` | `boolean` | `false` | 제목 뒤 필수 별 표시 여부 |
2828
+ | `helpText?` | `string[]` | — | 도움말 툴팁 메시지 |
2829
+ | `subtitle?` | `ReactNode` | — | 부제 |
2830
+ | `slot?` | `ReactNode` | — | 헤더 우측 슬롯 |
2831
+ | `thickness?` | `SSectionHeaderCardThickness` | `false` | 상단 border 색상 타입. false면 표시하지 않습니다. |
2832
+
2833
+ ### SSectionHeaderCard
2834
+
2835
+ #### Props
2836
+
2837
+ | Prop | Type | Default | Description |
2838
+ |------|------|---------|-------------|
2839
+ | `children?` | `SSectionHeaderCardChildren` | — | SSectionHeaderCard 슬롯. 특정 컴포넌트 타입으로 제한하지 않습니다. |
2840
+
2841
+ ## Dependencies
2842
+
2843
+ ### Depends on
2844
+
2845
+ - [SBadge](../SBadge)
2846
+ - [SIcon](../SIcon)
2847
+ - [STooltip](../STooltip)
2848
+
2849
+ ### Graph
2850
+
2851
+ ---
2852
+
2853
+ # SSelect
2854
+
2855
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
2856
+
2857
+ ### SSelect
2858
+
2859
+ #### Props
2860
+
2861
+ | Prop | Type | Default | Description |
2862
+ |------|------|---------|-------------|
2863
+ | `value?` | `(string \| number \| SSelectOption) \| (string \| number \| SSelectOption)[] \| null` | — | 선택 값. multi 타입이면 배열. emitValue=false(기본) 라운드트립을 위해 SSelectOption 객체도 허용 |
2864
+ | `emitValue?` | `boolean` | `false` | true면 SSelectOption 대신 원시값(string|number) 방출 |
2865
+ | `name?` | `string` | — | 폼 연동용 name 속성 |
2866
+ | `options?` | `SSelectOption[]` | `[]` | 옵션 목록 (children으로 계층 표현) |
2867
+ | `type?` | `SSelectType` | `'default'` | 타입: default | multi | default_depth | multi_depth |
2868
+ | `useSearch?` | `boolean` | `false` | 검색 입력 사용 |
2869
+ | `useSelectAll?` | `boolean` | `false` | 전체선택 옵션 사용 (multi 전용) |
2870
+ | `allSelectedLabel?` | `string` | `'전체'` | 전체선택 레이블 |
2871
+ | `placeholder?` | `string` | `'선택'` | placeholder |
2872
+ | `disabled?` | `boolean` | `false` | 비활성 |
2873
+ | `error?` | `boolean` | `false` | 에러 상태 |
2874
+ | `rules?` | `Rule[]` | — | 유효성 규칙 — 닫힐 때 자동 검증 |
2875
+ | `labelTooltipProps?` | `Partial<STooltipProps>` | — | 레이블 툴팁 상세 옵션 |
2876
+ | `dropdownHeight?` | `string` | `'260px'` | 드롭다운 최대 높이 |
2877
+ | `dropdownWidth?` | `string` | — | 드롭다운 너비 (없으면 트리거 너비) |
2878
+ | `maxDropdownWidth?` | `string` | `'640px'` | 드롭다운 최대 너비 |
2879
+ | `label?` | `string` | — | |
2880
+ | `labelWidth?` | `number \| string` | — | |
2881
+ | `icon?` | `SIconName` | — | 레이블 영역 아이콘 |
2882
+ | `iconColor?` | `SColor` | — | |
2883
+ | `labelTooltip?` | `string` | — | 레이블 툴팁 텍스트 |
2884
+ | `addonLabel?` | `string` | — | 우측 어드온 레이블 |
2885
+ | `addonAlign?` | `SFieldAddonAlign` | — | 어드온 정렬 |
2886
+ | `hint?` | `string` | — | |
2887
+ | `errorMessage?` | `string` | — | |
2888
+ | `width?` | `number \| string` | — | |
2889
+ | `className?` | `string` | — | |
2890
+ | `style?` | `CSSProperties` | — | |
2891
+
2892
+ #### Events
2893
+
2894
+ | Event | Type | Description |
2895
+ |-------|------|-------------|
2896
+ | `onValueChange` | `(value: any) => void` | 값 변경 (sdUpdate). emitValue=false(기본)면 SSelectOption(들), true면 원시값 |
2897
+ | `onOpenChange` | `(open: boolean) => void` | 열림/닫힘 변경 (sdDropDownShow) |
2898
+
2899
+ #### Methods (ref)
2900
+
2901
+ | Method | Type | Description |
2902
+ |--------|------|-------------|
2903
+ | `focus` | `() => void` | 트리거 버튼에 포커스 (sdFocus) |
2904
+ | `open` | `() => void` | 드롭다운 열기 (sdOpen) |
2905
+
2906
+ ## Dependencies
2907
+
2908
+ ### Used by
2909
+
2910
+ - [SKeyValueTable](../SKeyValueTable)
2911
+ - [STable](../STable)
2912
+
2913
+ ### Depends on
2914
+
2915
+ - [SField](../SField)
2916
+ - [SGhostButton](../SGhostButton)
2917
+ - [SIcon](../SIcon)
2918
+ - [SPortal](../SPortal)
2919
+
2920
+ ### Graph
2921
+
2922
+ ---
2923
+
2924
+ # SStepper
2925
+
2926
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
2927
+
2928
+ ### SStepper
2929
+
2930
+ #### Props
2931
+
2932
+ | Prop | Type | Default | Description |
2933
+ |------|------|---------|-------------|
2934
+ | `items` | `SStepperItem[]` | — | 단계 목록 |
2935
+ | `value?` | `string` | — | 현재 활성 단계 value |
2936
+ | `size?` | `SStepperSize` | `'sm'` | 크기 |
2937
+ | `ariaLabel?` | `string` | `'진행 단계'` | 접근성 레이블 |
2938
+ | `className?` | `string` | — | |
2939
+ | `style?` | `CSSProperties` | — | |
2940
+
2941
+ ## Dependencies
2942
+
2943
+ ### Depends on
2944
+
2945
+ - [SIcon](../SIcon)
2946
+
2947
+ ### Graph
2948
+
2949
+ ---
2950
+
2951
+ # SSwitch
2952
+
2953
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
2954
+
2955
+ ### SSwitch
2956
+
2957
+ #### Props
2958
+
2959
+ | Prop | Type | Default | Description |
2960
+ |------|------|---------|-------------|
2961
+ | `value?` | `boolean` | `false` | on/off 상태 |
2962
+ | `leftLabel?` | `string` | — | 스위치 좌측 라벨 |
2963
+ | `rightLabel?` | `string` | — | 스위치 우측 라벨 |
2964
+ | `label?` | `string` | — | |
2965
+ | `disabled?` | `boolean` | `false` | 비활성 |
2966
+ | `className?` | `string` | — | |
2967
+ | `style?` | `CSSProperties` | — | |
2968
+
2969
+ #### Events
2970
+
2971
+ | Event | Type | Description |
2972
+ |-------|------|-------------|
2973
+ | `onValueChange` | `(value: boolean) => void` | 변경 (sdUpdate) |
2974
+
2975
+ ---
2976
+
2977
+ # STable
2978
+
2979
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
2980
+
2981
+ ### STable
2982
+
2983
+ #### Props
2984
+
2985
+ | Prop | Type | Default | Description |
2986
+ |------|------|---------|-------------|
2987
+ | `columns?` | `STableColumn[]` | `[]` | |
2988
+ | `rows?` | `SRow[]` | `[]` | |
2989
+ | `rowKey?` | `string` | `'id'` | 행 식별 필드 |
2990
+ | `selectable?` | `boolean` | `false` | 행 선택 체크박스 |
2991
+ | `selected?` | `SRow[]` | `[]` | |
2992
+ | `resizable?` | `boolean` | `false` | 컬럼 너비 조절 |
2993
+ | `width?` | `string` | — | |
2994
+ | `height?` | `string` | — | |
2995
+ | `stickyHeader?` | `boolean` | — | |
2996
+ | `stickyColumn?` | `STableStickyColumn` | — | 고정할 좌/우 컬럼 수 |
2997
+ | `radius?` | `'default' \| 'useTop' \| 'full'` | `'default'` | border-radius 제어 |
2998
+ | `noDataLabel?` | `string` | `'데이터가 없습니다.'` | |
2999
+ | `noDataSlot?` | `ReactNode` | — | 데이터가 없을 때 body 영역 전체를 대체하는 슬롯. 지정하면 `noDataLabel` 대신 이 콘텐츠가 헤더 아래 영역을 채우며, 버튼 등 인터랙션도 동작한다. |
3000
+ | `isLoading?` | `boolean` | `false` | |
3001
+ | `dense?` | `boolean` | `false` | |
3002
+ | `noHover?` | `boolean` | `false` | true면 행에 마우스를 올려도 hover 배경(grey_05)을 표시하지 않는다 |
3003
+ | `pagination?` | `STablePagination` | — | 페이지네이션 (있으면 하단 표시) |
3004
+ | `useInternalPagination?` | `boolean` | `false` | 테이블 내부에서 페이지네이션을 직접 관리 (rows를 내부 슬라이싱) |
3005
+ | `useRowsPerPageSelect?` | `boolean` | `false` | 페이지당 행 수 셀렉트 표시 |
3006
+ | `rowsPerPageOption?` | `SSelectOption[]` | `DEFAULT_ROWS_PER_PAGE_OPTION` | |
3007
+ | `useVirtualScroll?` | `boolean` | `false` | 가상 스크롤 |
3008
+ | `rowHeight?` | `number` | — | |
3009
+ | `virtualBuffer?` | `number` | `5` | |
3010
+ | `virtualEndThreshold?` | `number` | `10` | |
3011
+ | `tableId?` | `string` | — | 테이블 식별자 (없으면 자동 생성) |
3012
+ | `className?` | `string` | — | |
3013
+ | `style?` | `CSSProperties` | — | |
3014
+
3015
+ #### Events
3016
+
3017
+ | Event | Type | Description |
3018
+ |-------|------|-------------|
3019
+ | `onSelectedChange` | `(rows: SRow[]) => void` | |
3020
+ | `onPageChange` | `(page: number) => void` | |
3021
+ | `onRowsPerPageChange` | `(perPage: number) => void` | |
3022
+ | `onVirtualUpdate` | `(range: { from: number; to: number }) => void` | |
3023
+ | `onVirtualReachEnd` | `(info: { from: number; to: number; rowCount: number; threshold: number; }) =>…` | |
3024
+ | `onRowClick` | `(row: SRow) => void` | 행 클릭 |
3025
+
3026
+ #### Methods (ref)
3027
+
3028
+ | Method | Type | Description |
3029
+ |--------|------|-------------|
3030
+ | `isRowSelected` | `(row: SRow) => boolean` | 행 선택 여부 |
3031
+ | `getIsAllChecked` | `(rows: SRow[]) => boolean \| null` | 주어진 행들의 전체 선택 상태 (true/false/null=indeterminate) |
3032
+ | `getPaginationInfo` | `<union 2종>` | 내부 페이지네이션 정보 (useInternalPagination 아니면 null) |
3033
+ | `getStickyStyle` | `(colIdx: number) => CSSProperties` | 컬럼 sticky 스타일 (CSS 변수 + 고정폭) |
3034
+ | `setRowCount` | `(count: number) => void` | 가상 스크롤 총 행 수 설정 |
3035
+ | `updateRowSelect` | `(row: SRow) => void` | 단일 행 선택 토글 (sd-table updateRowSelect) — onSelectedChange 발생 |
3036
+ | `toggleSelectAll` | `(checked: boolean, rows: SRow[]) => void` | 주어진 행들 전체 선택/해제 (sd-table toggleSelectAll) — onSelectedChange 발생 |
3037
+
3038
+ ## Dependencies
3039
+
3040
+ ### Depends on
3041
+
3042
+ - [SCheckbox](../SCheckbox)
3043
+ - [SCircleProgress](../SCircleProgress)
3044
+ - [SIcon](../SIcon)
3045
+ - [SPagination](../SPagination)
3046
+ - [SSelect](../SSelect)
3047
+
3048
+ ### Graph
3049
+
3050
+ ---
3051
+
3052
+ # STableBar
3053
+
3054
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
3055
+
3056
+ ### STableBar
3057
+
3058
+ #### Props
3059
+
3060
+ | Prop | Type | Default | Description |
3061
+ |------|------|---------|-------------|
3062
+ | `total?` | `number` | — | 전체 건수. 지정하면 "총 N 건" 을 표시한다 (천단위 구분 자동) |
3063
+ | `selected?` | `number` | — | 선택 건수. 지정하면 "N건 선택" 을 표시한다 |
3064
+ | `actions?` | `ReactNode` | — | 구분선 오른쪽 좌측 액션 영역 |
3065
+ | `rightActions?` | `ReactNode` | — | 우측 정렬 액션 영역 |
3066
+
3067
+ ## Dependencies
3068
+
3069
+ ### Depends on
3070
+
3071
+ - [SDivider](../SDivider)
3072
+
3073
+ ### Graph
3074
+
3075
+ ---
3076
+
3077
+ # STabs
3078
+
3079
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
3080
+
3081
+ ### STabs
3082
+
3083
+ #### Props
3084
+
3085
+ | Prop | Type | Default | Description |
3086
+ |------|------|---------|-------------|
3087
+ | `value` | `string` | — | 현재 선택된 탭 value |
3088
+ | `tabs` | `STabOption[]` | — | 탭 옵션 목록 |
3089
+ | `size?` | `STabSize` | `'md'` | 탭 크기 (main 전용) |
3090
+ | `isSub?` | `boolean` | `false` | 서브 탭(밑줄형) 스타일 |
3091
+ | `vertical?` | `boolean` | `false` | 세로 배치 (sub 전용 — main 폴더형은 항상 가로) |
3092
+ | `className?` | `string` | — | |
3093
+ | `style?` | `CSSProperties` | — | |
3094
+
3095
+ #### Events
3096
+
3097
+ | Event | Type | Description |
3098
+ |-------|------|-------------|
3099
+ | `onValueChange` | `(value: string) => void` | 탭 변경 (sdUpdate) |
3100
+
3101
+ ## Dependencies
3102
+
3103
+ ### Depends on
3104
+
3105
+ - [STag](../STag)
3106
+
3107
+ ### Graph
3108
+
3109
+ ---
3110
+
3111
+ # STag
3112
+
3113
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
3114
+
3115
+ ### STag
3116
+
3117
+ #### Props
3118
+
3119
+ | Prop | Type | Default | Description |
3120
+ |------|------|---------|-------------|
3121
+ | `shape?` | `STagShape` | `'square'` | 모양 |
3122
+ | `size?` | `STagSize` | `'sm'` | 크기 |
3123
+ | `color?` | `STagColor` | `'grey'` | 색상 |
3124
+ | `label?` | `string` | `''` | 레이블 텍스트 |
3125
+ | `icon?` | `SIconName` | — | 아이콘명 |
3126
+ | `iconLeft?` | `boolean` | `true` | 아이콘을 레이블 왼쪽에 배치 |
3127
+ | `width?` | `string \| number` | — | 태그 너비 (숫자면 px, 문자열이면 그대로 적용). 미지정 시 콘텐츠 크기 |
3128
+
3129
+ ## Dependencies
3130
+
3131
+ ### Used by
3132
+
3133
+ - [SConfirmModal](../SConfirmModal)
3134
+ - [SGhostButton](../SGhostButton)
3135
+ - [SGnb](../SGnb)
3136
+ - [STabs](../STabs)
3137
+ - [STextLink](../STextLink)
3138
+
3139
+ ### Depends on
3140
+
3141
+ - [SIcon](../SIcon)
3142
+
3143
+ ### Graph
3144
+
3145
+ ---
3146
+
3147
+ # STextLink
3148
+
3149
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
3150
+
3151
+ ### STextLink
3152
+
3153
+ #### Props
3154
+
3155
+ | Prop | Type | Default | Description |
3156
+ |------|------|---------|-------------|
3157
+ | `label?` | `string` | `''` | 레이블 |
3158
+ | `icon?` | `SIconName` | — | 좌측 아이콘 |
3159
+ | `iconColor?` | `SColor` | — | 좌측 아이콘 색상. 팔레트 키(`grey_65`, `red_95` …) 또는 임의 CSS 색상 |
3160
+ | `labelClass?` | `string` | — | 레이블 span에 추가할 클래스 |
3161
+ | `rightArrow?` | `STextLinkArrow` | `'none'` | 우측 화살표 |
3162
+ | `underline?` | `boolean` | `false` | 밑줄 여부 |
3163
+ | `disabled?` | `boolean` | `false` | 비활성 |
3164
+ | `size?` | `STextLinkSize` | `'sm'` | 크기 |
3165
+ | `tooltipText?` | `string` | — | 호버 시 표시할 툴팁 텍스트 (회색 STag 스타일) |
3166
+
3167
+ #### Events
3168
+
3169
+ | Event | Type | Description |
3170
+ |-------|------|-------------|
3171
+ | `onClick` | `() => void` | |
3172
+
3173
+ ## Dependencies
3174
+
3175
+ ### Used by
3176
+
3177
+ - [SChipInput](../SChipInput)
3178
+ - [SPopover](../SPopover)
3179
+
3180
+ ### Depends on
3181
+
3182
+ - [SIcon](../SIcon)
3183
+ - [STag](../STag)
3184
+
3185
+ ### Graph
3186
+
3187
+ ---
3188
+
3189
+ # STextarea
3190
+
3191
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
3192
+
3193
+ ### STextarea
3194
+
3195
+ #### Props
3196
+
3197
+ | Prop | Type | Default | Description |
3198
+ |------|------|---------|-------------|
3199
+ | `value?` | `string` | — | 값 (제어) |
3200
+ | `rows?` | `number` | `3` | 행 수 |
3201
+ | `rules?` | `Rule[]` | — | 유효성 규칙 — blur 시 자동 검증 |
3202
+ | `status?` | `SFieldStatus` | — | 필드 상태 ('default' | 'pass' | 'error') |
3203
+ | `focused?` | `boolean` | — | 포커스 상태 (제어/반영) |
3204
+ | `hovered?` | `boolean` | — | 호버 상태 (제어/반영) |
3205
+ | `textareaClass?` | `string` | — | 내부 textarea 요소 className |
3206
+ | `textareaStyle?` | `CSSProperties` | — | 내부 textarea 요소 style |
3207
+ | `label?` | `string` | — | |
3208
+ | `labelWidth?` | `number \| string` | — | |
3209
+ | `icon?` | `SIconName` | — | 레이블 영역 아이콘 |
3210
+ | `iconColor?` | `SColor` | — | |
3211
+ | `labelTooltip?` | `string` | — | 레이블 툴팁 텍스트 |
3212
+ | `labelTooltipProps?` | `Partial<STooltipProps>` | — | 레이블 툴팁 상세 옵션 |
3213
+ | `addonLabel?` | `string` | — | 우측 어드온 레이블 |
3214
+ | `addonAlign?` | `SFieldAddonAlign` | — | 어드온 정렬 |
3215
+ | `hint?` | `string` | — | |
3216
+ | `error?` | `boolean` | — | |
3217
+ | `errorMessage?` | `string` | — | |
3218
+ | `width?` | `number \| string` | — | |
3219
+ | `disabled?` | `boolean` | `false` | |
3220
+ | `readOnly?` | `boolean` | `false` | |
3221
+ | `className?` | `string` | — | |
3222
+ | `style?` | `CSSProperties` | — | |
3223
+
3224
+ #### Events
3225
+
3226
+ | Event | Type | Description |
3227
+ |-------|------|-------------|
3228
+ | `onValueChange` | `(value: string) => void` | 값 변경 (sdUpdate) |
3229
+ | `onChange` | `TextareaHTMLAttributes<HTMLTextAreaElement>['onChange']` | 네이티브 onChange (form-agnostic) |
3230
+
3231
+ ## Dependencies
3232
+
3233
+ ### Used by
3234
+
3235
+ - [SKeyValueTable](../SKeyValueTable)
3236
+
3237
+ ### Depends on
3238
+
3239
+ - [SField](../SField)
3240
+
3241
+ ### Graph
3242
+
3243
+ ---
3244
+
3245
+ # STimePicker
3246
+
3247
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
3248
+
3249
+ ### STimePicker
3250
+
3251
+ #### Props
3252
+
3253
+ | Prop | Type | Default | Description |
3254
+ |------|------|---------|-------------|
3255
+ | `value?` | `string \| null` | — | 선택 시간 (HH:mm) |
3256
+ | `type?` | `STimePickerType` | `'default'` | 표시 타입: default(24시간) / midday(오전·오후) |
3257
+ | `size?` | `STimePickerSize` | `'sm'` | |
3258
+ | `placeholder?` | `string` | `'00:00'` | |
3259
+ | `disabled?` | `boolean` | `false` | |
3260
+ | `clearable?` | `boolean` | `false` | |
3261
+ | `useMeridiem?` | `boolean` | — | 오전/오후 선택 표시 여부. 지정하지 않으면 type="midday"일 때만 켜집니다. |
3262
+ | `minuteStep?` | `number` | `1` | |
3263
+ | `width?` | `number \| string` | — | |
3264
+ | `name?` | `string` | — | |
3265
+ | `rules?` | `Rule[]` | — | |
3266
+ | `status?` | `SFieldStatus` | — | |
3267
+ | `label?` | `string` | — | |
3268
+ | `labelWidth?` | `number \| string` | — | |
3269
+ | `icon?` | `SIconName` | — | |
3270
+ | `iconColor?` | `SColor` | — | |
3271
+ | `labelTooltip?` | `string` | — | |
3272
+ | `labelTooltipProps?` | `Partial<STooltipProps>` | — | |
3273
+ | `addonLabel?` | `string` | — | |
3274
+ | `addonAlign?` | `SFieldAddonAlign` | — | |
3275
+ | `hint?` | `string` | — | |
3276
+ | `error?` | `boolean` | — | |
3277
+ | `errorMessage?` | `string` | — | |
3278
+ | `className?` | `string` | — | |
3279
+ | `style?` | `CSSProperties` | — | |
3280
+
3281
+ #### Events
3282
+
3283
+ | Event | Type | Description |
3284
+ |-------|------|-------------|
3285
+ | `onValueChange` | `(time: string \| null) => void` | 선택 변경 (sdUpdate) |
3286
+ | `onOpenChange` | `(open: boolean) => void` | 열림/닫힘 변경 (sdDropDownShow) |
3287
+
3288
+ ## Dependencies
3289
+
3290
+ ### Used by
3291
+
3292
+ - [STimeRangePicker](../STimeRangePicker)
3293
+
3294
+ ### Depends on
3295
+
3296
+ - [SField](../SField)
3297
+ - [SGhostButton](../SGhostButton)
3298
+ - [SIcon](../SIcon)
3299
+
3300
+ ### Graph
3301
+
3302
+ ---
3303
+
3304
+ # STimeRangePicker
3305
+
3306
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
3307
+
3308
+ ### STimeRangePicker
3309
+
3310
+ #### Props
3311
+
3312
+ | Prop | Type | Default | Description |
3313
+ |------|------|---------|-------------|
3314
+ | `value?` | `STimeRangeValue` | — | 선택 시간 범위 [시작, 종료] (HH:mm) |
3315
+ | `type?` | `STimeRangePickerType` | `'default'` | 표시 타입: default(24시간) / midday(오전·오후) |
3316
+ | `size?` | `STimeRangePickerSize` | `'sm'` | |
3317
+ | `placeholder?` | `string` | `'00:00 ~ 23:59'` | |
3318
+ | `disabled?` | `boolean` | `false` | |
3319
+ | `clearable?` | `boolean` | `false` | |
3320
+ | `useMeridiem?` | `boolean` | — | 오전/오후 선택 표시 여부. 지정하지 않으면 type="midday"일 때만 켜집니다. |
3321
+ | `minuteStep?` | `number` | `1` | |
3322
+ | `rangeOrder?` | `STimeRangePickerRangeOrder` | `'strict'` | 범위 순서 정책. strict는 시작 시간이 종료 시간보다 늦어지지 않도록 보정합니다. |
3323
+ | `width?` | `number \| string` | — | |
3324
+ | `name?` | `string` | — | |
3325
+ | `rules?` | `Rule[]` | — | |
3326
+ | `status?` | `SFieldStatus` | — | |
3327
+ | `label?` | `string` | — | |
3328
+ | `labelWidth?` | `number \| string` | — | |
3329
+ | `icon?` | `SIconName` | — | |
3330
+ | `iconColor?` | `SColor` | — | |
3331
+ | `labelTooltip?` | `string` | — | |
3332
+ | `labelTooltipProps?` | `Partial<STooltipProps>` | — | |
3333
+ | `addonLabel?` | `string` | — | |
3334
+ | `addonAlign?` | `SFieldAddonAlign` | — | |
3335
+ | `hint?` | `string` | — | |
3336
+ | `error?` | `boolean` | — | |
3337
+ | `errorMessage?` | `string` | — | |
3338
+ | `className?` | `string` | — | |
3339
+ | `style?` | `CSSProperties` | — | |
3340
+
3341
+ #### Events
3342
+
3343
+ | Event | Type | Description |
3344
+ |-------|------|-------------|
3345
+ | `onValueChange` | `(range: STimeRangeValue) => void` | 선택 변경 (sdUpdate) |
3346
+ | `onOpenChange` | `(open: boolean) => void` | 열림/닫힘 변경 (sdDropDownShow) |
3347
+
3348
+ ## Dependencies
3349
+
3350
+ ### Depends on
3351
+
3352
+ - [SField](../SField)
3353
+ - [SGhostButton](../SGhostButton)
3354
+ - [SIcon](../SIcon)
3355
+ - [STimePicker](../STimePicker)
3356
+
3357
+ ### Graph
3358
+
3359
+ ---
3360
+
3361
+ # SToast
3362
+
3363
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
3364
+
3365
+ ### SToastContainer
3366
+
3367
+ #### Props
3368
+
3369
+ | Prop | Type | Default | Description |
3370
+ |------|------|---------|-------------|
3371
+ | `position?` | `SToastPosition` | `'bottom-center'` | |
3372
+ | `maxVisible?` | `number` | `3` | |
3373
+ | `defaultDuration?` | `number` | `4000` | 자동 닫힘 기본 지연(ms) |
3374
+ | `zIndex?` | `number` | `1100` | 스택 z-index. 기본값(1100)은 모달(1000/1001) 위 — 토스트는 전역 알림이라 항상 최상단 |
3375
+
3376
+ #### Events
3377
+
3378
+ | Event | Type | Description |
3379
+ |-------|------|-------------|
3380
+ | `onShow` | `(payload: { id: string }) => void` | 토스트가 표시될 때 (sdToastShow) |
3381
+ | `onDismiss` | `(payload: { id: string }) => void` | 토스트가 제거될 때 (sdToastDismiss) |
3382
+
3383
+ #### Methods (ref)
3384
+
3385
+ | Method | Type | Description |
3386
+ |--------|------|-------------|
3387
+ | `create` | `(options: SToastNotifyOptions) => string` | |
3388
+ | `dismiss` | `(id: string) => void` | |
3389
+ | `dismissAll` | `() => void` | |
3390
+
3391
+ ### SToast
3392
+
3393
+ #### Props
3394
+
3395
+ | Prop | Type | Default | Description |
3396
+ |------|------|---------|-------------|
3397
+ | `icon?` | `SIconName` | — | 좌측 아이콘 |
3398
+ | `message` | `string` | — | 메시지 |
3399
+ | `link?` | `string` | — | 우측 링크 URL |
3400
+ | `linkLabel?` | `string` | — | 링크 레이블 (없으면 URL) |
3401
+ | `buttonLabel?` | `string` | — | 우측 버튼 레이블 |
3402
+ | `useClose?` | `boolean` | `false` | 닫기 버튼 표시 |
3403
+ | `type?` | `SToastType` | `'default'` | 타입(색상/프리셋) |
3404
+ | `className?` | `string` | — | |
3405
+ | `style?` | `CSSProperties` | — | |
3406
+
3407
+ #### Events
3408
+
3409
+ | Event | Type | Description |
3410
+ |-------|------|-------------|
3411
+ | `onClose` | `() => void` | 닫기 (sdClose) |
3412
+ | `onButtonClick` | `(e: MouseEvent) => void` | 버튼 클릭 (sdButtonClick) |
3413
+
3414
+ ## Dependencies
3415
+
3416
+ ### Depends on
3417
+
3418
+ - [SButton](../SButton)
3419
+ - [SGhostButton](../SGhostButton)
3420
+ - [SIcon](../SIcon)
3421
+
3422
+ ### Graph
3423
+
3424
+ ---
3425
+
3426
+ # SToggle
3427
+
3428
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
3429
+
3430
+ ### SToggle
3431
+
3432
+ #### Props
3433
+
3434
+ | Prop | Type | Default | Description |
3435
+ |------|------|---------|-------------|
3436
+ | `value?` | `boolean` | `false` | 선택(on/off) 상태 |
3437
+ | `label?` | `string` | `''` | 라벨 |
3438
+ | `disabled?` | `boolean` | `false` | 비활성 |
3439
+ | `className?` | `string` | — | |
3440
+ | `style?` | `CSSProperties` | — | |
3441
+
3442
+ #### Events
3443
+
3444
+ | Event | Type | Description |
3445
+ |-------|------|-------------|
3446
+ | `onValueChange` | `(value: boolean) => void` | 변경 (sdUpdate) |
3447
+
3448
+ ---
3449
+
3450
+ # STooltip
3451
+
3452
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
3453
+
3454
+ ### STooltip
3455
+
3456
+ #### Props
3457
+
3458
+ | Prop | Type | Default | Description |
3459
+ |------|------|---------|-------------|
3460
+ | `content?` | `ReactNode` | — | 툴팁 본문 (ReactNode). message 보다 우선 |
3461
+ | `message?` | `string[]` | `[]` | 툴팁 본문 줄 목록 (각 항목이 한 줄, HTML 문자열 허용 — innerHTML 렌더링) |
3462
+ | `trigger?` | `STooltipTrigger` | `'hover'` | 트리거 방식 |
3463
+ | `placement?` | `STooltipPlacement` | `'top'` | 표시 방향 |
3464
+ | `tooltipType?` | `STooltipType` | `'default'` | 배경/텍스트 색상 타입 |
3465
+ | `useClose?` | `boolean` | `false` | 우측 상단 닫기 버튼. 켜면 자동 닫힘이 비활성화되고 X(또는 hide())로만 닫힘 (hover·click 공통) |
3466
+ | `disabled?` | `boolean` | `false` | 비활성 — 트리거만 렌더하고 툴팁을 열지 않음 |
3467
+ | `children?` | `ReactNode` | — | 커스텀 트리거. 없으면 아이콘 트리거를 렌더 |
3468
+ | `icon?` | `SIconName` | `'helpOutline'` | 기본(아이콘) 트리거 설정 |
3469
+ | `iconSize?` | `number` | `12` | |
3470
+ | `color?` | `SColor` | `'#01BB4B'` | 트리거 아이콘 색상. 팔레트 키(`grey_65`, `red_95` …) 또는 임의 CSS 색상 |
3471
+ | `ariaLabel?` | `string` | — | 접근성 레이블 |
3472
+ | `className?` | `string` | — | |
3473
+ | `triggerClassName?` | `string` | — | |
3474
+ | `style?` | `CSSProperties` | — | |
3475
+
3476
+ #### Methods (ref)
3477
+
3478
+ | Method | Type | Description |
3479
+ |--------|------|-------------|
3480
+ | `show` | `() => void` | 툴팁을 표시합니다. |
3481
+ | `hide` | `() => void` | 툴팁을 숨깁니다. |
3482
+
3483
+ ## Dependencies
3484
+
3485
+ ### Used by
3486
+
3487
+ - [SField](../SField)
3488
+ - [SKeyValueTable](../SKeyValueTable)
3489
+ - [SList](../SList)
3490
+
3491
+ ### Depends on
3492
+
3493
+ - [SGhostButton](../SGhostButton)
3494
+ - [SIcon](../SIcon)
3495
+
3496
+ ### Graph