sellmate-design-system-react 5.0.2 → 6.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.
- package/AGENTS.md +107 -19
- package/dist/components/SCalendar/README.md +2 -0
- package/dist/components/SConfirmModal/README.md +1 -1
- package/dist/components/SConfirmModal/SConfirmModal.d.ts +1 -1
- package/dist/components/SDateRangePicker/README.md +2 -0
- package/dist/components/SDivider/README.md +4 -0
- package/dist/components/SLayout/SLayout.d.ts +16 -0
- package/dist/components/SLayout/index.d.ts +1 -1
- package/dist/components/SPage/README.md +1 -0
- package/dist/components/SPage/SPage.d.ts +6 -0
- package/dist/components/SPopup/README.md +1 -0
- package/dist/components/SPopup/SPopup.d.ts +2 -0
- package/dist/components/SSectionHeaderCard/README.md +1 -0
- package/dist/components/SSectionHeaderCard/SSectionHeaderCard.d.ts +6 -0
- package/dist/components/SSectionHeaderCard/index.d.ts +1 -1
- package/dist/components/SStepper/stepper.config.d.ts +1 -0
- package/dist/index.cjs +40 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +40 -40
- package/dist/index.js.map +1 -1
- package/dist/llms-full.txt +120 -22
- package/dist/llms.txt +112 -21
- package/dist/styles.css +122 -24
- package/dist/theme.css +93 -16
- package/eslint/scale.gen.mjs +2 -2
- package/package.json +1 -1
package/dist/llms-full.txt
CHANGED
|
@@ -165,15 +165,24 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
|
|
|
165
165
|
- 형제 요소 간격은 margin 대신 부모의 `flex`/`grid` + `gap-sd-*`으로 잡는다.
|
|
166
166
|
- 시맨틱 간격 토큰 (텍스트 덩어리·요소 사이 기본 리듬):
|
|
167
167
|
|
|
168
|
+
화면은 세 층이다. **여백(padding)과 간격(gap)은 서로 다른 축이고, 규칙도 다르다.**
|
|
169
|
+
|
|
170
|
+
```text
|
|
171
|
+
페이지 프레임 SPage 가 여백을 넣는다. 직접 주지 않는다
|
|
172
|
+
└ 블록 페이지 직계 자식 (헤더·필터·툴바·테이블·섹션 카드)
|
|
173
|
+
블록 ↔ 블록 간격은 gap-sd-12
|
|
174
|
+
└ 섹션·패널 블록 중 "안에 콘텐츠를 담는 컨테이너"인 것
|
|
175
|
+
여기에만 안쪽 여백 선택지가 있다 (아래 "섹션·패널 안쪽 여백")
|
|
176
|
+
```
|
|
177
|
+
|
|
168
178
|
| 상황 | 값 |
|
|
169
179
|
| --- | --- |
|
|
170
|
-
| **페이지 콘텐츠 패딩** |
|
|
171
|
-
| **섹션 ↔
|
|
172
|
-
| **페이지 내 블록 ↔ 블록** (헤더·필터·툴바·테이블 사이) | **`gap-sd-12`** |
|
|
180
|
+
| **페이지 콘텐츠 패딩** | **`SPage` 가 `--cmp-pageBody-padding-default` 로 이미 넣는다.** 직접 주지 않는다 (덮어쓰면 토큰이 바뀌어도 안 따라간다). `SPage` 밖에서 같은 패딩이 필요하면 `p-sd-16` |
|
|
181
|
+
| **섹션 ↔ 섹션**, **블록 ↔ 블록** (헤더·필터·툴바·테이블 사이) | **`gap-sd-12`** |
|
|
173
182
|
| 요소 ↔ 요소 | **기본 `gap-sd-8`** (`--sys-space-stack-gap-element-normal`) · 타이트 `gap-sd-4`(`-tight`) · 여유 `gap-sd-12`(`-relaxed`) / `gap-sd-16`(`-wide`) |
|
|
174
183
|
| 제목 ↔ 설명 텍스트 | **수직 배치 `gap-sd-4`**(`--sys-space-stack-gap-text-normal`, 타이트 `gap-sd-2`) · **가로 배치 `gap-sd-8`**(`-relaxed`) |
|
|
175
184
|
|
|
176
|
-
정보 밀도가 높은 서비스라 블록
|
|
185
|
+
정보 밀도가 높은 서비스라 블록 **간격**을 넓게 벌리지 않는다. `gap-sd-16` / `gap-sd-24` 를 페이지 골격의 기본값으로 쓰지 않는다. (아래 나오는 `24` 는 **안쪽 여백**에만 열리는 값이고, 간격은 여기 표대로 12 다.)
|
|
177
186
|
|
|
178
187
|
#### 같은 컴포넌트를 여러 개 늘어놓을 때 (그룹 간격)
|
|
179
188
|
|
|
@@ -207,6 +216,64 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
|
|
|
207
216
|
- `SRadioButton` 그룹의 간격은 `-1px`(테두리 겹침 처리)이라 손으로 만들지 않는다.
|
|
208
217
|
- 정확한 값이 필요하면 토큰을 직접 참조해도 된다: `gap-[var(--cmp-checkbox-group-gap-horizontal)]`
|
|
209
218
|
|
|
219
|
+
#### 섹션·패널 안쪽 여백
|
|
220
|
+
|
|
221
|
+
**페이지 프레임은 예외 없이 `SPage` 가 넣는다.** 아래 규칙은 그 안의 **섹션·패널 레벨에만** 적용된다.
|
|
222
|
+
|
|
223
|
+
**컴포넌트가 자체 여백을 가지면 컴포넌트 기준이 우선한다.** `SKeyValueTable`·`STable` 처럼 자기 여백을 토큰으로 갖고 있는 컴포넌트에는 이 판정을 적용하지 않는다 — 손댈 것이 없다. 아래 판정이 필요한 자리는 **직접 만든 컨테이너**와 **`SSectionHeaderCard.Body`** 두 곳뿐이다.
|
|
224
|
+
|
|
225
|
+
판정은 **그 영역이 담고 있는 콘텐츠 덩어리의 종류 수**로 한다.
|
|
226
|
+
|
|
227
|
+
```text
|
|
228
|
+
자체 면(배경 또는 테두리)을 가진 덩어리만 센다.
|
|
229
|
+
제목·설명 같은 맨 텍스트와 검색·필터·버튼 같은 조작은 세지 않는다.
|
|
230
|
+
|
|
231
|
+
두 종류 반복 → p-sd-16 (그중 일부가 내부적으로 반복되더라도)
|
|
232
|
+
세 종류 이상 → p-sd-24
|
|
233
|
+
|
|
234
|
+
판단이 서지 않으면 16 으로 둔다.
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
| 안에 들어가는 것 | 안쪽 여백 |
|
|
238
|
+
| --- | --- |
|
|
239
|
+
| **같은 요소의 규칙적 반복** — 테이블 행, `SKeyValueTable` 행, 아코디언 목록, 카드 목록 | **`p-sd-16`** |
|
|
240
|
+
| **서로 다른 요소의 복합 구성** — 말풍선 + 버튼 묶음 + 시스템 안내 / 콜아웃 + 토글 카드 + 설명 | **`p-sd-24`** |
|
|
241
|
+
|
|
242
|
+
**카드라서 24 가 아니다.** 같은 카드가 규칙적으로 반복되면 16 이다. **섹션 내부에 어떤 정보가 들어가는지도 무관하다** — 섹션이 동일하게 반복되면 안에 버튼·입력·태그가 섞여 있어도 16 이다.
|
|
243
|
+
|
|
244
|
+
적용은 **화면 단위가 아니라 영역 단위**다. 한 화면 안에서도 칸마다 다르다.
|
|
245
|
+
|
|
246
|
+
| 영역 (상담 콘솔 예) | 안쪽 여백 | 이유 |
|
|
247
|
+
| --- | --- | --- |
|
|
248
|
+
| 좌측 상담 목록 | `p-sd-16` | 같은 항목 반복 |
|
|
249
|
+
| 중앙 대화 | `p-sd-24` | 말풍선·버튼 묶음·시스템 메시지 혼재 |
|
|
250
|
+
| 우측 템플릿 목록 | `p-sd-16` | 같은 아코디언 반복 |
|
|
251
|
+
|
|
252
|
+
§4 의 표준 골격(목록·폼·상세)은 **전부 16** 이다. 24 는 반복 구조가 없고 성격이 다른 덩어리가 쌓이는 영역에만 쓴다.
|
|
253
|
+
|
|
254
|
+
**위자드·탭처럼 한 프레임을 공유하는 화면은 가장 복합적인 화면을 기준으로 통일한다.** 예를 들어 어떤 단계가 콜아웃 + 토글 카드로 2종류라 단독으로는 16 이지만, 3종류인 단계와 한 위자드를 공유하므로 통일 규칙에 따라 위자드 전체가 24 가 된다.
|
|
255
|
+
|
|
256
|
+
**중첩되면 안쪽 여백을 주지 않는다.** 24 영역 안에 또 여백을 주면 가장자리가 40 으로 벌어져 한 면적처럼 읽힌다. 안쪽 카드·목록이 **배경색이 다르거나 테두리가 있어** 경계가 스스로 보이는 경우에만 자기 여백을 유지한다.
|
|
257
|
+
|
|
258
|
+
`SSectionHeaderCard.Body` 는 이 규칙을 **prop 으로 받는다** — 직접 `p-sd-*` 를 주지 않는다.
|
|
259
|
+
|
|
260
|
+
```tsx
|
|
261
|
+
<SSectionHeaderCard.Body>…</SSectionHeaderCard.Body> {/* 기본 = 16 */}
|
|
262
|
+
<SSectionHeaderCard.Body padding="wide">…</SSectionHeaderCard.Body> {/* 3종류 이상 */}
|
|
263
|
+
<SSectionHeaderCard.Body padding="none">…</SSectionHeaderCard.Body> {/* 표를 가장자리까지 */}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
#### 스크롤 영역의 하단 여백
|
|
267
|
+
|
|
268
|
+
스크롤을 끝까지 내렸을 때 마지막 항목이 화면 경계에 붙으면 **목록이 끝난 것인지 더 있는 것인지** 읽히지 않는다. 그래서 스크롤 영역은 **하단만** 넓게 둔다. 나머지 세 방향은 위 16 / 24 규칙 그대로다.
|
|
269
|
+
|
|
270
|
+
| 스크롤 종류 | 어떻게 |
|
|
271
|
+
| --- | --- |
|
|
272
|
+
| **패널 자체 스크롤** (좌측 목록, 중앙 대화 등) | 그 패널 안쪽 하단에 `pb-sd-80` |
|
|
273
|
+
| **페이지 단위 스크롤** | **`SPage` 가 넣는다. 직접 주지 않는다** |
|
|
274
|
+
|
|
275
|
+
`SPage` 는 기본으로 넣으므로 **아무것도 하지 않으면 맞다.** 끄는 경우는 하나뿐이다 — **페이지네이션이 붙은 테이블.** 페이지네이션이 이미 "여기서 끝"을 알려주므로 `scrollEndSpacing={false}` 로 끈다 (§4-2 목록 페이지).
|
|
276
|
+
|
|
210
277
|
### 2-3. 색상
|
|
211
278
|
|
|
212
279
|
**시맨틱 유틸리티를 우선 사용한다** — 의미가 이름에 담긴 토큰이 이미 유틸리티로 존재한다.
|
|
@@ -347,7 +414,8 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
|
|
|
347
414
|
- 작은 창이 아니라 **화면 하나가 통째로** 들어간다 — 검색 필터·테이블·페이지네이션이 그대로 있는 목록(`엑셀 파일 관리`), 헤더 카드·탭이 있는 상세(`이동 오더 상세`).
|
|
348
415
|
- 상세를 팝업으로 여는 이유는 **목록을 떠나지 않고 여러 건을 번갈아 보기** 위해서다. 단 상세가 **항상** 팝업인 것은 아니고, 데이터 양이 많을 때 팝업을 쓴다.
|
|
349
416
|
- 구조는 헤더(제목 중앙) + 본문 + 푸터다. **확정할 작업이 있으면 푸터에 `저장`, 조회만이면 `useFooter={false}`** 로 푸터를 없앤다.
|
|
350
|
-
-
|
|
417
|
+
- **본문 패딩은 `SPopup` 이 토큰으로 넣는다. 직접 주지 않는다** (`p-sd-*` 로 덮어쓰면 토큰이 바뀌어도 안 따라간다). 표를 가장자리까지 채우는 등 콘텐츠가 여백을 직접 다뤄야 할 때만 `noPadding` 으로 끈다.
|
|
418
|
+
- 그 밖에는 팝업 안도 일반 페이지와 같은 규칙(§4)을 따른다: 블록 간격 `gap-sd-12`, 표는 `SKeyValueTable` / `STable`.
|
|
351
419
|
|
|
352
420
|
```tsx
|
|
353
421
|
// 1) 목록에서 별도 창을 연다 — 창 크기 = 콘텐츠 크기
|
|
@@ -363,7 +431,8 @@ function openDetailPopup(orderId: string) {
|
|
|
363
431
|
export default function TransferOrderPopupPage() {
|
|
364
432
|
return (
|
|
365
433
|
<SPopup popupTitle="이동 오더 상세" useFooter={false}>
|
|
366
|
-
|
|
434
|
+
{/* 본문 패딩은 SPopup 이 토큰으로 넣는다 — p-sd-* 로 덮어쓰지 않는다 */}
|
|
435
|
+
<div className="flex flex-col gap-sd-12">
|
|
367
436
|
<SSectionHeaderCard>…</SSectionHeaderCard>
|
|
368
437
|
<STabs value={tab} tabs={TABS} onValueChange={setTab} />
|
|
369
438
|
<STable columns={columns} rows={rows} rowKey="id" />
|
|
@@ -404,21 +473,23 @@ SModal.create({ component: OrderModal, componentProps: { orderId } })
|
|
|
404
473
|
.onDismissed(() => {});
|
|
405
474
|
```
|
|
406
475
|
|
|
407
|
-
#### 3-3-5. 닫기 경로 — `persistent`
|
|
476
|
+
#### 3-3-5. 닫기 경로 — `persistent` 기본값은 컴포넌트마다 다르다
|
|
408
477
|
|
|
409
|
-
|
|
478
|
+
**입력을 담는 모달은 백드롭 클릭·ESC 로 닫히지 않는 것이 기본**이다. 닫기 시도는 흔들림(shake)으로만 반응한다. 작성 중인 내용을 실수로 잃지 않게 하기 위한 것이다. 반면 `SConfirmModal` 은 잃을 입력이 없으므로 **백드롭·ESC 로 닫히는 것이 기본**이다.
|
|
410
479
|
|
|
411
|
-
| | 기본 닫기 경로 | 백드롭·ESC |
|
|
412
|
-
| --- | --- | --- |
|
|
413
|
-
| `SActionModal` · `
|
|
414
|
-
| `SDrawer` | X 버튼, footer 버튼 | 막힘 (흔들림) |
|
|
415
|
-
| `
|
|
480
|
+
| | `persistent` 기본값 | 기본 닫기 경로 | 백드롭·ESC |
|
|
481
|
+
| --- | --- | --- | --- |
|
|
482
|
+
| `SActionModal` · `SLoadingModal` | `true` | X 버튼, 모달 안의 버튼 | 막힘 (흔들림) |
|
|
483
|
+
| `SDrawer` | `true` | X 버튼, footer 버튼 | 막힘 (흔들림) |
|
|
484
|
+
| `SConfirmModal` | `false` | X 버튼, 확인/취소 버튼 | **닫힌다** |
|
|
485
|
+
| `SPopover` · `STooltip` · `SSelect` 등 floating | — | 바깥 클릭·ESC | **막지 않는다** — 이 규칙의 대상이 아니다 |
|
|
416
486
|
|
|
417
487
|
따라서 다음을 지킨다.
|
|
418
488
|
|
|
419
489
|
- **닫을 수단을 반드시 하나는 둔다.** `SActionModal` 에 `showClose` 도 `button`/`footerLeft` 도 없으면 사용자가 모달을 닫을 방법이 없다. 백드롭이 더 이상 탈출구가 아니다.
|
|
420
|
-
-
|
|
421
|
-
- **`persistent={false}` 는 잃을 입력이 없을 때만.** 단순 알림처럼 임의로 닫혀도 아무것도 사라지지 않는 경우로 한정한다.
|
|
490
|
+
- **기본값과 같은 `persistent` 를 직접 주지 않는다.** 중복이다.
|
|
491
|
+
- **`SActionModal`·`SDrawer` 에 `persistent={false}` 는 잃을 입력이 없을 때만.** 단순 알림처럼 임의로 닫혀도 아무것도 사라지지 않는 경우로 한정한다.
|
|
492
|
+
- **`SConfirmModal` 에 `persistent` 를 켜는 것은 반드시 답을 받아야 할 때만.** 되돌릴 수 없는 파괴적 작업의 확인처럼, 임의로 닫히면 안 되는 경우로 한정한다.
|
|
422
493
|
|
|
423
494
|
작성 중인 내용이 있을 때 닫기를 시도하면 이탈 안내를 띄우는 것은 **소비 앱 몫**이다. 디자인 시스템은 dirty 상태를 알지 못하므로 백드롭·ESC 를 일괄 차단할 뿐이다. 안내가 필요하면 앱이 자체 dirty 판정 후 `SModal.confirm` 으로 띄운다.
|
|
424
495
|
|
|
@@ -634,18 +705,26 @@ const MENU: SGnbMenuItem[] = [
|
|
|
634
705
|
{ label: '상품', value: 'products', icon: 'box', children: [{ label: '목록', value: 'product-list' }] },
|
|
635
706
|
];
|
|
636
707
|
|
|
637
|
-
export default function AppShell({
|
|
708
|
+
export default function AppShell({
|
|
709
|
+
children,
|
|
710
|
+
scrollEndSpacing,
|
|
711
|
+
}: { children: React.ReactNode; scrollEndSpacing?: boolean }) {
|
|
638
712
|
return (
|
|
639
713
|
<SLayout type="box" header="fix">
|
|
640
714
|
{/* type/header/folded 는 SLayout 에만 준다 — SGnb 는 context 에서 읽는다 */}
|
|
641
715
|
<SGnb items={MENU} value={current} onValueChange={navigate} logo={<Logo />} />
|
|
642
|
-
{/* 콘텐츠
|
|
643
|
-
|
|
716
|
+
{/* 콘텐츠 패딩은 SPage 가 토큰으로 넣는다 — p-sd-* 로 덮어쓰지 않는다 */}
|
|
717
|
+
{/* 스크롤 끝 여백도 SPage 가 넣는다. 끄는 건 페이지네이션 있는 목록뿐이라 페이지가 정한다 */}
|
|
718
|
+
<SPage background="frame" scrollEndSpacing={scrollEndSpacing}>{children}</SPage>
|
|
644
719
|
</SLayout>
|
|
645
720
|
);
|
|
646
721
|
}
|
|
647
722
|
```
|
|
648
723
|
|
|
724
|
+
**최소 너비는 `SLayout` 이 보장한다 — 앱이 `min-w-*` 를 직접 주지 않는다.** 창이 최소 너비(`SLAYOUT_MIN_WIDTH`, GNB 포함한 전체 기준)보다 좁아지면 GNB·상단바는 제자리에 남고 **`SPage` 안에만 가로 스크롤이 생긴다.** 문서(브라우저 창)에는 가로 스크롤이 생기지 않는다. 그래서 셸을 감싸는 요소에 `min-width` 나 `overflow-x` 를 걸지 않는다 — 걸면 창 전체가 스크롤되어 GNB 가 화면 밖으로 밀린다.
|
|
725
|
+
|
|
726
|
+
**셸의 `SPage` 는 모든 페이지가 공유하므로, 스크롤 끝 여백을 끄려면 페이지가 셸에 알려야 한다.** 위처럼 프레임 컴포넌트가 `scrollEndSpacing` 을 받아 그대로 넘기고, 페이지네이션이 있는 목록 페이지만 `false` 를 준다 (§4-2). 나머지 페이지는 넘기지 않으면 기본값(켬)이 적용된다.
|
|
727
|
+
|
|
649
728
|
**상단바 배치는 `header` 가 정한다.** 요소 순서가 달라지므로 슬롯을 채우기 전에 어느 쪽인지부터 정한다.
|
|
650
729
|
|
|
651
730
|
| `header` | 상단바 배치 | 로고 폭 | `topContent` |
|
|
@@ -684,6 +763,7 @@ export default function AppShell({ children }: { children: React.ReactNode }) {
|
|
|
684
763
|
- **페이지 제목 줄에는 액션 버튼을 두지 않는다.** 가이드·매뉴얼 링크 등 부가 정보만 온다.
|
|
685
764
|
- **주요 액션(등록 등)은 `STableBar` 의 `rightActions`** 에 둔다.
|
|
686
765
|
- **선택 상태 액션(선택 삭제 등)은 `STableBar` 의 `actions`** 에 둔다. `actions` 슬롯은 건수 요약이 있으면 앞에 구분선(`SDivider`)을 **자동으로** 넣으므로 직접 구분선을 만들지 않는다.
|
|
766
|
+
- **페이지네이션이 있으면 스크롤 끝 여백을 끈다** — 셸의 `SPage` 에 `scrollEndSpacing={false}` 를 넘긴다 (§2-2). 페이지네이션이 이미 "여기서 끝"을 알려준다.
|
|
687
767
|
|
|
688
768
|
```tsx
|
|
689
769
|
import {
|
|
@@ -921,6 +1001,10 @@ export default function ProductDetailPage() {
|
|
|
921
1001
|
| `slot` | 헤더 우측 영역 (버튼 등) |
|
|
922
1002
|
| `thickness` | 상단 강조선 — `false`(기본) / `'default'` / `'accent'` |
|
|
923
1003
|
|
|
1004
|
+
| Prop (Body) | 용도 |
|
|
1005
|
+
| --- | --- |
|
|
1006
|
+
| `padding` | 안쪽 여백 — `'default'`(기본) / `'wide'` / `'none'`. 판정은 §2-2 "섹션·패널 안쪽 여백". `p-sd-*` 를 직접 주지 않는다 |
|
|
1007
|
+
|
|
924
1008
|
---
|
|
925
1009
|
|
|
926
1010
|
## 5. 자가 점검 체크리스트
|
|
@@ -933,9 +1017,13 @@ export default function ProductDetailPage() {
|
|
|
933
1017
|
- [ ] 간격이 전부 `sd-` 접두 스케일 값인가 (`gap-13`·`gap-sd-13` ❌ → `gap-sd-12` ✅)
|
|
934
1018
|
- [ ] 본문이 12px(`typo-body-sm-default`)인가 (14px 본문 ❌)
|
|
935
1019
|
- [ ] 텍스트 회색 위계를 순차 적용했는가 (기본 → `text-fg-secondary` → `text-fg-tertiary`, 단계 건너뛰기 ❌)
|
|
936
|
-
- [ ] `SPage` 의 기본
|
|
1020
|
+
- [ ] `SPage`·`SPopup` 의 기본 패딩을 `p-sd-*` 로 덮어쓰지 않았는가, 블록·섹션 **간격**이 `gap-sd-12` 인가 (`gap-sd-16`/`gap-sd-24` ❌ — 24 는 안쪽 여백에만 열린다)
|
|
1021
|
+
- [ ] 섹션·패널의 안쪽 **여백**이 §2-2 판정과 맞는가 (덩어리 두 종류 → 16 / 세 종류 이상 → 24, 서면 16)
|
|
1022
|
+
- [ ] `SSectionHeaderCard.Body` 의 여백을 `p-sd-*` 가 아니라 `padding` prop 으로 줬는가
|
|
1023
|
+
- [ ] 자체 스크롤하는 패널의 하단에 `pb-sd-80` 이 있는가, 페이지네이션 있는 목록에서 `scrollEndSpacing={false}` 를 넘겼는가
|
|
937
1024
|
- [ ] 같은 컴포넌트를 나열할 때 §2-2 그룹 간격을 썼는가 (체크박스 가로 `gap-sd-24` 등)
|
|
938
1025
|
- [ ] 페이지가 §4의 표준 골격에서 시작했는가
|
|
1026
|
+
- [ ] 앱 셸이나 그 바깥에 `min-width`·`overflow-x` 를 직접 걸지 않았는가 (최소 너비는 `SLayout` 이 보장한다, §4-1)
|
|
939
1027
|
- [ ] 필터·폼·상세 정보를 `SKeyValueTable` 로 만들었는가 (컨트롤을 `div` 로 나열하지 않았는가)
|
|
940
1028
|
- [ ] 섹션 구분에 `SSectionHeaderCard` 를 썼는가 (직접 만든 카드가 아니라)
|
|
941
1029
|
- [ ] 목록의 주요 액션이 `STableBar` 의 `rightActions` 에 있는가 (페이지 제목 줄이 아니라)
|
|
@@ -1009,7 +1097,7 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
1009
1097
|
- brilliantblue: 10 20 25 40 50 60 70 75 80 85 90 95 99 05
|
|
1010
1098
|
- bg: screen frame brand subtle accent accentLight accentBright accentFaint deep danger dangerLight warning caution progress success successLight neutralLight neutralBright tableHeader
|
|
1011
1099
|
- fg: primary secondary tertiary quaternary brand subtleWarm subtleCool deep success dangerLight accent accentLight danger warning inverse
|
|
1012
|
-
- border: default strong accent accentLight danger success
|
|
1100
|
+
- border: default strong bold accent accentLight danger warning success
|
|
1013
1101
|
- disabled: bg text icon border
|
|
1014
1102
|
- link: accent
|
|
1015
1103
|
- divider: default
|
|
@@ -1025,8 +1113,9 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
1025
1113
|
### 타이포그래피 프리셋 (클래스 하나로 size/weight/line-height 적용)
|
|
1026
1114
|
| 클래스 | size | weight | line-height |
|
|
1027
1115
|
| --- | --- | --- | --- |
|
|
1028
|
-
| typo-heading-sm | 14px | 700 | 24px |
|
|
1029
1116
|
| typo-heading-xs | 12px | 700 | 20px |
|
|
1117
|
+
| typo-heading-xxs | 11px | 700 | 18px |
|
|
1118
|
+
| typo-heading-sm | 14px | 700 | 24px |
|
|
1030
1119
|
| typo-heading-md | 16px | 700 | 26px |
|
|
1031
1120
|
| typo-heading-lg | 18px | 700 | 30px |
|
|
1032
1121
|
| typo-body-xs-default | 11px | 400 | 18px |
|
|
@@ -1102,10 +1191,12 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
1102
1191
|
--sys-color-blue-subtle
|
|
1103
1192
|
--sys-color-border-accent
|
|
1104
1193
|
--sys-color-border-accentLight
|
|
1194
|
+
--sys-color-border-bold
|
|
1105
1195
|
--sys-color-border-danger
|
|
1106
1196
|
--sys-color-border-default
|
|
1107
1197
|
--sys-color-border-strong
|
|
1108
1198
|
--sys-color-border-success
|
|
1199
|
+
--sys-color-border-warning
|
|
1109
1200
|
--sys-color-control-bg-inverseHover
|
|
1110
1201
|
--sys-color-darkblue-strong
|
|
1111
1202
|
--sys-color-darkblue-subtle
|
|
@@ -1340,6 +1431,7 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
1340
1431
|
|
|
1341
1432
|
### Depends on
|
|
1342
1433
|
|
|
1434
|
+
- [SDivider](../SDivider)
|
|
1343
1435
|
- [SGhostButton](../SGhostButton)
|
|
1344
1436
|
|
|
1345
1437
|
### Graph
|
|
@@ -1589,7 +1681,7 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
1589
1681
|
| Prop | Type | Default | Description |
|
|
1590
1682
|
|------|------|---------|-------------|
|
|
1591
1683
|
| `open?` | `boolean` | — | |
|
|
1592
|
-
| `persistent?` | `boolean` |
|
|
1684
|
+
| `persistent?` | `boolean` | `false` | 백드롭·ESC로 안 닫히고 흔들림 (sd-modal-container persistent). **기본값 false** — 확인 모달은 잃을 입력이 없어 백드롭·ESC로 닫힌다 |
|
|
1593
1685
|
| `type?` | `SConfirmModalType` | `'positive'` | 타입 (아이콘·메인버튼 색 결정) |
|
|
1594
1686
|
| `modalTitle?` | `string` | `''` | |
|
|
1595
1687
|
| `titleClass?` | `string` | — | 제목 엘리먼트에 추가할 클래스 (sd-confirm-modal titleClass) |
|
|
@@ -1739,6 +1831,7 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
1739
1831
|
|
|
1740
1832
|
### Depends on
|
|
1741
1833
|
|
|
1834
|
+
- [SDivider](../SDivider)
|
|
1742
1835
|
- [SField](../SField)
|
|
1743
1836
|
- [SGhostButton](../SGhostButton)
|
|
1744
1837
|
- [SIcon](../SIcon)
|
|
@@ -1763,6 +1856,8 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
1763
1856
|
|
|
1764
1857
|
### Used by
|
|
1765
1858
|
|
|
1859
|
+
- [SCalendar](../SCalendar)
|
|
1860
|
+
- [SDateRangePicker](../SDateRangePicker)
|
|
1766
1861
|
- [STableBar](../STableBar)
|
|
1767
1862
|
|
|
1768
1863
|
### Graph
|
|
@@ -3064,6 +3159,7 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
3064
3159
|
| Prop | Type | Default | Description |
|
|
3065
3160
|
|------|------|---------|-------------|
|
|
3066
3161
|
| `background?` | `SPageBackground` | `'frame'` | 페이지 배경 표면. frame=흰 콘텐츠 면, neutral=옅은 회색 면, screen=앱 바탕. 스크롤바 처리도 여기 묶여 있다 — 셋 다 구분선+트랙 배경이고, 트랙 색만 neutral 에서 흰색이 된다. |
|
|
3162
|
+
| `scrollEndSpacing?` | `boolean` | `true` | 스크롤 끝 여백. 마지막 항목이 창 하단에 붙어 "여기서 끝"이 안 읽히는 것을 막는다. 기본으로 켜져 있고, 페이지네이션처럼 끝을 알려주는 것이 이미 있으면 끈다(목록 페이지). |
|
|
3067
3163
|
|
|
3068
3164
|
## Dependencies
|
|
3069
3165
|
|
|
@@ -3174,6 +3270,7 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
3174
3270
|
| `useFooter?` | `boolean` | `true` | 하단 footer 표시 여부 |
|
|
3175
3271
|
| `submitButton?` | `SPopupSubmitButton` | — | 확인 버튼 설정 |
|
|
3176
3272
|
| `footerLeft?` | `ReactNode` | — | footer 좌측 영역 |
|
|
3273
|
+
| `noPadding?` | `boolean` | `false` | 본문 기본 패딩을 없앱니다. 표를 가장자리까지 채우는 등 본문이 직접 여백을 다룰 때만 사용합니다. |
|
|
3177
3274
|
| `children?` | `ReactNode` | — | 본문 |
|
|
3178
3275
|
| `className?` | `string` | — | |
|
|
3179
3276
|
| `style?` | `CSSProperties` | — | |
|
|
@@ -3350,6 +3447,7 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
3350
3447
|
| Prop | Type | Default | Description |
|
|
3351
3448
|
|------|------|---------|-------------|
|
|
3352
3449
|
| `children?` | `SSectionHeaderCardBodyChildren` | — | 바디 슬롯. 특정 컴포넌트 타입으로 제한하지 않습니다. |
|
|
3450
|
+
| `padding?` | `SSectionHeaderCardBodyPadding` | `'default'` | 안쪽 여백. 기본은 `default`. 성격이 다른 요소가 세 종류 이상 섞인 영역에만 `wide`, 표를 가장자리까지 채울 때만 `none`. |
|
|
3353
3451
|
|
|
3354
3452
|
### SSectionHeaderCardHeader
|
|
3355
3453
|
|
package/dist/llms.txt
CHANGED
|
@@ -166,15 +166,24 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
|
|
|
166
166
|
- 형제 요소 간격은 margin 대신 부모의 `flex`/`grid` + `gap-sd-*`으로 잡는다.
|
|
167
167
|
- 시맨틱 간격 토큰 (텍스트 덩어리·요소 사이 기본 리듬):
|
|
168
168
|
|
|
169
|
+
화면은 세 층이다. **여백(padding)과 간격(gap)은 서로 다른 축이고, 규칙도 다르다.**
|
|
170
|
+
|
|
171
|
+
```text
|
|
172
|
+
페이지 프레임 SPage 가 여백을 넣는다. 직접 주지 않는다
|
|
173
|
+
└ 블록 페이지 직계 자식 (헤더·필터·툴바·테이블·섹션 카드)
|
|
174
|
+
블록 ↔ 블록 간격은 gap-sd-12
|
|
175
|
+
└ 섹션·패널 블록 중 "안에 콘텐츠를 담는 컨테이너"인 것
|
|
176
|
+
여기에만 안쪽 여백 선택지가 있다 (아래 "섹션·패널 안쪽 여백")
|
|
177
|
+
```
|
|
178
|
+
|
|
169
179
|
| 상황 | 값 |
|
|
170
180
|
| --- | --- |
|
|
171
|
-
| **페이지 콘텐츠 패딩** |
|
|
172
|
-
| **섹션 ↔
|
|
173
|
-
| **페이지 내 블록 ↔ 블록** (헤더·필터·툴바·테이블 사이) | **`gap-sd-12`** |
|
|
181
|
+
| **페이지 콘텐츠 패딩** | **`SPage` 가 `--cmp-pageBody-padding-default` 로 이미 넣는다.** 직접 주지 않는다 (덮어쓰면 토큰이 바뀌어도 안 따라간다). `SPage` 밖에서 같은 패딩이 필요하면 `p-sd-16` |
|
|
182
|
+
| **섹션 ↔ 섹션**, **블록 ↔ 블록** (헤더·필터·툴바·테이블 사이) | **`gap-sd-12`** |
|
|
174
183
|
| 요소 ↔ 요소 | **기본 `gap-sd-8`** (`--sys-space-stack-gap-element-normal`) · 타이트 `gap-sd-4`(`-tight`) · 여유 `gap-sd-12`(`-relaxed`) / `gap-sd-16`(`-wide`) |
|
|
175
184
|
| 제목 ↔ 설명 텍스트 | **수직 배치 `gap-sd-4`**(`--sys-space-stack-gap-text-normal`, 타이트 `gap-sd-2`) · **가로 배치 `gap-sd-8`**(`-relaxed`) |
|
|
176
185
|
|
|
177
|
-
정보 밀도가 높은 서비스라 블록
|
|
186
|
+
정보 밀도가 높은 서비스라 블록 **간격**을 넓게 벌리지 않는다. `gap-sd-16` / `gap-sd-24` 를 페이지 골격의 기본값으로 쓰지 않는다. (아래 나오는 `24` 는 **안쪽 여백**에만 열리는 값이고, 간격은 여기 표대로 12 다.)
|
|
178
187
|
|
|
179
188
|
#### 같은 컴포넌트를 여러 개 늘어놓을 때 (그룹 간격)
|
|
180
189
|
|
|
@@ -208,6 +217,64 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
|
|
|
208
217
|
- `SRadioButton` 그룹의 간격은 `-1px`(테두리 겹침 처리)이라 손으로 만들지 않는다.
|
|
209
218
|
- 정확한 값이 필요하면 토큰을 직접 참조해도 된다: `gap-[var(--cmp-checkbox-group-gap-horizontal)]`
|
|
210
219
|
|
|
220
|
+
#### 섹션·패널 안쪽 여백
|
|
221
|
+
|
|
222
|
+
**페이지 프레임은 예외 없이 `SPage` 가 넣는다.** 아래 규칙은 그 안의 **섹션·패널 레벨에만** 적용된다.
|
|
223
|
+
|
|
224
|
+
**컴포넌트가 자체 여백을 가지면 컴포넌트 기준이 우선한다.** `SKeyValueTable`·`STable` 처럼 자기 여백을 토큰으로 갖고 있는 컴포넌트에는 이 판정을 적용하지 않는다 — 손댈 것이 없다. 아래 판정이 필요한 자리는 **직접 만든 컨테이너**와 **`SSectionHeaderCard.Body`** 두 곳뿐이다.
|
|
225
|
+
|
|
226
|
+
판정은 **그 영역이 담고 있는 콘텐츠 덩어리의 종류 수**로 한다.
|
|
227
|
+
|
|
228
|
+
```text
|
|
229
|
+
자체 면(배경 또는 테두리)을 가진 덩어리만 센다.
|
|
230
|
+
제목·설명 같은 맨 텍스트와 검색·필터·버튼 같은 조작은 세지 않는다.
|
|
231
|
+
|
|
232
|
+
두 종류 반복 → p-sd-16 (그중 일부가 내부적으로 반복되더라도)
|
|
233
|
+
세 종류 이상 → p-sd-24
|
|
234
|
+
|
|
235
|
+
판단이 서지 않으면 16 으로 둔다.
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
| 안에 들어가는 것 | 안쪽 여백 |
|
|
239
|
+
| --- | --- |
|
|
240
|
+
| **같은 요소의 규칙적 반복** — 테이블 행, `SKeyValueTable` 행, 아코디언 목록, 카드 목록 | **`p-sd-16`** |
|
|
241
|
+
| **서로 다른 요소의 복합 구성** — 말풍선 + 버튼 묶음 + 시스템 안내 / 콜아웃 + 토글 카드 + 설명 | **`p-sd-24`** |
|
|
242
|
+
|
|
243
|
+
**카드라서 24 가 아니다.** 같은 카드가 규칙적으로 반복되면 16 이다. **섹션 내부에 어떤 정보가 들어가는지도 무관하다** — 섹션이 동일하게 반복되면 안에 버튼·입력·태그가 섞여 있어도 16 이다.
|
|
244
|
+
|
|
245
|
+
적용은 **화면 단위가 아니라 영역 단위**다. 한 화면 안에서도 칸마다 다르다.
|
|
246
|
+
|
|
247
|
+
| 영역 (상담 콘솔 예) | 안쪽 여백 | 이유 |
|
|
248
|
+
| --- | --- | --- |
|
|
249
|
+
| 좌측 상담 목록 | `p-sd-16` | 같은 항목 반복 |
|
|
250
|
+
| 중앙 대화 | `p-sd-24` | 말풍선·버튼 묶음·시스템 메시지 혼재 |
|
|
251
|
+
| 우측 템플릿 목록 | `p-sd-16` | 같은 아코디언 반복 |
|
|
252
|
+
|
|
253
|
+
§4 의 표준 골격(목록·폼·상세)은 **전부 16** 이다. 24 는 반복 구조가 없고 성격이 다른 덩어리가 쌓이는 영역에만 쓴다.
|
|
254
|
+
|
|
255
|
+
**위자드·탭처럼 한 프레임을 공유하는 화면은 가장 복합적인 화면을 기준으로 통일한다.** 예를 들어 어떤 단계가 콜아웃 + 토글 카드로 2종류라 단독으로는 16 이지만, 3종류인 단계와 한 위자드를 공유하므로 통일 규칙에 따라 위자드 전체가 24 가 된다.
|
|
256
|
+
|
|
257
|
+
**중첩되면 안쪽 여백을 주지 않는다.** 24 영역 안에 또 여백을 주면 가장자리가 40 으로 벌어져 한 면적처럼 읽힌다. 안쪽 카드·목록이 **배경색이 다르거나 테두리가 있어** 경계가 스스로 보이는 경우에만 자기 여백을 유지한다.
|
|
258
|
+
|
|
259
|
+
`SSectionHeaderCard.Body` 는 이 규칙을 **prop 으로 받는다** — 직접 `p-sd-*` 를 주지 않는다.
|
|
260
|
+
|
|
261
|
+
```tsx
|
|
262
|
+
<SSectionHeaderCard.Body>…</SSectionHeaderCard.Body> {/* 기본 = 16 */}
|
|
263
|
+
<SSectionHeaderCard.Body padding="wide">…</SSectionHeaderCard.Body> {/* 3종류 이상 */}
|
|
264
|
+
<SSectionHeaderCard.Body padding="none">…</SSectionHeaderCard.Body> {/* 표를 가장자리까지 */}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
#### 스크롤 영역의 하단 여백
|
|
268
|
+
|
|
269
|
+
스크롤을 끝까지 내렸을 때 마지막 항목이 화면 경계에 붙으면 **목록이 끝난 것인지 더 있는 것인지** 읽히지 않는다. 그래서 스크롤 영역은 **하단만** 넓게 둔다. 나머지 세 방향은 위 16 / 24 규칙 그대로다.
|
|
270
|
+
|
|
271
|
+
| 스크롤 종류 | 어떻게 |
|
|
272
|
+
| --- | --- |
|
|
273
|
+
| **패널 자체 스크롤** (좌측 목록, 중앙 대화 등) | 그 패널 안쪽 하단에 `pb-sd-80` |
|
|
274
|
+
| **페이지 단위 스크롤** | **`SPage` 가 넣는다. 직접 주지 않는다** |
|
|
275
|
+
|
|
276
|
+
`SPage` 는 기본으로 넣으므로 **아무것도 하지 않으면 맞다.** 끄는 경우는 하나뿐이다 — **페이지네이션이 붙은 테이블.** 페이지네이션이 이미 "여기서 끝"을 알려주므로 `scrollEndSpacing={false}` 로 끈다 (§4-2 목록 페이지).
|
|
277
|
+
|
|
211
278
|
### 2-3. 색상
|
|
212
279
|
|
|
213
280
|
**시맨틱 유틸리티를 우선 사용한다** — 의미가 이름에 담긴 토큰이 이미 유틸리티로 존재한다.
|
|
@@ -348,7 +415,8 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
|
|
|
348
415
|
- 작은 창이 아니라 **화면 하나가 통째로** 들어간다 — 검색 필터·테이블·페이지네이션이 그대로 있는 목록(`엑셀 파일 관리`), 헤더 카드·탭이 있는 상세(`이동 오더 상세`).
|
|
349
416
|
- 상세를 팝업으로 여는 이유는 **목록을 떠나지 않고 여러 건을 번갈아 보기** 위해서다. 단 상세가 **항상** 팝업인 것은 아니고, 데이터 양이 많을 때 팝업을 쓴다.
|
|
350
417
|
- 구조는 헤더(제목 중앙) + 본문 + 푸터다. **확정할 작업이 있으면 푸터에 `저장`, 조회만이면 `useFooter={false}`** 로 푸터를 없앤다.
|
|
351
|
-
-
|
|
418
|
+
- **본문 패딩은 `SPopup` 이 토큰으로 넣는다. 직접 주지 않는다** (`p-sd-*` 로 덮어쓰면 토큰이 바뀌어도 안 따라간다). 표를 가장자리까지 채우는 등 콘텐츠가 여백을 직접 다뤄야 할 때만 `noPadding` 으로 끈다.
|
|
419
|
+
- 그 밖에는 팝업 안도 일반 페이지와 같은 규칙(§4)을 따른다: 블록 간격 `gap-sd-12`, 표는 `SKeyValueTable` / `STable`.
|
|
352
420
|
|
|
353
421
|
```tsx
|
|
354
422
|
// 1) 목록에서 별도 창을 연다 — 창 크기 = 콘텐츠 크기
|
|
@@ -364,7 +432,8 @@ function openDetailPopup(orderId: string) {
|
|
|
364
432
|
export default function TransferOrderPopupPage() {
|
|
365
433
|
return (
|
|
366
434
|
<SPopup popupTitle="이동 오더 상세" useFooter={false}>
|
|
367
|
-
|
|
435
|
+
{/* 본문 패딩은 SPopup 이 토큰으로 넣는다 — p-sd-* 로 덮어쓰지 않는다 */}
|
|
436
|
+
<div className="flex flex-col gap-sd-12">
|
|
368
437
|
<SSectionHeaderCard>…</SSectionHeaderCard>
|
|
369
438
|
<STabs value={tab} tabs={TABS} onValueChange={setTab} />
|
|
370
439
|
<STable columns={columns} rows={rows} rowKey="id" />
|
|
@@ -405,21 +474,23 @@ SModal.create({ component: OrderModal, componentProps: { orderId } })
|
|
|
405
474
|
.onDismissed(() => {});
|
|
406
475
|
```
|
|
407
476
|
|
|
408
|
-
#### 3-3-5. 닫기 경로 — `persistent`
|
|
477
|
+
#### 3-3-5. 닫기 경로 — `persistent` 기본값은 컴포넌트마다 다르다
|
|
409
478
|
|
|
410
|
-
|
|
479
|
+
**입력을 담는 모달은 백드롭 클릭·ESC 로 닫히지 않는 것이 기본**이다. 닫기 시도는 흔들림(shake)으로만 반응한다. 작성 중인 내용을 실수로 잃지 않게 하기 위한 것이다. 반면 `SConfirmModal` 은 잃을 입력이 없으므로 **백드롭·ESC 로 닫히는 것이 기본**이다.
|
|
411
480
|
|
|
412
|
-
| | 기본 닫기 경로 | 백드롭·ESC |
|
|
413
|
-
| --- | --- | --- |
|
|
414
|
-
| `SActionModal` · `
|
|
415
|
-
| `SDrawer` | X 버튼, footer 버튼 | 막힘 (흔들림) |
|
|
416
|
-
| `
|
|
481
|
+
| | `persistent` 기본값 | 기본 닫기 경로 | 백드롭·ESC |
|
|
482
|
+
| --- | --- | --- | --- |
|
|
483
|
+
| `SActionModal` · `SLoadingModal` | `true` | X 버튼, 모달 안의 버튼 | 막힘 (흔들림) |
|
|
484
|
+
| `SDrawer` | `true` | X 버튼, footer 버튼 | 막힘 (흔들림) |
|
|
485
|
+
| `SConfirmModal` | `false` | X 버튼, 확인/취소 버튼 | **닫힌다** |
|
|
486
|
+
| `SPopover` · `STooltip` · `SSelect` 등 floating | — | 바깥 클릭·ESC | **막지 않는다** — 이 규칙의 대상이 아니다 |
|
|
417
487
|
|
|
418
488
|
따라서 다음을 지킨다.
|
|
419
489
|
|
|
420
490
|
- **닫을 수단을 반드시 하나는 둔다.** `SActionModal` 에 `showClose` 도 `button`/`footerLeft` 도 없으면 사용자가 모달을 닫을 방법이 없다. 백드롭이 더 이상 탈출구가 아니다.
|
|
421
|
-
-
|
|
422
|
-
- **`persistent={false}` 는 잃을 입력이 없을 때만.** 단순 알림처럼 임의로 닫혀도 아무것도 사라지지 않는 경우로 한정한다.
|
|
491
|
+
- **기본값과 같은 `persistent` 를 직접 주지 않는다.** 중복이다.
|
|
492
|
+
- **`SActionModal`·`SDrawer` 에 `persistent={false}` 는 잃을 입력이 없을 때만.** 단순 알림처럼 임의로 닫혀도 아무것도 사라지지 않는 경우로 한정한다.
|
|
493
|
+
- **`SConfirmModal` 에 `persistent` 를 켜는 것은 반드시 답을 받아야 할 때만.** 되돌릴 수 없는 파괴적 작업의 확인처럼, 임의로 닫히면 안 되는 경우로 한정한다.
|
|
423
494
|
|
|
424
495
|
작성 중인 내용이 있을 때 닫기를 시도하면 이탈 안내를 띄우는 것은 **소비 앱 몫**이다. 디자인 시스템은 dirty 상태를 알지 못하므로 백드롭·ESC 를 일괄 차단할 뿐이다. 안내가 필요하면 앱이 자체 dirty 판정 후 `SModal.confirm` 으로 띄운다.
|
|
425
496
|
|
|
@@ -635,18 +706,26 @@ const MENU: SGnbMenuItem[] = [
|
|
|
635
706
|
{ label: '상품', value: 'products', icon: 'box', children: [{ label: '목록', value: 'product-list' }] },
|
|
636
707
|
];
|
|
637
708
|
|
|
638
|
-
export default function AppShell({
|
|
709
|
+
export default function AppShell({
|
|
710
|
+
children,
|
|
711
|
+
scrollEndSpacing,
|
|
712
|
+
}: { children: React.ReactNode; scrollEndSpacing?: boolean }) {
|
|
639
713
|
return (
|
|
640
714
|
<SLayout type="box" header="fix">
|
|
641
715
|
{/* type/header/folded 는 SLayout 에만 준다 — SGnb 는 context 에서 읽는다 */}
|
|
642
716
|
<SGnb items={MENU} value={current} onValueChange={navigate} logo={<Logo />} />
|
|
643
|
-
{/* 콘텐츠
|
|
644
|
-
|
|
717
|
+
{/* 콘텐츠 패딩은 SPage 가 토큰으로 넣는다 — p-sd-* 로 덮어쓰지 않는다 */}
|
|
718
|
+
{/* 스크롤 끝 여백도 SPage 가 넣는다. 끄는 건 페이지네이션 있는 목록뿐이라 페이지가 정한다 */}
|
|
719
|
+
<SPage background="frame" scrollEndSpacing={scrollEndSpacing}>{children}</SPage>
|
|
645
720
|
</SLayout>
|
|
646
721
|
);
|
|
647
722
|
}
|
|
648
723
|
```
|
|
649
724
|
|
|
725
|
+
**최소 너비는 `SLayout` 이 보장한다 — 앱이 `min-w-*` 를 직접 주지 않는다.** 창이 최소 너비(`SLAYOUT_MIN_WIDTH`, GNB 포함한 전체 기준)보다 좁아지면 GNB·상단바는 제자리에 남고 **`SPage` 안에만 가로 스크롤이 생긴다.** 문서(브라우저 창)에는 가로 스크롤이 생기지 않는다. 그래서 셸을 감싸는 요소에 `min-width` 나 `overflow-x` 를 걸지 않는다 — 걸면 창 전체가 스크롤되어 GNB 가 화면 밖으로 밀린다.
|
|
726
|
+
|
|
727
|
+
**셸의 `SPage` 는 모든 페이지가 공유하므로, 스크롤 끝 여백을 끄려면 페이지가 셸에 알려야 한다.** 위처럼 프레임 컴포넌트가 `scrollEndSpacing` 을 받아 그대로 넘기고, 페이지네이션이 있는 목록 페이지만 `false` 를 준다 (§4-2). 나머지 페이지는 넘기지 않으면 기본값(켬)이 적용된다.
|
|
728
|
+
|
|
650
729
|
**상단바 배치는 `header` 가 정한다.** 요소 순서가 달라지므로 슬롯을 채우기 전에 어느 쪽인지부터 정한다.
|
|
651
730
|
|
|
652
731
|
| `header` | 상단바 배치 | 로고 폭 | `topContent` |
|
|
@@ -685,6 +764,7 @@ export default function AppShell({ children }: { children: React.ReactNode }) {
|
|
|
685
764
|
- **페이지 제목 줄에는 액션 버튼을 두지 않는다.** 가이드·매뉴얼 링크 등 부가 정보만 온다.
|
|
686
765
|
- **주요 액션(등록 등)은 `STableBar` 의 `rightActions`** 에 둔다.
|
|
687
766
|
- **선택 상태 액션(선택 삭제 등)은 `STableBar` 의 `actions`** 에 둔다. `actions` 슬롯은 건수 요약이 있으면 앞에 구분선(`SDivider`)을 **자동으로** 넣으므로 직접 구분선을 만들지 않는다.
|
|
767
|
+
- **페이지네이션이 있으면 스크롤 끝 여백을 끈다** — 셸의 `SPage` 에 `scrollEndSpacing={false}` 를 넘긴다 (§2-2). 페이지네이션이 이미 "여기서 끝"을 알려준다.
|
|
688
768
|
|
|
689
769
|
```tsx
|
|
690
770
|
import {
|
|
@@ -922,6 +1002,10 @@ export default function ProductDetailPage() {
|
|
|
922
1002
|
| `slot` | 헤더 우측 영역 (버튼 등) |
|
|
923
1003
|
| `thickness` | 상단 강조선 — `false`(기본) / `'default'` / `'accent'` |
|
|
924
1004
|
|
|
1005
|
+
| Prop (Body) | 용도 |
|
|
1006
|
+
| --- | --- |
|
|
1007
|
+
| `padding` | 안쪽 여백 — `'default'`(기본) / `'wide'` / `'none'`. 판정은 §2-2 "섹션·패널 안쪽 여백". `p-sd-*` 를 직접 주지 않는다 |
|
|
1008
|
+
|
|
925
1009
|
---
|
|
926
1010
|
|
|
927
1011
|
## 5. 자가 점검 체크리스트
|
|
@@ -934,9 +1018,13 @@ export default function ProductDetailPage() {
|
|
|
934
1018
|
- [ ] 간격이 전부 `sd-` 접두 스케일 값인가 (`gap-13`·`gap-sd-13` ❌ → `gap-sd-12` ✅)
|
|
935
1019
|
- [ ] 본문이 12px(`typo-body-sm-default`)인가 (14px 본문 ❌)
|
|
936
1020
|
- [ ] 텍스트 회색 위계를 순차 적용했는가 (기본 → `text-fg-secondary` → `text-fg-tertiary`, 단계 건너뛰기 ❌)
|
|
937
|
-
- [ ] `SPage` 의 기본
|
|
1021
|
+
- [ ] `SPage`·`SPopup` 의 기본 패딩을 `p-sd-*` 로 덮어쓰지 않았는가, 블록·섹션 **간격**이 `gap-sd-12` 인가 (`gap-sd-16`/`gap-sd-24` ❌ — 24 는 안쪽 여백에만 열린다)
|
|
1022
|
+
- [ ] 섹션·패널의 안쪽 **여백**이 §2-2 판정과 맞는가 (덩어리 두 종류 → 16 / 세 종류 이상 → 24, 서면 16)
|
|
1023
|
+
- [ ] `SSectionHeaderCard.Body` 의 여백을 `p-sd-*` 가 아니라 `padding` prop 으로 줬는가
|
|
1024
|
+
- [ ] 자체 스크롤하는 패널의 하단에 `pb-sd-80` 이 있는가, 페이지네이션 있는 목록에서 `scrollEndSpacing={false}` 를 넘겼는가
|
|
938
1025
|
- [ ] 같은 컴포넌트를 나열할 때 §2-2 그룹 간격을 썼는가 (체크박스 가로 `gap-sd-24` 등)
|
|
939
1026
|
- [ ] 페이지가 §4의 표준 골격에서 시작했는가
|
|
1027
|
+
- [ ] 앱 셸이나 그 바깥에 `min-width`·`overflow-x` 를 직접 걸지 않았는가 (최소 너비는 `SLayout` 이 보장한다, §4-1)
|
|
940
1028
|
- [ ] 필터·폼·상세 정보를 `SKeyValueTable` 로 만들었는가 (컨트롤을 `div` 로 나열하지 않았는가)
|
|
941
1029
|
- [ ] 섹션 구분에 `SSectionHeaderCard` 를 썼는가 (직접 만든 카드가 아니라)
|
|
942
1030
|
- [ ] 목록의 주요 액션이 `STableBar` 의 `rightActions` 에 있는가 (페이지 제목 줄이 아니라)
|
|
@@ -1010,7 +1098,7 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
1010
1098
|
- brilliantblue: 10 20 25 40 50 60 70 75 80 85 90 95 99 05
|
|
1011
1099
|
- bg: screen frame brand subtle accent accentLight accentBright accentFaint deep danger dangerLight warning caution progress success successLight neutralLight neutralBright tableHeader
|
|
1012
1100
|
- fg: primary secondary tertiary quaternary brand subtleWarm subtleCool deep success dangerLight accent accentLight danger warning inverse
|
|
1013
|
-
- border: default strong accent accentLight danger success
|
|
1101
|
+
- border: default strong bold accent accentLight danger warning success
|
|
1014
1102
|
- disabled: bg text icon border
|
|
1015
1103
|
- link: accent
|
|
1016
1104
|
- divider: default
|
|
@@ -1026,8 +1114,9 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
1026
1114
|
### 타이포그래피 프리셋 (클래스 하나로 size/weight/line-height 적용)
|
|
1027
1115
|
| 클래스 | size | weight | line-height |
|
|
1028
1116
|
| --- | --- | --- | --- |
|
|
1029
|
-
| typo-heading-sm | 14px | 700 | 24px |
|
|
1030
1117
|
| typo-heading-xs | 12px | 700 | 20px |
|
|
1118
|
+
| typo-heading-xxs | 11px | 700 | 18px |
|
|
1119
|
+
| typo-heading-sm | 14px | 700 | 24px |
|
|
1031
1120
|
| typo-heading-md | 16px | 700 | 26px |
|
|
1032
1121
|
| typo-heading-lg | 18px | 700 | 30px |
|
|
1033
1122
|
| typo-body-xs-default | 11px | 400 | 18px |
|
|
@@ -1103,10 +1192,12 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
1103
1192
|
--sys-color-blue-subtle
|
|
1104
1193
|
--sys-color-border-accent
|
|
1105
1194
|
--sys-color-border-accentLight
|
|
1195
|
+
--sys-color-border-bold
|
|
1106
1196
|
--sys-color-border-danger
|
|
1107
1197
|
--sys-color-border-default
|
|
1108
1198
|
--sys-color-border-strong
|
|
1109
1199
|
--sys-color-border-success
|
|
1200
|
+
--sys-color-border-warning
|
|
1110
1201
|
--sys-color-control-bg-inverseHover
|
|
1111
1202
|
--sys-color-darkblue-strong
|
|
1112
1203
|
--sys-color-darkblue-subtle
|