sellmate-design-system-react 8.0.0 → 9.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +492 -116
- package/README.md +38 -8
- package/dist/components/SChipFilter/README.md +69 -0
- package/dist/components/SChipFilter/SChipFilter.d.ts +160 -0
- package/dist/components/SChipFilter/index.d.ts +1 -0
- package/dist/components/SDatePicker/README.md +2 -0
- package/dist/components/SDateRangePicker/README.md +21 -0
- package/dist/components/SDateRangePicker/SDateRangePicker.d.ts +16 -0
- package/dist/components/SDateRangePicker/index.d.ts +1 -1
- package/dist/components/SDraggableItem/README.md +1 -0
- package/dist/components/SDraggableItem/SDraggableItem.d.ts +3 -0
- package/dist/components/SDrawer/README.md +5 -4
- package/dist/components/SDrawer/SDrawer.d.ts +16 -5
- package/dist/components/SExpansionItem/README.md +1 -0
- package/dist/components/SExpansionItem/SExpansionItem.d.ts +3 -0
- package/dist/components/SGhostButton/README.md +4 -0
- package/dist/components/SGnb/README.md +3 -0
- package/dist/components/SGnb/SGnb.d.ts +15 -0
- package/dist/components/SGuide/README.md +1 -1
- package/dist/components/SGuide/SGuide.d.ts +1 -1
- package/dist/components/SIcon/README.md +2 -0
- package/dist/components/SIcon/icons.gen.d.ts +1 -0
- package/dist/components/SList/README.md +1 -0
- package/dist/components/SList/SList.d.ts +2 -0
- package/dist/components/SListItem/README.md +3 -2
- package/dist/components/SListItem/SListItem.d.ts +5 -2
- package/dist/components/SPage/README.md +21 -0
- package/dist/components/SPage/SPage.d.ts +10 -0
- package/dist/components/SPage/SPageHeader.d.ts +32 -0
- package/dist/components/SPage/index.d.ts +1 -0
- package/dist/components/SPortal/README.md +1 -1
- package/dist/components/SPortal/SPortal.d.ts +7 -1
- package/dist/components/SRadio/README.md +2 -0
- package/dist/components/SRadioButton/README.md +13 -0
- package/dist/components/SSelect/README.md +2 -2
- package/dist/components/SSelect/SSelect.d.ts +2 -2
- package/dist/components/STag/README.md +2 -0
- package/dist/components/STextLink/README.md +2 -0
- package/dist/components/STooltip/README.md +2 -0
- package/dist/index.cjs +1750 -232
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1761 -245
- package/dist/index.js.map +1 -1
- package/dist/lib/floating-width.d.ts +22 -0
- package/dist/llms-full.txt +628 -126
- package/dist/llms.txt +498 -118
- package/dist/styles.css +249 -22
- package/dist/theme.css +36 -10
- package/eslint/scale.gen.mjs +1 -1
- package/package.json +8 -4
package/dist/llms-full.txt
CHANGED
|
@@ -30,22 +30,26 @@
|
|
|
30
30
|
|
|
31
31
|
무엇을 만들지 정했으면 **이 표에서 먼저 찾는다.** 상세 Props 는 `dist/components/<이름>/README.md` 참조.
|
|
32
32
|
|
|
33
|
+
**이 표에서 어느 것을 골라야 할지 모르겠으면 §3-0 "의도 → 컴포넌트 라우팅" 으로 간다.** 하려는 일을 문장으로 찾으면 답이 하나 나온다 — 여기 인덱스는 "무엇이 있는지", §3-0 은 "언제 그걸 쓰는지" 를 담당한다.
|
|
34
|
+
|
|
33
35
|
| 분류 | 컴포넌트 |
|
|
34
36
|
| --- | --- |
|
|
35
37
|
| **버튼·링크** | `SButton` `SGhostButton` `SDropdownButton` `STextLink` `SSwitch` `SToggle` |
|
|
36
|
-
| **입력 (폼)** | `SForm` `SField` `SInput` `SNumberInput` `STextarea` `SSelect` `SCheckbox` `SRadio` `SRadioButton` `SChip` `SChipInput` `SBarcodeInput` `SFilePicker` |
|
|
38
|
+
| **입력 (폼)** | `SForm` `SField` `SInput` `SNumberInput` `STextarea` `SSelect` `SCheckbox` `SRadio` `SRadioGroup` `SRadioButton` `SChip` `SChipInput` `SBarcodeInput` `SFilePicker` |
|
|
37
39
|
| **날짜·시간** | `SCalendar` `SDatePicker` `SDateRangePicker` `STimePicker` `STimeRangePicker` |
|
|
38
|
-
| **표·목록** | `STable` `STableBar` `SKeyValueTable` `SList` `SListItem` `SDraggableItem` |
|
|
39
|
-
| **레이아웃** | `SLayout` `SGnb` `SPage` `SSectionHeaderCard` `SCard` `SDivider` `SSplitter` `SScrollArea` `SExpansionItem` |
|
|
40
|
+
| **표·목록** | `STable` `STableBar` `SChipFilter` `SKeyValueTable` `SList` `SListItem` `SExpansionList` `SDraggableList` `SDraggableItem` `STree` |
|
|
41
|
+
| **레이아웃** | `SLayout` `SGnb` `SPage`(제목 영역은 `header` prop) `SSectionHeaderCard` `SCard` `SDivider` `SSplitter` `SScrollArea` `SExpansionItem` |
|
|
40
42
|
| **내비게이션** | `STabs` `SPagination` `SStepper` |
|
|
41
43
|
| **표시·상태** | `STag` `SBadge` `SIcon` `SCallout` `SGuide` |
|
|
42
44
|
| **진행·로딩** | `SLinearProgress` `SCircleProgress` `SLoadingContainer` `SLoadingModal` |
|
|
43
|
-
| **오버레이** | `STooltip` `SPopover` `SPopup` `SPortal` |
|
|
45
|
+
| **오버레이** | `STooltip` `SPopover` `SPopup` `SDrawer` `SPortal` |
|
|
44
46
|
| **모달** | `SModal.confirm()` `SModal.create()` + `SActionModal` `SConfirmModal` `SModalOutlet`(앱 루트 1회) |
|
|
45
47
|
| **알림** | `SToast` `SToastContainer` |
|
|
46
48
|
|
|
47
49
|
표에 없는 UI 를 만들어야 할 때만 `div` 로 직접 조립하고, 그때도 §1-2 · §2 의 토큰 규칙을 지킨다.
|
|
48
50
|
|
|
51
|
+
**이 표는 패키지가 실제로 export 하는 컴포넌트와 일치해야 한다** — `npm run check:routing` 이 강제한다. 표에 없는 컴포넌트는 소비 앱 입장에서 존재하지 않는 것과 같다.
|
|
52
|
+
|
|
49
53
|
### 0-2. 프로젝트 설정
|
|
50
54
|
|
|
51
55
|
설정(Tailwind v4 `theme.css` import, `@source` 지정, Next.js 주의사항)은 패키지 [README.md](./README.md)를 따른다. 이 문서는 설정이 끝난 상태에서의 **화면 작성 규칙**만 다룬다.
|
|
@@ -130,7 +134,76 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
|
|
|
130
134
|
|
|
131
135
|
---
|
|
132
136
|
|
|
133
|
-
## 2. 조합
|
|
137
|
+
## 2. 조합 규칙 — 화면을 어떻게 쌓는가
|
|
138
|
+
|
|
139
|
+
§3 이 "무엇을 쓸지" 라면 여기는 **"고른 것들을 어떻게 붙일지"** 다. 층 구조(§2-0)가 먼저고, 타이포·간격·색(§2-1~2-3)은 그 층에 붙는 값이다.
|
|
140
|
+
|
|
141
|
+
### 2-0. 화면의 층 구조 — 무엇을 어디에 놓는가
|
|
142
|
+
|
|
143
|
+
**모든 화면은 다섯 층으로 쌓인다. 컴포넌트는 저마다 놓이는 층이 정해져 있고, 층이 다르면 붙이는 방법도 다르다.** 조합이 어색해지는 원인은 대개 층을 건너뛴 것이다 — 요소를 페이지에 바로 놓거나, 인라인 요소를 블록처럼 세우거나, 카드 안에 카드를 겹치는 식이다.
|
|
144
|
+
|
|
145
|
+
| 층 | 무엇인가 | 컴포넌트 |
|
|
146
|
+
| --- | --- | --- |
|
|
147
|
+
| **셸** | 앱 전체 뼈대. 페이지가 바뀌어도 남는다 | `SLayout` `SGnb` `SPage`(제목 영역은 `header` prop) |
|
|
148
|
+
| **블록** | `SPage` 의 직계 자식. 페이지를 세로로 쌓는 단위 | `SSectionHeaderCard` `SCard` `SForm` `SSplitter` `SScrollArea` `STable` `STableBar` `SChipFilter` `SKeyValueTable` `SList` `SExpansionList` `SDraggableList` `STree` `SCallout` `STabs` `SStepper` `SPagination` `SDivider` |
|
|
149
|
+
| **요소** | 블록 **안에** 놓이는 컨트롤. 혼자 페이지에 서지 않는다 | `SButton` `SGhostButton` `SDropdownButton` `SField` `SInput` `SNumberInput` `STextarea` `SSelect` `SCheckbox` `SRadio` `SRadioGroup` `SRadioButton` `SSwitch` `SToggle` `SChipInput` `SBarcodeInput` `SFilePicker` `SDatePicker` `SDateRangePicker` `STimePicker` `STimeRangePicker` `SCalendar` `SListItem` `SExpansionItem` `SDraggableItem` `SLinearProgress` `SCircleProgress` |
|
|
150
|
+
| **인라인** | 텍스트 흐름·셀·라벨 안에 섞인다. 혼자 블록이 되지 않는다 | `STag` `SBadge` `SIcon` `STextLink` `SChip` |
|
|
151
|
+
| **레이어** | 문서 흐름 **밖**에 떠서 그려진다. 어느 층에서 띄우든 레이아웃에 영향이 없다 | `SModal` `SActionModal` `SConfirmModal` `SPopup` `SDrawer` `SPopover` `STooltip` `SPortal` `SToast` `SLoadingModal` `SLoadingContainer` `SGuide` |
|
|
152
|
+
|
|
153
|
+
여기에 화면을 차지하지 않는 **부트스트랩** 이 따로 있다 — `SModalOutlet` `SToastContainer` 는 앱 진입점에 한 번만 렌더한다 (§4-1).
|
|
154
|
+
|
|
155
|
+
> 이 표는 §0-1 인덱스 전체를 덮는다 (`npm run check:routing` 이 강제). **컴포넌트를 골랐으면 그것이 어느 층인지 먼저 확인하고, 아래 포함 규칙에 맞는 자리에 놓는다.**
|
|
156
|
+
|
|
157
|
+
#### 블록은 두 종류다
|
|
158
|
+
|
|
159
|
+
같은 블록층이어도 **안에 다른 것을 담느냐** 로 갈린다. 이걸 구분해야 포함 규칙이 선다.
|
|
160
|
+
|
|
161
|
+
- **담는 블록** — `SSectionHeaderCard` `SCard` `SForm` `SSplitter` `SScrollArea`. 안이 비어 있고 다른 블록·요소를 받는다.
|
|
162
|
+
- **그리는 블록** — 나머지 전부. 자기가 내용을 그리므로 안에 무엇을 넣을지 고민할 일이 없다 (`STable` 의 셀처럼 지정된 슬롯 제외).
|
|
163
|
+
|
|
164
|
+
#### 포함 규칙 — 무엇 안에 무엇이 올 수 있나
|
|
165
|
+
|
|
166
|
+
| 담는 것 | 올 수 있는 것 | 오면 안 되는 것 |
|
|
167
|
+
| --- | --- | --- |
|
|
168
|
+
| `SPage` | **블록만** | **요소를 직접** — 버튼 하나도 블록에 담아 놓는다 |
|
|
169
|
+
| `SSectionHeaderCard.Body` | 그리는 블록 · 요소 | `SSectionHeaderCard` · `SCard` (카드 겹침, §3-7-8) |
|
|
170
|
+
| `SCard` | 그리는 블록 · 요소 | `SCard` · `SSectionHeaderCard` |
|
|
171
|
+
| `SForm` | 블록 (보통 `SKeyValueTable` + 하단 액션) | — |
|
|
172
|
+
| `SSplitter.Before` / `.After` | 블록 | — |
|
|
173
|
+
| `SScrollArea` | 블록 | — |
|
|
174
|
+
| `STable` 셀 (`render`) | 인라인 · 요소 | 블록 — 표 안에 표·카드를 넣지 않는다 |
|
|
175
|
+
| `SKeyValueTable` 값 셀 | 인라인 · 요소 | 블록 |
|
|
176
|
+
| `SListItem` | 인라인 | 블록 · 요소 |
|
|
177
|
+
|
|
178
|
+
- **블록을 `div` 로 감싸지 않는다.** 감싸면 페이지 스택에서 빠져나가 `gap-sd-12` 리듬이 끊긴다. 여러 블록을 묶어야 하면 그건 섹션이므로 `SSectionHeaderCard` 다.
|
|
179
|
+
- **요소를 페이지에 직접 놓지 않는다.** 하단 액션 버튼들처럼 블록이 없는 자리는 `div` 로 한 줄을 만들어 그 `div` 가 블록이 된다 (§4-3·§4-4).
|
|
180
|
+
- **레이어는 어디서 띄워도 된다.** `body` 로 portal 되므로 셸 안에 넣을 필요가 없고, 넣어도 레이아웃이 바뀌지 않는다 (§4-1).
|
|
181
|
+
|
|
182
|
+
#### 블록을 쌓는 방법과 순서
|
|
183
|
+
|
|
184
|
+
**`SPage` 는 자식을 자동으로 쌓지 않는다.** 페이지가 직접 세로 스택을 만든다 — 이게 모든 §4 레시피가 `flex flex-col gap-sd-12` 로 시작하는 이유다.
|
|
185
|
+
|
|
186
|
+
```tsx
|
|
187
|
+
<SPage background="frame">
|
|
188
|
+
<div className="flex flex-col gap-sd-12"> {/* 블록 스택 — 페이지가 만든다 */}
|
|
189
|
+
…블록들…
|
|
190
|
+
</div>
|
|
191
|
+
</SPage>
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
블록 순서는 화면 종류와 무관하게 같다. **필요한 것만 남기되 순서를 바꾸지 않는다.**
|
|
195
|
+
|
|
196
|
+
```text
|
|
197
|
+
1. 페이지 제목 `SPage` 의 `header` prop (+ 가이드·매뉴얼 링크는 slot. 액션 버튼은 오지 않는다 §4-2)
|
|
198
|
+
2. 상시 안내 SCallout
|
|
199
|
+
3. 필터 SKeyValueTable · SChipFilter (§3-7-11)
|
|
200
|
+
4. 툴바 STableBar (건수 요약 + 액션)
|
|
201
|
+
5. 본문 STable · 섹션 카드들 · SList …
|
|
202
|
+
6. 페이지네이션 SPagination (STable 이 pagination prop 으로 직접 그린다)
|
|
203
|
+
7. 하단 액션 되돌리기 왼쪽 · 실행 오른쪽 (§4-3)
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
간격은 층마다 다르다 — 블록 ↔ 블록은 `gap-sd-12`, 요소 ↔ 요소는 `gap-sd-8` 이 기본이고, 같은 컴포넌트를 나열할 때는 컴포넌트별 그룹 간격이 따로 있다. 전부 §2-2 에 있다.
|
|
134
207
|
|
|
135
208
|
### 2-1. 타이포그래피 프리셋
|
|
136
209
|
|
|
@@ -145,15 +218,22 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
|
|
|
145
218
|
|
|
146
219
|
**기본 선택 — 이 조합을 쓴다.** 이 서비스는 정보 밀도가 높아 본문이 12px 이다. 14px 를 본문 기본으로 쓰지 않는다.
|
|
147
220
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
|
151
|
-
|
|
|
152
|
-
|
|
|
153
|
-
|
|
|
221
|
+
**제목 위계는 §2-0 의 층을 따라간다** — 층이 한 단 내려가면 제목도 한 단 내려간다. 층을 건너뛰지 않듯 제목도 건너뛰지 않는다.
|
|
222
|
+
|
|
223
|
+
| 층 (§2-0) | 역할 | 클래스 | 크기 |
|
|
224
|
+
| --- | --- | --- | --- |
|
|
225
|
+
| 셸 | 페이지 제목 (`SPage` 의 `header.title`) | `typo-heading-lg` | 18px |
|
|
226
|
+
| 블록 | 섹션 제목 | `typo-heading-sm` | 14px |
|
|
227
|
+
| 블록 내부 | 하위 제목 (섹션 안을 더 나눌 때) | `typo-heading-xs` | 12px |
|
|
228
|
+
| — | 본문 | `typo-body-sm-default` | 12px |
|
|
229
|
+
| — | 보조 설명 | `typo-body-sm-default` + `text-fg-tertiary` | 12px / `grey_65` |
|
|
154
230
|
|
|
155
231
|
페이지 제목만 18px 로 크게 두고 그 아래는 14 / 12 로 촘촘하게 간다. 중간 크기(16px)는 기본 골격에서 쓰지 않는다.
|
|
156
232
|
|
|
233
|
+
- **페이지·섹션 제목의 타이포를 직접 주지 않는다.** `SPage` 의 `header.title`, `SSectionHeaderCard.Header` 의 `title` 이 이미 넣는다 — 그 위에 `typo-heading-lg`/`typo-heading-sm` 을 또 씌우지 않는다.
|
|
234
|
+
- **하위 제목이 필요하면 먼저 섹션을 나눌 수 없는지 본다.** 한 섹션 안에서 제목이 두 단으로 갈린다는 것은 대개 섹션이 둘이라는 뜻이다 (§3-7-8).
|
|
235
|
+
- 본문 안에서 한 단어를 강조할 때는 `typo-body-sm-medium` 을 쓴다. `typo-body-sm-bold` 는 제목 성격의 짧은 라벨에만 쓴다. <!-- TODO(디자인): 강조 굵기 기준 확정 -->
|
|
236
|
+
|
|
157
237
|
**보조 설명의 색** — 기본은 `text-fg-tertiary`(`grey_65`) 다. 보조 설명 안에서 위계가 한 단계 더 필요할 때만 `text-fg-secondary`(`grey_80`) → `text-fg-tertiary`(`grey_65`) 순으로 내려 쓴다 (§2-3).
|
|
158
238
|
|
|
159
239
|
### 2-2. 간격 (spacing)
|
|
@@ -165,14 +245,14 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
|
|
|
165
245
|
- 형제 요소 간격은 margin 대신 부모의 `flex`/`grid` + `gap-sd-*`으로 잡는다.
|
|
166
246
|
- 시맨틱 간격 토큰 (텍스트 덩어리·요소 사이 기본 리듬):
|
|
167
247
|
|
|
168
|
-
|
|
248
|
+
**여백(padding)과 간격(gap)은 서로 다른 축이고, 규칙도 다르다.** 어느 쪽이든 값은 §2-0 의 층이 정한다.
|
|
169
249
|
|
|
170
250
|
```text
|
|
171
|
-
|
|
172
|
-
└ 블록
|
|
173
|
-
|
|
174
|
-
└ 섹션·패널 블록 중 "안에 콘텐츠를 담는 컨테이너"인 것
|
|
251
|
+
셸 (SPage) 여백을 SPage 가 넣는다. 직접 주지 않는다
|
|
252
|
+
└ 블록 블록 ↔ 블록 간격은 gap-sd-12
|
|
253
|
+
└ 담는 블록 "안에 콘텐츠를 담는" 블록 (SSectionHeaderCard·SCard 등, §2-0)
|
|
175
254
|
여기에만 안쪽 여백 선택지가 있다 (아래 "섹션·패널 안쪽 여백")
|
|
255
|
+
└ 요소 요소 ↔ 요소 간격은 gap-sd-8 이 기본
|
|
176
256
|
```
|
|
177
257
|
|
|
178
258
|
| 상황 | 값 |
|
|
@@ -321,6 +401,126 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
|
|
|
321
401
|
|
|
322
402
|
> ⚠️ 초안(개발 작성). <!-- TODO(디자인): 전체 검수·확정 --> 표시가 있는 행은 디자인 확정 전까지 초안 기준으로 사용.
|
|
323
403
|
|
|
404
|
+
### 3-0. 의도 → 컴포넌트 라우팅 (여기서 시작한다)
|
|
405
|
+
|
|
406
|
+
**§3 의 입구는 이 표다.** 아래 §3-1 부터는 계열별로 정리돼 있어서 "내가 만들 게 어느 계열인지"를 이미 알아야 펼 수 있다. 그런데 실제 출발점은 계열이 아니라 **하려는 일**이다. 그 문장을 여기서 찾으면 답이 하나 나온다.
|
|
407
|
+
|
|
408
|
+
- **읽는 법**: 왼쪽에서 하려는 일을 찾고 → 가운데 컴포넌트를 쓴다. 오른쪽에 § 참조가 있으면 그 자리는 답이 갈리므로 **반드시 그 절을 읽고 고른다.** 참조가 없으면 더 볼 것 없이 그대로 쓴다.
|
|
409
|
+
- 이 표는 §0-1 인덱스 전체를 덮는다 (`npm run check:routing` 이 강제). **여기에 해당하는 일이 없으면 대응 컴포넌트가 없는 것이므로** §1-1 의 예외 규칙(순수 레이아웃 요소)으로 간다.
|
|
410
|
+
|
|
411
|
+
#### A. 값을 입력받는다
|
|
412
|
+
|
|
413
|
+
| 하려는 일 | 컴포넌트 | 갈림 |
|
|
414
|
+
| --- | --- | --- |
|
|
415
|
+
| 한 줄 텍스트를 받는다 | `SInput` | §3-7-1 |
|
|
416
|
+
| 여러 줄 텍스트를 받는다 | `STextarea` | §3-7-1 |
|
|
417
|
+
| 숫자(수량·금액)를 받는다 | `SNumberInput` | |
|
|
418
|
+
| 바코드를 스캔해 받는다 | `SBarcodeInput` | |
|
|
419
|
+
| 목록에서 하나 고르게 한다 | `SSelect` | §3-7-2 |
|
|
420
|
+
| 선택지를 항상 펼쳐 두고 하나 고르게 한다 | `SRadioGroup` | §3-7-2 |
|
|
421
|
+
| 버튼 모양으로 모드를 하나 고르게 한다 | `SRadioButton` | §3-7-2 |
|
|
422
|
+
| 라디오 하나를 표 셀 등에 직접 배치한다 | `SRadio` | §3-7-2 |
|
|
423
|
+
| 여러 개를 고르게 한다 / 동의를 받는다 | `SCheckbox` | §3-7-3 |
|
|
424
|
+
| 켜는 즉시 반영되는 설정을 준다 | `SSwitch` | §3-7-3 |
|
|
425
|
+
| 목록을 좁히는 필터를 켜고 끄게 한다 | `SToggle` | §3-7-3 |
|
|
426
|
+
| 자유 입력값을 여러 개 쌓게 한다 | `SChipInput` | §3-1 |
|
|
427
|
+
| 입력된 값 하나를 지우거나 고치게 한다 | `SChip` | §3-1 |
|
|
428
|
+
| 파일을 받는다 | `SFilePicker` | |
|
|
429
|
+
| 날짜 하나를 받는다 | `SDatePicker` | §3-7-4 |
|
|
430
|
+
| 날짜 기간을 받는다 | `SDateRangePicker` | §3-7-4 |
|
|
431
|
+
| 시각 하나를 받는다 | `STimePicker` | |
|
|
432
|
+
| 시각 범위를 받는다 | `STimeRangePicker` | |
|
|
433
|
+
| 달력 자체를 화면에 펼쳐 보여준다 | `SCalendar` | §3-7-4 |
|
|
434
|
+
| 컨트롤에 라벨·필수·에러를 붙인다 | `SField` | §3-7-5 |
|
|
435
|
+
| 입력 여러 개를 묶어 한 번에 검증한다 | `SForm` | §4-3 |
|
|
436
|
+
| 폼·필터를 표 형태로 배치한다 | `SKeyValueTable` | §4 |
|
|
437
|
+
| 필요한 검색 조건만 칩으로 골라 붙이게 한다 | `SChipFilter` | §3-7-11 |
|
|
438
|
+
|
|
439
|
+
#### B. 정보를 읽게 보여준다
|
|
440
|
+
|
|
441
|
+
| 하려는 일 | 컴포넌트 | 갈림 |
|
|
442
|
+
| --- | --- | --- |
|
|
443
|
+
| 여러 건을 여러 열로 보여주고 열끼리 비교하게 한다 | `STable` | §3-7-6 |
|
|
444
|
+
| 항목 하나의 속성들을 `라벨: 값` 으로 보여준다 | `SKeyValueTable` | §4-4 |
|
|
445
|
+
| 한 줄로 읽히는 항목을 세로로 나열한다 | `SList` + `SListItem` | §3-7-6 |
|
|
446
|
+
| 나열한 항목을 펼쳐 하위 내용을 보여준다 | `SExpansionList` + `SExpansionItem` | §3-7-7 |
|
|
447
|
+
| 부모-자식 계층을 들여쓰기로 보여준다 | `STree` | §3-7-7 |
|
|
448
|
+
| 사용자가 순서를 드래그로 바꾸게 한다 | `SDraggableList` + `SDraggableItem` | §3-7-6 |
|
|
449
|
+
| 표 위에 건수 요약과 액션을 얹는다 | `STableBar` | §4-2 |
|
|
450
|
+
| 상태·분류를 라벨로 찍는다 | `STag` | §3-1 |
|
|
451
|
+
| 색 점만으로 상태를 찍는다 | `SBadge` | §3-1 |
|
|
452
|
+
| 아이콘을 넣는다 | `SIcon` | |
|
|
453
|
+
| 문장 안에서 다른 화면으로 보낸다 | `STextLink` | §3-5-6 |
|
|
454
|
+
|
|
455
|
+
#### C. 동작을 실행시킨다
|
|
456
|
+
|
|
457
|
+
| 하려는 일 | 컴포넌트 | 갈림 |
|
|
458
|
+
| --- | --- | --- |
|
|
459
|
+
| 라벨이 있는 일반 액션을 준다 | `SButton` | §3-5 |
|
|
460
|
+
| 아이콘 하나로 뜻이 통하는 부가 조작을 준다 | `SGhostButton` | §3-5-5 |
|
|
461
|
+
| 한 버튼에 여러 선택지를 매단다 | `SDropdownButton` | §3-5-4 |
|
|
462
|
+
|
|
463
|
+
#### D. 화면을 담고 나눈다
|
|
464
|
+
|
|
465
|
+
| 하려는 일 | 컴포넌트 | 갈림 |
|
|
466
|
+
| --- | --- | --- |
|
|
467
|
+
| 앱 셸(상단바 + 내비 + 본문)을 세운다 | `SLayout` | §4-1 |
|
|
468
|
+
| 좌측 내비게이션을 만든다 | `SGnb` | §4-1 |
|
|
469
|
+
| 페이지 본문을 담는다 (패딩·스크롤) | `SPage` | §4-1 |
|
|
470
|
+
| 페이지 제목(+ 서브 텍스트·뒤로가기·우측 슬롯)을 만든다 | `SPage` 의 `header` prop | §4-1 |
|
|
471
|
+
| 제목 있는 섹션으로 묶는다 | `SSectionHeaderCard` | §3-7-8 |
|
|
472
|
+
| 제목 없이 흰 면으로만 묶는다 | `SCard` | §3-7-8 |
|
|
473
|
+
| 가로선으로 끊는다 | `SDivider` | §3-6 |
|
|
474
|
+
| 사용자가 영역 크기를 조절하게 한다 | `SSplitter` | §3-6 |
|
|
475
|
+
| 특정 영역 안에서만 스크롤시킨다 | `SScrollArea` | |
|
|
476
|
+
|
|
477
|
+
#### E. 다른 곳으로 이동시킨다
|
|
478
|
+
|
|
479
|
+
| 하려는 일 | 컴포넌트 | 갈림 |
|
|
480
|
+
| --- | --- | --- |
|
|
481
|
+
| 같은 화면에서 보는 관점을 바꾼다 | `STabs` | §3-7-2 |
|
|
482
|
+
| 긴 목록을 페이지로 끊는다 | `SPagination` | §4-2 |
|
|
483
|
+
| 여러 단계의 진행 위치를 보여준다 | `SStepper` | |
|
|
484
|
+
|
|
485
|
+
#### F. 흐름을 끊고 띄운다
|
|
486
|
+
|
|
487
|
+
> 이 그룹은 **전부 §3-3-1 판별 순서를 먼저 밟는다.** 아래는 그 결과를 되짚는 표다.
|
|
488
|
+
|
|
489
|
+
| 하려는 일 | 컴포넌트 | 갈림 |
|
|
490
|
+
| --- | --- | --- |
|
|
491
|
+
| 실행 여부만 확정받는다 | `SModal.confirm()` | §3-3-1 |
|
|
492
|
+
| 모달 안에서 작성·선택하게 한다 | `SActionModal` + `SModal.create()` | §3-3-1 |
|
|
493
|
+
| 띄우는 것 자체가 하나의 화면이다 | `SPopup` | §3-3-1 |
|
|
494
|
+
| 화면 옆에서 밀려 나오는 작업 패널을 연다 | `SDrawer` | §3-3-5 |
|
|
495
|
+
| 확인 다이얼로그를 화면에 직접 배치한다 | `SConfirmModal` | §3-3-4 |
|
|
496
|
+
| 클릭하면 상호작용 가능한 작은 콘텐츠를 띄운다 | `SPopover` | §3-3 |
|
|
497
|
+
| hover 하면 짧은 설명을 띄운다 | `STooltip` | §3-3 |
|
|
498
|
+
| 임의 요소에 붙는 저수준 레이어가 필요하다 | `SPortal` | §3-7-10 |
|
|
499
|
+
|
|
500
|
+
#### G. 알리고 안내한다
|
|
501
|
+
|
|
502
|
+
| 하려는 일 | 컴포넌트 | 갈림 |
|
|
503
|
+
| --- | --- | --- |
|
|
504
|
+
| 화면에 상시 노출되는 안내·경고를 둔다 | `SCallout` | §3-2 |
|
|
505
|
+
| 작업 결과를 일시적으로 알린다 | `SToast` | §3-2 |
|
|
506
|
+
| 기능 온보딩·도움말을 붙인다 | `SGuide` | §3-2 |
|
|
507
|
+
|
|
508
|
+
#### H. 기다리게 한다
|
|
509
|
+
|
|
510
|
+
| 하려는 일 | 컴포넌트 | 갈림 |
|
|
511
|
+
| --- | --- | --- |
|
|
512
|
+
| 화면 전체를 잠그고 기다리게 한다 | `SLoadingModal` (또는 `SModal.loading()`) | §3-2 |
|
|
513
|
+
| 특정 영역만 덮고 기다리게 한다 | `SLoadingContainer` | §3-2 |
|
|
514
|
+
| 진행률을 가로 막대로 보여준다 | `SLinearProgress` | §3-7-9 |
|
|
515
|
+
| 진행률·대기를 원형으로 보여준다 | `SCircleProgress` | §3-7-9 |
|
|
516
|
+
|
|
517
|
+
#### I. 앱을 켤 때 한 번만 (§4-1)
|
|
518
|
+
|
|
519
|
+
| 하려는 일 | 컴포넌트 | 갈림 |
|
|
520
|
+
| --- | --- | --- |
|
|
521
|
+
| `SModal.*` 로 띄운 모달이 그려질 자리를 만든다 | `SModalOutlet` | §4-1 |
|
|
522
|
+
| `SToast` 가 그려질 자리를 만든다 | `SToastContainer` | §4-1 |
|
|
523
|
+
|
|
324
524
|
### 3-1. 라벨/표시류 — STag vs SBadge vs SChip
|
|
325
525
|
|
|
326
526
|
| 상황 | 사용 |
|
|
@@ -762,19 +962,153 @@ const columns: STableColumn[] = [
|
|
|
762
962
|
| `separator-class` / `separator-style` | `dividerClassName` / `dividerStyle` |
|
|
763
963
|
| `reverse` · `dark` | 없음 — 모델은 항상 첫 패널 크기다 |
|
|
764
964
|
|
|
965
|
+
### 3-7. 나머지 판별 — 입력·목록·컨테이너
|
|
966
|
+
|
|
967
|
+
> §3-0 라우팅에서 이 절을 가리키는 자리들이다. <!-- TODO(디자인): 전체 검수·확정 -->
|
|
968
|
+
|
|
969
|
+
#### 3-7-1. SInput vs STextarea
|
|
970
|
+
|
|
971
|
+
**줄 수가 아니라 값의 성격으로 고른다.** 값의 길이를 미리 알 수 있으면 `SInput`, 없으면 `STextarea` 다.
|
|
972
|
+
|
|
973
|
+
| 값 | 사용 |
|
|
974
|
+
| --- | --- |
|
|
975
|
+
| 이름·코드·전화번호·URL 처럼 형식이 정해진 값 | `SInput` |
|
|
976
|
+
| 메모·사유·설명처럼 길이가 예측되지 않는 문장 | `STextarea` |
|
|
977
|
+
|
|
978
|
+
값이 길어질 수 있는데 `SInput` 을 쓰면 사용자가 자기가 쓴 것을 다시 읽지 못한다 — 한 줄 안에서 좌우로 스크롤해야 하기 때문이다. 반대로 짧은 값에 `STextarea` 를 쓰면 빈 공간이 남아 입력량을 잘못 기대하게 한다.
|
|
979
|
+
|
|
980
|
+
#### 3-7-2. 하나를 고르게 하는 다섯 — SSelect vs SRadioGroup vs SRadioButton vs STabs vs SRadio
|
|
981
|
+
|
|
982
|
+
**먼저 "고르면 무엇이 바뀌는가"를 본다.**
|
|
983
|
+
|
|
984
|
+
1. **화면의 내용이 통째로 바뀐다** → `STabs`. 값을 고르는 게 아니라 보는 관점을 바꾸는 것이다. 폼 값이 아니다.
|
|
985
|
+
2. 그 밖에는 폼 값이므로 **선택지 수와 노출 여부**로 고른다.
|
|
986
|
+
|
|
987
|
+
| 선택지 | 사용 |
|
|
988
|
+
| --- | --- |
|
|
989
|
+
| 6개 이상, 또는 서버에서 오는 동적 목록 | `SSelect` |
|
|
990
|
+
| 2~5개 고정 + 선택지를 항상 보여야 함 | `SRadioGroup` |
|
|
991
|
+
| 2~4개 + 짧은 라벨의 배타적 모드 전환 (세그먼트) | `SRadioButton` |
|
|
992
|
+
|
|
993
|
+
- **`SRadio` 를 직접 나열하지 않는다.** 그룹 간격은 `SRadioGroup` 이 맞춘다 (§2-2). `SRadio` 단독은 `SRadioGroup` 이 만들 수 없는 배치 — 표 셀 안에 행마다 하나씩 놓는 경우 — 에만 쓴다.
|
|
994
|
+
- `SRadioButton` 은 `options` 를 통째로 받는 세그먼트 컨트롤이라 `SRadio` 를 여러 개 넣는 게 아니다.
|
|
995
|
+
|
|
996
|
+
#### 3-7-3. 켜고 끄는 셋 — SCheckbox vs SSwitch vs SToggle
|
|
997
|
+
|
|
998
|
+
셋 다 on/off 지만 **값이 언제 반영되는지**가 다르다. 이걸 틀리면 사용자가 저장 버튼을 찾다가 못 찾거나, 눌렀는데 반영이 안 돼 다시 누른다.
|
|
999
|
+
|
|
1000
|
+
| 판별 | 사용 |
|
|
1001
|
+
| --- | --- |
|
|
1002
|
+
| **폼 값으로 제출된다** (저장 버튼을 눌러야 반영) | `SCheckbox` |
|
|
1003
|
+
| **누르는 즉시 반영된다** (저장 버튼 없음) | `SSwitch` |
|
|
1004
|
+
| **목록을 좁히는 필터** (여러 개를 나란히 켜고 끔) | `SToggle` |
|
|
1005
|
+
|
|
1006
|
+
- `SCheckbox` 만 다중 선택(배열)과 `indeterminate`(부분 선택)를 갖는다. 전체 선택 체크박스는 반드시 `SCheckbox` 다.
|
|
1007
|
+
- 약관 동의처럼 **제출 시점에 값이 필요한 것은 항상 `SCheckbox`** 다 — 모양이 스위치에 가까워 보여도 그렇다.
|
|
1008
|
+
- `SToggle` 은 알약형 버튼이라 여러 개를 가로로 늘어놓는 필터 자리에 맞는다. 설정 화면의 on/off 한 줄에는 쓰지 않는다.
|
|
1009
|
+
|
|
1010
|
+
#### 3-7-4. 날짜 셋 — SDatePicker vs SDateRangePicker vs SCalendar
|
|
1011
|
+
|
|
1012
|
+
| 판별 | 사용 |
|
|
1013
|
+
| --- | --- |
|
|
1014
|
+
| 날짜 **하나**를 값으로 받는다 | `SDatePicker` |
|
|
1015
|
+
| **시작~종료** 를 값으로 받는다 | `SDateRangePicker` |
|
|
1016
|
+
| 달력 격자 **자체가 화면 콘텐츠** 다 (일정·이벤트 보기) | `SCalendar` |
|
|
1017
|
+
|
|
1018
|
+
- **기간을 `SDatePicker` 두 개로 만들지 않는다.** 시작이 종료보다 뒤인 입력을 막는 검증과 한쪽만 고른 중간 상태 처리가 `SDateRangePicker` 안에 이미 있다. 두 개로 쪼개면 그게 전부 앱 몫이 된다.
|
|
1019
|
+
- `SDatePicker`·`SDateRangePicker` 는 내부적으로 `SCalendar` 를 팝오버로 띄운다. 값을 받는 자리에 `SCalendar` 를 직접 쓰지 않는다.
|
|
1020
|
+
|
|
1021
|
+
#### 3-7-5. SField 를 직접 쓰는 경우
|
|
1022
|
+
|
|
1023
|
+
**거의 없다.** `SInput`·`SNumberInput`·`STextarea`·`SSelect`·날짜/시간 피커는 이미 내부에서 `SField` 를 쓰고 있어서 `label`·`required`·에러 표시를 자기 prop 으로 받는다. 그 위에 `SField` 를 한 겹 더 감싸면 라벨이 두 번 나온다.
|
|
1024
|
+
|
|
1025
|
+
직접 쓰는 경우는 하나뿐이다 — **디자인 시스템에 없는 컨트롤**에 다른 필드와 똑같은 라벨·필수·에러 모양을 붙일 때.
|
|
1026
|
+
|
|
1027
|
+
#### 3-7-6. 여러 건을 나열하는 셋 — STable vs SList vs SDraggableList
|
|
1028
|
+
|
|
1029
|
+
| 판별 | 사용 |
|
|
1030
|
+
| --- | --- |
|
|
1031
|
+
| 열이 둘 이상이고 **열끼리 값을 비교**한다 (정렬·합계·자릿수 맞춤) | `STable` |
|
|
1032
|
+
| 한 항목이 **한 줄로 읽힌다** (제목 + 보조 텍스트) | `SList` + `SListItem` |
|
|
1033
|
+
| **순서 자체가 데이터**라 사용자가 끌어서 바꾼다 | `SDraggableList` + `SDraggableItem` |
|
|
1034
|
+
|
|
1035
|
+
- **항목 하나의 속성을 나열하는 것은 목록이 아니다.** `라벨: 값` 이 세로로 쌓이는 것은 `SKeyValueTable` 이다 (§4-4).
|
|
1036
|
+
- `SList` 는 레이아웃만 담당한다. 펼침·단일 선택 동작이 필요하면 `SExpansionList` 다 (§3-7-7).
|
|
1037
|
+
- **항목 사이 구분선은 리스트가 그린다.** `SListItem`·`SExpansionItem`·`SDraggableItem` 셋 다 스스로 구분선을 그리지 않으므로, 목록을 감싸는 `SList`·`SExpansionList`·`SDraggableList` 에 `separator` 를 준다 — 아이템에 `border-b` 를 직접 붙이지 않는다. 테두리형(`bordered`)은 테두리가 구분 역할을 하므로 `separator` 대신 `useGap` 으로 띄운다.
|
|
1038
|
+
- **`SListItem` 은 기본이 표시 전용이다.** 눌러서 이동·선택하게 하려면 `clickable` 을 준다 — hover·`selected`·`interaction="chevron"` 표현이 전부 여기에 딸려 있어서, `clickable` 없이 `selected` 만 주면 선택 표시가 나오지 않는다. `SExpansionList` 는 선택을 자기가 관리하므로 자식 아이템을 알아서 클릭 가능하게 만든다.
|
|
1039
|
+
|
|
1040
|
+
```tsx
|
|
1041
|
+
✅ <SList separator><SListItem title="일반 문의" />…</SList> {/* 줄로 구분되는 목록 */}
|
|
1042
|
+
✅ <SList useGap><SListItem title="일반 문의" bordered />…</SList> {/* 카드처럼 떨어진 목록 */}
|
|
1043
|
+
✅ <SListItem title="일반 문의" clickable selected onClick={…} /> {/* 눌러서 고르는 목록 */}
|
|
1044
|
+
❌ <SList><SListItem title="일반 문의" className="border-b" />…</SList>
|
|
1045
|
+
❌ <SListItem title="일반 문의" selected /> {/* clickable 없으면 선택 표시가 안 나온다 */}
|
|
1046
|
+
```
|
|
1047
|
+
|
|
1048
|
+
#### 3-7-7. 펼치는 셋 — SExpansionItem vs SExpansionList vs STree
|
|
1049
|
+
|
|
1050
|
+
| 판별 | 사용 |
|
|
1051
|
+
| --- | --- |
|
|
1052
|
+
| 항목들이 **서로 독립적으로** 여닫힌다 (여러 개 동시에 열려도 됨) | `SExpansionItem` 단독 |
|
|
1053
|
+
| **한 번에 하나만** 열려야 한다 (아코디언) | `SExpansionList` + `SExpansionItem` |
|
|
1054
|
+
| **부모-자식 계층 자체**를 보여줘야 한다 (2단 이상, 연결선) | `STree` |
|
|
1055
|
+
|
|
1056
|
+
`SExpansionList` 는 depth 별 단일 확장·선택을 관리하는 wrapper 다. 직접 `useState` 로 "열린 항목 하나"를 들고 있지 않는다. 항목 사이 구분선은 여기서도 리스트가 그린다 — `separator` 를 준다 (§3-7-6).
|
|
1057
|
+
|
|
1058
|
+
#### 3-7-8. SCard vs SSectionHeaderCard
|
|
1059
|
+
|
|
1060
|
+
| 판별 | 사용 |
|
|
1061
|
+
| --- | --- |
|
|
1062
|
+
| **제목이 붙는 섹션** 이다 | `SSectionHeaderCard` |
|
|
1063
|
+
| 제목 없이 **흰 면만** 필요하다 (요약 타일, 빈 상태 박스) | `SCard` |
|
|
1064
|
+
|
|
1065
|
+
- 페이지 골격에서 콘텐츠를 묶는 섹션은 **사실상 전부 `SSectionHeaderCard`** 다 (§4-4·§4-5). 제목·필수 표시·도움말·헤더 우측 액션이 전부 여기 붙는다.
|
|
1066
|
+
- **카드 안에 카드를 겹치지 않는다.** 섹션 안을 더 나눠야 하면 `SDivider` 로 끊거나(§3-6) 섹션을 둘로 분리한다.
|
|
1067
|
+
- 안쪽 여백은 `SSectionHeaderCard.Body` 의 `padding` prop 으로 준다 — `p-sd-*` 를 직접 주지 않는다 (§2-2).
|
|
1068
|
+
|
|
1069
|
+
#### 3-7-9. SLinearProgress vs SCircleProgress
|
|
1070
|
+
|
|
1071
|
+
| 판별 | 사용 |
|
|
1072
|
+
| --- | --- |
|
|
1073
|
+
| 진행률(%)이 있고 가로로 길게 놓을 자리가 있다 | `SLinearProgress` |
|
|
1074
|
+
| 자리가 좁다, 또는 **끝나는 시점을 모른다**(대기) | `SCircleProgress` |
|
|
1075
|
+
|
|
1076
|
+
`SCircleProgress` 는 `indeterminate` 로 두면 스피너가 된다. **다만 화면이나 영역을 막아야 하는 상황이면 progress 가 아니라 `SLoadingModal`·`SLoadingContainer` 다** (§3-2) — 진행 표시와 입력 차단은 다른 일이고, 막지 않으면 사용자가 로딩 중에 또 누른다.
|
|
1077
|
+
|
|
1078
|
+
#### 3-7-10. SPortal — 직접 쓸 일이 거의 없다
|
|
1079
|
+
|
|
1080
|
+
`STooltip`·`SPopover`·`SSelect`·날짜 피커가 내부에서 쓰는 저수준 레이어다. 앵커에 붙여 띄우는 동작이 필요하면 **먼저 §3-3 에서 대응 컴포넌트를 찾는다.** `SPortal` 을 직접 쓰는 것은 그 넷 중 어느 것도 아닌 새로운 부착형 레이어를 만들 때뿐이고, 그때도 모달 안에서 열릴 수 있다면 소속 컨테이너를 맞춰야 한다.
|
|
1081
|
+
|
|
1082
|
+
#### 3-7-11. 필터 둘 — SKeyValueTable vs SChipFilter
|
|
1083
|
+
|
|
1084
|
+
둘 다 §2-0 블록 순서의 **3번 자리(필터)** 에 놓이고, 한 화면에 둘을 같이 두지 않는다.
|
|
1085
|
+
|
|
1086
|
+
| 상황 | 컴포넌트 |
|
|
1087
|
+
| --- | --- |
|
|
1088
|
+
| 조건이 정해져 있고 **항상 다 보여야** 한다 (기본 형태) | `SKeyValueTable` |
|
|
1089
|
+
| 조건 후보가 많아 **쓸 것만 골라 붙이고** 나머지는 숨겨야 한다 | `SChipFilter` |
|
|
1090
|
+
|
|
1091
|
+
- **기본은 `SKeyValueTable` 이다** (§4-2). 조건이 대여섯 개 이하로 고정이면 표로 펼쳐 두는 편이 한눈에 읽힌다.
|
|
1092
|
+
- `SChipFilter` 는 조건을 **칩 한 줄**로 접고, "필터 추가" 로 필요한 것만 꺼내 쓰게 한다. 칩을 누르면 편집 팝오버가 열리고, 날짜는 프리셋(오늘·지난 7일·사용자 지정)으로 고른다. 조건 후보가 많은 목록 화면에서 필터가 화면을 세로로 잡아먹는 것을 막는 용도다.
|
|
1093
|
+
- 검색 실행 시점이 다르다 — `SKeyValueTable` 필터는 앱이 검색 버튼을 직접 놓지만, `SChipFilter` 는 편집 팝오버가 닫히거나 "검색" 을 누를 때 `onSearch` 가 값 맵과 함께 호출된다. 값이 바뀌지 않았으면 호출되지 않는다.
|
|
1094
|
+
- 함께 걸어야 하는 조건(예: 기간 중 하나는 필수)이 있으면 `fields` 를 그룹으로 넘긴다. 규칙을 못 채운 동안 경고 툴팁이 떠 있고 `onSearch` 가 막힌다.
|
|
1095
|
+
|
|
765
1096
|
---
|
|
766
1097
|
|
|
767
1098
|
## 4. 페이지 레시피 — 표준 골격
|
|
768
1099
|
|
|
769
1100
|
> 새 페이지는 반드시 아래 골격에서 시작한다. 임의 골격을 발명하지 않는다.
|
|
770
1101
|
>
|
|
1102
|
+
> **아래 레시피는 §2-0 조합 문법으로 유도된 결과다.** 여기 없는 화면(대시보드·설정·마법사 등)을 만들 때는 임의로 짜지 말고 §2-0 의 층 구조·포함 규칙·블록 순서로 직접 유도한다.
|
|
1103
|
+
>
|
|
771
1104
|
> **핵심 원칙 — 표 형태의 정보는 `SKeyValueTable` 로 만든다.** 필터·등록/수정 폼·상세 정보가 모두 여기 해당한다.
|
|
772
1105
|
> `SField` 컨트롤을 `div` 로 직접 나열해 폼을 만들지 않는다.
|
|
1106
|
+
> (필터만 예외가 하나 있다 — 조건 후보가 많아 골라 붙이게 해야 하면 `SChipFilter` 다. §3-7-11)
|
|
773
1107
|
|
|
774
1108
|
### 4-1. 앱 셸 (모든 페이지 공통)
|
|
775
1109
|
|
|
776
1110
|
```tsx
|
|
777
|
-
import { SLayout, SGnb, SPage, type SGnbMenuItem } from 'sellmate-design-system-react';
|
|
1111
|
+
import { SLayout, SGnb, SPage, type SGnbMenuItem, type SPageHeaderProps } from 'sellmate-design-system-react';
|
|
778
1112
|
|
|
779
1113
|
const MENU: SGnbMenuItem[] = [
|
|
780
1114
|
{ label: '주문', value: 'orders', icon: 'bill' },
|
|
@@ -783,20 +1117,32 @@ const MENU: SGnbMenuItem[] = [
|
|
|
783
1117
|
|
|
784
1118
|
export default function AppShell({
|
|
785
1119
|
children,
|
|
1120
|
+
header,
|
|
786
1121
|
scrollEndSpacing,
|
|
787
|
-
}: { children: React.ReactNode; scrollEndSpacing?: boolean }) {
|
|
1122
|
+
}: { children: React.ReactNode; header?: SPageHeaderProps; scrollEndSpacing?: boolean }) {
|
|
788
1123
|
return (
|
|
789
1124
|
<SLayout type="box" header="fix">
|
|
790
1125
|
{/* type/header/folded 는 SLayout 에만 준다 — SGnb 는 context 에서 읽는다 */}
|
|
791
1126
|
<SGnb items={MENU} value={current} onValueChange={navigate} logo={<Logo />} />
|
|
792
1127
|
{/* 콘텐츠 패딩은 SPage 가 토큰으로 넣는다 — p-sd-* 로 덮어쓰지 않는다 */}
|
|
793
1128
|
{/* 스크롤 끝 여백도 SPage 가 넣는다. 끄는 건 페이지네이션 있는 목록뿐이라 페이지가 정한다 */}
|
|
794
|
-
|
|
1129
|
+
{/* header 는 페이지마다 달라 AppShell 이 그대로 받아 넘긴다 — 페이지 제목은 여기서 만들지 않는다 */}
|
|
1130
|
+
<SPage background="frame" scrollEndSpacing={scrollEndSpacing} header={header}>
|
|
1131
|
+
{children}
|
|
1132
|
+
</SPage>
|
|
795
1133
|
</SLayout>
|
|
796
1134
|
);
|
|
797
1135
|
}
|
|
798
1136
|
```
|
|
799
1137
|
|
|
1138
|
+
**페이지는 `AppShell` 을 직접 호출하며 자기 `header` 를 넘긴다** — SPage 는 셸 안에 하나뿐이므로, 페이지 제목이 페이지마다 다르다는 사실은 이렇게 프레임 컴포넌트를 통해 흘려보낸다(§4-2·§4-3·§4-4 참고).
|
|
1139
|
+
|
|
1140
|
+
**페이지 헤더(`header`) 사용 규칙 — 이 앱에서는 값이 아니라 값의 일관성이 규칙이다.**
|
|
1141
|
+
|
|
1142
|
+
- **`fix` 는 앱 전체에서 하나로 고정한다.** 어떤 페이지는 `fix: true`(바), 다른 페이지는 `fix: false`(투명) 로 섞어 쓰지 않는다. 프로젝트에서 하나를 고르면(예: 전부 `fix: true`) 모든 `header` 가 그 값을 쓴다.
|
|
1143
|
+
- **`slot`·`onBack` 도 페이지 성격이 실제로 다른 경우가 아니면 있는 대로 통일한다.** "목록 페이지엔 없고 상세·등록 페이지엔 있다"처럼 화면 종류에 따라 갈리는 것은 허용되지만, 같은 종류의 화면끼리는 임의로 넣었다 뺐다 하지 않는다.
|
|
1144
|
+
- **`slot` 은 `ReactNode` 를 그대로 받지만, 원칙은 `size="sm"` 버튼 위주로만 채운다.** `STextLink`·복잡한 커스텀 마크업을 슬롯에 넣지 않는다 — 그 이상이 필요하면 페이지 헤더가 아니라 §4-2 의 `STableBar` 처럼 본문 쪽 액션 자리를 쓴다.
|
|
1145
|
+
|
|
800
1146
|
**GNB 폭을 사용자가 조절하게 하려면 `SGnb` 에 `resizable` 을 준다.** 메뉴 오른쪽 경계가 조절선이 되고, 레일 폭은 고정된 채 메뉴 컬럼만 늘고 준다. 범위는 컴포넌트가 정하므로 숫자를 직접 주지 않는다.
|
|
801
1147
|
|
|
802
1148
|
```tsx
|
|
@@ -854,20 +1200,38 @@ import { SModalOutlet } from 'sellmate-design-system-react';
|
|
|
854
1200
|
</SLayout>
|
|
855
1201
|
```
|
|
856
1202
|
|
|
1203
|
+
**메뉴 목록과 함께 스크롤되면 안 되는 것은 `SGnb` 의 위아래 고정 슬롯에 둔다.** 레일과 메뉴에 각각 위(`railTop`·`menuTop`)와 아래(`railFooter`·`menuFooter`) 슬롯이 있다. 아이템이 많아 넘치면 **목록만 스크롤되고 이 슬롯들은 제자리에 남는다** — 메뉴 검색, 워크스페이스 전환, 계정 행처럼 항상 보여야 하는 것이 여기 온다. 레일 슬롯은 `useRail` 일 때만, 메뉴 슬롯은 깔 메뉴가 있을 때만 렌더된다.
|
|
1204
|
+
|
|
1205
|
+
**접으면 레일·메뉴가 통째로 빠져나가면서 그 슬롯들도 함께 사라진다.** 접힌 상태에서도 남겨야 할 것은 `foldedTop`·`foldedFooter` 로 따로 준다 — `header="fix"` 로 접혔을 때만 나타나며, 폭이 좁은 폴드 레일이므로 아이콘 버튼 하나 정도로 줄인다.
|
|
1206
|
+
|
|
1207
|
+
```tsx
|
|
1208
|
+
{/* 접히면 menuTop·menuFooter 가 함께 빠지므로, 폴드 레일에 남길 것만 foldedTop 으로 따로 준다 */}
|
|
1209
|
+
<SGnb
|
|
1210
|
+
items={MENU} value={current} onValueChange={navigate} useRail
|
|
1211
|
+
menuTop={<SInput value={keyword} onValueChange={setKeyword} placeholder="메뉴 검색" />}
|
|
1212
|
+
menuFooter={<AccountRow />}
|
|
1213
|
+
foldedTop={<SGhostButton icon="search" size="sm" ariaLabel="메뉴 검색" onClick={openSearch} />}
|
|
1214
|
+
/>
|
|
1215
|
+
```
|
|
1216
|
+
|
|
1217
|
+
슬롯 안쪽 여백은 **슬롯 내용이 직접 갖는다** — 컴포넌트는 자리만 잡는다(폴드 슬롯만 좁은 폭에 맞춰 가운데 정렬한다). 메뉴 폭은 `resizable` 로 바뀔 수 있으므로 슬롯 내용은 고정 폭 대신 `w-full` 로 따라가게 둔다.
|
|
1218
|
+
|
|
857
1219
|
### 4-2. 목록 페이지 (필터 + 테이블)
|
|
858
1220
|
|
|
859
|
-
구조: **페이지 헤더(
|
|
1221
|
+
구조: **페이지 헤더(`AppShell` 의 `header` prop, 가이드 등 부가 액션은 slot) → 필터(`SKeyValueTable`) → `STableBar` → `STable`**
|
|
1222
|
+
|
|
1223
|
+
필터 자리는 `SChipFilter` 로 바꿔 놓을 수 있다 — 조건 후보가 많아 쓸 것만 골라 붙이게 하는 화면이면 그쪽이다 (§3-7-11). 나머지 골격은 같다.
|
|
860
1224
|
|
|
861
1225
|
액션 버튼의 위치가 핵심이다:
|
|
862
1226
|
|
|
863
|
-
- **페이지 제목 줄에는
|
|
1227
|
+
- **페이지 제목 줄에는 이 페이지의 주요 액션을 두지 않는다.** 부가적인 것만 `header.slot` 에 `SButton size="sm"` 으로 온다 (§4-1 "페이지 헤더 사용 규칙").
|
|
864
1228
|
- **주요 액션(등록 등)은 `STableBar` 의 `rightActions`** 에 둔다.
|
|
865
1229
|
- **선택 상태 액션(선택 삭제 등)은 `STableBar` 의 `actions`** 에 둔다. `actions` 슬롯은 건수 요약이 있으면 앞에 구분선(`SDivider`)을 **자동으로** 넣으므로 직접 구분선을 만들지 않는다.
|
|
866
|
-
- **페이지네이션이 있으면 스크롤 끝 여백을 끈다** —
|
|
1230
|
+
- **페이지네이션이 있으면 스크롤 끝 여백을 끈다** — `AppShell` 에 `scrollEndSpacing={false}` 를 넘긴다 (§2-2). 페이지네이션이 이미 "여기서 끝"을 알려준다.
|
|
867
1231
|
|
|
868
1232
|
```tsx
|
|
869
1233
|
import {
|
|
870
|
-
SButton,
|
|
1234
|
+
SButton, SKeyValueTable, STableBar, STable, STag,
|
|
871
1235
|
type STableColumn, type SRow, type SKeyValueField,
|
|
872
1236
|
} from 'sellmate-design-system-react';
|
|
873
1237
|
|
|
@@ -905,55 +1269,58 @@ export default function ProductListPage() {
|
|
|
905
1269
|
const [selected, setSelected] = useState<SRow[]>([]);
|
|
906
1270
|
|
|
907
1271
|
return (
|
|
908
|
-
<
|
|
909
|
-
{
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
<
|
|
913
|
-
|
|
1272
|
+
<AppShell
|
|
1273
|
+
header={{
|
|
1274
|
+
title: '상품 목록',
|
|
1275
|
+
// 이 페이지의 주요 액션이 아니라 부가 액션 — slot 은 sm 버튼으로만 채운다
|
|
1276
|
+
slot: <SButton size="sm" color="neutral" outline label="이용 가이드" onClick={openGuide} />,
|
|
1277
|
+
}}
|
|
1278
|
+
scrollEndSpacing={false} // 페이지네이션이 있으므로 끈다
|
|
1279
|
+
>
|
|
1280
|
+
<div className="flex flex-col gap-sd-12">
|
|
1281
|
+
{/* 필터 — search 를 켜면 우측에 검색 패널이 붙는다 */}
|
|
1282
|
+
<SKeyValueTable
|
|
1283
|
+
fields={filterFields}
|
|
1284
|
+
values={filters}
|
|
1285
|
+
search
|
|
1286
|
+
onChange={({ values }) => setFilters(values)}
|
|
1287
|
+
onSearch={fetchList}
|
|
1288
|
+
/>
|
|
914
1289
|
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
rowKey="id"
|
|
943
|
-
selectable
|
|
944
|
-
selected={selected}
|
|
945
|
-
onSelectedChange={setSelected}
|
|
946
|
-
pagination={{ currentPage, lastPage }}
|
|
947
|
-
isLoading={isLoading}
|
|
948
|
-
/>
|
|
949
|
-
</div>
|
|
1290
|
+
{/* 툴바 — 좌: 건수 + (구분선 자동) + 선택 액션 / 우: 주요 액션 */}
|
|
1291
|
+
<STableBar
|
|
1292
|
+
total={total}
|
|
1293
|
+
selected={selected.length}
|
|
1294
|
+
actions={
|
|
1295
|
+
/* 선택 항목 단위 파괴 액션 → danger outline (§3-5-3) */
|
|
1296
|
+
<SButton size="sm" color="danger" outline label="선택 삭제"
|
|
1297
|
+
disabled={!selected.length} onClick={removeSelected} />
|
|
1298
|
+
}
|
|
1299
|
+
rightActions={
|
|
1300
|
+
/* 이 페이지의 유일한 primary 채움 (§3-5-1) */
|
|
1301
|
+
<SButton size="sm" label="상품 등록" onClick={goCreate} />
|
|
1302
|
+
}
|
|
1303
|
+
/>
|
|
1304
|
+
|
|
1305
|
+
<STable
|
|
1306
|
+
columns={columns}
|
|
1307
|
+
rows={rows}
|
|
1308
|
+
rowKey="id"
|
|
1309
|
+
selectable
|
|
1310
|
+
selected={selected}
|
|
1311
|
+
onSelectedChange={setSelected}
|
|
1312
|
+
pagination={{ currentPage, lastPage }}
|
|
1313
|
+
isLoading={isLoading}
|
|
1314
|
+
/>
|
|
1315
|
+
</div>
|
|
1316
|
+
</AppShell>
|
|
950
1317
|
);
|
|
951
1318
|
}
|
|
952
1319
|
```
|
|
953
1320
|
|
|
954
1321
|
### 4-3. 폼 페이지 (등록/수정)
|
|
955
1322
|
|
|
956
|
-
구조: **페이지 제목 → `SForm` + `SKeyValueTable` → 하단 버튼**
|
|
1323
|
+
구조: **페이지 제목(`AppShell` 의 `header` prop) → `SForm` + `SKeyValueTable` → 하단 버튼**
|
|
957
1324
|
|
|
958
1325
|
- 필드를 `div` 로 나열하지 않고 **`SKeyValueTable` 의 행으로 구성**한다.
|
|
959
1326
|
- 검증 규칙은 각 field 의 `options.rules` 로 넘긴다. `SForm` 이 하위 컨트롤을 자동 수집해 submit 시 일괄 검증하고, 실패 시 첫 실패 필드로 포커스를 옮긴다.
|
|
@@ -991,33 +1358,33 @@ export default function ProductCreatePage() {
|
|
|
991
1358
|
const [values, setValues] = useState<Record<string, unknown>>({});
|
|
992
1359
|
|
|
993
1360
|
return (
|
|
994
|
-
<
|
|
995
|
-
<
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1361
|
+
<AppShell header={{ title: '상품 등록', onBack: goBack }}>
|
|
1362
|
+
<div className="flex flex-col gap-sd-12">
|
|
1363
|
+
<SForm ref={formRef} formClass="flex flex-col gap-sd-12" onSubmit={save}>
|
|
1364
|
+
<SKeyValueTable
|
|
1365
|
+
fields={fields}
|
|
1366
|
+
values={values}
|
|
1367
|
+
onChange={({ values }) => setValues(values)}
|
|
1368
|
+
/>
|
|
1369
|
+
|
|
1370
|
+
{/* 하단 버튼은 양끝으로 벌린다. 부가 요소(체크박스 등)는 저장 바로 왼쪽 */}
|
|
1371
|
+
<div className="flex items-center justify-between">
|
|
1372
|
+
<SButton type="button" color="neutral" outline label="취소" onClick={goBack} />
|
|
1373
|
+
<div className="flex items-center gap-sd-8">
|
|
1374
|
+
<SCheckbox label="계속 등록하기" value={keepOpen} onValueChange={v => setKeepOpen(v as boolean)} />
|
|
1375
|
+
<SButton type="submit" label="저장" />
|
|
1376
|
+
</div>
|
|
1010
1377
|
</div>
|
|
1011
|
-
</
|
|
1012
|
-
</
|
|
1013
|
-
</
|
|
1378
|
+
</SForm>
|
|
1379
|
+
</div>
|
|
1380
|
+
</AppShell>
|
|
1014
1381
|
);
|
|
1015
1382
|
}
|
|
1016
1383
|
```
|
|
1017
1384
|
|
|
1018
1385
|
### 4-4. 상세(조회) 페이지
|
|
1019
1386
|
|
|
1020
|
-
구조: **페이지 헤더(
|
|
1387
|
+
구조: **페이지 헤더(`AppShell` 의 `header` prop, 목록에서 들어오는 뒤로가기는 onBack) → 섹션별 `SSectionHeaderCard` + `SKeyValueTable` → 하단 버튼**
|
|
1021
1388
|
|
|
1022
1389
|
- 조회 값은 `type: 'text'` 행으로 표시한다. **상태·분류 태그도 별도 영역이 아니라 표의 한 행**으로 넣는다 (`render` 에 `STag`).
|
|
1023
1390
|
- 행이 많아지면 **유형별로 섹션을 나누고, 각 섹션을 `SSectionHeaderCard` 로 감싼다.**
|
|
@@ -1051,38 +1418,39 @@ const priceFields: SKeyValueField[][] = [
|
|
|
1051
1418
|
|
|
1052
1419
|
export default function ProductDetailPage() {
|
|
1053
1420
|
return (
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1421
|
+
// 목록에서 들어온 상세 페이지 — onBack 으로 뒤로가기를 준다
|
|
1422
|
+
<AppShell header={{ fix: true, title: '클래식 셔츠', onBack: goList }}>
|
|
1423
|
+
<div className="flex flex-col gap-sd-12">
|
|
1424
|
+
<SSectionHeaderCard>
|
|
1425
|
+
<SSectionHeaderCard.Header title="기본 정보" marker thickness="accent" />
|
|
1426
|
+
<SSectionHeaderCard.Body>
|
|
1427
|
+
<SKeyValueTable fields={basicFields} values={product} />
|
|
1428
|
+
</SSectionHeaderCard.Body>
|
|
1429
|
+
</SSectionHeaderCard>
|
|
1430
|
+
|
|
1431
|
+
<SSectionHeaderCard>
|
|
1432
|
+
{/* 헤더 우측에 액션이 필요하면 slot 을 쓴다 */}
|
|
1433
|
+
<SSectionHeaderCard.Header
|
|
1434
|
+
title="가격 정보"
|
|
1435
|
+
marker
|
|
1436
|
+
helpText={['부가세 포함 금액입니다.']}
|
|
1437
|
+
slot={<SButton size="sm" color="secondary" label="이력" onClick={openHistory} />}
|
|
1438
|
+
/>
|
|
1439
|
+
<SSectionHeaderCard.Body>
|
|
1440
|
+
<SKeyValueTable fields={priceFields} values={product} />
|
|
1441
|
+
</SSectionHeaderCard.Body>
|
|
1442
|
+
</SSectionHeaderCard>
|
|
1443
|
+
|
|
1444
|
+
{/* 액션은 하단 — 목록(되돌리기)은 왼쪽 끝, 실행 액션은 오른쪽 끝 */}
|
|
1445
|
+
<div className="flex items-center justify-between">
|
|
1446
|
+
<SButton color="neutral" outline label="목록" onClick={goList} />
|
|
1447
|
+
<div className="flex items-center gap-sd-8">
|
|
1448
|
+
<SButton color="danger" outline label="삭제" onClick={confirmDelete} />
|
|
1449
|
+
<SButton label="수정" onClick={goEdit} />
|
|
1450
|
+
</div>
|
|
1083
1451
|
</div>
|
|
1084
1452
|
</div>
|
|
1085
|
-
</
|
|
1453
|
+
</AppShell>
|
|
1086
1454
|
);
|
|
1087
1455
|
}
|
|
1088
1456
|
```
|
|
@@ -1123,8 +1491,10 @@ export default function ProductDetailPage() {
|
|
|
1123
1491
|
- [ ] 자체 스크롤하는 패널의 하단에 `pb-[var(--cmp-pageBody-padding-scrollEnd)]` 이 있는가, 페이지네이션 있는 목록에서 `scrollEndSpacing={false}` 를 넘겼는가
|
|
1124
1492
|
- [ ] 같은 컴포넌트를 나열할 때 §2-2 그룹 간격을 썼는가 (체크박스 가로 `gap-sd-24` 등)
|
|
1125
1493
|
- [ ] 페이지가 §4의 표준 골격에서 시작했는가
|
|
1494
|
+
- [ ] `header.fix` 가 프로젝트 전체와 같은 값인가 (다른 페이지와 다르게 섞어 쓰지 않았는가, §4-1)
|
|
1495
|
+
- [ ] `header.slot` 을 채웠다면 `SButton size="sm"` 위주인가 (§4-1 "페이지 헤더 사용 규칙")
|
|
1126
1496
|
- [ ] 앱 셸이나 그 바깥에 `min-width`·`overflow-x` 를 직접 걸지 않았는가 (최소 너비는 `SLayout` 이 보장한다, §4-1)
|
|
1127
|
-
- [ ] 필터·폼·상세 정보를 `SKeyValueTable` 로 만들었는가 (컨트롤을 `div` 로 나열하지 않았는가)
|
|
1497
|
+
- [ ] 필터·폼·상세 정보를 `SKeyValueTable` 로 만들었는가 (컨트롤을 `div` 로 나열하지 않았는가). 필터를 `SChipFilter` 로 했다면 §3-7-11 의 판정을 거쳤는가
|
|
1128
1498
|
- [ ] 섹션 구분에 `SSectionHeaderCard` 를 썼는가 (직접 만든 카드가 아니라)
|
|
1129
1499
|
- [ ] 목록의 주요 액션이 `STableBar` 의 `rightActions` 에 있는가 (페이지 제목 줄이 아니라)
|
|
1130
1500
|
- [ ] 상태 표시에 `STag size="sm"` 을 썼는가
|
|
@@ -1142,6 +1512,12 @@ export default function ProductDetailPage() {
|
|
|
1142
1512
|
- [ ] 작업용 모달을 `SActionModal` + `SModal.create` 로 만들었는가 (직접 오버레이 ❌)
|
|
1143
1513
|
- [ ] 모달·드로어의 하단 버튼을 본문이 아니라 `button` · `footerLeft` prop 으로 넘겼는가 (§3-3-4)
|
|
1144
1514
|
- [ ] 앱 부트스트랩의 Provider 안쪽에 `<SModalOutlet />` 이 한 번 렌더되어 있는가 (§4-1 — 없으면 모달 안에서 앱 훅이 죽는다), 그 대신으로 모달 컴포넌트를 Provider 로 다시 감싸지 않았는가
|
|
1515
|
+
- [ ] 고른 컴포넌트를 §2-0 의 제 층에 놓았는가 (요소를 `SPage` 에 직접 놓지 않았는가, 블록을 `div` 로 감싸지 않았는가)
|
|
1516
|
+
- [ ] §2-0 포함 규칙을 지켰는가 (카드 안 카드 ❌, 표 셀 안 블록 ❌)
|
|
1517
|
+
- [ ] 블록 순서가 §2-0 순서와 맞는가 (제목 → 안내 → 필터 → 툴바 → 본문 → 페이지네이션 → 하단 액션)
|
|
1518
|
+
- [ ] 제목 위계가 층을 따라갔는가 (18 → 14 → 12, 건너뛰기 ❌), 섹션 제목 타이포를 `SSectionHeaderCard` 위에 덧씌우지 않았는가
|
|
1519
|
+
- [ ] 화면 요소마다 §3-0 라우팅에서 컴포넌트를 골랐는가 (직접 만들거나 비슷한 것으로 대체하지 않았는가)
|
|
1520
|
+
- [ ] §3-0 의 "갈림" 열이 가리킨 판별 절을 읽고 골랐는가 (§3-7-2 선택 컨트롤, §3-7-3 켜고 끄기 등)
|
|
1145
1521
|
- [ ] 상태 표시·알림·확인 다이얼로그가 §3의 선택 규칙을 따르는가
|
|
1146
1522
|
|
|
1147
1523
|
---
|
|
@@ -1269,6 +1645,10 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
1269
1645
|
| typo-table-header | 12px | 500 | 20px |
|
|
1270
1646
|
| typo-table-body | 12px | 400 | 20px |
|
|
1271
1647
|
| typo-table-accent | 12px | 700 | 20px |
|
|
1648
|
+
| typo-item-sm-default | 12px | 500 | 20px |
|
|
1649
|
+
| typo-item-sm-selected | 12px | 700 | 20px |
|
|
1650
|
+
| typo-item-md-default | 14px | 500 | 24px |
|
|
1651
|
+
| typo-item-md-selected | 14px | 700 | 24px |
|
|
1272
1652
|
|
|
1273
1653
|
### 시맨틱 색 토큰 (var() 참조 전용 — 예: bg-[var(--sys-color-bg-frame)])
|
|
1274
1654
|
--sys-color-bg-accent
|
|
@@ -1673,6 +2053,64 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
1673
2053
|
|
|
1674
2054
|
---
|
|
1675
2055
|
|
|
2056
|
+
# SChipFilter
|
|
2057
|
+
|
|
2058
|
+
> 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
|
|
2059
|
+
|
|
2060
|
+
### SChipFilter
|
|
2061
|
+
|
|
2062
|
+
#### Props
|
|
2063
|
+
|
|
2064
|
+
| Prop | Type | Default | Description |
|
|
2065
|
+
|------|------|---------|-------------|
|
|
2066
|
+
| `fields?` | `SChipFilterField[] \| SChipFilterGroup[]` | — | 필터 정의 목록. 그룹으로 묶으려면 SChipFilterGroup[]을 넘긴다 — 그룹이 시작될 때마다 앞에 구분선이 자동으로 붙는다(showLabel·인접 그룹·인라인 date 필터와 중첩되지 않도록 처리됨). |
|
|
2067
|
+
| `value?` | `SChipFilterValueMap` | — | 필터 값 맵 |
|
|
2068
|
+
| `activeKeys?` | `string[]` | — | 노출 필터 key 목록. 지정하면 제어 컴포넌트로 동작 — "필터 추가"로 고른 필드를 이 배열에 직접 넣어줘야 칩이 나타난다(onActiveKeysChange에서 받은 keys로 갱신). 지정하지 않으면 필드별 defaultActive 값을 기준으로 컴포넌트가 내부 상태로 관리하는 비제어 방식으로 동작한다 |
|
|
2069
|
+
| `label?` | `string` | `'검색 필터'` | 좌측 태그 텍스트 |
|
|
2070
|
+
| `showLabel?` | `boolean` | `false` | 좌측 태그(label)·구분선 표시 여부 |
|
|
2071
|
+
| `showAddButton?` | `boolean` | `true` | 필터 추가 버튼 표시 여부 |
|
|
2072
|
+
| `showReset?` | `boolean` | `true` | 검색 초기화 링크 표시 여부 |
|
|
2073
|
+
| `disabled?` | `boolean` | `false` | 바 비활성 상태 |
|
|
2074
|
+
| `className?` | `string` | — | |
|
|
2075
|
+
| `style?` | `CSSProperties` | — | |
|
|
2076
|
+
|
|
2077
|
+
#### Events
|
|
2078
|
+
|
|
2079
|
+
| Event | Type | Description |
|
|
2080
|
+
|-------|------|-------------|
|
|
2081
|
+
| `onValueChange` | `(value: SChipFilterValueMap) => void` | 전체 값 변경 — 편집 중인 값이 바뀔 때마다(선택할 때마다) 호출된다. 실제 검색 실행은 onSearch를 쓴다 |
|
|
2082
|
+
| `onFilterChange` | `(detail: SChipFilterChangeDetail) => void` | 개별 필터 값 변경 |
|
|
2083
|
+
| `onSearch` | `(value: SChipFilterValueMap) => void` | 실제 검색을 실행할 시점 — 편집 팝오버의 "검색" 버튼을 누르거나 팝오버가 닫힐 때(바깥 클릭·Esc·다른 칩으로 전환 포함) 그 시점의 전체 값 맵과 함께 호출된다. 팝오버가 없는 필드(인라인 date 프리셋·custom, clearable ×, 검색 초기화)는 값이 바뀌는 즉시 호출된다. dirty 체크가 기본 적용되어 있어 — 마지막으로 실제 검색이 실행된 값 맵과 비교해 하나라도 달라진 게 없으면(예: 팝오버를 열었다 아무것도 안 바꾸고 닫는 경우) 호출되지 않는다. fields를 그룹으로 넘겼다면 rule을 만족하지 못한 그룹이 있는 동안엔 onSearch가 호출되지 않는다 — 해당 그룹의 경고 툴팁은 이 시점과 무관하게 값이 비어 있는 동안 항상 실시간으로 떠 있다(별도로 validate()를 호출할 필요 없음) |
|
|
2084
|
+
| `onActiveKeysChange` | `(keys: string[]) => void` | 노출 필터 key 변경(칩 추가·제거) — 비제어 방식에서도 참고용으로 호출된다. activeKeys를 직접 제어할 때는 이 값을 그대로 activeKeys에 반영해야 한다 |
|
|
2085
|
+
| `onReset` | `() => void` | "검색 초기화" 클릭 — 모든 필드가 기본값(또는 null)으로 리셋된 뒤 호출된다 |
|
|
2086
|
+
| `onAddFilter` | `(key: string) => void` | "필터 추가" 목록에서 항목을 골랐을 때 — activeKeys를 직접 제어 중이면 이 콜백에서 (또는 onActiveKeysChange에서) key를 activeKeys에 추가해줘야 칩이 실제로 나타난다. activeKeys를 넘기지 않았다면(비제어) 별도 처리 없이도 컴포넌트가 알아서 칩을 노출한다 |
|
|
2087
|
+
|
|
2088
|
+
#### Methods (ref)
|
|
2089
|
+
|
|
2090
|
+
| Method | Type | Description |
|
|
2091
|
+
|--------|------|-------------|
|
|
2092
|
+
| `open` | `(key: string) => void` | 특정 필터 편집 팝오버를 엽니다. |
|
|
2093
|
+
| `reset` | `() => void` | 모든 필터 값을 초기화합니다. |
|
|
2094
|
+
| `validate` | `() => boolean` | fields를 그룹(SChipFilterGroup[])으로 넘겼을 때, 현재 값 기준으로 각 그룹의 rule을 만족하는지 반환합니다. 경고 툴팁은 이 호출과 무관하게 rule 위반 상태인 동안 항상 실시간으로 떠 있으므로, 이 메서드는 그 상태를 그대로 읽어오는 용도다(예: 검색 버튼을 눌러도 되는지 사전 확인) |
|
|
2095
|
+
|
|
2096
|
+
## Dependencies
|
|
2097
|
+
|
|
2098
|
+
### Depends on
|
|
2099
|
+
|
|
2100
|
+
- [SDatePicker](../SDatePicker)
|
|
2101
|
+
- [SDateRangePicker](../SDateRangePicker)
|
|
2102
|
+
- [SGhostButton](../SGhostButton)
|
|
2103
|
+
- [SIcon](../SIcon)
|
|
2104
|
+
- [SRadio](../SRadio)
|
|
2105
|
+
- [SRadioButton](../SRadioButton)
|
|
2106
|
+
- [STag](../STag)
|
|
2107
|
+
- [STextLink](../STextLink)
|
|
2108
|
+
- [STooltip](../STooltip)
|
|
2109
|
+
|
|
2110
|
+
### Graph
|
|
2111
|
+
|
|
2112
|
+
---
|
|
2113
|
+
|
|
1676
2114
|
# SChipInput
|
|
1677
2115
|
|
|
1678
2116
|
> 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
|
|
@@ -1872,6 +2310,7 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
1872
2310
|
|
|
1873
2311
|
### Used by
|
|
1874
2312
|
|
|
2313
|
+
- [SChipFilter](../SChipFilter)
|
|
1875
2314
|
- [SKeyValueTable](../SKeyValueTable)
|
|
1876
2315
|
|
|
1877
2316
|
### Depends on
|
|
@@ -1926,10 +2365,30 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
1926
2365
|
| `onValueChange` | `(range: SDateRangeValue) => void` | 선택 변경 (sdUpdate) — [start, end] |
|
|
1927
2366
|
| `onViewChange` | `(view: { year: number; month: number }) => void` | 보이는 연·월 변경 (sdViewChange) |
|
|
1928
2367
|
|
|
2368
|
+
### SRangeCalendar
|
|
2369
|
+
|
|
2370
|
+
#### Props
|
|
2371
|
+
|
|
2372
|
+
| Prop | Type | Default | Description |
|
|
2373
|
+
|------|------|---------|-------------|
|
|
2374
|
+
| `value` | `SDateRangeValue` | — | |
|
|
2375
|
+
| `selectable?` | `[string, string]` | — | |
|
|
2376
|
+
| `maxRange?` | `number` | — | |
|
|
2377
|
+
| `useTimePicker?` | `boolean` | `false` | |
|
|
2378
|
+
|
|
2379
|
+
#### Events
|
|
2380
|
+
|
|
2381
|
+
| Event | Type | Description |
|
|
2382
|
+
|-------|------|-------------|
|
|
2383
|
+
| `onSelect` | `(r: [string, string]) => void` | |
|
|
2384
|
+
| `onPendingStartChange` | `(start: string \| null) => void` | 시작일만 선택된(종료일 대기) 상태를 상위로 전달 — 트리거에 `start ~` 프리뷰 표시용 |
|
|
2385
|
+
| `onViewChange` | `(view: { year: number; month: number }) => void` | |
|
|
2386
|
+
|
|
1929
2387
|
## Dependencies
|
|
1930
2388
|
|
|
1931
2389
|
### Used by
|
|
1932
2390
|
|
|
2391
|
+
- [SChipFilter](../SChipFilter)
|
|
1933
2392
|
- [SKeyValueTable](../SKeyValueTable)
|
|
1934
2393
|
|
|
1935
2394
|
### Depends on
|
|
@@ -1986,6 +2445,7 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
1986
2445
|
| `selected?` | `boolean` | `false` | 선택 상태 여부 |
|
|
1987
2446
|
| `dragging?` | `boolean` | `false` | 드래그 중인 스타일을 고정해서 표시할지 여부 |
|
|
1988
2447
|
| `dense?` | `boolean` | `false` | 조밀한 높이 사용 여부 |
|
|
2448
|
+
| `size?` | `SDraggableItemSize` | `'sm'` | 타이포그래피·세로 패딩 크기 |
|
|
1989
2449
|
| `disabled?` | `boolean` | `false` | 비활성 상태 여부 |
|
|
1990
2450
|
| `dragOverlay?` | `boolean` | `true` | 드래그 시 마우스를 따라가는 overlay 표시 여부 |
|
|
1991
2451
|
| `dragOverlayOpacity?` | `number` | `0.75` | 드래그 overlay 투명도 |
|
|
@@ -2078,10 +2538,10 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2078
2538
|
| `open?` | `boolean` | — | 표시 여부 |
|
|
2079
2539
|
| `persistent?` | `boolean` | `true` | backdrop·ESC로 닫히지 않고 흔들림 효과를 준다. **기본값 true** — Drawer는 내용을 작성·구성하는 곳이라 임의 닫힘을 막는 것이 기본이다. 닫기 경로는 X 버튼과 footer 버튼뿐이다. false로 주면 backdrop·ESC 닫기가 열린다. |
|
|
2080
2540
|
| `title?` | `string` | `''` | 접근성 제목 및 헤더 제목 |
|
|
2081
|
-
| `width?` | `number \| string` | `572` | Drawer 너비. 기본값은 Figma drawer 기준 572px이다. |
|
|
2082
|
-
| `resizable?` | `boolean` | `false` |
|
|
2083
|
-
| `minWidth?` | `number \| string` | — | 리사이즈 가능한 최소 너비(px, %, vw, vh) |
|
|
2084
|
-
| `maxWidth?` | `number \| string` | — | 리사이즈 가능한 최대 너비(px, %, vw, vh) |
|
|
2541
|
+
| `width?` | `number \| string` | `572` | Drawer 너비. 기본값은 Figma drawer 기준 572px이다. 창이 이보다 좁으면 창 폭까지만 넓어진다 — 드로어가 화면 밖으로 나가지 않는다. `resizable` 로 조절한 폭은 이 값이 바뀔 때 되돌아간다 — 앱이 폭을 저장해 두었다가 다시 열 때 넘겨주면 그 폭으로 열린다. |
|
|
2542
|
+
| `resizable?` | `boolean` | `false` | 왼쪽 테두리를 끌어 너비를 조절할 수 있게 한다. 조절선은 패널 왼쪽 경계 전체이며 평소엔 보이지 않다가 hover·포커스·조절 중에만 드러난다(SSplitter·SGnb 와 같은 선). 포커스를 받아 방향키(Shift 는 크게)·Home·End 로도 조절된다. |
|
|
2543
|
+
| `minWidth?` | `number \| string` | — | 리사이즈 가능한 최소 너비(px, %, vw, vh). % 는 viewport 기준이며, 창 폭보다 클 수 없다 |
|
|
2544
|
+
| `maxWidth?` | `number \| string` | — | 리사이즈 가능한 최대 너비(px, %, vw, vh). % 는 viewport 기준이며, 주지 않으면 창 폭이 상한이다 |
|
|
2085
2545
|
| `footerLeft?` | `ReactNode` | — | footer 좌측 슬롯 |
|
|
2086
2546
|
| `button?` | `SDrawerButton` | — | 우측 기본 액션 버튼 |
|
|
2087
2547
|
| `children?` | `ReactNode` | — | |
|
|
@@ -2094,6 +2554,7 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2094
2554
|
|-------|------|-------------|
|
|
2095
2555
|
| `onOpenChange` | `(open: boolean) => void` | 표시 상태 변경 |
|
|
2096
2556
|
| `onClose` | `() => void` | 닫기(X) 버튼 클릭 |
|
|
2557
|
+
| `onWidthChange` | `(width: number) => void` | 너비가 확정될 때(드래그를 놓거나 방향키 조작). 드래그하는 동안에는 오지 않는다 |
|
|
2097
2558
|
|
|
2098
2559
|
## Dependencies
|
|
2099
2560
|
|
|
@@ -2176,6 +2637,7 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2176
2637
|
| `interaction?` | `SExpansionItemInteraction` | — | hover/selected 상태에서 적용할 인터랙션 preset |
|
|
2177
2638
|
| `accentStripe?` | `boolean` | `false` | 아이템 왼쪽 accent stripe 표시 여부 |
|
|
2178
2639
|
| `dense?` | `boolean` | `false` | |
|
|
2640
|
+
| `size?` | `SExpansionItemSize` | `'sm'` | 타이포그래피 크기 |
|
|
2179
2641
|
| `disabled?` | `boolean` | `false` | |
|
|
2180
2642
|
|
|
2181
2643
|
#### Events
|
|
@@ -2454,6 +2916,7 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2454
2916
|
- [SBarcodeInput](../SBarcodeInput)
|
|
2455
2917
|
- [SCalendar](../SCalendar)
|
|
2456
2918
|
- [SChip](../SChip)
|
|
2919
|
+
- [SChipFilter](../SChipFilter)
|
|
2457
2920
|
- [SDateRangePicker](../SDateRangePicker)
|
|
2458
2921
|
- [SFilePicker](../SFilePicker)
|
|
2459
2922
|
- [SGnb](../SGnb)
|
|
@@ -2461,6 +2924,7 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2461
2924
|
- [SInput](../SInput)
|
|
2462
2925
|
- [SModalContainer](../SModalContainer)
|
|
2463
2926
|
- [SOverlayHeader](../SOverlayHeader)
|
|
2927
|
+
- [SPage](../SPage)
|
|
2464
2928
|
- [SPopover](../SPopover)
|
|
2465
2929
|
- [SSelect](../SSelect)
|
|
2466
2930
|
- [STimePicker](../STimePicker)
|
|
@@ -2497,8 +2961,11 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2497
2961
|
| `folded?` | `boolean` | — | 접힘(레일) 상태. 미지정 시 SLayout 의 folded 를 따른다. |
|
|
2498
2962
|
| `logo?` | `ReactNode` | — | 상단바 로고 영역 (slot). header="full" 이면 폭이 140px 로 고정된다. |
|
|
2499
2963
|
| `topContent?` | `ReactNode` | — | 상단바 로고 오른쪽 슬롯 (검색·액션 등). 로고와 16px 띄고 남는 폭을 모두 차지하므로 안에서 자유롭게 정렬한다. 상단바가 전폭인 header="full" 에서만 렌더된다 (fix 는 상단바가 좁은 GNB 컬럼 안이라 놓을 자리가 없다). |
|
|
2964
|
+
| `railTop?` | `ReactNode` | — | 레일 상단 고정 슬롯. 레일 아이템이 많아 넘치면 아이템 목록(ul)만 스크롤되고 이 슬롯은 레일 상단(상단바 바로 아래)에 붙어 고정된다. useRail 일 때만 렌더된다. |
|
|
2500
2965
|
| `railFooter?` | `ReactNode` | — | 레일 하단 고정 슬롯. 레일 아이템이 많아 넘치면 아이템 목록(ul)만 스크롤되고 이 슬롯은 레일 하단에 붙어 고정된다. useRail 일 때만 렌더된다. |
|
|
2966
|
+
| `menuTop?` | `ReactNode` | — | 메뉴 상단 고정 슬롯. 메뉴 아이템이 많아 넘치면 메뉴 목록(ul)만 스크롤되고 이 슬롯은 메뉴 상단에 붙어 고정된다. 메뉴가 렌더될 때만(showMenu) 나타난다. |
|
|
2501
2967
|
| `menuFooter?` | `ReactNode` | — | 메뉴 하단 고정 슬롯. 메뉴 아이템이 많아 넘치면 메뉴 목록(ul)만 스크롤되고 이 슬롯은 메뉴 하단에 붙어 고정된다. 메뉴가 렌더될 때만(showMenu) 나타난다. |
|
|
2968
|
+
| `foldedTop?` | `ReactNode` | — | 접힘(fix 레일) 상단 고정 슬롯. 접으면 본문이 빠져나가며 rail/menu top 도 사라지므로, 48px 폴드 레일 상단(상단바 바로 아래)에 붙는 별도 슬롯이다. header="fix" 로 접혔을 때만 나타난다. |
|
|
2502
2969
|
| `foldedFooter?` | `ReactNode` | — | 접힘(fix 레일) 하단 고정 슬롯. 접으면 본문이 빠져나가며 rail/menu footer 도 사라지므로, 48px 폴드 레일 바닥에 붙는 별도 슬롯이다. header="fix" 로 접혔을 때만 나타난다. |
|
|
2503
2970
|
| `resizable?` | `boolean` | `false` | 메뉴 폭을 드래그로 조절할 수 있게 한다. 레일 폭은 고정이고 **메뉴 컬럼만** 늘고 준다. 조절선은 GNB 컬럼의 오른쪽 경계 전체다 — fix 는 상단바 높이까지, full 은 상단바가 전폭이라 본문 높이까지. 접혀 있거나 깔 메뉴가 없으면(레일 리프가 활성) 조절선이 나오지 않는다. |
|
|
2504
2971
|
| `menuWidth?` | `number` | — | 메뉴 폭(px). 주면 controlled — onMenuWidthChange 로 직접 갱신해야 움직인다 |
|
|
@@ -2543,7 +3010,7 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2543
3010
|
| `message?` | `SGuideMessage` | `''` | 팝업 메시지 (배열이면 리스트, 중첩 배열이면 depth 표현) |
|
|
2544
3011
|
| `url?` | `string` | `''` | notion 타입 클릭 시 이동 URL |
|
|
2545
3012
|
| `popupTitle?` | `string` | `''` | 팝업 제목 (없으면 type 기본값) |
|
|
2546
|
-
| `popupWidth?` | `number` | — | 팝업 너비 (px, 기본 426) |
|
|
3013
|
+
| `popupWidth?` | `number` | — | 팝업 너비 (px, 기본 426). 창보다 넓게 줘도 창 폭까지만 벌어진다. |
|
|
2547
3014
|
| `className?` | `string` | — | |
|
|
2548
3015
|
| `style?` | `CSSProperties` | — | |
|
|
2549
3016
|
|
|
@@ -2583,6 +3050,7 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2583
3050
|
- [SButton](../SButton)
|
|
2584
3051
|
- [SCallout](../SCallout)
|
|
2585
3052
|
- [SCheckbox](../SCheckbox)
|
|
3053
|
+
- [SChipFilter](../SChipFilter)
|
|
2586
3054
|
- [SChipInput](../SChipInput)
|
|
2587
3055
|
- [SConfirmModal](../SConfirmModal)
|
|
2588
3056
|
- [SDatePicker](../SDatePicker)
|
|
@@ -2787,6 +3255,7 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2787
3255
|
| `children?` | `ReactNode` | — | 리스트 컨테이너 내부에 렌더링할 내용 |
|
|
2788
3256
|
| `useGap?` | `boolean` | `false` | 리스트 아이템 사이 gap 토큰 적용 여부 |
|
|
2789
3257
|
| `usePadding?` | `boolean` | `false` | 리스트 컨테이너 padding 토큰 적용 여부 |
|
|
3258
|
+
| `separator?` | `boolean` | `false` | 아이템 하단 구분선 표시 여부. 테두리를 가진 아이템(`bordered`)에는 쓰지 않는다 |
|
|
2790
3259
|
|
|
2791
3260
|
## Dependencies
|
|
2792
3261
|
|
|
@@ -2816,11 +3285,12 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2816
3285
|
| `leading?` | `SListItemSlot` | — | 타이틀 앞에 표시할 아이콘/콘텐츠 |
|
|
2817
3286
|
| `trailing?` | `SListItemSlot` | — | 타이틀 뒤에 표시할 태그/콘텐츠 |
|
|
2818
3287
|
| `bordered?` | `boolean` | `false` | 외곽 테두리 사용 여부 |
|
|
2819
|
-
| `selected?` | `boolean` | `false` | 선택 상태
|
|
2820
|
-
| `clickable?` | `boolean` | `
|
|
3288
|
+
| `selected?` | `boolean` | `false` | 선택 상태 여부. `clickable` 이 false 면 무시된다 |
|
|
3289
|
+
| `clickable?` | `boolean` | `false` | 클릭 가능 상태 여부. hover·selected·chevron 등 인터랙션 표현이 여기에 딸린다 |
|
|
2821
3290
|
| `interaction?` | `SListItemInteraction` | — | hover/selected 상태에서 적용할 인터랙션 preset |
|
|
2822
3291
|
| `accentStripe?` | `boolean` | `false` | 아이템 왼쪽 accent stripe 표시 여부 |
|
|
2823
3292
|
| `dense?` | `boolean` | `false` | 조밀한 높이 사용 여부 |
|
|
3293
|
+
| `size?` | `SListItemSize` | `'sm'` | 타이포그래피 크기 |
|
|
2824
3294
|
| `disabled?` | `boolean` | `false` | 비활성 상태 여부 |
|
|
2825
3295
|
|
|
2826
3296
|
## Dependencies
|
|
@@ -3294,6 +3764,24 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
3294
3764
|
|
|
3295
3765
|
> 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
|
|
3296
3766
|
|
|
3767
|
+
### SPageHeader
|
|
3768
|
+
|
|
3769
|
+
#### Props
|
|
3770
|
+
|
|
3771
|
+
| Prop | Type | Default | Description |
|
|
3772
|
+
|------|------|---------|-------------|
|
|
3773
|
+
| `title` | `string` | — | 타이틀 (필수) |
|
|
3774
|
+
| `subText?` | `string` | — | 서브 텍스트. fix=true 면 타이틀 오른쪽에, fix=false(기본) 면 타이틀 아래에 놓인다. |
|
|
3775
|
+
| `fix?` | `boolean` | `false` | true: 높이 56 고정 + 흰 배경 + 하단 구분선, 서브 텍스트가 타이틀과 같은 줄. false(기본): 최소 높이 66(상하 패딩 20/16 안에서 타이틀 한 줄 높이로 결정) + 투명 배경, 서브 텍스트가 타이틀 아래로 내려간다. |
|
|
3776
|
+
| `slot?` | `ReactNode` | — | 오른쪽 끝 커스텀 슬롯. 가이드 링크·액션 버튼 등 페이지마다 다른 것이 온다. |
|
|
3777
|
+
| `scrolled?` | `boolean` | `false` | `<SPage>` 가 스크롤 여부를 보고 자동으로 넣어준다 — 직접 주지 않는다. fix=false 일 때만 의미가 있다: 스크롤이 시작되면 그림자(floating)가 들어가 스크롤되는 콘텐츠 위에 뜬 것처럼 보인다. 배경은 그대로 페이지 배경을 따라간다 — 배경을 바꾸는 건 scrolled 가 아니라 fix 다. |
|
|
3778
|
+
|
|
3779
|
+
#### Events
|
|
3780
|
+
|
|
3781
|
+
| Event | Type | Description |
|
|
3782
|
+
|-------|------|-------------|
|
|
3783
|
+
| `onBack` | `() => void` | 뒤로가기 클릭 핸들러. 지정하면 왼쪽에 뒤로가기 버튼이 나타난다. |
|
|
3784
|
+
|
|
3297
3785
|
### SPage
|
|
3298
3786
|
|
|
3299
3787
|
#### Props
|
|
@@ -3302,11 +3790,13 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
3302
3790
|
|------|------|---------|-------------|
|
|
3303
3791
|
| `background?` | `SPageBackground` | `'frame'` | 페이지 배경 표면. frame=흰 콘텐츠 면, neutral=옅은 회색 면, screen=앱 바탕. 스크롤바 처리도 여기 묶여 있다 — 셋 다 구분선+트랙 배경이고, 트랙 색만 neutral 에서 흰색이 된다. |
|
|
3304
3792
|
| `scrollEndSpacing?` | `boolean` | `true` | 스크롤 끝 여백. 마지막 항목이 창 하단에 붙어 "여기서 끝"이 안 읽히는 것을 막는다. 기본으로 켜져 있고, 페이지네이션처럼 끝을 알려주는 것이 이미 있으면 끈다(목록 페이지). |
|
|
3793
|
+
| `header?` | `SPageHeaderProps` | — | 페이지 타이틀 영역(pageHeader 포팅). 주면 스크롤·페이지 패딩 밖으로 빼내 상단에 고정 배치한다. `children` 은 항상 본문이다 — header 유무와 무관하게 같은 자리에 같은 뜻으로 들어간다. |
|
|
3305
3794
|
|
|
3306
3795
|
## Dependencies
|
|
3307
3796
|
|
|
3308
3797
|
### Depends on
|
|
3309
3798
|
|
|
3799
|
+
- [SGhostButton](../SGhostButton)
|
|
3310
3800
|
- [SLayout](../SLayout)
|
|
3311
3801
|
|
|
3312
3802
|
### Graph
|
|
@@ -3453,7 +3943,7 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
3453
3943
|
| `maxHeight?` | `number` | — | 콘텐츠 최대 높이 px. 넘치면 세로 스크롤. |
|
|
3454
3944
|
| `autoClose?` | `boolean` | `false` | 콘텐츠 내부를 클릭하면 자동으로 닫는다. |
|
|
3455
3945
|
| `persistent?` | `boolean` | `false` | 외부 클릭 · ESC 로 닫히지 않게 한다. |
|
|
3456
|
-
| `viewportPadding?` | `number` | `0` | 뷰포트 경계 여백 px (충돌 회피 패딩). |
|
|
3946
|
+
| `viewportPadding?` | `number` | `0` | 뷰포트 경계 여백 px (충돌 회피 패딩). 콘텐츠 폭은 항상 `창 폭 - viewportPadding*2` 를 넘지 않게 잘린다. |
|
|
3457
3947
|
| `children?` | `ReactNode` | — | 본문 내용 (콘텐츠 자유). |
|
|
3458
3948
|
| `className?` | `string` | — | |
|
|
3459
3949
|
| `style?` | `CSSProperties` | — | |
|
|
@@ -3528,6 +4018,7 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
3528
4018
|
|
|
3529
4019
|
### Used by
|
|
3530
4020
|
|
|
4021
|
+
- [SChipFilter](../SChipFilter)
|
|
3531
4022
|
- [SKeyValueTable](../SKeyValueTable)
|
|
3532
4023
|
|
|
3533
4024
|
### Graph
|
|
@@ -3558,6 +4049,14 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
3558
4049
|
|-------|------|-------------|
|
|
3559
4050
|
| `onValueChange` | `(value: string \| number) => void` | 변경 (sdUpdate) |
|
|
3560
4051
|
|
|
4052
|
+
## Dependencies
|
|
4053
|
+
|
|
4054
|
+
### Used by
|
|
4055
|
+
|
|
4056
|
+
- [SChipFilter](../SChipFilter)
|
|
4057
|
+
|
|
4058
|
+
### Graph
|
|
4059
|
+
|
|
3561
4060
|
---
|
|
3562
4061
|
|
|
3563
4062
|
# SScrollArea
|
|
@@ -3650,8 +4149,8 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
3650
4149
|
| `rules?` | `Rule[]` | — | 유효성 규칙 — 닫힐 때 자동 검증 |
|
|
3651
4150
|
| `labelTooltipProps?` | `Partial<STooltipProps>` | — | 레이블 툴팁 상세 옵션 |
|
|
3652
4151
|
| `dropdownHeight?` | `string` | `'260px'` | 드롭다운 최대 높이 |
|
|
3653
|
-
| `dropdownWidth?` | `string` | — | 드롭다운 너비 (없으면 트리거 너비) |
|
|
3654
|
-
| `maxDropdownWidth?` | `string` | `'640px'` | 드롭다운 최대
|
|
4152
|
+
| `dropdownWidth?` | `string` | — | 드롭다운 너비 (없으면 트리거 너비). 창보다 넓게 줘도 창 폭까지만 벌어진다. |
|
|
4153
|
+
| `maxDropdownWidth?` | `string` | `'640px'` | 드롭다운 최대 너비. 창이 이보다 좁으면 창 폭이 상한이 된다. |
|
|
3655
4154
|
| `label?` | `string` | — | |
|
|
3656
4155
|
| `labelWidth?` | `number \| string` | — | |
|
|
3657
4156
|
| `icon?` | `SIconName` | — | 레이블 영역 아이콘 |
|
|
@@ -3951,6 +4450,7 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
3951
4450
|
|
|
3952
4451
|
### Used by
|
|
3953
4452
|
|
|
4453
|
+
- [SChipFilter](../SChipFilter)
|
|
3954
4454
|
- [SConfirmModal](../SConfirmModal)
|
|
3955
4455
|
- [SGhostButton](../SGhostButton)
|
|
3956
4456
|
- [SGnb](../SGnb)
|
|
@@ -3995,6 +4495,7 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
3995
4495
|
|
|
3996
4496
|
### Used by
|
|
3997
4497
|
|
|
4498
|
+
- [SChipFilter](../SChipFilter)
|
|
3998
4499
|
- [SChipInput](../SChipInput)
|
|
3999
4500
|
- [SPopover](../SPopover)
|
|
4000
4501
|
|
|
@@ -4314,6 +4815,7 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
4314
4815
|
|
|
4315
4816
|
### Used by
|
|
4316
4817
|
|
|
4818
|
+
- [SChipFilter](../SChipFilter)
|
|
4317
4819
|
- [SField](../SField)
|
|
4318
4820
|
- [SKeyValueTable](../SKeyValueTable)
|
|
4319
4821
|
- [SSectionHeaderCard](../SSectionHeaderCard)
|