sellmate-design-system-react 9.0.0-beta.3 → 9.0.0-beta.30
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 +599 -94
- package/README.md +111 -9
- package/dist/components/SBadge/README.md +24 -0
- package/dist/components/SBadge/SBadge.d.ts +1 -1
- package/dist/components/SBarcodeInput/README.md +9 -1
- package/dist/components/SBarcodeInput/SBarcodeInput.d.ts +7 -1
- package/dist/components/SButton/README.md +36 -0
- package/dist/components/SCalendar/README.md +13 -0
- package/dist/components/SCallout/README.md +15 -0
- package/dist/components/SCard/README.md +16 -0
- package/dist/components/SCard/SCard.d.ts +17 -2
- package/dist/components/SCheckbox/README.md +8 -0
- package/dist/components/SChipFilter/README.md +326 -13
- package/dist/components/SChipFilter/SChipFilter.d.ts +137 -58
- package/dist/components/SChipFilter/index.d.ts +1 -1
- package/dist/components/SChipInput/README.md +15 -1
- package/dist/components/SChipInput/SChipInput.d.ts +7 -1
- package/dist/components/SChipInput/index.d.ts +1 -1
- package/dist/components/SCircleProgress/README.md +8 -0
- package/dist/components/SConfirmModal/README.md +17 -1
- package/dist/components/SConfirmModal/SConfirmModal.d.ts +2 -2
- package/dist/components/SConfirmModal/index.d.ts +1 -1
- package/dist/components/SDatePicker/README.md +57 -2
- package/dist/components/SDatePicker/SDatePicker.d.ts +57 -4
- package/dist/components/SDatePicker/index.d.ts +1 -1
- package/dist/components/SDateRangePicker/README.md +17 -2
- package/dist/components/SDateRangePicker/SDateRangePicker.d.ts +16 -3
- package/dist/components/SDivider/README.md +5 -0
- package/dist/components/SDivider/SDivider.d.ts +11 -0
- package/dist/components/SDraggableItem/README.md +37 -0
- package/dist/components/SDraggableItem/SDraggableItem.d.ts +2 -0
- package/dist/components/SDraggableItem/index.d.ts +1 -1
- package/dist/components/SDraggableList/README.md +30 -0
- package/dist/components/SDraggableList/SDraggableList.d.ts +11 -0
- package/dist/components/SDraggableList/index.d.ts +1 -1
- package/dist/components/SDrawer/README.md +8 -0
- package/dist/components/SDropdownButton/README.md +19 -0
- package/dist/components/SEditor/EditorBody.d.ts +36 -0
- package/dist/components/SEditor/EditorToolbar.d.ts +93 -0
- package/dist/components/SEditor/README.md +203 -0
- package/dist/components/SEditor/SEditor.d.ts +114 -0
- package/dist/components/SEditor/editor-icons.d.ts +63 -0
- package/dist/components/SEditor/editor.config.d.ts +119 -0
- package/dist/components/SEditor/index.d.ts +8 -0
- package/dist/components/SEditor/tiptap-api.d.ts +29 -0
- package/dist/components/SEditor/use-is-mobile.d.ts +14 -0
- package/dist/components/SExpansionItem/README.md +36 -0
- package/dist/components/SExpansionItem/index.d.ts +1 -1
- package/dist/components/SField/README.md +27 -2
- package/dist/components/SField/SField.d.ts +22 -4
- package/dist/components/SFilePicker/README.md +15 -1
- package/dist/components/SFilePicker/SFilePicker.d.ts +7 -1
- package/dist/components/SFooter/README.md +25 -0
- package/dist/components/SFooter/SFooter.d.ts +4 -3
- package/dist/components/SForm/README.md +11 -0
- package/dist/components/SGhostButton/README.md +20 -4
- package/dist/components/SGnb/README.md +51 -0
- package/dist/components/SGnb/SGnb.d.ts +22 -0
- package/dist/components/SGnb/gnb.config.d.ts +7 -0
- package/dist/components/SGnb/index.d.ts +1 -1
- package/dist/components/SGnbSystem/README.md +121 -0
- package/dist/components/SGnbSystem/SGnbSystem.d.ts +93 -0
- package/dist/components/SGnbSystem/gnbSystem.config.d.ts +89 -0
- package/dist/components/SGnbSystem/index.d.ts +2 -0
- package/dist/components/SGuide/README.md +15 -0
- package/dist/components/SIcon/README.md +8 -0
- package/dist/components/SIcon/SIcon.d.ts +1 -1
- package/dist/components/SIcon/icons.gen.d.ts +2 -0
- package/dist/components/SImage/README.md +14 -0
- package/dist/components/SInput/README.md +3 -1
- package/dist/components/SInput/SInput.d.ts +7 -1
- package/dist/components/SKeyValueTable/README.md +87 -0
- package/dist/components/SKeyValueTable/SKeyValueTable.d.ts +14 -3
- package/dist/components/SKeyValueTable/index.d.ts +1 -1
- package/dist/components/SLayout/README.md +16 -0
- package/dist/components/SLayout/index.d.ts +1 -1
- package/dist/components/SLinearProgress/README.md +15 -0
- package/dist/components/SLinearProgress/SLinearProgress.d.ts +3 -0
- package/dist/components/SLinearProgress/index.d.ts +1 -1
- package/dist/components/SList/README.md +5 -1
- package/dist/components/SList/SList.d.ts +0 -2
- package/dist/components/SListItem/README.md +41 -0
- package/dist/components/SListItem/index.d.ts +1 -1
- package/dist/components/SLoadingModal/README.md +9 -1
- package/dist/components/SLoadingModal/SLoadingModal.d.ts +2 -2
- package/dist/components/SLoadingModal/index.d.ts +1 -1
- package/dist/components/SNumberInput/README.md +9 -1
- package/dist/components/SNumberInput/SNumberInput.d.ts +7 -1
- package/dist/components/SPage/README.md +41 -1
- package/dist/components/SPage/SPage.d.ts +24 -2
- package/dist/components/SPage/index.d.ts +1 -1
- package/dist/components/SPage/page.config.d.ts +8 -0
- package/dist/components/SPopover/README.md +15 -0
- package/dist/components/SPopup/README.md +19 -0
- package/dist/components/SPortal/README.md +14 -0
- package/dist/components/SRadio/README.md +20 -0
- package/dist/components/SRadioButton/README.md +18 -0
- package/dist/components/SRadioButton/SRadioButton.d.ts +1 -1
- package/dist/components/SRadioCard/README.md +78 -0
- package/dist/components/SRadioCard/SRadioCard.d.ts +29 -0
- package/dist/components/SRadioCard/SRadioCardGroup.d.ts +36 -0
- package/dist/components/SRadioCard/index.d.ts +2 -0
- package/dist/components/SScrollArea/README.md +14 -0
- package/dist/components/SSearchInput/README.md +63 -0
- package/dist/components/SSearchInput/SSearchInput.d.ts +52 -0
- package/dist/components/SSearchInput/index.d.ts +1 -0
- package/dist/components/SSectionHeaderCard/README.md +44 -20
- package/dist/components/SSectionHeaderCard/SSectionHeaderCard.d.ts +31 -14
- package/dist/components/SSectionHeaderCard/index.d.ts +1 -1
- package/dist/components/SSelect/README.md +24 -4
- package/dist/components/SSelect/SSelect.d.ts +14 -2
- package/dist/components/SSplitter/README.md +15 -0
- package/dist/components/SStepper/README.md +26 -0
- package/dist/components/SSwitch/README.md +13 -0
- package/dist/components/SSystemActionButton/README.md +134 -0
- package/dist/components/SSystemActionButton/SSystemActionButton.d.ts +79 -0
- package/dist/components/SSystemActionButton/index.d.ts +2 -0
- package/dist/components/SSystemActionButton/systemActionButton.config.d.ts +87 -0
- package/dist/components/STable/README.md +87 -1
- package/dist/components/STable/STable.d.ts +141 -11
- package/dist/components/STable/index.d.ts +1 -1
- package/dist/components/STabs/README.md +12 -1
- package/dist/components/STabs/STabs.d.ts +2 -4
- package/dist/components/STabs/index.d.ts +1 -1
- package/dist/components/STabs/tabs.config.d.ts +3 -4
- package/dist/components/STag/README.md +52 -0
- package/dist/components/STag/STag.d.ts +3 -1
- package/dist/components/STextLink/README.md +17 -0
- package/dist/components/STextLink/STextLink.d.ts +2 -0
- package/dist/components/STextarea/README.md +1 -1
- package/dist/components/STextarea/STextarea.d.ts +7 -1
- package/dist/components/STimePicker/README.md +15 -1
- package/dist/components/STimePicker/STimePicker.d.ts +7 -1
- package/dist/components/STimePicker/timepicker.config.d.ts +7 -0
- package/dist/components/STimeRangePicker/README.md +27 -1
- package/dist/components/STimeRangePicker/STimeRangePicker.d.ts +7 -1
- package/dist/components/SToast/README.md +24 -0
- package/dist/components/SToggle/README.md +8 -0
- package/dist/components/SToggle/index.d.ts +1 -1
- package/dist/components/STooltip/README.md +22 -0
- package/dist/components/STree/README.md +41 -0
- package/dist/components/STree/STree.d.ts +8 -0
- package/dist/components/STree/index.d.ts +1 -1
- package/dist/index.cjs +5483 -1150
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.js +5458 -1149
- package/dist/index.js.map +1 -1
- package/dist/lib/field-width.d.ts +31 -0
- package/dist/lib/floating-height.d.ts +15 -0
- package/dist/lib/story-docs.d.ts +19 -3
- package/dist/lib/truncated-value-tooltip.d.ts +18 -0
- package/dist/llms-full.txt +2813 -193
- package/dist/llms.txt +604 -96
- package/dist/styles.css +1249 -135
- package/dist/theme.css +142 -16
- package/eslint/index.mjs +14 -0
- package/eslint/lib/table-column.mjs +26 -0
- package/eslint/rules/divider-vertical-height.mjs +137 -0
- package/eslint/rules/field-width-grade.d.mts +41 -0
- package/eslint/rules/field-width-grade.mjs +311 -0
- package/eslint/rules/table-column-width.mjs +110 -0
- package/eslint/scale.gen.mjs +5 -2
- package/package.json +11 -1
package/dist/llms-full.txt
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# sellmate-design-system-react — AI 에이전트 참조 문서
|
|
4
4
|
|
|
5
|
-
사용 규칙(§1) · 토큰 어휘(§2) · 전체 컴포넌트 Props(§3) 를 한 파일에 담은 판본이다.
|
|
5
|
+
사용 규칙(§1) · 토큰 어휘(§2) · 전체 컴포넌트 Props·Types(§3) 를 한 파일에 담은 판본이다.
|
|
6
6
|
|
|
7
7
|
## 1. 사용 규칙 (AGENTS.md 전문)
|
|
8
8
|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
> **대상**: 이 패키지로 화면을 만드는 소비 앱의 개발자와 AI 코딩 에이전트(Claude 등).
|
|
12
12
|
> 이 문서는 "무엇을 언제 쓰고, 무엇을 쓰면 안 되는지"의 단일 기준이다.
|
|
13
|
-
> 개별 컴포넌트의 상세 Props/Events는 `node_modules/sellmate-design-system-react/dist/components/<이름>/README.md` 를 참조한다.
|
|
13
|
+
> 개별 컴포넌트의 상세 Props/Events와 그 Props 가 쓰는 타입 정의(Types)는 `node_modules/sellmate-design-system-react/dist/components/<이름>/README.md` 를 참조한다.
|
|
14
14
|
|
|
15
15
|
## 0. 최우선 원칙 — 디자인 시스템 컴포넌트가 먼저다
|
|
16
16
|
|
|
@@ -28,17 +28,17 @@
|
|
|
28
28
|
|
|
29
29
|
### 0-1. 전체 컴포넌트 인덱스
|
|
30
30
|
|
|
31
|
-
무엇을 만들지 정했으면 **이 표에서 먼저 찾는다.** 상세 Props 는 `dist/components/<이름>/README.md` 참조.
|
|
31
|
+
무엇을 만들지 정했으면 **이 표에서 먼저 찾는다.** 상세 Props·Types 는 `dist/components/<이름>/README.md` 참조.
|
|
32
32
|
|
|
33
33
|
**이 표에서 어느 것을 골라야 할지 모르겠으면 §3-0 "의도 → 컴포넌트 라우팅" 으로 간다.** 하려는 일을 문장으로 찾으면 답이 하나 나온다 — 여기 인덱스는 "무엇이 있는지", §3-0 은 "언제 그걸 쓰는지" 를 담당한다.
|
|
34
34
|
|
|
35
35
|
| 분류 | 컴포넌트 |
|
|
36
36
|
| --- | --- |
|
|
37
|
-
| **버튼·링크** | `SButton` `SGhostButton` `SDropdownButton` `STextLink` `SSwitch` `SToggle` |
|
|
38
|
-
| **입력 (폼)** | `SForm` `SField` `SInput` `SNumberInput` `STextarea` `SSelect` `SCheckbox` `SRadio` `SRadioGroup` `SRadioButton` `SChip` `SChipInput` `SBarcodeInput` `SFilePicker` |
|
|
39
|
-
| **날짜·시간** | `SCalendar` `SDatePicker` `SDateRangePicker` `STimePicker` `STimeRangePicker` |
|
|
37
|
+
| **버튼·링크** | `SButton` `SGhostButton` `SDropdownButton` `SSystemActionButton` `STextLink` `SSwitch` `SToggle` |
|
|
38
|
+
| **입력 (폼)** | `SForm` `SField` `SInput` `SSearchInput` `SNumberInput` `STextarea` `SEditor` `SSelect` `SCheckbox` `SRadio` `SRadioGroup` `SRadioButton` `SRadioCard` `SRadioCardGroup` `SChip` `SChipInput` `SBarcodeInput` `SFilePicker` |
|
|
39
|
+
| **날짜·시간** | `SCalendar` `SDatePicker` `SDatePickerYearListbox` `SDatePickerMonthListbox` `SDateRangePicker` `STimePicker` `STimeRangePicker` |
|
|
40
40
|
| **표·목록** | `STable` `STableBar` `SChipFilter` `SKeyValueTable` `SList` `SListItem` `SExpansionList` `SDraggableList` `SDraggableItem` `STree` |
|
|
41
|
-
| **레이아웃** | `SLayout` `SGnb` `SPage`(제목 영역은 `header` prop) `SSectionHeaderCard` `SCard` `SDivider` `SSplitter` `SScrollArea` `SExpansionItem` |
|
|
41
|
+
| **레이아웃** | `SLayout` `SGnb` `SGnbSystem` `SPage`(제목 영역은 `header` prop) `SSectionHeaderCard` `SCard` `SDivider` `SSplitter` `SScrollArea` `SExpansionItem` |
|
|
42
42
|
| **내비게이션** | `STabs` `SPagination` `SStepper` |
|
|
43
43
|
| **표시·상태** | `STag` `SBadge` `SIcon` `SImage` `SCallout` `SGuide` |
|
|
44
44
|
| **진행·로딩** | `SLinearProgress` `SCircleProgress` `SLoadingContainer` `SLoadingModal` |
|
|
@@ -68,19 +68,21 @@ AI 에이전트는 코드를 생성하기 전에 이 목록을 반드시 지킨
|
|
|
68
68
|
| --- | --- |
|
|
69
69
|
| `<button>` | `SButton`, `SGhostButton`, `SDropdownButton`, `STextLink` |
|
|
70
70
|
| `<input type="text/password/...">` | `SInput` |
|
|
71
|
+
| `<input type="search">` | `SSearchInput` |
|
|
71
72
|
| `<input type="number">` | `SNumberInput` |
|
|
72
73
|
| `<input type="checkbox">` | `SCheckbox`, `SToggle`, `SSwitch` |
|
|
73
74
|
| `<input type="radio">` | `SRadio`, `SRadioButton` |
|
|
74
75
|
| `<input type="file">` | `SFilePicker` |
|
|
75
76
|
| `<select>` | `SSelect` |
|
|
76
77
|
| `<textarea>` | `STextarea` |
|
|
78
|
+
| `contenteditable`, 직접 붙인 에디터 라이브러리 | `SEditor` |
|
|
77
79
|
| `<table>` | `STable`, `SKeyValueTable` |
|
|
78
80
|
| `<form>` | `SForm` |
|
|
79
81
|
| `<dialog>`, 직접 만든 오버레이 | `SModal.confirm(...)`, `SModal.create(...)`, `SPopup` |
|
|
80
82
|
| `alert()`, `confirm()` | `SToast`, `SModal.confirm(...)` |
|
|
81
83
|
| 직접 만든 탭/페이지네이션/스텝퍼 | `STabs`, `SPagination`, `SStepper` |
|
|
82
84
|
| `<ul>`/`<li>` 로 만든 목록 UI | `SList` + `SListItem` (드래그 정렬은 `SDraggableItem`) |
|
|
83
|
-
| 직접 만든 섹션 카드(제목 바 + 본문 박스) | `SSectionHeaderCard`
|
|
85
|
+
| 직접 만든 섹션 카드(제목 바 + 본문 박스) | `SSectionHeaderCard` 의 `title` / `padding` props |
|
|
84
86
|
| `<svg>` 직접 삽입, 이모지 아이콘 | `SIcon` |
|
|
85
87
|
| `<hr>` | `SDivider` |
|
|
86
88
|
| `<details>` / `<summary>` | `SExpansionItem` |
|
|
@@ -117,7 +119,7 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
|
|
|
117
119
|
|
|
118
120
|
`text-14 font-bold` 같은 조합을 즉흥으로 만들지 않는다. §2-1의 `typo-*` 프리셋 클래스를 쓴다.
|
|
119
121
|
|
|
120
|
-
### 1-4.
|
|
122
|
+
### 1-4. 숫자·날짜 표기
|
|
121
123
|
|
|
122
124
|
**숫자를 화면에 표시할 때는 예외 없이 `toLocaleString()` 을 거쳐 세 자리마다 콤마를 넣는다.**
|
|
123
125
|
금액·수량·건수·재고 무엇이든, 테이블·상세·요약 문구 어디에 놓이든 같다.
|
|
@@ -132,6 +134,18 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
|
|
|
132
134
|
|
|
133
135
|
**번호·코드는 제외한다.** 전화번호·사업자번호·송장번호·상품코드처럼 대상을 가리키는 값은 크기를 비교하는 숫자가 아니라 **서식이 정해진 문자열**이다. 여기에 콤마를 넣으면 송장번호 `123456789` 가 `123,456,789` 로 보여 값 자체가 달라진다.
|
|
134
136
|
|
|
137
|
+
**날짜는 `YYYY-MM-DD` 로 쓴다.** 자릿수를 채우고 하이픈으로 구분한다 — `2026-08-13`.
|
|
138
|
+
`2026. 8. 13.` 처럼 점으로 구분하거나 한 자리로 줄이지 않는다. 자릿수가 고정돼야 세로줄이 맞고,
|
|
139
|
+
컬럼 폭을 형식으로 계산할 수 있다(§3-4). 일시가 필요하면 `YYYY-MM-DD HH:mm`.
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
❌ {new Date(v).toLocaleDateString()} ❌ {`${y}. ${m}. ${d}.`}
|
|
143
|
+
✅ {v} // 서버가 이미 YYYY-MM-DD 로 준 값
|
|
144
|
+
✅ format: (v: string) => v.slice(0, 10)
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
**`toLocaleDateString()` 은 쓰지 않는다** — 로케일에 따라 결과가 바뀌어 표기를 지킬 수 없다.
|
|
148
|
+
|
|
135
149
|
---
|
|
136
150
|
|
|
137
151
|
## 2. 조합 규칙 — 화면을 어떻게 쌓는가
|
|
@@ -144,9 +158,9 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
|
|
|
144
158
|
|
|
145
159
|
| 층 | 무엇인가 | 컴포넌트 |
|
|
146
160
|
| --- | --- | --- |
|
|
147
|
-
| **셸** | 앱 전체 뼈대. 페이지가 바뀌어도 남는다 | `SLayout` `SGnb` `SPage`(제목 영역은 `header` prop) |
|
|
161
|
+
| **셸** | 앱 전체 뼈대. 페이지가 바뀌어도 남는다 | `SLayout` `SGnb` `SGnbSystem`(GNB 맨 아래 패널) `SPage`(제목 영역은 `header` prop) |
|
|
148
162
|
| **블록** | `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` `SImage` `SLinearProgress` `SCircleProgress` |
|
|
163
|
+
| **요소** | 블록 **안에** 놓이는 컨트롤. 혼자 페이지에 서지 않는다 | `SButton` `SGhostButton` `SDropdownButton` `SSystemActionButton` `SField` `SInput` `SSearchInput` `SNumberInput` `STextarea` `SEditor` `SSelect` `SCheckbox` `SRadio` `SRadioGroup` `SRadioButton` `SRadioCard` `SRadioCardGroup` `SSwitch` `SToggle` `SChipInput` `SBarcodeInput` `SFilePicker` `SDatePicker` `SDatePickerYearListbox` `SDatePickerMonthListbox` `SDateRangePicker` `STimePicker` `STimeRangePicker` `SCalendar` `SListItem` `SExpansionItem` `SDraggableItem` `SImage` `SLinearProgress` `SCircleProgress` |
|
|
150
164
|
| **인라인** | 텍스트 흐름·셀·라벨 안에 섞인다. 혼자 블록이 되지 않는다 | `STag` `SBadge` `SIcon` `STextLink` `SChip` |
|
|
151
165
|
| **레이어** | 문서 흐름 **밖**에 떠서 그려진다. 어느 층에서 띄우든 레이아웃에 영향이 없다 | `SModal` `SActionModal` `SConfirmModal` `SPopup` `SDrawer` `SPopover` `STooltip` `SPortal` `SToast` `SLoadingModal` `SLoadingContainer` `SGuide` |
|
|
152
166
|
|
|
@@ -166,7 +180,7 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
|
|
|
166
180
|
| 담는 것 | 올 수 있는 것 | 오면 안 되는 것 |
|
|
167
181
|
| --- | --- | --- |
|
|
168
182
|
| `SPage` | **블록만** | **요소를 직접** — 버튼 하나도 블록에 담아 놓는다 |
|
|
169
|
-
| `SSectionHeaderCard
|
|
183
|
+
| `SSectionHeaderCard` 의 `children` | 그리는 블록 · 요소 | `SSectionHeaderCard` · `SCard` (카드 겹침, §3-7-8) |
|
|
170
184
|
| `SCard` | 그리는 블록 · 요소 | `SCard` · `SSectionHeaderCard` |
|
|
171
185
|
| `SForm` | 블록 (보통 `SKeyValueTable` + 하단 액션) | — |
|
|
172
186
|
| `SSplitter.Before` / `.After` | 블록 | — |
|
|
@@ -230,7 +244,7 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
|
|
|
230
244
|
|
|
231
245
|
페이지 제목만 18px 로 크게 두고 그 아래는 14 / 12 로 촘촘하게 간다. 중간 크기(16px)는 기본 골격에서 쓰지 않는다.
|
|
232
246
|
|
|
233
|
-
- **페이지·섹션 제목의 타이포를 직접 주지 않는다.** `SPage` 의 `header.title`, `SSectionHeaderCard
|
|
247
|
+
- **페이지·섹션 제목의 타이포를 직접 주지 않는다.** `SPage` 의 `header.title`, `SSectionHeaderCard` 의 `header.title` 이 이미 넣는다 — 그 위에 `typo-heading-lg`/`typo-heading-sm` 을 또 씌우지 않는다.
|
|
234
248
|
- **하위 제목이 필요하면 먼저 섹션을 나눌 수 없는지 본다.** 한 섹션 안에서 제목이 두 단으로 갈린다는 것은 대개 섹션이 둘이라는 뜻이다 (§3-7-8).
|
|
235
249
|
- 본문 안에서 한 단어를 강조할 때는 `typo-body-sm-medium` 을 쓴다. `typo-body-sm-bold` 는 제목 성격의 짧은 라벨에만 쓴다. <!-- TODO(디자인): 강조 굵기 기준 확정 -->
|
|
236
250
|
|
|
@@ -294,13 +308,14 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
|
|
|
294
308
|
|
|
295
309
|
- **라디오는 `SRadioGroup` 을 쓴다.** `direction="horizontal" | "vertical"` 만 주면 간격을 알아서 맞춘다 — 직접 `flex` 로 감싸지 않는다.
|
|
296
310
|
- `SRadioButton` 그룹의 간격은 `-1px`(테두리 겹침 처리)이라 손으로 만들지 않는다.
|
|
311
|
+
- **카드형 라디오는 `SRadioCardGroup` 을 쓴다.** 카드 사이 간격은 수평·수직이 같고 `SRadioGroup` 과 다르므로, 손으로 `flex gap-…` 을 맞추지 말고 `direction` 만 준다.
|
|
297
312
|
- 정확한 값이 필요하면 토큰을 직접 참조해도 된다: `gap-[var(--cmp-checkbox-group-gap-horizontal)]`
|
|
298
313
|
|
|
299
314
|
#### 섹션·패널 안쪽 여백
|
|
300
315
|
|
|
301
316
|
**페이지 프레임은 예외 없이 `SPage` 가 넣는다.** 아래 규칙은 그 안의 **섹션·패널 레벨에만** 적용된다.
|
|
302
317
|
|
|
303
|
-
**컴포넌트가 자체 여백을 가지면 컴포넌트 기준이 우선한다.** `SKeyValueTable`·`STable` 처럼 자기 여백을 토큰으로 갖고 있는 컴포넌트에는 이 판정을 적용하지 않는다 — 손댈 것이 없다. 아래 판정이 필요한 자리는 **직접 만든 컨테이너**와 **`SSectionHeaderCard
|
|
318
|
+
**컴포넌트가 자체 여백을 가지면 컴포넌트 기준이 우선한다.** `SKeyValueTable`·`STable` 처럼 자기 여백을 토큰으로 갖고 있는 컴포넌트에는 이 판정을 적용하지 않는다 — 손댈 것이 없다. 아래 판정이 필요한 자리는 **직접 만든 컨테이너**와 **`SSectionHeaderCard` 의 `padding`** 두 곳뿐이다.
|
|
304
319
|
|
|
305
320
|
판정은 **그 영역이 담고 있는 콘텐츠 덩어리의 종류 수**로 한다.
|
|
306
321
|
|
|
@@ -335,14 +350,67 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
|
|
|
335
350
|
|
|
336
351
|
**중첩되면 안쪽 여백을 주지 않는다.** 24 영역 안에 또 여백을 주면 가장자리가 40 으로 벌어져 한 면적처럼 읽힌다. 안쪽 카드·목록이 **배경색이 다르거나 테두리가 있어** 경계가 스스로 보이는 경우에만 자기 여백을 유지한다.
|
|
337
352
|
|
|
338
|
-
`SSectionHeaderCard
|
|
353
|
+
`SSectionHeaderCard` 는 이 규칙을 **`padding` prop 으로 받는다** — 직접 `p-sd-*` 를 주지 않는다.
|
|
354
|
+
|
|
355
|
+
```tsx
|
|
356
|
+
<SSectionHeaderCard title="기본 정보">…</SSectionHeaderCard> {/* 기본 = 16 */}
|
|
357
|
+
<SSectionHeaderCard title="기본 정보" padding="wide">…</SSectionHeaderCard> {/* 3종류 이상 */}
|
|
358
|
+
<SSectionHeaderCard title="기본 정보" padding="none">…</SSectionHeaderCard> {/* 표를 가장자리까지 */}
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
##### 카드 가장자리까지 채우는 표는 자기 테두리를 끈다
|
|
362
|
+
|
|
363
|
+
여기서 "표"는 **`STable` 과 `SKeyValueTable` 둘 다**다. 두 컴포넌트 모두 자기 바깥 테두리를 그리는데, 카드도 바깥 테두리를 그린다. `padding="none"` 으로 붙이면 **1px 두 개가 나란히 놓여 그 변만 2px** 로 보인다(카드의 다른 변은 1px 그대로라 굵기가 어긋난다).
|
|
364
|
+
|
|
365
|
+
```tsx
|
|
366
|
+
<SSectionHeaderCard title="발주 내역" padding="none">
|
|
367
|
+
<SKeyValueTable fields={…} bordered={false} radius="useTop" />
|
|
368
|
+
<STable columns={…} rows={…} bordered={false} radius="useTop" />
|
|
369
|
+
</SSectionHeaderCard>
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
- **`bordered={false}`** 로 표의 테두리를 끈다. 카드가 이미 그린다.
|
|
373
|
+
- **`radius="useTop"`** 으로 위쪽 모서리를 죽인다. 아래쪽 라운드는 카드가 처리한다.
|
|
374
|
+
- `STable` 은 **페이지네이션 바의 테두리와 `-mt-px` 겹침도 함께 꺼진다** — 본문 테두리가 없으면 겹칠 대상이 없어, 그대로 두면 페이지네이션만 테두리를 갖고 1px 어긋난다.
|
|
375
|
+
|
|
376
|
+
#### 본문 바탕 눌러앉히기 (선택)
|
|
377
|
+
|
|
378
|
+
`SSectionHeaderCard` 는 **`background="neutral"`** 로 본문 바탕을 한 단계 눌러앉힐 수 있다. 흰 면 덩어리(표·리스트)가 여럿일 때 그 덩어리들이 **"면 위에 놓인 객체"로 읽혀 묶음이 더 강하게 보인다.**
|
|
339
379
|
|
|
340
380
|
```tsx
|
|
341
|
-
<SSectionHeaderCard
|
|
342
|
-
<SSectionHeaderCard.Body padding="wide">…</SSectionHeaderCard.Body> {/* 3종류 이상 */}
|
|
343
|
-
<SSectionHeaderCard.Body padding="none">…</SSectionHeaderCard.Body> {/* 표를 가장자리까지 */}
|
|
381
|
+
<SSectionHeaderCard title="발주 상세" background="neutral">…흰 면 표 여럿…</SSectionHeaderCard>
|
|
344
382
|
```
|
|
345
383
|
|
|
384
|
+
**기본값(`frame`, 흰 면)이 틀린 것이 아니다.** 이 저장소의 표·리스트는 테두리·라운드·헤더 줄과 `gap-sd-12` 를 이미 갖고 있어 흰 바탕에서도 경계가 읽힌다. 위계를 한 단계 더 주고 싶을 때 고르는 수단이지, 덩어리가 둘 이상이면 반드시 깔아야 하는 규칙이 아니다.
|
|
385
|
+
|
|
386
|
+
깔아도 **효과가 없는** 자리는 있다.
|
|
387
|
+
|
|
388
|
+
- **덩어리가 가장자리까지 차는 경우** — `padding="none"` 으로 표를 채우면 깐 바탕이 표에 완전히 가려 보이지 않는다.
|
|
389
|
+
- **덩어리에 회색 면이 섞인 경우** — 그 덩어리가 바탕과 같은 색이 되어 묻힌다.
|
|
390
|
+
- **맨 텍스트·폼 컨트롤만 있는 본문** — 떠오를 흰 면이 없다.
|
|
391
|
+
|
|
392
|
+
바탕을 깐 경우, 표 사이 구분선(`SDivider`)은 대개 불필요해진다 — 색이 이미 경계를 만든다.
|
|
393
|
+
|
|
394
|
+
#### 페이지 높이 — 화면을 꽉 채우고, 스크롤은 각 영역 안에서
|
|
395
|
+
|
|
396
|
+
**대부분의 화면은 본문이 창을 꽉 채우고, 스크롤은 각 영역 안에서 일어난다.** 표는 자기 안에서 스크롤하고, 좌측 목록은 목록 안에서 스크롤하고, 페이지네이션·하단 액션은 자리에 고정된다. 이것이 표준이다 — 목록 페이지만의 예외가 아니다.
|
|
397
|
+
|
|
398
|
+
`SPage` 의 `contentHeight="fill"` 이 그 모드다. 프레임 컴포넌트에서 넘긴다(§4-1).
|
|
399
|
+
|
|
400
|
+
```tsx
|
|
401
|
+
<SPage contentHeight="fill">
|
|
402
|
+
<div className="flex h-full min-h-0 flex-col gap-sd-12">
|
|
403
|
+
<STableBar … />
|
|
404
|
+
<STable className="min-h-0 flex-1" pagination={…} />
|
|
405
|
+
</div>
|
|
406
|
+
</SPage>
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
- **`min-h-0 flex-1` 사슬이 페이지의 기본 골격이다.** `fill` 은 본문 래퍼에 `h-full` 을 주고, 거기서부터 스크롤될 자리까지 `min-h-0 flex-1` 이 이어져야 자식이 남은 높이를 잡는다.
|
|
410
|
+
- **사슬이 한 군데만 끊겨도 자식이 높이를 못 잡는데, 그 실패가 조용하다** — 화면은 그려지고 스크롤만 엉뚱한 데서 일어난다. 체크리스트(§5)로 확인한다.
|
|
411
|
+
- **페이지 스크롤은 예외다.** 블록의 높이가 정해져 있고 그 높이가 창보다 클 때만 페이지가 스크롤한다. 그때만 `contentHeight="auto"` 와 `scrollEndSpacing` 을 켠다.
|
|
412
|
+
- **`SScrollArea` 로 페이지 본문 전체를 감싸지 않는다.** 스크롤은 `SPage` 의 `<main>` 몫이고, 감싸면 스크롤바가 본문 패딩 안쪽으로 들어와 가장자리에서 뜬다. `SScrollArea` 는 페이지 안의 특정 영역에만 쓴다(§3-0 D).
|
|
413
|
+
|
|
346
414
|
#### 스크롤 영역의 하단 여백
|
|
347
415
|
|
|
348
416
|
스크롤을 끝까지 내렸을 때 마지막 항목이 화면 경계에 붙으면 **목록이 끝난 것인지 더 있는 것인지** 읽히지 않는다. 그래서 스크롤 영역은 **하단만** 넓게 둔다. 나머지 세 방향은 위 16 / 24 규칙 그대로다.
|
|
@@ -350,9 +418,9 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
|
|
|
350
418
|
| 스크롤 종류 | 어떻게 |
|
|
351
419
|
| --- | --- |
|
|
352
420
|
| **패널 자체 스크롤** (좌측 목록, 중앙 대화 등) | 그 패널 안쪽 하단에 `pb-[var(--cmp-pageBody-padding-scrollEnd)]` — `SPage` 와 같은 토큰이라 값이 바뀌어도 함께 따라간다 |
|
|
353
|
-
| **페이지 단위 스크롤** | **`SPage`
|
|
421
|
+
| **페이지 단위 스크롤** | **`SPage` 의 `scrollEndSpacing` 으로 켠다. 직접 패딩을 주지 않는다** |
|
|
354
422
|
|
|
355
|
-
`
|
|
423
|
+
**`scrollEndSpacing` 은 기본이 꺼져 있다.** 페이지가 실제로 스크롤될 때만 필요한 값이라, 조건 없이 붙이면 내용이 화면에 거의 딱 맞는 페이지까지 그 여백 때문에 스크롤되게 만든다. 페이지 스크롤을 쓰는 화면(`contentHeight="auto"` + 내용이 창보다 김)에서만 켠다. 페이지네이션처럼 끝을 알려주는 것이 이미 있으면 켜지 않는다.
|
|
356
424
|
|
|
357
425
|
### 2-3. 색상
|
|
358
426
|
|
|
@@ -414,11 +482,15 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
|
|
|
414
482
|
| --- | --- | --- |
|
|
415
483
|
| 한 줄 텍스트를 받는다 | `SInput` | §3-7-1 |
|
|
416
484
|
| 여러 줄 텍스트를 받는다 | `STextarea` | §3-7-1 |
|
|
485
|
+
| 제목·굵게·목록·색 같은 **서식이 남아야 하는** 글을 받는다 | `SEditor` | §3-7-1 |
|
|
486
|
+
| 목록·결과를 검색어로 좁힌다 | `SSearchInput` | §3-7-1 |
|
|
417
487
|
| 숫자(수량·금액)를 받는다 | `SNumberInput` | |
|
|
418
488
|
| 바코드를 스캔해 받는다 | `SBarcodeInput` | |
|
|
419
489
|
| 목록에서 하나 고르게 한다 | `SSelect` | §3-7-2 |
|
|
420
490
|
| 선택지를 항상 펼쳐 두고 하나 고르게 한다 | `SRadioGroup` | §3-7-2 |
|
|
421
491
|
| 버튼 모양으로 모드를 하나 고르게 한다 | `SRadioButton` | §3-7-2 |
|
|
492
|
+
| 선택지마다 설명이 붙어 한 줄로는 안 된다 | `SRadioCardGroup` | §3-7-2 |
|
|
493
|
+
| 설명이 붙은 선택지 카드 하나를 직접 배치한다 | `SRadioCard` | §3-7-2 |
|
|
422
494
|
| 라디오 하나를 표 셀 등에 직접 배치한다 | `SRadio` | §3-7-2 |
|
|
423
495
|
| 여러 개를 고르게 한다 / 동의를 받는다 | `SCheckbox` | §3-7-3 |
|
|
424
496
|
| 켜는 즉시 반영되는 설정을 준다 | `SSwitch` | §3-7-3 |
|
|
@@ -427,6 +499,8 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
|
|
|
427
499
|
| 입력된 값 하나를 지우거나 고치게 한다 | `SChip` | §3-1 |
|
|
428
500
|
| 파일을 받는다 | `SFilePicker` | |
|
|
429
501
|
| 날짜 하나를 받는다 | `SDatePicker` | §3-7-4 |
|
|
502
|
+
| 연도 선택 리스트만 커스텀 조합에 넣는다 | `SDatePickerYearListbox` | §3-7-4 |
|
|
503
|
+
| 연도+월 선택 리스트만 커스텀 조합에 넣는다 | `SDatePickerMonthListbox` | §3-7-4 |
|
|
430
504
|
| 날짜 기간을 받는다 | `SDateRangePicker` | §3-7-4 |
|
|
431
505
|
| 시각 하나를 받는다 | `STimePicker` | |
|
|
432
506
|
| 시각 범위를 받는다 | `STimeRangePicker` | |
|
|
@@ -460,6 +534,7 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
|
|
|
460
534
|
| 라벨이 있는 일반 액션을 준다 | `SButton` | §3-5 |
|
|
461
535
|
| 아이콘 하나로 뜻이 통하는 부가 조작을 준다 | `SGhostButton` | §3-5-5 |
|
|
462
536
|
| 한 버튼에 여러 선택지를 매단다 | `SDropdownButton` | §3-5-4 |
|
|
537
|
+
| GNB system 패널에서 한 줄짜리 행 액션을 준다 | `SSystemActionButton` | §3-5-7 |
|
|
463
538
|
|
|
464
539
|
#### D. 화면을 담고 나눈다
|
|
465
540
|
|
|
@@ -467,6 +542,7 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
|
|
|
467
542
|
| --- | --- | --- |
|
|
468
543
|
| 앱 셸(상단바 + 내비 + 본문)을 세운다 | `SLayout` | §4-1 |
|
|
469
544
|
| 좌측 내비게이션을 만든다 | `SGnb` | §4-1 |
|
|
545
|
+
| 좌측 내비게이션 맨 아래에 서비스·도메인·알림·설정·계정 묶음을 붙인다 | `SGnbSystem` | §4-1 |
|
|
470
546
|
| 페이지 본문을 담는다 (패딩·스크롤) | `SPage` | §4-1 |
|
|
471
547
|
| 페이지 제목(+ 서브 텍스트·뒤로가기·우측 슬롯)을 만든다 | `SPage` 의 `header` prop | §4-1 |
|
|
472
548
|
| 제목 있는 섹션으로 묶는다 | `SSectionHeaderCard` | §3-7-8 |
|
|
@@ -475,6 +551,8 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
|
|
|
475
551
|
| 사용자가 영역 크기를 조절하게 한다 | `SSplitter` | §3-6 |
|
|
476
552
|
| 특정 영역 안에서만 스크롤시킨다 | `SScrollArea` | |
|
|
477
553
|
|
|
554
|
+
**`SScrollArea` 로 페이지 본문 전체를 감싸지 않는다.** 페이지 스크롤은 `SPage` 의 `<main>` 몫이다 — 감싸면 스크롤바가 본문 패딩 안쪽으로 들어와 페이지 가장자리에서 떨어져 그려진다 (§2-2).
|
|
555
|
+
|
|
478
556
|
#### E. 다른 곳으로 이동시킨다
|
|
479
557
|
|
|
480
558
|
| 하려는 일 | 컴포넌트 | 갈림 |
|
|
@@ -558,9 +636,26 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
|
|
|
558
636
|
| | 무엇인가 | 크기 |
|
|
559
637
|
| --- | --- | --- |
|
|
560
638
|
| **SPopup** | **별도 브라우저 창** (`window.open` 으로 여는 전용 라우트) | 창 크기 = 콘텐츠 크기 |
|
|
561
|
-
| **SActionModal** | 같은 창 위 오버레이 카드 | `width`
|
|
639
|
+
| **SActionModal** | 같은 창 위 오버레이 카드 | `width` prop. **높이는 주지 않는다** (아래) |
|
|
562
640
|
| **SModal.confirm** (`SConfirmModal`) | 같은 창 위 확인창 | 고정 |
|
|
563
641
|
|
|
642
|
+
##### 모달 높이는 내용이 정하고, 상한은 시스템이 건다
|
|
643
|
+
|
|
644
|
+
**`height` 를 주지 않는다.** 높이는 내용이 정하고, 카드는 **뷰포트의 85%** 에서 멈춘다(시스템이 모든 모달에 건다). 데이터가 적으면 내용만큼 작아지고, 많으면 85% 에서 멈춘다.
|
|
645
|
+
|
|
646
|
+
가로는 좌우 24px 씩을 뺀 값으로 클램핑하는데 **세로만 비율**인 이유는, 모달이 화면을 거의 다 덮으면 뒤 맥락이 사라져 "떠 있는 것"으로 읽히지 않기 때문이다.
|
|
647
|
+
|
|
648
|
+
**상한에 닿았을 때 스크롤되어야 하는 것은 모달 본문이 아니라 표다.**
|
|
649
|
+
|
|
650
|
+
```tsx
|
|
651
|
+
<SActionModal modalTitle="발주 검토" button={{ label: '확정', onClick: submit }}>
|
|
652
|
+
{/* 표가 남은 높이를 먹고 자기 안에서 스크롤한다 — 헤더·합계·푸터는 늘 보인다 */}
|
|
653
|
+
<STable className="min-h-0 flex-1" columns={columns} rows={rows} />
|
|
654
|
+
</SActionModal>
|
|
655
|
+
```
|
|
656
|
+
|
|
657
|
+
본문(`overflow-auto` 영역)이 통째로 스크롤되면 표 헤더와 합계 줄이 위로 밀려 사라진다. `SActionModal` 의 본문은 이미 `min-h-0 flex-1` 이므로, 표에 `min-h-0 flex-1` 을 주면 세로 축이 이어져 표만 스크롤한다 (§4-2 목록 페이지와 같은 사슬이다).
|
|
658
|
+
|
|
564
659
|
**성격이 먼저 둘로 갈린다.**
|
|
565
660
|
|
|
566
661
|
| 성격 | 정의 | 컴포넌트 |
|
|
@@ -698,36 +793,51 @@ SModal.create({ component: OrderModal, componentProps: { orderId } })
|
|
|
698
793
|
|
|
699
794
|
작성 중인 내용이 있을 때 닫기를 시도하면 이탈 안내를 띄우는 것은 **소비 앱 몫**이다. 디자인 시스템은 dirty 상태를 알지 못하므로 백드롭·ESC 를 일괄 차단할 뿐이다. 안내가 필요하면 앱이 자체 dirty 판정 후 `SModal.confirm` 으로 띄운다.
|
|
700
795
|
|
|
701
|
-
### 3-4. 테이블 컬럼 —
|
|
796
|
+
### 3-4. 테이블 컬럼 — 정렬·너비·헤더
|
|
702
797
|
|
|
703
|
-
####
|
|
798
|
+
#### 정렬과 너비는 같은 표에서 정한다
|
|
704
799
|
|
|
705
|
-
|
|
706
|
-
자릿수가 세로로 맞아야 값의 크기를 눈으로 비교할 수 있기 때문이다.
|
|
800
|
+
컬럼을 정의할 때 정렬과 너비는 따로 판단하는 것이 아니다. 둘 다 **값의 성격**에서 나온다.
|
|
707
801
|
|
|
708
|
-
|
|
802
|
+
원칙 한 줄: **길이를 형식이 정하면 고정, 사용자가 정하면 가변.**
|
|
709
803
|
|
|
710
|
-
| 값 성격 | 정렬 | 예 |
|
|
711
|
-
| --- | --- | --- |
|
|
712
|
-
|
|
|
713
|
-
|
|
|
714
|
-
|
|
|
715
|
-
|
|
|
716
|
-
|
|
|
717
|
-
| 상태 태그·아이콘·체크박스 등 고정폭 요소 | `'center'` | `STag`, `SIcon` |
|
|
804
|
+
| 값 성격 | 정렬 | 너비 | 예 |
|
|
805
|
+
| --- | --- | --- | --- |
|
|
806
|
+
| **금액·수량·개수·비율** (양을 나타내는 값) | **`'right'`** | 고정 | `39,000원` · `12개` · `3건` · `15%` |
|
|
807
|
+
| 코드·식별자, 전화번호, 일자·일시 | **`'center'`** | 고정 | `RV20250728-000010` · `010-1234-5678` · `2026-08-13` |
|
|
808
|
+
| **닫힌 값 집합** (enum · 마스터 목록에서 고르는 값) | **`'center'`** | 고정 | 상태 · 직급 · 공개 범위 · 고용 형태 · 요일 |
|
|
809
|
+
| 상태 태그·아이콘·버튼·체크박스 | `'center'` | 고정 (`contentType: 'control'`) | `STag` · `SIcon` · `SGhostButton` |
|
|
810
|
+
| **텍스트** (사용자가 자유 입력) | 생략(기본 `left`) | 기준 폭 + `resizable` | 이름 · 목표명 · 이메일 · 메모 |
|
|
718
811
|
|
|
719
812
|
**중앙 정렬은 `align: 'center'` 를 명시한다.** 기본값이 좌측이라 생략하면 중앙이 되지 않는다.
|
|
720
813
|
|
|
814
|
+
##### 판별 — 값의 크기를 비교하는가
|
|
815
|
+
|
|
816
|
+
우측 정렬의 근거는 "자릿수를 세로로 맞춰 크기를 읽는다"다. 자릿수 차이가 거의 없고 값끼리 대소를 견줄 일이 없으면 숫자로만 이루어져 있어도 우측 정렬하지 않는다 — 송장번호 `123456789` 는 크기를 비교하는 값이 아니다.
|
|
817
|
+
|
|
818
|
+
##### 판별 — 값 집합이 닫혀 있는가
|
|
819
|
+
|
|
820
|
+
**닫힌 값 집합이면 태그로 그리든 맨 텍스트로 그리든 `center` 다.** 판별 질문 하나 — *사용자가 그 칸을 직접 치는 값인가?* 아니면 닫힌 집합이다.
|
|
821
|
+
|
|
822
|
+
| | 값의 출처 | 정렬 | 예 |
|
|
823
|
+
| --- | --- | --- | --- |
|
|
824
|
+
| **닫힘** | enum · 마스터 목록에서 선택 (`SSelect` 의 `options` 에서 오는 값) | `center` | 직급 · 상태 · 공개 범위 · 최종 등급 · 고용 형태 · 요일 |
|
|
825
|
+
| **열림** | 사용자가 자유 입력 (자유 입력 필드에서 오는 값) | `left` | 이름 · 목표명 · 이메일 · 문항 그룹명 |
|
|
826
|
+
|
|
827
|
+
- **무엇으로 그렸는지로 가르지 않는다.** 같은 성격의 값이 `STag` 면 `center`, 맨 텍스트면 `left` 가 되면 한 테이블 안에서 기준이 어긋난다.
|
|
828
|
+
- **길이로도 가르지 않는다.** "짧은 라벨이면 center" 같은 단서를 붙이면 `프로덕트디자인팀`(8자)처럼 경계에 걸리는 값에서 매번 판단이 갈린다.
|
|
829
|
+
- 한 열에 텍스트와 태그가 함께 오면 태그 기준(`center`)에 맞춘다.
|
|
830
|
+
|
|
721
831
|
```tsx
|
|
722
832
|
const columns: STableColumn[] = [
|
|
723
|
-
{ name: 'orderNo', label: '주문번호', field: 'orderNo', width:
|
|
724
|
-
{ name: 'orderedAt', label: '주문일자', field: 'orderedAt', width:
|
|
725
|
-
{ name: 'name', label: '상품명', field: 'name' },
|
|
726
|
-
{ name: 'qty', label: '수량', field: 'qty', width:
|
|
833
|
+
{ name: 'orderNo', label: '주문번호', field: 'orderNo', width: 140, align: 'center' },
|
|
834
|
+
{ name: 'orderedAt', label: '주문일자', field: 'orderedAt', width: 100, align: 'center' },
|
|
835
|
+
{ name: 'name', label: '상품명', field: 'name', width: 240 }, // 자유 입력 → 생략
|
|
836
|
+
{ name: 'qty', label: '수량', field: 'qty', width: 80, align: 'right',
|
|
727
837
|
format: (v: number) => `${Number(v).toLocaleString()}개` },
|
|
728
|
-
{ name: 'price', label: '판매가', field: 'price', width:
|
|
838
|
+
{ name: 'price', label: '판매가', field: 'price', width: 120, align: 'right',
|
|
729
839
|
format: (v: number) => `${Number(v).toLocaleString()}원` },
|
|
730
|
-
{ name: 'status', label: '상태', field: 'status', width:
|
|
840
|
+
{ name: 'status', label: '상태', field: 'status', width: 100, align: 'center',
|
|
731
841
|
render: () => <STag size="sm" color="green" label="판매중" /> },
|
|
732
842
|
];
|
|
733
843
|
```
|
|
@@ -735,38 +845,73 @@ const columns: STableColumn[] = [
|
|
|
735
845
|
- `format` 으로 단위를 붙이더라도 **양을 나타내면 오른쪽 정렬**이다. 단위 때문에 문자열이 되는 것은 정렬 판단과 무관하다.
|
|
736
846
|
- 양을 나타내는 숫자는 §1-4 대로 **`toLocaleString()` 이 필수**다. 세 자리 콤마 없이 출력하지 않는다.
|
|
737
847
|
- **번호·코드에는 세 자리 콤마를 넣지 않는다.** 송장번호 `123456789` 를 `123,456,789` 로 표시하면 값 자체가 달라 보인다.
|
|
848
|
+
- 날짜는 §1-4 대로 `YYYY-MM-DD` 로 적는다. 자릿수가 고정이라 폭을 형식으로 계산할 수 있다.
|
|
738
849
|
- **헤더는 가운데, 셀만 우측**으로 두려면 `align` 이 아니라 `tdClass` 를 쓴다. `align` 은 `<th>` 와 `<td>` 에 함께 적용된다.
|
|
739
850
|
|
|
740
851
|
```tsx
|
|
741
|
-
{ name: 'views', label: '조회수', field: 'views', align: 'center', tdClass: 'text-right!',
|
|
852
|
+
{ name: 'views', label: '조회수', field: 'views', align: 'center', width: 100, tdClass: 'text-right!',
|
|
742
853
|
format: (v: number) => Number(v).toLocaleString() },
|
|
743
854
|
```
|
|
744
855
|
|
|
745
856
|
- `SKeyValueTable` 의 값 셀도 같은 기준을 따른다.
|
|
746
857
|
|
|
747
|
-
####
|
|
858
|
+
#### 너비는 px 로만 준다
|
|
748
859
|
|
|
749
|
-
|
|
860
|
+
**컬럼 폭은 px 이다.** 숫자를 주면 px 로 읽고, 문자열은 `'120px'` 형태만 받는다. `%` · `clamp()` · `min()` 은 쓰지 않는다.
|
|
750
861
|
|
|
751
|
-
-
|
|
752
|
-
|
|
862
|
+
컬럼 폭은 `<colgroup>` 의 `<col width>` 로 들어가고 테이블이 `table-fixed` 라, 함수형 값은 계산되지 않고 통째로 무시된 뒤 auto 폭으로 떨어진다. `'30%'` 는 더 나쁘게 `30`(px)으로 읽힌다. **둘 다 에러 없이 화면만 틀어진다.**
|
|
863
|
+
|
|
864
|
+
- **내용이 들어가는 열은 전부 폭을 명시한다.** 생략하면 기본 120px 이 조용히 들어가고, "짧은 열이라 그대로 둔 것"과 "판단을 빠뜨린 것"이 구분되지 않는다. 120px 이 맞더라도 `width: 120` 을 적는다.
|
|
865
|
+
- **`autoWidth` 는 남은 폭을 흡수하는 스페이서 열 하나에만 쓴다.** 내용이 들어가는 열에는 쓰지 않는다 — 폭이 다른 열에 좌우돼 화면마다 달라진다. 스페이서 열은 값을 그리지 않으므로 `field` 도 생략한다.
|
|
866
|
+
|
|
867
|
+
```tsx
|
|
868
|
+
{ name: 'spacer', label: '', autoWidth: true },
|
|
869
|
+
```
|
|
870
|
+
|
|
871
|
+
- **`minWidth` · `maxWidth` 는 `resizable` 손잡이의 이동 범위일 뿐, 레이아웃에는 관여하지 않는다.** 폭을 주지 않은 열이 이 값 안에서 잡히는 것이 아니다.
|
|
872
|
+
- **고정폭 합이 최소 창 폭을 넘으면 가로 스크롤이 된다.** `STable` 이 자기 안에서 가로로 스크롤하고 헤더·바디가 함께 움직이므로 별도 조치는 필요 없다 — 폭을 줄여 맞추지 말고, 열이 정말 그만큼 필요한지를 본다.
|
|
873
|
+
|
|
874
|
+
##### 고정폭을 어떻게 정하는가
|
|
875
|
+
|
|
876
|
+
```text
|
|
877
|
+
폭 = ceil( ( max(값 폭 + 값 기준 패딩, 헤더 폭 + 32) + 여유 ) / 8 ) × 8
|
|
878
|
+
```
|
|
879
|
+
|
|
880
|
+
- **값과 헤더를 따로 계산해 큰 쪽을 쓴다.** `contentType: 'control'` 은 `<td>` 에만 적용되고 `<th>` 는 항상 텍스트 패딩이라, 짧은 컨트롤 + 긴 헤더 조합에서 헤더가 잘린다.
|
|
753
881
|
- 값에 따라 폭이 달라지는 요소(`STag` 라벨, 라벨 있는 버튼)는 **가장 긴 값** 기준으로 잡는다. `판매중` 에 맞춰두면 `판매중지 요청` 에서 잘린다.
|
|
882
|
+
- **정렬 가능한 헤더(`sortable`)는 아이콘 버튼 + 간격만큼 `+20px` 더 든다.** `helpText` 를 함께 달면 그만큼 또 더한다.
|
|
883
|
+
- **`editable` · `navigable` 표식과 `required` 표시(`*`) 도 각각 폭을 먹는다.** 헤더에 붙는 것이 늘수록 **라벨이 먼저 잘리므로**, 붙인 열은 폭을 함께 넓힌다.
|
|
884
|
+
- **계산값은 픽셀 단위까지 맞추면 어긋난다** — 서브픽셀 반올림 때문이다. 여유 8px 을 얹고 8 단위로 올림한다.
|
|
885
|
+
- 좌우 패딩은 `STable` 이 토큰으로 넣으므로 직접 주지 않는다. 그만큼을 뺀 나머지가 요소 몫이라는 점만 계산에 넣는다.
|
|
886
|
+
|
|
887
|
+
#### 컨트롤이 들어가는 컬럼
|
|
888
|
+
|
|
889
|
+
`<td>` 는 폭을 넘는 내용을 잘라낸다(`overflow: hidden`). 텍스트라면 말줄임으로 끝나지만, 셀에 `STag` · `SButton` · `SGhostButton` · `SSelect` · `SInput` · `SNumberInput` 처럼 **자기 폭을 가진 요소**를 넣으면 요소 자체가 잘려 **누르거나 읽거나 입력할 수 없게 된다.**
|
|
890
|
+
|
|
891
|
+
- **컨트롤이 들어가는 컬럼에는 `contentType: 'control'` 을 함께 준다.** 좌우 패딩이 텍스트용(넓게)에서 컨트롤용(좁게)으로 바뀌어, 같은 컬럼 폭에서도 요소가 쓸 폭이 넓어진다. 기본값은 `text` 다.
|
|
892
|
+
- 요소가 둘 이상이면 요소 폭의 합에 **`gap` 까지** 더한다 (행 내부 인라인 액션 간격은 `gap-sd-4` 고정, §3-5-5).
|
|
754
893
|
- `SSelect` · `SInput` 처럼 셀 폭을 채우는 컨트롤은 **컬럼 폭이 곧 컨트롤 폭**이다. 실제 선택값·입력값이 말줄임 없이 읽히는 폭인지 확인한다.
|
|
755
894
|
- 폭을 넉넉히 줄 수 없는 자리는 폭을 줄이는 게 아니라 **요소를 바꾼다** — 라벨 버튼 대신 아이콘만 있는 `SGhostButton`, `size="xs"` (§3-5-2, §3-5-5).
|
|
756
|
-
|
|
895
|
+
|
|
896
|
+
```tsx
|
|
897
|
+
{ name: 'normal', label: '정상', field: 'normal', width: 96,
|
|
898
|
+
align: 'center', contentType: 'control', render: row => <SNumberInput … /> },
|
|
899
|
+
```
|
|
900
|
+
|
|
901
|
+
**셀 좌우 여백은 내용이 정한다.** 텍스트는 넓게, 컨트롤은 좁게다 — `SKeyValueTable` 은 `field.type` 으로 이 판정을 스스로 하지만, `STable` 의 셀은 소비 앱이 넘긴 임의의 `render` 결과라 컴포넌트가 알 수 없다. 그래서 `contentType` 으로 알려준다. 여백 값 자체는 토큰이 정하므로 `tdClass` 로 패딩을 직접 덮어쓰지 않는다.
|
|
757
902
|
|
|
758
903
|
**`resizable` 테이블이면 `minWidth` 를 함께 준다.** resize 하한 기본값은 어떤 컨트롤도 담지 못할 만큼 작아, 사용자가 끝까지 끌면 그대로 잘린다. `width` 를 정한 근거와 같은 값을 하한으로 둔다 — 텍스트 컬럼과 달리 여기서는 더 줄일 여지가 없다.
|
|
759
904
|
|
|
760
905
|
```tsx
|
|
761
906
|
const columns: STableColumn[] = [
|
|
762
907
|
// 태그 — 가장 긴 라벨 기준
|
|
763
|
-
{ name: 'status', label: '상태', field: 'status', width:
|
|
908
|
+
{ name: 'status', label: '상태', field: 'status', width: 120, minWidth: 120, align: 'center',
|
|
764
909
|
render: (row: SRow) => <STag size="sm" color="green" label={row.statusLabel} /> },
|
|
765
910
|
// 셀 안 입력 — 컬럼 폭이 곧 입력 폭
|
|
766
|
-
{ name: 'qty', label: '수량', field: 'qty', width:
|
|
911
|
+
{ name: 'qty', label: '수량', field: 'qty', width: 100, minWidth: 100, align: 'right',
|
|
767
912
|
render: (row: SRow) => <SNumberInput value={row.qty} onValueChange={v => setQty(row, v)} /> },
|
|
768
913
|
// 인라인 액션 둘 — 폭 = xs 버튼 2개 + gap-sd-4 + 셀 좌우 패딩
|
|
769
|
-
{ name: 'actions', label: '', field: 'id', width:
|
|
914
|
+
{ name: 'actions', label: '', field: 'id', width: 84, minWidth: 84, align: 'center',
|
|
770
915
|
render: (row: SRow) => (
|
|
771
916
|
<div className="flex items-center justify-center gap-sd-4">
|
|
772
917
|
<SGhostButton size="xs" intent="action" icon="edit" ariaLabel="수정" onClick={() => editRow(row)} />
|
|
@@ -774,11 +919,34 @@ const columns: STableColumn[] = [
|
|
|
774
919
|
</div>
|
|
775
920
|
) },
|
|
776
921
|
|
|
777
|
-
// ❌ 컨트롤 컬럼에 width 생략 — 기본
|
|
922
|
+
// ❌ 컨트롤 컬럼에 width 생략 — 기본 폭(120px)에 맡기면 버튼이 잘린다
|
|
778
923
|
{ name: 'move', label: '', field: 'id', render: () => <SButton label="재고 이동" size="xs" /> },
|
|
779
924
|
];
|
|
780
925
|
```
|
|
781
926
|
|
|
927
|
+
#### 정렬 가능한 컬럼
|
|
928
|
+
|
|
929
|
+
**정렬 상태는 `STable` 이 갖지 않는다.** 컬럼에 `sortable: true` 를 주고, 페이지가 `sort` · `onSortChange` 로 상태를 들고 있는다.
|
|
930
|
+
|
|
931
|
+
```tsx
|
|
932
|
+
const [sort, setSort] = useState<STableSort | null>({ name: 'orderedAt', dir: 'desc' });
|
|
933
|
+
|
|
934
|
+
<STable
|
|
935
|
+
columns={columns}
|
|
936
|
+
rows={rows}
|
|
937
|
+
sort={sort}
|
|
938
|
+
onSortChange={setSort}
|
|
939
|
+
/>
|
|
940
|
+
```
|
|
941
|
+
|
|
942
|
+
- **정렬은 조회 조건이다.** 서버 정렬이면 `?sort=createdAt&dir=desc` 가 곧 요청이고, 뒤로가기·새로고침·링크 공유로 복원돼야 한다. 컴포넌트가 사본을 들면 URL 과 화면이 어긋난다 — `SExpansionList` 의 선택을 앱이 드는 것과 같은 이유다 (§3-7-7).
|
|
943
|
+
- **행을 실제로 정렬하는 것도 페이지 몫이다.** `STable` 은 받은 순서대로 그린다.
|
|
944
|
+
- **동작** — 헤더 클릭 시 `asc → desc → 해제` 3단. 다른 열을 누르면 그 열의 `asc` 로 시작한다. 해제되면 `onSortChange(null)`.
|
|
945
|
+
- **아이콘** — 미정렬 `updown`, 오름 `arrowUp`, 내림 `arrowDown`. 정렬 중인 열만 `action` 색으로 올라온다. `SGhostButton size="xxs"` 로 그려지므로 직접 만들지 않는다.
|
|
946
|
+
- **클릭 영역은 정렬 버튼뿐이다.** 헤더 셀 전체를 누르게 하지 않는다 — 라벨을 드래그해 고르거나 `helpText` 아이콘에 hover 하는 것과 뒤섞인다.
|
|
947
|
+
- **다중 정렬은 지원하지 않는다.** 한 번에 한 열이다.
|
|
948
|
+
- `renderHeader` 로 헤더를 통째로 교체하면 정렬 아이콘도 클릭도 그리지 않는다 — 헤더 전체가 소비 앱 책임이 된다.
|
|
949
|
+
|
|
782
950
|
#### 값이 없는 셀은 회색 하이픈
|
|
783
951
|
|
|
784
952
|
셀을 **빈칸으로 두지 않는다.** 값이 `null` · `undefined` · 빈 문자열이면 `-` 를 `text-fg-tertiary`(`grey_65`)로 표시한다.
|
|
@@ -790,9 +958,9 @@ const emptyCell = <span className="text-fg-tertiary">-</span>;
|
|
|
790
958
|
const hasValue = (v: unknown) => v !== null && v !== undefined && v !== '';
|
|
791
959
|
|
|
792
960
|
const columns: STableColumn[] = [
|
|
793
|
-
{ name: 'memo', label: '메모', field: 'memo',
|
|
961
|
+
{ name: 'memo', label: '메모', field: 'memo', width: 240,
|
|
794
962
|
render: (row: SRow) => (hasValue(row.memo) ? row.memo : emptyCell) },
|
|
795
|
-
{ name: 'price', label: '판매가', field: 'price', width:
|
|
963
|
+
{ name: 'price', label: '판매가', field: 'price', width: 120, align: 'right',
|
|
796
964
|
render: (row: SRow) =>
|
|
797
965
|
hasValue(row.price) ? `${Number(row.price).toLocaleString()}원` : emptyCell },
|
|
798
966
|
];
|
|
@@ -800,6 +968,54 @@ const columns: STableColumn[] = [
|
|
|
800
968
|
|
|
801
969
|
`0` 은 값이 있는 것이므로 하이픈으로 바꾸지 않는다 — `0원` 그대로 표시한다.
|
|
802
970
|
|
|
971
|
+
#### 라벨만으로 뜻이 안 통하는 컬럼은 `helpText`
|
|
972
|
+
|
|
973
|
+
헤더 라벨은 컬럼 폭 안에 들어가야 해서 짧아진다. **산출 기준·단위·상태 값의 뜻처럼 라벨에 담기지 않는 설명은 `column.helpText` 로 준다** — 라벨 뒤에 도움말 아이콘이 붙고 hover 하면 툴팁이 뜬다. 배열의 각 항목이 한 줄이다. `SKeyValueTable` 의 `field.helpText`, `SSectionHeaderCard` 의 `helpText` 와 같은 것이다.
|
|
974
|
+
|
|
975
|
+
판단 기준 한 줄: *컬럼 제목이 줄임말·사내 용어·계산식이거나, 값이 아니라 열 자체의 설명이 필요할 때 헤더에 단다.*
|
|
976
|
+
|
|
977
|
+
```tsx
|
|
978
|
+
const columns: STableColumn[] = [
|
|
979
|
+
{ name: 'orderCount', label: '주문 수', field: 'orderCount', width: 120, align: 'right',
|
|
980
|
+
helpText: ['취소·반품을 제외한 확정 주문 수입니다.'],
|
|
981
|
+
format: (v: number) => `${Number(v).toLocaleString()}건` },
|
|
982
|
+
{ name: 'status', label: '상태', field: 'status', width: 100, align: 'center',
|
|
983
|
+
helpText: ['활성: 최근 30일 내 주문 있음', '보관됨: 거래 종료'] },
|
|
984
|
+
];
|
|
985
|
+
```
|
|
986
|
+
|
|
987
|
+
- **`renderHeader` 로 헤더를 직접 만들어 `STooltip` 을 붙이지 않는다.** `renderHeader` 는 헤더 전체를 교체하므로 `helpText` 가 무시되고, 아이콘·크기·색·간격을 손으로 맞추게 된다.
|
|
988
|
+
- **모든 컬럼에 달지 않는다.** 라벨로 뜻이 통하는 컬럼(`주문번호`·`상품명`)까지 붙이면 헤더가 아이콘으로 뒤덮여 정작 설명이 필요한 컬럼이 묻힌다.
|
|
989
|
+
- 긴 문장을 넣는 자리가 아니다. 한 줄에 한 가지 사실만 담고, 그 이상은 페이지 상단 안내(`SCallout`)로 뺀다.
|
|
990
|
+
- 아이콘도 폭을 먹는다 — 헤더 폭 계산에 넣는다.
|
|
991
|
+
|
|
992
|
+
#### 헤더에 붙는 것들의 순서 · 열을 어떻게 다루는지 알리는 표식
|
|
993
|
+
|
|
994
|
+
헤더 라벨 뒤에 붙는 것은 네 가지고, **순서는 `STable` 이 고정한다.** 소비 앱이 바꾸는 것이 아니다.
|
|
995
|
+
|
|
996
|
+
```text
|
|
997
|
+
라벨 [helpText ?] [editable] [navigable] [required *] [sortable 정렬버튼]
|
|
998
|
+
```
|
|
999
|
+
|
|
1000
|
+
**`editable` 은 값을 직접 고칠 수 있는 열, `navigable` 은 눌러서 다른 화면으로 넘어가는 열에 준다.** 둘 다 **표식일 뿐 버튼이 아니다** — 아이콘·크기·색은 컴포넌트가 고정하고 클릭은 받지 않는다.
|
|
1001
|
+
|
|
1002
|
+
```tsx
|
|
1003
|
+
const columns: STableColumn[] = [
|
|
1004
|
+
{ name: 'name', label: '상품명', field: 'name', width: 200,
|
|
1005
|
+
navigable: true,
|
|
1006
|
+
render: (row: SRow) => <STextLink label={row.name} onClick={() => goDetail(row.id)} /> },
|
|
1007
|
+
{ name: 'stock', label: '재고', field: 'stock', width: 160, contentType: 'control',
|
|
1008
|
+
editable: true, required: true,
|
|
1009
|
+
render: (row: SRow) => <SNumberInput value={row.stock} width="100%" /> },
|
|
1010
|
+
];
|
|
1011
|
+
```
|
|
1012
|
+
|
|
1013
|
+
- **실제로 그렇게 동작하는 열에만 켠다.** 표식만 켜고 셀은 텍스트 그대로 두면, 고칠 수 있다고 해 놓고 고칠 방법이 없고 넘어갈 수 있다고 해 놓고 누를 것이 없다. `editable` 이면 셀에 입력 컨트롤이, `navigable` 이면 셀에 링크·클릭이 있어야 한다.
|
|
1014
|
+
- **표식으로 동작을 대신하지 않는다.** 고치는 UI 도 넘어가는 동작도 셀(`column.render`) 몫이다.
|
|
1015
|
+
- **`renderHeader` 로 헤더를 통째로 교체하면 표식도 `required` 도 그려지지 않는다** — 헤더 전체가 소비 앱 책임이 되므로 순서·크기·색을 손으로 맞추게 된다.
|
|
1016
|
+
|
|
1017
|
+
**값을 반드시 채워야 하는 열에는 `column.required`** 를 준다 — 라벨 뒤에 `*` 가 붙는다. `SKeyValueTable` 의 `field.required` 와 같은 것이다. **읽기 전용 열에 붙이지 않는다** — 표시만 있고 채울 방법이 없어 사용자가 막힌다.
|
|
1018
|
+
|
|
803
1019
|
### 3-5. 버튼류
|
|
804
1020
|
|
|
805
1021
|
| 상황 | 사용 |
|
|
@@ -809,6 +1025,7 @@ const columns: STableColumn[] = [
|
|
|
809
1025
|
| **아이콘 하나로 뜻이 통하는 부가 조작** | **`SGhostButton`** — 라벨이 없는 아이콘 전용 버튼 (아래 3-5-5) |
|
|
810
1026
|
| 본문 속 이동 링크 | `STextLink` |
|
|
811
1027
|
| 메인 액션 + 부가 메뉴 | `SDropdownButton` |
|
|
1028
|
+
| **GNB system 패널의 한 줄 액션** | **`SSystemActionButton`** — 행 전체가 버튼인 표면형 (아래 3-5-7) |
|
|
812
1029
|
|
|
813
1030
|
#### 3-5-1. 채움 버튼은 페이지당 개수 제한이 있다
|
|
814
1031
|
|
|
@@ -823,6 +1040,19 @@ const columns: STableColumn[] = [
|
|
|
823
1040
|
|
|
824
1041
|
`SDropdownButton` 도 같은 규칙을 따르며, **페이지당 `primary` 채움 1개 계산에 포함**된다.
|
|
825
1042
|
|
|
1043
|
+
##### 무엇이 어느 위계인가
|
|
1044
|
+
|
|
1045
|
+
개수만으로는 후보가 여럿일 때 어느 것을 올릴지 갈리지 않는다. 기준은 **그 조작이 무엇에 미치는가**다.
|
|
1046
|
+
|
|
1047
|
+
| 위계 | 무엇에 쓰나 |
|
|
1048
|
+
| --- | --- |
|
|
1049
|
+
| `primary` 채움 | **페이지 전체에 해당하는 데이터를 확정**하는 실행 (폼 저장, 상세 수정 확정, 일괄 반영) |
|
|
1050
|
+
| `secondary` 채움 | **페이지 안 중심 데이터에 대한 처리** (선택 항목 상태 변경, 발송, 승인) |
|
|
1051
|
+
| `outline` (`neutral` · `primary`) | 단순 등록, 설정 변경, 이동·취소 |
|
|
1052
|
+
|
|
1053
|
+
- **`primary` 채움은 페이지 전체를 대표하는 실행 하나에만 쓴다. 그런 조작이 없으면 페이지에 `primary` 가 없어도 된다.** 개수 제한이 "반드시 하나 있어야 한다"는 뜻은 아니다.
|
|
1054
|
+
- **`secondary` 연속 배치 금지는 섹션이 다르면 적용되지 않는다.** 섹션마다 독립 인라인 폼이 있는 상세 페이지(§4-4)가 그렇다 — 나란히 놓인 두 버튼이 같은 판단 단위 안에 있을 때의 규칙이다.
|
|
1055
|
+
|
|
826
1056
|
#### 3-5-2. `size` 는 놓이는 위치가 정한다
|
|
827
1057
|
|
|
828
1058
|
| 위치 | size |
|
|
@@ -928,6 +1158,35 @@ const columns: STableColumn[] = [
|
|
|
928
1158
|
</div>
|
|
929
1159
|
```
|
|
930
1160
|
|
|
1161
|
+
#### 3-5-7. `SSystemActionButton` — GNB system 패널의 한 줄 액션
|
|
1162
|
+
|
|
1163
|
+
**행 전체가 하나의 버튼**인 표면형 컨트롤이다. 아이콘 프레임 + 라벨 + 우측 액션이 한 줄에 붙고, hover 하면 표면색이 한 단계 바뀐다.
|
|
1164
|
+
|
|
1165
|
+
| 상황 | 사용 |
|
|
1166
|
+
| --- | --- |
|
|
1167
|
+
| 페이지·모달의 일반 액션 | `SButton` — 라벨 폭만큼만 차지한다 |
|
|
1168
|
+
| 목록에서 항목 하나를 고르게 한다 | `SListItem` · `STree` (§3-7-6·§3-7-7) |
|
|
1169
|
+
| 좌측 내비게이션의 메뉴 | `SGnb` 의 `menus` (§4-1) |
|
|
1170
|
+
| **GNB system 패널에서 설정·연결 상태를 한 줄씩 쌓고, 그 줄을 눌러 들어가게 한다** | **`SSystemActionButton`** |
|
|
1171
|
+
|
|
1172
|
+
**무엇이 그려지는지는 `type` 이 정한다.** 타입마다 다른 값만 `option` 으로 받으므로, 타입에 맞지 않는 `option` 은 타입 에러가 된다.
|
|
1173
|
+
|
|
1174
|
+
| `type` | 선행 아이콘 | 라벨 | 우측 | 언제 |
|
|
1175
|
+
| --- | --- | --- | --- | --- |
|
|
1176
|
+
| `account` | `user` 고정 | `label` | 없음 | 로그인한 계정을 보여주고 계정 화면으로 보낼 때 |
|
|
1177
|
+
| `select` | `option.icon` (필수) | `label` | 화살표 | 눌러서 하위 화면으로 들어갈 때 |
|
|
1178
|
+
| `switch` | `option.icon` (필수) | `label` | `option.text` + 새로고침 | 지금 붙어 있는 대상을 갈아 끼울 때 |
|
|
1179
|
+
| `subSelect` | `option.icon` (필수) | `label` | `option.text` + 화살표 | 지금 선택돼 있는 값을 함께 보여줄 때 |
|
|
1180
|
+
| `status` | 없음 | 상태 태그(`option.color`·`option.label`) | 화살표 | 라벨 대신 상태 자체가 제목일 때 |
|
|
1181
|
+
|
|
1182
|
+
- **`SPage` 본문에 직접 놓지 않는다.** 폭을 가득 채우는 행이라 페이지에 바로 놓으면 표처럼 보인다. GNB system 패널·`SDrawer`·`SPopover` 처럼 **좁은 폭이 정해진 자리**에 쌓는다. 행 사이 간격은 `--cmp-gnb-system-panel-gap` 이다.
|
|
1183
|
+
- **GNB 맨 아래에 쌓는 것이라면 이 버튼을 직접 배치하지 말고 `SGnbSystem` 의 `actions` 에 넘긴다** (§4-1). 패널의 배경·구분선·도메인 태그·알림·계정까지 한 벌로 그려주므로, 조각을 손으로 조립할 이유가 없다.
|
|
1184
|
+
- **`color` 는 놓이는 표면을 따라간다** — 흰 면이면 `light`, 오션블루 면이면 `dark`, 네이비 면이면 `darker`. 배경과 다른 색을 고르면 버튼만 떠 보인다.
|
|
1185
|
+
- **상태는 default·hover 둘뿐이다.** 토큰에 selected 가 없으므로 "지금 열려 있는 항목"을 이 버튼으로 표시하지 않는다. 선택 상태가 필요한 자리면 `SGnb` 메뉴나 `SListItem`(§3-7-6) 이다.
|
|
1186
|
+
- **선행 아이콘이 비는 행은 없다.** `account` 는 `user` 고정, `status` 는 태그가 자리를 차지하고, 나머지는 `option.icon` 이 필수다.
|
|
1187
|
+
- **우측 아이콘은 `type` 이 정하므로 밖에서 바꾸지 않는다.** 화살표가 필요한데 문구가 없으면 `select`, 문구가 함께 필요하면 `subSelect` 다.
|
|
1188
|
+
- 높이·라운드·아이콘 크기·좌우 여백은 전부 토큰이 넣는다. 직접 주지 않는다.
|
|
1189
|
+
|
|
931
1190
|
### 3-6. 영역 나누기 — SDivider vs SSplitter
|
|
932
1191
|
|
|
933
1192
|
| 상황 | 사용 |
|
|
@@ -937,6 +1196,25 @@ const columns: STableColumn[] = [
|
|
|
937
1196
|
|
|
938
1197
|
`SSplitter` 의 구분선은 평소 자리만 잡고 칠해지지 않다가, 경계에 커서를 올리거나 포커스를 주면 그때 드러난다 — 조절 가능한 자리라는 신호다. **항상 보이는 선이 필요하면 `SDivider` 를 쓴다.** 선 색·두께·주변 여백은 토큰이 정하므로 직접 주지 않는다.
|
|
939
1198
|
|
|
1199
|
+
**세로 구분선의 길이는 `height` prop 으로 준다 — `className` 의 `h-*` 로 주지 않는다.**
|
|
1200
|
+
|
|
1201
|
+
```tsx
|
|
1202
|
+
// 줄 높이를 그대로 채운다 (기본)
|
|
1203
|
+
<div className="flex items-center gap-sd-8">
|
|
1204
|
+
<span>총 주문 3건</span>
|
|
1205
|
+
<SDivider vertical />
|
|
1206
|
+
<span>총 품목 12건</span>
|
|
1207
|
+
</div>
|
|
1208
|
+
|
|
1209
|
+
// 양옆 글자보다 짧은 선이 필요할 때만 길이를 정한다
|
|
1210
|
+
<SDivider vertical height={20} />
|
|
1211
|
+
|
|
1212
|
+
// 하지 말 것 — 선이 줄 맨 위에 붙는다
|
|
1213
|
+
<SDivider vertical className="h-sd-20" />
|
|
1214
|
+
```
|
|
1215
|
+
|
|
1216
|
+
길이를 주지 않으면 `align-self: stretch` 로 부모 줄 높이를 채우는데, `stretch` 는 높이가 `auto` 일 때만 늘린다. `className` 으로 높이를 정하면 `stretch` 가 조용히 무효가 되어 선이 위로 솟는다. `height` 는 길이와 교차축 가운데 정렬을 함께 적용하므로 이 함정이 없다 — 위·아래 정렬이 필요하면 `className="self-start"` 처럼 명시한다. (`sellmate/divider-vertical-height` 규칙이 잡는다.)
|
|
1217
|
+
|
|
940
1218
|
```tsx
|
|
941
1219
|
<SSplitter defaultValue={30} limits={[20, 60]}>
|
|
942
1220
|
<SSplitter.Before>내비게이션</SSplitter.Before>
|
|
@@ -967,18 +1245,42 @@ const columns: STableColumn[] = [
|
|
|
967
1245
|
|
|
968
1246
|
> §3-0 라우팅에서 이 절을 가리키는 자리들이다. <!-- TODO(디자인): 전체 검수·확정 -->
|
|
969
1247
|
|
|
970
|
-
#### 3-7-1. SInput vs STextarea
|
|
1248
|
+
#### 3-7-1. SInput vs STextarea vs SEditor vs SSearchInput
|
|
971
1249
|
|
|
972
|
-
|
|
1250
|
+
**먼저 "그 값이 저장되는가"를 본다.** 저장되면 폼 필드(`SInput`·`STextarea`), 화면을 좁히기만 하고 사라지면 `SSearchInput` 이다.
|
|
973
1251
|
|
|
974
1252
|
| 값 | 사용 |
|
|
975
1253
|
| --- | --- |
|
|
976
1254
|
| 이름·코드·전화번호·URL 처럼 형식이 정해진 값 | `SInput` |
|
|
977
1255
|
| 메모·사유·설명처럼 길이가 예측되지 않는 문장 | `STextarea` |
|
|
1256
|
+
| 서식(제목·굵게·목록·정렬·색·링크·이미지)이 값의 일부로 저장되어야 하는 글 | `SEditor` |
|
|
1257
|
+
| 지금 보이는 목록·결과를 좁히는 검색어 | `SSearchInput` |
|
|
1258
|
+
|
|
1259
|
+
폼 필드 둘은 **줄 수가 아니라 값의 성격으로** 갈린다. 값이 길어질 수 있는데 `SInput` 을 쓰면 사용자가 자기가 쓴 것을 다시 읽지 못한다 — 한 줄 안에서 좌우로 스크롤해야 하기 때문이다. 반대로 짧은 값에 `STextarea` 를 쓰면 빈 공간이 남아 입력량을 잘못 기대하게 한다.
|
|
1260
|
+
|
|
1261
|
+
`SEditor` 는 **서식이 값의 일부일 때만** 쓴다. 값을 HTML 문자열로 주고받으므로 저장·검색·비교가 평문보다 비싸고, 화면에 다시 보여줄 때도 HTML 로 렌더해야 한다. 서식이 필요 없는 메모·사유는 `STextarea` 다 — "입력창이 커 보여서" 고르는 컴포넌트가 아니다. 반대로 공지·안내문·상품 상세처럼 **작성자가 정한 강조와 목록이 그대로 보여야 하는 글**이면 `STextarea` 로는 표현할 수 없다.
|
|
1262
|
+
|
|
1263
|
+
`SEditor` 도 `SInput`·`STextarea` 와 같은 폼 필드다 — `label`·`hint`·`rules`·`errorMessage` 를 자기 prop 으로 받고 `SForm` 제출 검증에도 들어간다. 빈 문서는 빈 문자열로 나오므로 `required` 규칙이 그대로 걸린다. **툴바를 끄는 길은 없다** — 서식 입력이 필요 없는 자리라면 서식 없는 `SEditor` 가 아니라 `STextarea` 를 고른다.
|
|
1264
|
+
|
|
1265
|
+
툴바는 **프리셋 둘 중 하나뿐이다.** 기본은 쓸 수 있는 것을 모두 보이고, **`simple` 을 켜면 글자에 거는 서식만 남는다** — 목록·정렬·인용·코드·링크·이미지·구분선이 빠지고 선택했을 때 뜨는 판도 같은 범위로 줄어든다. 받은 글의 **문단 구조까지 작성자를 따라가면 곤란한 자리**(좁은 칸의 메모·사유·짧은 안내문)가 `simple` 이다. 항목을 직접 조합하는 prop 은 없다 — 화면마다 다른 툴바가 서면 그 자체가 학습 비용이 된다.
|
|
1266
|
+
|
|
1267
|
+
본문에서 따옴표·하이픈·화살표는 **자동으로 치환된다**(`"` → `“”`, `--` → `—`, `->` → `→`). 끌 수 없으므로, 상품 코드·규격 문자열처럼 **입력한 그대로 남아야 하는 값**은 `SEditor` 본문이 아니라 `SInput` 으로 따로 받는다.
|
|
978
1268
|
|
|
979
|
-
|
|
1269
|
+
툴바는 **자리를 지킨다**(고정). 긴 글을 쓰는 동안 막대가 화면 밖으로 나가지 않으므로, 툴바를 따로 감싸거나 위치를 주지 않는다.
|
|
980
1270
|
|
|
981
|
-
|
|
1271
|
+
툴바에는 **글자 크기 드롭다운**과 **글자색 팔레트**가 들어 있다 — 작성자가 문단마다 크기·색을 직접 지정할 수 있고, 크기 목록 맨 위 `기본` 은 지정을 떼는 자리다. 크기 눈금은 화면 타이포(§2-1)가 아니라 워드프로세서의 눈금이라 본문보다 훨씬 큰 단계까지 있다. **눈금도 팔레트도 좁히는 prop 이 없다** — 화면마다 고를 수 있는 것이 다르면 같은 글이 어디에 붙느냐에 따라 다르게 보이기 때문이다. 그래서 **작성자가 화면 리듬을 벗어나면 곤란한 자리(상품 상세 설명·반복 노출되는 안내문 등)라면 `SEditor` 가 맞는 자리인지 먼저 본다** — 서식이 값의 일부가 아니라면 `STextarea` 다. 고른 크기·색은 저장되는 HTML 에 그대로 남아 나중에 되돌릴 수 없다.
|
|
1272
|
+
|
|
1273
|
+
본문에는 **표**도 들어간다 — 툴바의 `표` 드롭다운에서 격자를 끌어 크기를 고르고(최대 8행 × 10열), 행·열을 늘리고, 칸을 병합한다. **열 너비는 균등 고정이고 바꿀 수 없다**: 너비를 저장하면 그 값이 px 로 박혀 작성한 화면보다 좁은 곳에서 표가 넘친다. 대신 어떤 폭에서도 표가 상자 안에 들어오도록 열을 고르게 나눈다 — 열이 많은 표는 좁은 칸에서 글자가 잘게 접히므로, **열이 넷을 넘어가는 표라면 `SEditor` 본문이 아니라 `STable` 이 맞는 자리인지 본다.** 표는 작성자가 쓰는 글의 일부일 때만 여기에 있고, 데이터를 줄 세워 보여 주는 것은 `STable` 이다.
|
|
1274
|
+
|
|
1275
|
+
글을 선택하면 **그 위에 서식 판이 뜬다**(버블 메뉴). 툴바까지 커서를 옮기지 않고 바로 굵게·색·링크를 거는 길이다. **끄고 켜는 prop 은 없다** — 편집할 수 있으면 언제나 뜨고, 읽기 전용·비활성일 때는 뜨지 않는다. 화면마다 있고 없고가 달라지면 그 자체가 학습 비용이 되기 때문이다. 판의 구성은 `simple` 이 막대와 함께 정한다. 뜨는 자리는 DS 가 잡는다, 직접 감싸거나 위치를 주지 않는다.
|
|
1276
|
+
|
|
1277
|
+
`SEditor` 는 화면에 처음 놓일 때 **에디터 엔진을 따로 불러온다** — 앱 초기 번들에는 들어가지 않는다. 그동안은 같은 크기의 빈 편집 영역이 자리를 지키므로 레이아웃은 흔들리지 않지만, **마운트하자마자 `ref.current.getHTML()` 로 값을 읽거나 툴바를 누를 수는 없다.** 열자마자 커서를 놓고 싶으면 `ref.current.focus()` 를 그냥 부르면 된다 — 준비되는 순간 대신 실행된다.
|
|
1278
|
+
|
|
1279
|
+
**이미지를 넣으려면 `onImageUpload` 를 준다** — 고른 파일을 저장하고 표시할 URL 을 돌려주는 훅이다. 저장 위치는 앱마다 다르므로 DS 가 정하지 않고, 훅이 없으면 툴바에서 이미지 항목이 빠진다. 본문에 base64 를 박는 길은 막아 두었다 — 저장 HTML 이 수 MB 로 부풀어 그대로 DB·API 에 실리기 때문이다.
|
|
1280
|
+
|
|
1281
|
+
`SSearchInput` 은 폼 필드가 아니다 — 라벨·힌트·유효성 규칙·에러 메시지를 받지 않고, `SForm` 의 제출 검증 대상에도 들어가지 않는다. 돋보기 아이콘이 항상 앞에 붙어 "여기는 검색"임을 스스로 밝히므로 라벨을 따로 붙이지 않는다. 검색 실행은 `onSearch`(Enter) 로 받고, 값이 바뀔 때마다 좁히는 실시간 필터라면 `onValueChange` 만 쓴다. 반대로 검색어를 **저장하거나 검증해야 한다면** 그것은 폼 값이므로 `SInput` 이다.
|
|
1282
|
+
|
|
1283
|
+
#### 3-7-2. 하나를 고르게 하는 여섯 — SSelect vs SRadioGroup vs SRadioCardGroup vs SRadioButton vs STabs vs SRadio
|
|
982
1284
|
|
|
983
1285
|
**먼저 "고르면 무엇이 바뀌는가"를 본다.**
|
|
984
1286
|
|
|
@@ -989,10 +1291,17 @@ const columns: STableColumn[] = [
|
|
|
989
1291
|
| --- | --- |
|
|
990
1292
|
| 6개 이상, 또는 서버에서 오는 동적 목록 | `SSelect` |
|
|
991
1293
|
| 2~5개 고정 + 선택지를 항상 보여야 함 | `SRadioGroup` |
|
|
1294
|
+
| 2~5개 고정 + **선택지마다 설명 한 줄이 필요함** | `SRadioCardGroup` |
|
|
992
1295
|
| 2~4개 + 짧은 라벨의 배타적 모드 전환 (세그먼트) | `SRadioButton` |
|
|
993
1296
|
|
|
994
1297
|
- **`SRadio` 를 직접 나열하지 않는다.** 그룹 간격은 `SRadioGroup` 이 맞춘다 (§2-2). `SRadio` 단독은 `SRadioGroup` 이 만들 수 없는 배치 — 표 셀 안에 행마다 하나씩 놓는 경우 — 에만 쓴다.
|
|
995
1298
|
- `SRadioButton` 은 `options` 를 통째로 받는 세그먼트 컨트롤이라 `SRadio` 를 여러 개 넣는 게 아니다.
|
|
1299
|
+
- **`SRadioGroup` 과 `SRadioCardGroup` 의 갈림길은 "설명이 필요한가" 하나다.** 라벨만으로 뜻이 통하면 `SRadioGroup`, 고르기 전에 한 줄 설명을 읽어야 하면 `SRadioCardGroup` 이다. 라벨 옆에 설명을 괄호로 덧붙여 늘이지 않는다.
|
|
1300
|
+
- **`SRadioCardGroup` 은 `SRadioGroup` 의 대체가 아니라 격상판이다.** 라벨이 한 단계 크고 그 아래 설명 줄까지 들어가 카드 한 장이 자리를 넉넉히 쓴다 — 선택지가 조밀하게 반복되는 자리에는 과하다.
|
|
1301
|
+
- **쓰는 자리는 설정 화면·마법사(스텝)의 소수 결정이다.** 고르고 나면 뒤 화면이 통째로 바뀌거나 되돌리기 어려운 선택 — 평가 대상자 범위(전체/특정 구성원), 상담 자동 배정 방식 같은 것. 목록 필터·표 안 조건처럼 같은 선택지가 화면마다 반복 노출되는 자리에는 `SRadioGroup` 을 쓴다.
|
|
1302
|
+
- `SRadioCard` 단독은 `SRadioCardGroup` 이 만들 수 없는 배치에만 쓴다 — 나열은 그룹이 맡는다 (§2-2).
|
|
1303
|
+
- 카드 안에 상태 배지를 넣으려면 `tag` 슬롯에 `STag` 를 준다. 라벨 문자열에 "(추천)" 처럼 섞어 쓰지 않는다.
|
|
1304
|
+
- **카드처럼 생겼다고 `SCard`/`SSectionHeaderCard` 로 감싸지 않는다.** `SRadioCard` 자체가 완결된 요소이고, 나열 간격은 `SRadioCardGroup` 의 `direction` 이 맞춘다 (§2-2).
|
|
996
1305
|
|
|
997
1306
|
#### 3-7-3. 켜고 끄는 셋 — SCheckbox vs SSwitch vs SToggle
|
|
998
1307
|
|
|
@@ -1013,15 +1322,45 @@ const columns: STableColumn[] = [
|
|
|
1013
1322
|
| 판별 | 사용 |
|
|
1014
1323
|
| --- | --- |
|
|
1015
1324
|
| 날짜 **하나**를 값으로 받는다 | `SDatePicker` |
|
|
1325
|
+
| 연도 선택 리스트만 필요하다 (트리거·팝오버는 직접 조합) | `SDatePickerYearListbox` |
|
|
1326
|
+
| 연도+월 선택 리스트만 필요하다 (트리거·팝오버는 직접 조합) | `SDatePickerMonthListbox` |
|
|
1016
1327
|
| **시작~종료** 를 값으로 받는다 | `SDateRangePicker` |
|
|
1017
1328
|
| 달력 격자 **자체가 화면 콘텐츠** 다 (일정·이벤트 보기) | `SCalendar` |
|
|
1018
1329
|
|
|
1019
1330
|
- **기간을 `SDatePicker` 두 개로 만들지 않는다.** 시작이 종료보다 뒤인 입력을 막는 검증과 한쪽만 고른 중간 상태 처리가 `SDateRangePicker` 안에 이미 있다. 두 개로 쪼개면 그게 전부 앱 몫이 된다.
|
|
1020
1331
|
- `SDatePicker`·`SDateRangePicker` 는 내부적으로 `SCalendar` 를 팝오버로 띄운다. 값을 받는 자리에 `SCalendar` 를 직접 쓰지 않는다.
|
|
1332
|
+
- `SDatePickerYearListbox`·`SDatePickerMonthListbox` 는 `SDatePicker` 의 mode listbox 조각만 떼어낸 컴포넌트다. 일반 폼 입력에는 `SDatePicker mode="year" | "month"` 를 우선 쓰고, 다른 트리거·팝오버 안에 리스트만 끼워 넣을 때만 직접 쓴다.
|
|
1333
|
+
|
|
1334
|
+
**날짜·시간 피커는 폭 상한을 스스로 갖는다 — `width` 를 주지 않는다.** 값 길이가 `YYYY-MM-DD` 처럼 정해져 있어 컴포넌트가 사이즈별 상한을 안다. `SKeyValueTable` 이 모든 컨트롤에 `width="100%"` 를 넘기지만, 이 상한 덕분에 행 전체로 늘어나지 않고 제 폭에서 멈춘다.
|
|
1335
|
+
|
|
1336
|
+
| 컴포넌트 | `size="sm"` | `size="md"` |
|
|
1337
|
+
| --- | --- | --- |
|
|
1338
|
+
| `SDatePicker` | md | lg |
|
|
1339
|
+
| `SDateRangePicker` | lg | xl |
|
|
1340
|
+
| `STimePicker` | md | lg |
|
|
1341
|
+
| `STimeRangePicker` | md | lg (오전/오후 표시는 두 사이즈 모두 lg) |
|
|
1342
|
+
|
|
1343
|
+
`SDateRangePicker` 가 한 등급씩 위인 것은 값이 `YYYY-MM-DD ~ YYYY-MM-DD` 로 두 배가 넘기 때문이다. 같은 이유로 `STimeRangePicker` 의 오전/오후 모드도 sm 에서 한 등급 위를 쓴다 — 그 모드의 최소 폭이 md 등급을 이미 넘어, 그대로 두면 하한이 상한을 넘어 상한이 무력해진다.
|
|
1344
|
+
|
|
1345
|
+
`SDatePicker` 만 이 상한을 `maxWidth` 로 덮을 수 있다 — 등급을 주면 그 등급이 상한이 되고, `width="100%" maxWidth="100%"` 면 부모 폭을 그대로 채운다. **폭이 이미 좁게 정해진 자리(팝오버·좁은 카드)에서만 쓴다.** 폼·표 행에서는 쓰지 않는다 — 거기서 상한을 풀면 4~10글자짜리 값이 행 전체를 차지한다.
|
|
1346
|
+
|
|
1347
|
+
##### 값을 지울 수 있게 하려면 `clearable`
|
|
1348
|
+
|
|
1349
|
+
`SSelect` · `SDatePicker` · `SDateRangePicker` · `STimePicker` · `STimeRangePicker` 가 같은 규칙으로 갖는다. 값이 있을 때만 지우기 버튼이 나타나고, 누르면 **`onValueChange` 로 `null` 이 온다** (빈 문자열이 아니다). 받는 쪽 상태도 `null` 을 담을 수 있어야 한다.
|
|
1350
|
+
|
|
1351
|
+
```tsx
|
|
1352
|
+
const [from, setFrom] = useState<string | null>(null);
|
|
1353
|
+
|
|
1354
|
+
<SDatePicker label="시작일" clearable value={from} onValueChange={setFrom} />;
|
|
1355
|
+
```
|
|
1356
|
+
|
|
1357
|
+
- **조회 조건(필터)에는 켠다.** 한 번 고른 날짜를 되돌릴 방법이 없으면 전체 조회로 돌아가려고 새로고침하게 된다.
|
|
1358
|
+
- **필수 입력 필드에는 켜지 않는다.** 지우면 다시 고르기 전까지 폼이 통과하지 못한다 — 지울 수 있어야 하는 값이면 애초에 필수가 아니다.
|
|
1359
|
+
- `disabled` 이면 지우기 버튼도 함께 사라진다. 끈 필드를 지울 수 있으면 안 되기 때문이다.
|
|
1021
1360
|
|
|
1022
1361
|
#### 3-7-5. SField 를 직접 쓰는 경우
|
|
1023
1362
|
|
|
1024
|
-
**거의 없다.** `SInput`·`SNumberInput`·`STextarea`·`SSelect`·날짜/시간 피커는 이미 내부에서 `SField` 를 쓰고 있어서 `label`·`required`·에러 표시를 자기 prop 으로 받는다. 그 위에 `SField` 를 한 겹 더 감싸면 라벨이 두 번 나온다.
|
|
1363
|
+
**거의 없다.** `SInput`·`SNumberInput`·`STextarea`·`SEditor`·`SSelect`·날짜/시간 피커는 이미 내부에서 `SField` 를 쓰고 있어서 `label`·`required`·에러 표시를 자기 prop 으로 받는다. 그 위에 `SField` 를 한 겹 더 감싸면 라벨이 두 번 나온다.
|
|
1025
1364
|
|
|
1026
1365
|
직접 쓰는 경우는 하나뿐이다 — **디자인 시스템에 없는 컨트롤**에 다른 필드와 똑같은 라벨·필수·에러 모양을 붙일 때.
|
|
1027
1366
|
|
|
@@ -1034,15 +1373,17 @@ const columns: STableColumn[] = [
|
|
|
1034
1373
|
| **순서 자체가 데이터**라 사용자가 끌어서 바꾼다 | `SDraggableList` + `SDraggableItem` |
|
|
1035
1374
|
|
|
1036
1375
|
- **항목 하나의 속성을 나열하는 것은 목록이 아니다.** `라벨: 값` 이 세로로 쌓이는 것은 `SKeyValueTable` 이다 (§4-4).
|
|
1037
|
-
- `SList` 는 레이아웃만 담당한다.
|
|
1038
|
-
-
|
|
1039
|
-
-
|
|
1376
|
+
- `SList` 는 레이아웃만 담당한다. depth 별 단일 펼침이 필요하면 `SExpansionList` 다 (§3-7-7).
|
|
1377
|
+
- **`SList` 의 자식은 `SListItem` 을 권장한다.** 다른 자식도 그대로 렌더되지만, 펼치는 항목은 `SExpansionList` + `SExpansionItem` 이, 끌어서 순서를 바꾸는 항목은 `SDraggableList` + `SDraggableItem` 이 여닫힘·정렬 동작까지 함께 관리하므로 그쪽을 쓴다 (§3-7-7).
|
|
1378
|
+
- **항목 사이 구분선은 리스트가 알아서 그린다.** `SListItem`·`SExpansionItem`·`SDraggableItem` 셋 다 스스로 구분선을 그리지 않는다. `SList`·`SExpansionList`·`SDraggableList` 가 자식 **사이에** 구분선을 넣으므로 아이템에 `border-b` 를 직접 붙이지 않고, 켜는 prop 도 따로 없다. 마지막 항목 아래에는 선이 남지 않는다. 테두리형(`bordered`)은 테두리가 구분 역할을 하므로 리스트가 구분선을 빼고, `useGap` 으로 띄운다 — `useGap` 을 준 목록에도 구분선은 들어가지 않는다.
|
|
1379
|
+
- **`SListItem` 은 기본이 표시 전용이다.** 눌러서 이동·선택하게 하려면 `clickable` 을 준다 — hover·`selected`·`interaction="chevron"` 표현이 전부 여기에 딸려 있어서, `clickable` 없이 `selected` 만 주면 선택 표시가 나오지 않는다. `SExpansionList` 는 자식 아이템을 알아서 클릭 가능하게 만들어 이 함정을 막아 준다 — 선택 상태 자체는 앱이 든다 (§3-7-7).
|
|
1040
1380
|
|
|
1041
1381
|
```tsx
|
|
1042
|
-
✅ <SList
|
|
1382
|
+
✅ <SList><SListItem title="일반 문의" />…</SList> {/* 줄로 구분되는 목록 — 구분선은 자동 */}
|
|
1043
1383
|
✅ <SList useGap><SListItem title="일반 문의" bordered />…</SList> {/* 카드처럼 떨어진 목록 */}
|
|
1044
1384
|
✅ <SListItem title="일반 문의" clickable selected onClick={…} /> {/* 눌러서 고르는 목록 */}
|
|
1045
|
-
❌ <SList><SListItem title="일반 문의" className="border-b" />…</SList>
|
|
1385
|
+
❌ <SList><SListItem title="일반 문의" className="border-b" />…</SList> {/* 구분선을 직접 붙이지 않는다 */}
|
|
1386
|
+
❌ <SList><><SListItem title="일반 문의" /><SListItem title="결제 문의" /></></SList> {/* Fragment 로 묶으면 그 안쪽은 구분되지 않는다 */}
|
|
1046
1387
|
❌ <SListItem title="일반 문의" selected /> {/* clickable 없으면 선택 표시가 안 나온다 */}
|
|
1047
1388
|
```
|
|
1048
1389
|
|
|
@@ -1054,7 +1395,30 @@ const columns: STableColumn[] = [
|
|
|
1054
1395
|
| **한 번에 하나만** 열려야 한다 (아코디언) | `SExpansionList` + `SExpansionItem` |
|
|
1055
1396
|
| **부모-자식 계층 자체**를 보여줘야 한다 (2단 이상, 연결선) | `STree` |
|
|
1056
1397
|
|
|
1057
|
-
`SExpansionList` 는 depth 별
|
|
1398
|
+
`SExpansionList` 는 depth 별 **단일 확장**을 관리하는 wrapper 다. 직접 `useState` 로 "열린 항목 하나"를 들고 있지 않는다. 항목 사이 구분선은 여기서도 리스트가 알아서 그린다 — 따로 줄 prop 이 없다 (§3-7-6).
|
|
1399
|
+
|
|
1400
|
+
##### 펼침 ≠ 선택
|
|
1401
|
+
|
|
1402
|
+
**펼침은 리스트가 관리하고, 선택은 앱이 관리한다.**
|
|
1403
|
+
|
|
1404
|
+
펼침은 화면 밖에 진실이 없는 순간 UI 상태다. 각 항목이 자기 `expanded` 를 들고 있으면 "하나만 열림"을 만들 수 없어 누군가 나머지를 닫아야 하고, 그것이 이 wrapper 다.
|
|
1405
|
+
|
|
1406
|
+
선택은 다르다. 앱이 `selectedId` 스칼라 하나를 들면 상호배제가 구조적으로 보장되고, 그 값은 URL·store 로 복원돼야 한다. 리스트가 사본을 들면 그 순간 진실이 둘이 되어 어긋난다.
|
|
1407
|
+
|
|
1408
|
+
```tsx
|
|
1409
|
+
const [selectedId, setSelectedId] = useState<string>();
|
|
1410
|
+
|
|
1411
|
+
<SExpansionList>
|
|
1412
|
+
{/* 하위가 없는 항목(전체·미분류)은 SExpansionItem 이 아니라 SListItem 이다 */}
|
|
1413
|
+
<SListItem title="전체" selected={selectedId === 'all'} onClick={() => setSelectedId('all')} />
|
|
1414
|
+
<SExpansionItem title="조직">
|
|
1415
|
+
<SListItem title="영업팀" selected={selectedId === 'sales'} onClick={() => setSelectedId('sales')} />
|
|
1416
|
+
</SExpansionItem>
|
|
1417
|
+
</SExpansionList>
|
|
1418
|
+
```
|
|
1419
|
+
|
|
1420
|
+
- `clickable` 은 리스트가 자식 `SListItem` 에 기본으로 켜 준다 — `clickable` 없이 `selected` 만 주면 표시가 안 나오는 함정(§3-7-6)을 막는 값이다.
|
|
1421
|
+
- **하위를 가지지 않는 항목은 `SExpansionItem` 이 아니라 `SListItem`** 으로 둔다. 펼칠 것이 없는데 펼침 항목으로 만들면 화살표만 남는다.
|
|
1058
1422
|
|
|
1059
1423
|
#### 3-7-8. SCard vs SSectionHeaderCard
|
|
1060
1424
|
|
|
@@ -1065,7 +1429,23 @@ const columns: STableColumn[] = [
|
|
|
1065
1429
|
|
|
1066
1430
|
- 페이지 골격에서 콘텐츠를 묶는 섹션은 **사실상 전부 `SSectionHeaderCard`** 다 (§4-4·§4-5). 제목·필수 표시·도움말·헤더 우측 액션이 전부 여기 붙는다.
|
|
1067
1431
|
- **카드 안에 카드를 겹치지 않는다.** 섹션 안을 더 나눠야 하면 `SDivider` 로 끊거나(§3-6) 섹션을 둘로 분리한다.
|
|
1068
|
-
- 안쪽 여백은 `SSectionHeaderCard
|
|
1432
|
+
- 안쪽 여백은 `SSectionHeaderCard` 의 `padding` prop 으로 준다 — `p-sd-*` 를 직접 주지 않는다 (§2-2).
|
|
1433
|
+
|
|
1434
|
+
**카드가 자기 안에서 확정하는 액션을 가지면 하단 버튼을 `children` 에 직접 두지 않는다.** 두 카드 모두 모달·드로어와 같은 하단 액션 영역을 갖는다 — 주 액션은 `button`, 보조 버튼은 `footerLeft` 로 넘긴다(§3-3-4 와 같은 규칙). 배경·상단 구분선·좌우 여백·양끝 분리가 컴포넌트 규칙대로 잡히고, 좌우 끝이 헤더·본문과 맞는다. `footerLeft` 는 슬롯이라 `SButton` 을 직접 배치하며 `size="md"` 를 명시한다(§3-5-2).
|
|
1435
|
+
|
|
1436
|
+
```tsx
|
|
1437
|
+
{/* 섹션 안에서 바로 수정·저장하는 인라인 폼 섹션 (§4-4) */}
|
|
1438
|
+
<SSectionHeaderCard
|
|
1439
|
+
title="배송지"
|
|
1440
|
+
marker
|
|
1441
|
+
footerLeft={<SButton color="neutral" outline size="md" label="취소" onClick={reset} />}
|
|
1442
|
+
button={{ label: '저장', onClick: save }}
|
|
1443
|
+
>
|
|
1444
|
+
<SKeyValueTable … />
|
|
1445
|
+
</SSectionHeaderCard>
|
|
1446
|
+
```
|
|
1447
|
+
|
|
1448
|
+
**페이지 전체를 확정하는 액션은 카드 푸터가 아니라 페이지 하단에 둔다.** 카드 푸터는 **그 카드 안에서 닫히는 액션**의 자리다 — 여러 섹션을 한 번에 저장하는 버튼을 마지막 카드의 푸터에 넣으면 그 카드에만 걸리는 액션으로 읽힌다. 이때는 §4-4 처럼 카드 밖 하단 줄에 둔다.
|
|
1069
1449
|
|
|
1070
1450
|
#### 3-7-9. SLinearProgress vs SCircleProgress
|
|
1071
1451
|
|
|
@@ -1092,7 +1472,8 @@ const columns: STableColumn[] = [
|
|
|
1092
1472
|
- **기본은 `SKeyValueTable` 이다** (§4-2). 조건이 대여섯 개 이하로 고정이면 표로 펼쳐 두는 편이 한눈에 읽힌다.
|
|
1093
1473
|
- `SChipFilter` 는 조건을 **칩 한 줄**로 접고, "필터 추가" 로 필요한 것만 꺼내 쓰게 한다. 칩을 누르면 편집 팝오버가 열리고, 날짜는 프리셋(오늘·지난 7일·사용자 지정)으로 고른다. 조건 후보가 많은 목록 화면에서 필터가 화면을 세로로 잡아먹는 것을 막는 용도다.
|
|
1094
1474
|
- 검색 실행 시점이 다르다 — `SKeyValueTable` 필터는 앱이 검색 버튼을 직접 놓지만, `SChipFilter` 는 편집 팝오버가 닫히거나 "검색" 을 누를 때 `onSearch` 가 값 맵과 함께 호출된다. 값이 바뀌지 않았으면 호출되지 않는다.
|
|
1095
|
-
-
|
|
1475
|
+
- **노출할 칩을 앱이 계산하지 않는다.** 바에 놓이는 것은 `fixed`·`required` 필드와 **`value` 에 값이 들어 있는 필드**다. 노출 목록을 밖에서 넘기는 prop 은 없다. 쿼리스트링·서버 상태에서 조건을 복원하는 목록 화면도 **`value` 만 넘기면 칩이 함께 살아나고**, 그 조건을 빼면 칩도 함께 빠진다 — 칩과 조회 조건이 어긋날 자리가 없다. "필터 추가" 로 꺼낸 필터는 값을 넣기 전에도 자리를 지키지만, "검색 초기화" 를 누르거나 화면을 다시 그리면 사라진다. 걸린 조건이 없으니 문제되지 않는다.
|
|
1476
|
+
- **`fields` 는 항상 그룹 배열이다.** 묶을 것이 없어도 `[{ fields: [...] }]` 로 한 겹 감싼다. 함께 걸어야 하는 조건(예: 기간 중 하나는 필수)이 있으면 그 필드들만 별도 그룹으로 떼어 `rule` 을 준다 — 규칙을 못 채운 동안 경고 툴팁이 떠 있고 `onSearch` 가 막힌다. 그룹 앞 구분선은 `divider` 로 켠다. 검증 단위와 구분선은 별개라, 묶어서 검증만 하고 싶으면 `divider` 를 주지 않는다.
|
|
1096
1477
|
|
|
1097
1478
|
#### 3-7-12. 이미지 — SImage
|
|
1098
1479
|
|
|
@@ -1137,15 +1518,27 @@ export default function AppShell({
|
|
|
1137
1518
|
children,
|
|
1138
1519
|
header,
|
|
1139
1520
|
scrollEndSpacing,
|
|
1140
|
-
|
|
1521
|
+
contentHeight,
|
|
1522
|
+
}: {
|
|
1523
|
+
children: React.ReactNode;
|
|
1524
|
+
header?: SPageHeaderProps;
|
|
1525
|
+
scrollEndSpacing?: boolean;
|
|
1526
|
+
contentHeight?: SPageContentHeight;
|
|
1527
|
+
}) {
|
|
1141
1528
|
return (
|
|
1142
1529
|
<SLayout type="box" header="fix">
|
|
1143
1530
|
{/* type/header/folded 는 SLayout 에만 준다 — SGnb 는 context 에서 읽는다 */}
|
|
1144
1531
|
<SGnb items={MENU} value={current} onValueChange={navigate} logo={<Logo />} />
|
|
1145
1532
|
{/* 콘텐츠 패딩은 SPage 가 토큰으로 넣는다 — p-sd-* 로 덮어쓰지 않는다 */}
|
|
1146
|
-
{/*
|
|
1533
|
+
{/* 높이 모드는 페이지가 정한다 — 대부분 contentHeight="fill" 이다 (§2-2) */}
|
|
1534
|
+
{/* 스크롤 끝 여백도 SPage 가 넣는다. 페이지가 실제로 스크롤되는 화면에서만 켠다 */}
|
|
1147
1535
|
{/* header 는 페이지마다 달라 AppShell 이 그대로 받아 넘긴다 — 페이지 제목은 여기서 만들지 않는다 */}
|
|
1148
|
-
<SPage
|
|
1536
|
+
<SPage
|
|
1537
|
+
background="frame"
|
|
1538
|
+
scrollEndSpacing={scrollEndSpacing}
|
|
1539
|
+
contentHeight={contentHeight}
|
|
1540
|
+
header={header}
|
|
1541
|
+
>
|
|
1149
1542
|
{children}
|
|
1150
1543
|
</SPage>
|
|
1151
1544
|
</SLayout>
|
|
@@ -1187,7 +1580,9 @@ import { SModalOutlet } from 'sellmate-design-system-react';
|
|
|
1187
1580
|
|
|
1188
1581
|
**최소 너비는 `SLayout` 이 보장한다 — 앱이 `min-w-*` 를 직접 주지 않는다.** 창이 최소 너비(`SLAYOUT_MIN_WIDTH`, GNB 포함한 전체 기준)보다 좁아지면 GNB·상단바는 제자리에 남고 **`SPage` 안에만 가로 스크롤이 생긴다.** 문서(브라우저 창)에는 가로 스크롤이 생기지 않는다. 그래서 셸을 감싸는 요소에 `min-width` 나 `overflow-x` 를 걸지 않는다 — 걸면 창 전체가 스크롤되어 GNB 가 화면 밖으로 밀린다.
|
|
1189
1582
|
|
|
1190
|
-
**셸의 `SPage` 는 모든 페이지가 공유하므로,
|
|
1583
|
+
**셸의 `SPage` 는 모든 페이지가 공유하므로, 페이지마다 달라지는 것은 페이지가 셸에 알려야 한다.** 위처럼 프레임 컴포넌트가 `contentHeight` · `scrollEndSpacing` 을 받아 그대로 넘긴다.
|
|
1584
|
+
|
|
1585
|
+
**대부분의 페이지는 `contentHeight="fill"` 이다** — 본문이 창을 꽉 채우고 스크롤은 각 영역 안에서 일어나는 것이 표준이다(§2-2). 블록의 높이가 정해져 있고 그 높이가 창보다 커서 페이지 자체가 스크롤돼야 하는 화면에서만 `contentHeight="auto"`(기본값) + `scrollEndSpacing` 을 켠다.
|
|
1191
1586
|
|
|
1192
1587
|
**상단바 배치는 `header` 가 정한다.** 요소 순서가 달라지므로 슬롯을 채우기 전에 어느 쪽인지부터 정한다.
|
|
1193
1588
|
|
|
@@ -1209,7 +1604,7 @@ import { SModalOutlet } from 'sellmate-design-system-react';
|
|
|
1209
1604
|
topContent={
|
|
1210
1605
|
/* 남는 폭 전체를 받는다 — 왼쪽은 그대로, 오른쪽 끝은 ml-auto */
|
|
1211
1606
|
<div className="flex w-full items-center gap-sd-8">
|
|
1212
|
-
<
|
|
1607
|
+
<SSearchInput value={keyword} onValueChange={setKeyword} onSearch={runSearch} placeholder="통합 검색" />
|
|
1213
1608
|
<SButton size="sm" color="neutral" outline label="내 계정" className="ml-auto" onClick={openAccount} />
|
|
1214
1609
|
</div>
|
|
1215
1610
|
}
|
|
@@ -1226,7 +1621,7 @@ import { SModalOutlet } from 'sellmate-design-system-react';
|
|
|
1226
1621
|
{/* 접히면 menuTop·menuFooter 가 함께 빠지므로, 폴드 레일에 남길 것만 foldedTop 으로 따로 준다 */}
|
|
1227
1622
|
<SGnb
|
|
1228
1623
|
items={MENU} value={current} onValueChange={navigate} useRail
|
|
1229
|
-
menuTop={<
|
|
1624
|
+
menuTop={<SSearchInput value={keyword} onValueChange={setKeyword} placeholder="메뉴 검색" />}
|
|
1230
1625
|
menuFooter={<AccountRow />}
|
|
1231
1626
|
foldedTop={<SGhostButton icon="search" size="sm" ariaLabel="메뉴 검색" onClick={openSearch} />}
|
|
1232
1627
|
/>
|
|
@@ -1234,6 +1629,37 @@ import { SModalOutlet } from 'sellmate-design-system-react';
|
|
|
1234
1629
|
|
|
1235
1630
|
슬롯 안쪽 여백은 **슬롯 내용이 직접 갖는다** — 컴포넌트는 자리만 잡는다(폴드 슬롯만 좁은 폭에 맞춰 가운데 정렬한다). 메뉴 폭은 `resizable` 로 바뀔 수 있으므로 슬롯 내용은 고정 폭 대신 `w-full` 로 따라가게 둔다.
|
|
1236
1631
|
|
|
1632
|
+
#### GNB 맨 아래의 system 패널
|
|
1633
|
+
|
|
1634
|
+
**서비스 전환·도메인·알림·설정·계정처럼 "지금 어떤 자격으로 보고 있는가" 를 다루는 묶음은 `SGnbSystem` 을 `SGnb` 의 `system` 슬롯에 넣는다.** 이 조각들을 `menuFooter` 에 손으로 조립하지 않는다 — 패널의 배경·위쪽 경계·구분선·행 간격이 전부 토큰으로 정해져 있어 직접 만들면 어긋난다.
|
|
1635
|
+
|
|
1636
|
+
`system` 은 다른 슬롯과 달리 **펼침·접힘 양쪽을 혼자 맡는다** — 펼치면 메뉴 컬럼 바닥에, 접히면(`header="fix"`) `foldedFooter` 아래 레일 바닥에 여백 없이 붙는다. 그래서 `menuFooter`/`foldedFooter` 로 나눠 줄 필요가 없다.
|
|
1637
|
+
|
|
1638
|
+
```tsx
|
|
1639
|
+
<SGnb
|
|
1640
|
+
items={MENU} value={current} onValueChange={navigate} logo={<Logo />}
|
|
1641
|
+
system={
|
|
1642
|
+
<SGnbSystem
|
|
1643
|
+
/* 상단에 쌓는 행 — SSystemActionButton 의 props 배열이다 (§3-5). color 는 넘기지 않는다 */
|
|
1644
|
+
actions={[{ type: 'select', label: '서비스 전환', option: { icon: 'robot' }, onClick: openServices }]}
|
|
1645
|
+
domain={domainName}
|
|
1646
|
+
alert={{ count: unreadCount, onClick: openAlerts }}
|
|
1647
|
+
setting={{ onClick: openSettings }}
|
|
1648
|
+
account={{ label: userName, onClick: openAccount }}
|
|
1649
|
+
/>
|
|
1650
|
+
}
|
|
1651
|
+
/>
|
|
1652
|
+
```
|
|
1653
|
+
|
|
1654
|
+
- **`color` 와 `folded` 를 주지 않는다.** `system` 슬롯에 있으면 GNB 가 자기 색과 접힘 형태를 내려준다. 두 prop 은 GNB 밖에서 이 패널을 단독으로 쓸 때만 준다.
|
|
1655
|
+
- **각 조각은 해당 prop 을 줄 때만 나타난다 — 어떤 조합이어도 그것만으로 판이 성립한다.** 계정만, 알림만, 설정만, 알림+계정 … 서비스가 쓰는 것만 넘긴다. 조각을 빼려고 빈 문자열이나 빈 객체를 넣지 않는다(`setting={{}}` 은 "설정 버튼을 쓴다" 는 뜻이다).
|
|
1656
|
+
- 구분선은 **위아래 양쪽에 내용이 있을 때만** 그어진다 — `actions` 만 넘겨도 바닥에 뜻 없는 선이 남지 않는다.
|
|
1657
|
+
- 알림·설정은 **혼자 서면 폭을 다 먹고**, 둘이 나란히 서면 알림이 남는 폭을·설정이 제 폭을 갖는다. 어느 쪽이 빠져도 왼쪽 끝은 위아래 행과 맞으므로 앱이 정렬을 맞출 일이 없다.
|
|
1658
|
+
- **켜진 조각이 하나도 없으면 아무것도 렌더되지 않는다.** 조건부로 조각이 다 빠지는 화면에서도 GNB 바닥에 빈 판이 남지 않는다.
|
|
1659
|
+
- **접히면 알림·계정 아이콘만 남는다** — `actions`·`domain`·`setting` 은 48px 폭에 놓을 자리가 없어 렌더되지 않고, 알림 개수는 점 배지가 된다. **알림도 계정도 안 쓰는 조합이면 접힌 패널은 아예 렌더되지 않는다** — 접힌 상태에서도 반드시 눌러야 하는 것이 그 둘 밖에 있으면 `foldedTop`/`foldedFooter` 로 따로 준다.
|
|
1660
|
+
- **알림 개수는 `alert.count` 하나로 표현한다.** 1 이상이면 벨이 울리는 아이콘과 강조색으로 바뀐다 — 색은 GNB 색이 정하므로 앱이 직접 칠하지 않는다.
|
|
1661
|
+
- **`useRail` 에서 하위 메뉴가 없는 레일 아이템이 활성이면 메뉴 컬럼째 사라지므로 이 패널도 함께 빠진다.** 항상 보여야 하는 계정·알림이라면 그런 레일 리프를 두지 않는다.
|
|
1662
|
+
|
|
1237
1663
|
### 4-2. 목록 페이지 (필터 + 테이블)
|
|
1238
1664
|
|
|
1239
1665
|
구조: **페이지 헤더(`AppShell` 의 `header` prop, 가이드 등 부가 액션은 slot) → 필터(`SKeyValueTable`) → `STableBar` → `STable`**
|
|
@@ -1245,7 +1671,10 @@ import { SModalOutlet } from 'sellmate-design-system-react';
|
|
|
1245
1671
|
- **페이지 제목 줄에는 이 페이지의 주요 액션을 두지 않는다.** 부가적인 것만 `header.slot` 에 `SButton size="sm"` 으로 온다 (§4-1 "페이지 헤더 사용 규칙").
|
|
1246
1672
|
- **주요 액션(등록 등)은 `STableBar` 의 `rightActions`** 에 둔다.
|
|
1247
1673
|
- **선택 상태 액션(선택 삭제 등)은 `STableBar` 의 `actions`** 에 둔다. `actions` 슬롯은 건수 요약이 있으면 앞에 구분선(`SDivider`)을 **자동으로** 넣으므로 직접 구분선을 만들지 않는다.
|
|
1248
|
-
-
|
|
1674
|
+
- **본문이 남은 높이를 채우게 한다** — `AppShell` 에 `contentHeight="fill"` 을 넘긴다(§2-2 표준). 페이지가 통째로 스크롤되면 페이지네이션이 화면 밖으로 밀려 "여기서 끝"이 읽히지 않는다. `fill` 이면 **표만 자기 안에서 스크롤하고 페이지네이션은 하단에 고정**된다.
|
|
1675
|
+
- 본문 루트에 `h-full min-h-0` 으로 세로 축을 잇고, 남은 높이를 먹을 `STable` 에 `min-h-0 flex-1` 을 준다. 이 사슬이 하나라도 끊기면 표가 높이를 못 잡는다.
|
|
1676
|
+
- `fill` 에서는 페이지가 스크롤하지 않으므로 **`scrollEndSpacing` 은 무시된다** — 따로 끄지 않는다 (§2-2).
|
|
1677
|
+
- **정렬 가능한 컬럼은 `sortable` 로 준다.** 정렬 상태(`sort`)는 이 페이지가 들고 `onSortChange` 로 받는다 — 조회 조건이라 URL 에 실려야 한다 (§3-4).
|
|
1249
1678
|
|
|
1250
1679
|
```tsx
|
|
1251
1680
|
import {
|
|
@@ -1293,9 +1722,10 @@ export default function ProductListPage() {
|
|
|
1293
1722
|
// 이 페이지의 주요 액션이 아니라 부가 액션 — slot 은 sm 버튼으로만 채운다
|
|
1294
1723
|
slot: <SButton size="sm" color="neutral" outline label="이용 가이드" onClick={openGuide} />,
|
|
1295
1724
|
}}
|
|
1296
|
-
|
|
1725
|
+
contentHeight="fill" // 표가 남은 높이를 채우고 페이지네이션이 하단에 고정된다
|
|
1297
1726
|
>
|
|
1298
|
-
|
|
1727
|
+
{/* h-full min-h-0 → STable 의 min-h-0 flex-1 로 세로 축이 이어진다 */}
|
|
1728
|
+
<div className="flex h-full min-h-0 flex-col gap-sd-12">
|
|
1299
1729
|
{/* 필터 — search 를 켜면 우측에 검색 패널이 붙는다 */}
|
|
1300
1730
|
<SKeyValueTable
|
|
1301
1731
|
fields={filterFields}
|
|
@@ -1320,7 +1750,9 @@ export default function ProductListPage() {
|
|
|
1320
1750
|
}
|
|
1321
1751
|
/>
|
|
1322
1752
|
|
|
1753
|
+
{/* 남은 높이를 채우고 본문만 스크롤한다 — 페이지네이션 바는 표 안에서 하단 고정 */}
|
|
1323
1754
|
<STable
|
|
1755
|
+
className="min-h-0 flex-1"
|
|
1324
1756
|
columns={columns}
|
|
1325
1757
|
rows={rows}
|
|
1326
1758
|
rowKey="id"
|
|
@@ -1336,6 +1768,33 @@ export default function ProductListPage() {
|
|
|
1336
1768
|
}
|
|
1337
1769
|
```
|
|
1338
1770
|
|
|
1771
|
+
#### 한 화면에 더 많은 행을 — `dense` 와 밀도 토글
|
|
1772
|
+
|
|
1773
|
+
행 높이를 줄이는 것은 `dense` 다. 세로 여백만 줄고 좌우 패딩은 그대로라, 값이 잘리지 않으면서 한 화면에 들어가는 행 수가 늘어난다.
|
|
1774
|
+
|
|
1775
|
+
**어느 쪽이 편한지는 화면이 아니라 사용자가 안다.** 그래서 `dense` 는 밀도를 고정하는 스위치가 아니라 **시작 밀도이자 밀도 토글의 스위치**다 — 켜면 하단 바 우측(rows per page 셀렉트 왼쪽)에 `좁게 보기` · `넓게 보기` 링크가 붙는다. 켜고 끄는 별도 prop 은 없다.
|
|
1776
|
+
|
|
1777
|
+
```tsx
|
|
1778
|
+
// 좁게 시작하고, 사용자가 바꾸는 밀도는 표가 알아서 들고 간다
|
|
1779
|
+
<STable dense useRowsPerPageSelect pagination={{ currentPage, lastPage }} />;
|
|
1780
|
+
|
|
1781
|
+
// 사용자가 고른 밀도를 다음 방문에도 남기려는 화면만 받아서 저장한다.
|
|
1782
|
+
// 저장한 값은 시작 밀도로만 돌려준다 — 되돌려 넣지 않는다
|
|
1783
|
+
const [initialDense] = useState(() => loadPref('list.dense', true));
|
|
1784
|
+
|
|
1785
|
+
<STable
|
|
1786
|
+
dense={initialDense}
|
|
1787
|
+
onDenseChange={next => savePref('list.dense', next)}
|
|
1788
|
+
useRowsPerPageSelect
|
|
1789
|
+
pagination={{ currentPage, lastPage }}
|
|
1790
|
+
/>;
|
|
1791
|
+
```
|
|
1792
|
+
|
|
1793
|
+
- **누른 뒤의 밀도는 `STable` 이 내부 상태로 들고 간다.** `onDenseChange` 없이도 토글은 동작한다. 저장이 필요한 화면만 받아서 저장하면 된다.
|
|
1794
|
+
- **`dense` 에 `onDenseChange` 값을 되돌려 넣지 않는다.** 토글을 붙일지는 이 prop 이 정하므로, 넓게 본 순간 `dense` 가 `false` 가 되면 토글이 사라져 다시 좁힐 길이 없다. prop 값을 바꿔 넘기는 것은 외부 버튼 등으로 밀도를 **되돌릴 때**만 쓴다 — 내부 밀도가 그 값으로 맞춰진다.
|
|
1795
|
+
- **`dense` 면 페이지네이션이 없어도 토글이 나온다** — 하단 바를 토글만 담아 그린다. `dense` 가 아니면 토글도 없다.
|
|
1796
|
+
- 라벨과 아이콘은 현재 상태가 아니라 **누르면 되는 상태**를 가리킨다. 좁게 보고 있으면 `넓게 보기` 다.
|
|
1797
|
+
|
|
1339
1798
|
### 4-3. 폼 페이지 (등록/수정)
|
|
1340
1799
|
|
|
1341
1800
|
구조: **페이지 제목(`AppShell` 의 `header` prop) → `SForm` + `SKeyValueTable` → 하단 버튼**
|
|
@@ -1343,6 +1802,19 @@ export default function ProductListPage() {
|
|
|
1343
1802
|
- 필드를 `div` 로 나열하지 않고 **`SKeyValueTable` 의 행으로 구성**한다.
|
|
1344
1803
|
- 검증 규칙은 각 field 의 `options.rules` 로 넘긴다. `SForm` 이 하위 컨트롤을 자동 수집해 submit 시 일괄 검증하고, 실패 시 첫 실패 필드로 포커스를 옮긴다.
|
|
1345
1804
|
- **버튼 순서: 취소·닫기가 왼쪽, 저장·등록·수정·삭제가 오른쪽.** 이 순서는 모든 화면에서 동일하다.
|
|
1805
|
+
- **폼 페이지도 `contentHeight="fill"` 이 기본이다**(§2-2). 폼이 길어 페이지가 실제로 스크롤되는 화면에서만 `auto` + `scrollEndSpacing` 을 켠다.
|
|
1806
|
+
- **필드 폭은 등급으로 준다** — `width="md"` 처럼 `'xs' | 'sm' | 'md' | 'lg' | 'xl'` 중 하나다. px 를 직접 적지 않는다. 등급은 `maxLength`(= 스키마 상한)로 고르고, 상한이 `xl` 을 넘거나 상한이 없으면 `width="100%"` 로 행 전체를 쓴다 (§6 `field-width-grade`).
|
|
1807
|
+
|
|
1808
|
+
**`SKeyValueTable` 의 전체 열 수는 가장 긴 행이 정한다.** 어떤 행이 그보다 짧으면 남는 자리에 셀이 없어 그 구간의 행 구분선이 끊긴다. 마지막 필드에 `tdColSpan` 을 주어 채운다.
|
|
1809
|
+
|
|
1810
|
+
```tsx
|
|
1811
|
+
[
|
|
1812
|
+
[{ name: 'category', … }, { name: 'price', … }], // 필드 2개 → 4칸
|
|
1813
|
+
[{ name: 'memo', …, tdColSpan: 3 }], // th(1) + td(3) = 4칸
|
|
1814
|
+
]
|
|
1815
|
+
```
|
|
1816
|
+
|
|
1817
|
+
**한 행에 필드를 추가하면 다른 행들의 `tdColSpan` 도 함께 봐야 한다.** 전체 열 수가 늘면 나머지 행들이 조용히 짧아진다 — 화면에서만 드러나는 컴포넌트 고유 동작이라 자동으로 채워 주지 않는다.
|
|
1346
1818
|
|
|
1347
1819
|
```tsx
|
|
1348
1820
|
import {
|
|
@@ -1406,8 +1878,10 @@ export default function ProductCreatePage() {
|
|
|
1406
1878
|
|
|
1407
1879
|
- 조회 값은 `type: 'text'` 행으로 표시한다. **상태·분류 태그도 별도 영역이 아니라 표의 한 행**으로 넣는다 (`render` 에 `STag`).
|
|
1408
1880
|
- 행이 많아지면 **유형별로 섹션을 나누고, 각 섹션을 `SSectionHeaderCard` 로 감싼다.**
|
|
1409
|
-
|
|
1881
|
+
섹션 제목은 `title` prop 으로, 바디 여백은 `padding` prop 으로 준다.
|
|
1410
1882
|
- **수정·삭제 버튼은 하단에 둔다.** 내용이 짧아 우측 상단에 두는 변형도 있으나 기본은 하단이다.
|
|
1883
|
+
- **상세 페이지도 `contentHeight="fill"` 이 기본이다**(§2-2). 섹션이 많아 페이지가 실제로 스크롤되는 화면에서만 `auto` + `scrollEndSpacing` 을 켠다.
|
|
1884
|
+
- **섹션마다 독립 인라인 폼이 있는 형태**도 상세 페이지의 변형이다. 섹션 안에서 바로 수정·저장하게 하는 화면인데, 이때 버튼 강조는 **섹션 단위가 아니라 페이지 단위로 판단한다** — §3-5-1 의 "`secondary` 연속 배치 금지"는 섹션이 다르면 적용되지 않는다. 그 섹션 안에서 닫히는 저장·취소는 `SSectionHeaderCard` 의 `button`·`footerLeft` 로 넘긴다 (§3-7-8). 아래 예처럼 **페이지 전체를 확정하는 버튼은 카드 밖 하단 줄**에 둔다 — 둘을 섞지 않는다.
|
|
1411
1885
|
|
|
1412
1886
|
```tsx
|
|
1413
1887
|
import {
|
|
@@ -1439,24 +1913,18 @@ export default function ProductDetailPage() {
|
|
|
1439
1913
|
// 목록에서 들어온 상세 페이지 — onBack 으로 뒤로가기를 준다
|
|
1440
1914
|
<AppShell header={{ fix: true, title: '클래식 셔츠', onBack: goList }}>
|
|
1441
1915
|
<div className="flex flex-col gap-sd-12">
|
|
1442
|
-
<SSectionHeaderCard>
|
|
1443
|
-
<
|
|
1444
|
-
<SSectionHeaderCard.Body>
|
|
1445
|
-
<SKeyValueTable fields={basicFields} values={product} />
|
|
1446
|
-
</SSectionHeaderCard.Body>
|
|
1916
|
+
<SSectionHeaderCard title="기본 정보" marker thickness="accent">
|
|
1917
|
+
<SKeyValueTable fields={basicFields} values={product} />
|
|
1447
1918
|
</SSectionHeaderCard>
|
|
1448
1919
|
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
/>
|
|
1457
|
-
<SSectionHeaderCard.Body>
|
|
1458
|
-
<SKeyValueTable fields={priceFields} values={product} />
|
|
1459
|
-
</SSectionHeaderCard.Body>
|
|
1920
|
+
{/* 헤더 우측에 액션이 필요하면 slot 을 쓴다 */}
|
|
1921
|
+
<SSectionHeaderCard
|
|
1922
|
+
title="가격 정보"
|
|
1923
|
+
marker
|
|
1924
|
+
helpText={['부가세 포함 금액입니다.']}
|
|
1925
|
+
slot={<SButton size="sm" color="secondary" label="이력" onClick={openHistory} />}
|
|
1926
|
+
>
|
|
1927
|
+
<SKeyValueTable fields={priceFields} values={product} />
|
|
1460
1928
|
</SSectionHeaderCard>
|
|
1461
1929
|
|
|
1462
1930
|
{/* 액션은 하단 — 목록(되돌리기)은 왼쪽 끝, 실행 액션은 오른쪽 끝 */}
|
|
@@ -1490,6 +1958,29 @@ export default function ProductDetailPage() {
|
|
|
1490
1958
|
| Prop (Body) | 용도 |
|
|
1491
1959
|
| --- | --- |
|
|
1492
1960
|
| `padding` | 안쪽 여백 — `'default'`(기본) / `'wide'` / `'none'`. 판정은 §2-2 "섹션·패널 안쪽 여백". `p-sd-*` 를 직접 주지 않는다 |
|
|
1961
|
+
| `background` | 본문 바탕 — `'frame'`(기본) / `'neutral'`. 판정은 §2-2 "본문 바탕 눌러앉히기" |
|
|
1962
|
+
|
|
1963
|
+
| Prop (Footer) | 용도 |
|
|
1964
|
+
| --- | --- |
|
|
1965
|
+
| `button` | 하단 액션 영역 우측 주 액션 (§3-7-8) |
|
|
1966
|
+
| `footerLeft` | 하단 액션 영역 좌측 슬롯 — 보조 버튼. `SButton` 에 `size="md"` 를 명시한다 |
|
|
1967
|
+
|
|
1968
|
+
둘 중 하나라도 주면 하단 액션 영역이 렌더된다. 회색 바탕 + 상단 구분선이며 좌우 끝은 헤더에 맞는다 — 배경·여백을 직접 주지 않는다.
|
|
1969
|
+
|
|
1970
|
+
**한 페이지에 섹션 카드가 둘 이상이면 `marker` 를 켠다.** 점은 섹션을 서로 구분할 대상이 여럿일 때만 의미가 있어, 카드가 하나뿐인 페이지에서는 켜지 않는다. 한 페이지 안에서는 켜거나 끄거나 전부 같게 간다.
|
|
1971
|
+
|
|
1972
|
+
**섹션 본문이 자기 안에서 스크롤해야 하면 루트 `className` 으로 마지막 자식에 세로 축을 잇는다.**
|
|
1973
|
+
|
|
1974
|
+
```tsx
|
|
1975
|
+
<SSectionHeaderCard
|
|
1976
|
+
title="…"
|
|
1977
|
+
className="[&>div:last-child]:min-h-0 [&>div:last-child]:flex-1"
|
|
1978
|
+
>
|
|
1979
|
+
<STable className="min-h-0 flex-1" … />
|
|
1980
|
+
</SSectionHeaderCard>
|
|
1981
|
+
```
|
|
1982
|
+
|
|
1983
|
+
본문 래퍼는 `className` 을 받지 않으므로(여백은 `padding` prop 으로만 받는다) 루트에서 내려 준다. **하단 액션 영역이 있으면 본문이 더 이상 마지막 자식이 아니다** — 그때는 `[&>div:nth-last-child(2)]` 로 겨눈다. 흔한 구성은 아니다 — 대부분은 `STable` 이 자기 안에서 스크롤하므로 여기까지 갈 일이 없다.
|
|
1493
1984
|
|
|
1494
1985
|
---
|
|
1495
1986
|
|
|
@@ -1505,8 +1996,11 @@ export default function ProductDetailPage() {
|
|
|
1505
1996
|
- [ ] 텍스트 회색 위계를 순차 적용했는가 (기본 → `text-fg-secondary` → `text-fg-tertiary`, 단계 건너뛰기 ❌)
|
|
1506
1997
|
- [ ] `SPage`·`SPopup` 의 기본 패딩을 `p-sd-*` 로 덮어쓰지 않았는가, 블록·섹션 **간격**이 `gap-sd-12` 인가 (`gap-sd-16`/`gap-sd-24` ❌ — 24 는 안쪽 여백에만 열린다)
|
|
1507
1998
|
- [ ] 섹션·패널의 안쪽 **여백**이 §2-2 판정과 맞는가 (덩어리 두 종류 → 16 / 세 종류 이상 → 24, 서면 16)
|
|
1508
|
-
- [ ] `SSectionHeaderCard
|
|
1509
|
-
- [ ]
|
|
1999
|
+
- [ ] `SSectionHeaderCard` 의 여백을 `p-sd-*` 가 아니라 `padding` prop 으로 줬는가
|
|
2000
|
+
- [ ] 페이지에 `contentHeight="fill"` 을 넘겼는가 (§2-2 표준 — 페이지 스크롤을 쓰는 화면에서만 `auto` + `scrollEndSpacing`)
|
|
2001
|
+
- [ ] `fill` 을 쓴 블록에서 **자식까지 `min-h-0 flex-1` 이 끊기지 않았는가** (한 군데만 끊겨도 자식이 높이를 못 잡는데 실패가 조용하다)
|
|
2002
|
+
- [ ] 자체 스크롤하는 패널의 하단에 `pb-[var(--cmp-pageBody-padding-scrollEnd)]` 이 있는가
|
|
2003
|
+
- [ ] 한 페이지에 섹션 카드가 둘 이상이면 `marker` 를 켰는가, 하나뿐이면 껐는가 (§4-5)
|
|
1510
2004
|
- [ ] 같은 컴포넌트를 나열할 때 §2-2 그룹 간격을 썼는가 (체크박스 가로 `gap-sd-24` 등)
|
|
1511
2005
|
- [ ] 페이지가 §4의 표준 골격에서 시작했는가
|
|
1512
2006
|
- [ ] `header.fix` 가 프로젝트 전체와 같은 값인가 (다른 페이지와 다르게 섞어 쓰지 않았는가, §4-1)
|
|
@@ -1515,11 +2009,20 @@ export default function ProductDetailPage() {
|
|
|
1515
2009
|
- [ ] 필터·폼·상세 정보를 `SKeyValueTable` 로 만들었는가 (컨트롤을 `div` 로 나열하지 않았는가). 필터를 `SChipFilter` 로 했다면 §3-7-11 의 판정을 거쳤는가
|
|
1516
2010
|
- [ ] 섹션 구분에 `SSectionHeaderCard` 를 썼는가 (직접 만든 카드가 아니라)
|
|
1517
2011
|
- [ ] 목록의 주요 액션이 `STableBar` 의 `rightActions` 에 있는가 (페이지 제목 줄이 아니라)
|
|
2012
|
+
- [ ] 목록 페이지 표에 `dense` 로 밀도 토글을 띄웠는가, 그 값을 `onDenseChange` 결과로 되돌려 넣지는 않았는가 (§4-2 — 되돌려 넣으면 넓게 본 순간 토글이 사라진다)
|
|
1518
2013
|
- [ ] 상태 표시에 `STag size="sm"` 을 썼는가
|
|
1519
2014
|
- [ ] 테이블에서 양을 나타내는 컬럼(금액·수량·개수 등)이 전부 `align: 'right'` 인가
|
|
1520
2015
|
- [ ] 번호·코드·전화번호·일자 컬럼에 `align: 'center'` 를 **명시**했는가 (생략하면 좌측이 된다)
|
|
1521
|
-
- [ ]
|
|
2016
|
+
- [ ] 닫힌 값 집합(enum·마스터 목록에서 고르는 값) 컬럼에 `align: 'center'` 를 줬는가 — 태그로 그렸든 맨 텍스트로 그렸든 같다 (§3-4)
|
|
2017
|
+
- [ ] **모든 컬럼에 폭을 명시**했는가, px 로만 줬는가 (`%`·`clamp()` ❌), `autoWidth` 는 스페이서 열 하나뿐인가 (§3-4)
|
|
2018
|
+
- [ ] 컨트롤(`STag`·`SButton`·`SSelect`·`SInput` …)이 들어가는 컬럼이 `resizable` 이면 `minWidth` 도 줬는가 (§3-4 — 폭이 모자라면 요소가 잘려 못 쓴다)
|
|
2019
|
+
- [ ] 정렬 가능한 열에 `sortable` 을 줬는가 (`renderHeader` 로 직접 만들지 않았는가), 정렬 상태를 페이지가 들고 있는가 (§3-4)
|
|
2020
|
+
- [ ] `editable` · `navigable` 표식을 켠 열이 **셀에서도 실제로 그렇게 동작하는가** (입력 컨트롤 · 링크가 있는가), 표식을 붙인 열의 폭을 함께 넓혔는가 (§3-4)
|
|
1522
2021
|
- [ ] 금액·수량 등 양을 나타내는 숫자에 빠짐없이 `toLocaleString()` 을 썼는가 (번호·코드는 제외)
|
|
2022
|
+
- [ ] 날짜를 `YYYY-MM-DD` 로 표기했는가 (`toLocaleDateString()` ❌, §1-4)
|
|
2023
|
+
- [ ] 필드 폭을 등급(`'xs' | 'sm' | 'md' | 'lg' | 'xl'`)으로 줬는가 — px 직접 지정 ❌ (§4-3)
|
|
2024
|
+
- [ ] 조회 조건으로 쓰는 셀렉트·날짜·시간 피커에 `clearable` 을 줬는가, 그 상태가 `null` 을 담을 수 있는가 (§3-7-4 — 필수 입력 필드에는 켜지 않는다)
|
|
2025
|
+
- [ ] `SKeyValueTable` 의 짧은 행에 `tdColSpan` 을 주어 전체 열 수를 채웠는가 (§4-3 — 안 채우면 그 구간의 행 구분선이 끊긴다)
|
|
1523
2026
|
- [ ] 하단 버튼이 양끝 분리(`justify-between`)이고, 되돌리기가 왼쪽 끝 · 실행이 오른쪽 끝인가
|
|
1524
2027
|
- [ ] 페이지에 `color="primary"` 채움 버튼이 **1개뿐**인가 (`danger` 채움도 1개, `SDropdownButton` 포함)
|
|
1525
2028
|
- [ ] 버튼 `size` 가 위치 규칙과 맞는가 (행 내부 `xs` / 화면 액션 `sm` / 모달 푸터 `md`)
|
|
@@ -1528,7 +2031,7 @@ export default function ProductDetailPage() {
|
|
|
1528
2031
|
- [ ] `SGhostButton` 의 `intent` 가 조작 성격과 맞는가 (되돌릴 수 없는 삭제만 `danger`, 진입·추가는 `action`, 나머지는 `default`)
|
|
1529
2032
|
- [ ] 창을 띄울 때 §3-3-1 판별 순서를 따랐는가 (그 자체가 화면 → `SPopup` / 실행 여부만 확정 → `SModal.confirm` / 모달 안에서 작성 → `SActionModal`)
|
|
1530
2033
|
- [ ] 작업용 모달을 `SActionModal` + `SModal.create` 로 만들었는가 (직접 오버레이 ❌)
|
|
1531
|
-
- [ ] 모달·드로어의 하단 버튼을 본문이 아니라 `button` · `footerLeft` prop 으로 넘겼는가 (§3-3-4)
|
|
2034
|
+
- [ ] 모달·드로어의 하단 버튼을 본문이 아니라 `button` · `footerLeft` prop 으로 넘겼는가 (§3-3-4), 카드 안에서 닫히는 액션도 같은 prop 으로 넘겼는가 (§3-7-8)
|
|
1532
2035
|
- [ ] 앱 부트스트랩의 Provider 안쪽에 `<SModalOutlet />` 이 한 번 렌더되어 있는가 (§4-1 — 없으면 모달 안에서 앱 훅이 죽는다), 그 대신으로 모달 컴포넌트를 Provider 로 다시 감싸지 않았는가
|
|
1533
2036
|
- [ ] 고른 컴포넌트를 §2-0 의 제 층에 놓았는가 (요소를 `SPage` 에 직접 놓지 않았는가, 블록을 `div` 로 감싸지 않았는가)
|
|
1534
2037
|
- [ ] §2-0 포함 규칙을 지켰는가 (카드 안 카드 ❌, 표 셀 안 블록 ❌)
|
|
@@ -1556,6 +2059,8 @@ export default function ProductDetailPage() {
|
|
|
1556
2059
|
| `sellmate/component-group-gap` | warn | §2-2 컴포넌트 그룹 간격 (체크박스 가로 24 / 세로 8 등) |
|
|
1557
2060
|
| `sellmate/table-numeric-align` | warn | §3-4 숫자 컬럼의 `align: 'right'` 누락 (`--fix` 지원) |
|
|
1558
2061
|
| `sellmate/require-locale-number` | warn | §1-4 금액·수량 등 수량 컬럼의 `toLocaleString()` 누락 |
|
|
2062
|
+
| `sellmate/field-width-grade` | warn | §4-3 필드 폭이 `maxLength` 상한과 맞는 등급인가, px 를 직접 적지 않았는가 (px → 등급 `--fix` 지원) |
|
|
2063
|
+
| `sellmate/table-column-width` | warn | §3-4 컬럼 폭 미지정(기본 120px)·px 아닌 값(`%`·`clamp()`)·`autoWidth` 오용 |
|
|
1559
2064
|
| `sellmate/no-arbitrary-class` | off | §1-2 토큰 있는 속성의 임의 값 (`text-[14px]`, `bg-[#eee]`) — 팀이 켤 때만 |
|
|
1560
2065
|
|
|
1561
2066
|
`configs.strict` 를 쓰는 프로젝트는 전부 error 이고 간격 `sd-` 접두까지 강제된다.
|
|
@@ -1618,9 +2123,11 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
1618
2123
|
| typo-body-xs-default | 11px | 400 | 18px |
|
|
1619
2124
|
| typo-body-sm-default | 12px | 400 | 20px |
|
|
1620
2125
|
| typo-body-sm-medium | 12px | 500 | 20px |
|
|
2126
|
+
| typo-body-sm-semibold | 12px | 600 | 20px |
|
|
1621
2127
|
| typo-body-sm-bold | 12px | 700 | 20px |
|
|
1622
2128
|
| typo-body-md-default | 14px | 400 | 24px |
|
|
1623
2129
|
| typo-body-md-medium | 14px | 500 | 24px |
|
|
2130
|
+
| typo-body-md-semibold | 14px | 600 | 24px |
|
|
1624
2131
|
| typo-body-md-bold | 14px | 700 | 24px |
|
|
1625
2132
|
| typo-body-lg-default | 16px | 400 | 26px |
|
|
1626
2133
|
| typo-body-lg-medium | 16px | 500 | 26px |
|
|
@@ -1740,8 +2247,9 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
1740
2247
|
--sys-color-lightblue-subtle
|
|
1741
2248
|
--sys-color-link-accent
|
|
1742
2249
|
--sys-color-navigation-gnb-bg-dark
|
|
2250
|
+
--sys-color-navigation-gnb-bg-system
|
|
1743
2251
|
|
|
1744
|
-
## 3. 컴포넌트 카탈로그 (Props / Events / Methods)
|
|
2252
|
+
## 3. 컴포넌트 카탈로그 (Props / Events / Methods / Types)
|
|
1745
2253
|
|
|
1746
2254
|
# SActionModal
|
|
1747
2255
|
|
|
@@ -1794,6 +2302,30 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
1794
2302
|
|------|------|---------|-------------|
|
|
1795
2303
|
| `color?` | `SBadgeColor` | `'blue'` | 뱃지 색상 |
|
|
1796
2304
|
|
|
2305
|
+
## Types
|
|
2306
|
+
|
|
2307
|
+
### SBadgeColor
|
|
2308
|
+
|
|
2309
|
+
```ts
|
|
2310
|
+
export type SBadgeColor = (typeof BADGE_COLORS)[number];
|
|
2311
|
+
```
|
|
2312
|
+
|
|
2313
|
+
### BADGE_COLORS
|
|
2314
|
+
|
|
2315
|
+
```ts
|
|
2316
|
+
export const BADGE_COLORS = [
|
|
2317
|
+
'red',
|
|
2318
|
+
'orange',
|
|
2319
|
+
'yellow',
|
|
2320
|
+
'green',
|
|
2321
|
+
'lightblue',
|
|
2322
|
+
'blue',
|
|
2323
|
+
'darkblue',
|
|
2324
|
+
'indigo',
|
|
2325
|
+
'grey',
|
|
2326
|
+
] as const;
|
|
2327
|
+
```
|
|
2328
|
+
|
|
1797
2329
|
## Dependencies
|
|
1798
2330
|
|
|
1799
2331
|
### Used by
|
|
@@ -1841,7 +2373,7 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
1841
2373
|
| `hint?` | `string` | — | |
|
|
1842
2374
|
| `error?` | `boolean` | — | |
|
|
1843
2375
|
| `errorMessage?` | `string` | — | |
|
|
1844
|
-
| `width?` | `
|
|
2376
|
+
| `width?` | `SFieldWidth` | — | 컨트롤 너비 — 폭 등급(`'xs' | 'sm' | 'md' | 'lg' | 'xl'`) · 숫자=px · CSS 길이. 등급은 `--cmp-field-width-*` 토큰으로 풀리며 `maxLength`(= 스키마 상한)로 고른다. 상한이 `xl` 을 넘거나 상한이 없으면 `"100%"`. (`sellmate/field-width-grade` 가 검사한다) |
|
|
1845
2377
|
| `className?` | `string` | — | |
|
|
1846
2378
|
| `style?` | `CSSProperties` | — | |
|
|
1847
2379
|
|
|
@@ -1853,6 +2385,14 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
1853
2385
|
| `onFocus` | `() => void` | |
|
|
1854
2386
|
| `onBlur` | `() => void` | |
|
|
1855
2387
|
|
|
2388
|
+
## Types
|
|
2389
|
+
|
|
2390
|
+
### SBarcodeInputSize
|
|
2391
|
+
|
|
2392
|
+
```ts
|
|
2393
|
+
export type SBarcodeInputSize = SFieldSize;
|
|
2394
|
+
```
|
|
2395
|
+
|
|
1856
2396
|
## Dependencies
|
|
1857
2397
|
|
|
1858
2398
|
### Depends on
|
|
@@ -1881,12 +2421,47 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
1881
2421
|
| `rightIcon?` | `SIconName` | — | 레이블 오른쪽 아이콘 |
|
|
1882
2422
|
| `label?` | `string` | — | 버튼 텍스트 (문자열만 — 아이콘은 icon/rightIcon 사용) |
|
|
1883
2423
|
|
|
2424
|
+
## Types
|
|
2425
|
+
|
|
2426
|
+
### SButtonColor
|
|
2427
|
+
|
|
2428
|
+
```ts
|
|
2429
|
+
export type SButtonColor = (typeof BUTTON_COLORS)[number];
|
|
2430
|
+
```
|
|
2431
|
+
|
|
2432
|
+
### SButtonSize
|
|
2433
|
+
|
|
2434
|
+
```ts
|
|
2435
|
+
export type SButtonSize = (typeof BUTTON_SIZES)[number];
|
|
2436
|
+
```
|
|
2437
|
+
|
|
2438
|
+
### BUTTON_COLORS
|
|
2439
|
+
|
|
2440
|
+
```ts
|
|
2441
|
+
/**
|
|
2442
|
+
* SButton 색상/사이즈 설정 — sd-button(component.button 토큰) 충실 포팅.
|
|
2443
|
+
* Stencil `name`(예: primary_sm)의 preset을 color + outline(boolean) + size 로 분리.
|
|
2444
|
+
* - primary / danger : solid·outline 모두 지원
|
|
2445
|
+
* - secondary : solid 전용 (outline 스타일 없음 → outline 무시)
|
|
2446
|
+
* - neutral : 흰 배경 고정, outline 은 회색 테두리만 추가(solid = 테두리 없는 흰 버튼)
|
|
2447
|
+
* 색상은 theme.css의 `--cmp-button-*` CSS 변수를 참조한다.
|
|
2448
|
+
*/
|
|
2449
|
+
export const BUTTON_COLORS = ['primary', 'secondary', 'neutral', 'danger'] as const;
|
|
2450
|
+
```
|
|
2451
|
+
|
|
2452
|
+
### BUTTON_SIZES
|
|
2453
|
+
|
|
2454
|
+
```ts
|
|
2455
|
+
export const BUTTON_SIZES = ['xs', 'sm', 'md', 'lg'] as const;
|
|
2456
|
+
```
|
|
2457
|
+
|
|
1884
2458
|
## Dependencies
|
|
1885
2459
|
|
|
1886
2460
|
### Used by
|
|
1887
2461
|
|
|
1888
2462
|
- [SConfirmModal](../SConfirmModal)
|
|
1889
2463
|
- [SDropdownButton](../SDropdownButton)
|
|
2464
|
+
- [SEditor](../SEditor)
|
|
1890
2465
|
- [SFooter](../SFooter)
|
|
1891
2466
|
- [SKeyValueTable](../SKeyValueTable)
|
|
1892
2467
|
- [SLoadingModal](../SLoadingModal)
|
|
@@ -1925,6 +2500,19 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
1925
2500
|
| `onValueChange` | `(date: string) => void` | 선택 변경 (sdUpdate) |
|
|
1926
2501
|
| `onViewChange` | `(v: { year: number; month: number }) => void` | 보이는 연·월 변경 (sdViewChange) |
|
|
1927
2502
|
|
|
2503
|
+
## Types
|
|
2504
|
+
|
|
2505
|
+
### SCalendarEventGroup
|
|
2506
|
+
|
|
2507
|
+
```ts
|
|
2508
|
+
export interface SCalendarEventGroup {
|
|
2509
|
+
/** 도트 색상. 팔레트 키(`grey_65`, `red_95` …) 또는 임의 CSS 색상 */
|
|
2510
|
+
color: SColor;
|
|
2511
|
+
label: string;
|
|
2512
|
+
dates: string[];
|
|
2513
|
+
}
|
|
2514
|
+
```
|
|
2515
|
+
|
|
1928
2516
|
## Dependencies
|
|
1929
2517
|
|
|
1930
2518
|
### Used by
|
|
@@ -1956,6 +2544,21 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
1956
2544
|
| `className?` | `string` | — | |
|
|
1957
2545
|
| `style?` | `CSSProperties` | — | |
|
|
1958
2546
|
|
|
2547
|
+
## Types
|
|
2548
|
+
|
|
2549
|
+
### SCalloutType
|
|
2550
|
+
|
|
2551
|
+
```ts
|
|
2552
|
+
export type SCalloutType = 'default' | 'danger';
|
|
2553
|
+
```
|
|
2554
|
+
|
|
2555
|
+
### SCalloutMessage
|
|
2556
|
+
|
|
2557
|
+
```ts
|
|
2558
|
+
/** 중첩 메시지: 문자열 또는 (한 단계 더 들어간) 문자열 배열 */
|
|
2559
|
+
export type SCalloutMessage = string | SCalloutMessage[];
|
|
2560
|
+
```
|
|
2561
|
+
|
|
1959
2562
|
## Dependencies
|
|
1960
2563
|
|
|
1961
2564
|
### Depends on
|
|
@@ -1977,6 +2580,17 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
1977
2580
|
| Prop | Type | Default | Description |
|
|
1978
2581
|
|------|------|---------|-------------|
|
|
1979
2582
|
| `bordered?` | `boolean` | `false` | 테두리 표시 여부 |
|
|
2583
|
+
| `footerLeft?` | `ReactNode` | — | 하단 액션 영역 좌측 슬롯. 보조 버튼(취소·목록 등)이 오는 자리다. `footerLeft` 또는 `button` 중 하나라도 있으면 하단 액션 영역이 렌더된다. |
|
|
2584
|
+
| `button?` | `SFooterButton` | — | 하단 액션 영역 우측 주 액션 버튼 |
|
|
2585
|
+
| `footerClassName?` | `string` | — | 하단 액션 영역 클래스 |
|
|
2586
|
+
|
|
2587
|
+
## Dependencies
|
|
2588
|
+
|
|
2589
|
+
### Depends on
|
|
2590
|
+
|
|
2591
|
+
- [SFooter](../SFooter)
|
|
2592
|
+
|
|
2593
|
+
### Graph
|
|
1980
2594
|
|
|
1981
2595
|
---
|
|
1982
2596
|
|
|
@@ -2004,6 +2618,14 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2004
2618
|
|-------|------|-------------|
|
|
2005
2619
|
| `onValueChange` | `(value: boolean \| unknown[]) => void` | 값 변경 (sdUpdate) |
|
|
2006
2620
|
|
|
2621
|
+
## Types
|
|
2622
|
+
|
|
2623
|
+
### SCheckboxValue
|
|
2624
|
+
|
|
2625
|
+
```ts
|
|
2626
|
+
export type SCheckboxValue = boolean | unknown[] | null;
|
|
2627
|
+
```
|
|
2628
|
+
|
|
2007
2629
|
## Dependencies
|
|
2008
2630
|
|
|
2009
2631
|
### Used by
|
|
@@ -2082,12 +2704,9 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2082
2704
|
|
|
2083
2705
|
| Prop | Type | Default | Description |
|
|
2084
2706
|
|------|------|---------|-------------|
|
|
2085
|
-
| `fields?` | `
|
|
2707
|
+
| `fields?` | `SChipFilterGroup[]` | — | 필터 정의 목록. 묶을 것이 없어도 한 그룹으로 감싸 넘긴다 — `[{ fields: [...] }]`. 그룹은 rule로 함께 검증하거나 divider로 갈라 놓을 때 나눈다. |
|
|
2086
2708
|
| `value?` | `SChipFilterValueMap` | — | 필터 값 맵 |
|
|
2087
|
-
| `
|
|
2088
|
-
| `label?` | `string` | `'검색 필터'` | 좌측 태그 텍스트 |
|
|
2089
|
-
| `showLabel?` | `boolean` | `false` | 좌측 태그(label)·구분선 표시 여부 |
|
|
2090
|
-
| `showAddButton?` | `boolean` | `true` | 필터 추가 버튼 표시 여부 |
|
|
2709
|
+
| `label?` | `string` | — | 좌측 태그 텍스트. 지정하면 태그와 그 뒤 구분선이 함께 노출되고, 지정하지 않으면(undefined) 둘 다 생략된다 |
|
|
2091
2710
|
| `showReset?` | `boolean` | `true` | 검색 초기화 링크 표시 여부 |
|
|
2092
2711
|
| `disabled?` | `boolean` | `false` | 바 비활성 상태 |
|
|
2093
2712
|
| `className?` | `string` | — | |
|
|
@@ -2099,31 +2718,347 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2099
2718
|
|-------|------|-------------|
|
|
2100
2719
|
| `onValueChange` | `(value: SChipFilterValueMap) => void` | 전체 값 변경 — 편집 중인 값이 바뀔 때마다(선택할 때마다) 호출된다. 실제 검색 실행은 onSearch를 쓴다 |
|
|
2101
2720
|
| `onFilterChange` | `(detail: SChipFilterChangeDetail) => void` | 개별 필터 값 변경 |
|
|
2102
|
-
| `onSearch` | `(value: SChipFilterValueMap) => void` | 실제 검색을 실행할 시점 — 편집 팝오버의 "검색" 버튼을 누르거나 팝오버가 닫힐 때(바깥 클릭·Esc·다른 칩으로 전환 포함) 그 시점의 전체 값 맵과 함께 호출된다. 팝오버가 없는 필드(인라인 date 프리셋·custom, clearable ×, 검색 초기화)는 값이 바뀌는 즉시 호출된다. dirty 체크가 기본 적용되어 있어 — 마지막으로 실제 검색이 실행된 값 맵과 비교해 하나라도 달라진 게 없으면(예: 팝오버를 열었다 아무것도 안 바꾸고 닫는 경우) 호출되지 않는다. fields를 그룹으로 넘겼다면 rule을 만족하지 못한 그룹이 있는 동안엔 onSearch가 호출되지 않는다 — 해당 그룹의 경고 툴팁은 이 시점과 무관하게 값이 비어 있는 동안 항상 실시간으로 떠 있다(별도로 validate()를 호출할 필요 없음) |
|
|
2103
|
-
| `
|
|
2104
|
-
| `
|
|
2105
|
-
| `onAddFilter` | `(key: string) => void` | "필터 추가" 목록에서 항목을 골랐을 때 — activeKeys를 직접 제어 중이면 이 콜백에서 (또는 onActiveKeysChange에서) key를 activeKeys에 추가해줘야 칩이 실제로 나타난다. activeKeys를 넘기지 않았다면(비제어) 별도 처리 없이도 컴포넌트가 알아서 칩을 노출한다 |
|
|
2721
|
+
| `onSearch` | `(value: SChipFilterValueMap) => void` | 실제 검색을 실행할 시점 — 편집 팝오버의 "검색" 버튼을 누르거나 팝오버가 닫힐 때(바깥 클릭·Esc·다른 칩으로 전환 포함) 그 시점의 전체 값 맵과 함께 호출된다. 팝오버가 없는 필드(인라인 date 프리셋·custom, clearable ×, 검색 초기화)는 값이 바뀌는 즉시 호출된다. keyword 필터에서 Enter 로 키워드를 추가할 때도 그 즉시 호출된다 — 팝오버는 열린 채라 키워드를 이어서 더 넣을 수 있고, 넣을 때마다 조회가 갱신된다. dirty 체크가 기본 적용되어 있어 — 마지막으로 실제 검색이 실행된 값 맵과 비교해 하나라도 달라진 게 없으면(예: 팝오버를 열었다 아무것도 안 바꾸고 닫는 경우) 호출되지 않는다. fields를 그룹으로 넘겼다면 rule을 만족하지 못한 그룹이 있는 동안엔 onSearch가 호출되지 않는다 — 해당 그룹의 경고 툴팁은 이 시점과 무관하게 값이 비어 있는 동안 항상 실시간으로 떠 있다(별도로 validate()를 호출할 필요 없음). required 필드가 비어 있는 동안에도 마찬가지로 호출되지 않는다 — 정상 설정(내장 기본값 또는 defaultValue)이라면 원래 비워지지 않지만, defaultValue 없는 select-multi·keyword·custom에 required만 준 오설정에서는 "검색 초기화"·clearable이 값을 null로 비울 수 있어 이때의 안전장치다 |
|
|
2722
|
+
| `onReset` | `() => void` | "검색 초기화" 클릭 — 모든 필드가 기본값(또는 null)으로 리셋되고 "필터 추가"로 꺼낸 칩이 바에서 빠진 뒤 호출된다 |
|
|
2723
|
+
| `onAddFilter` | `(key: string) => void` | "필터 추가" 목록에서 항목을 골랐을 때 호출된다(알림). 칩 노출은 컴포넌트가 알아서 하므로 이 콜백에서 따로 해줄 일은 없다 |
|
|
2106
2724
|
|
|
2107
2725
|
#### Methods (ref)
|
|
2108
2726
|
|
|
2109
2727
|
| Method | Type | Description |
|
|
2110
2728
|
|--------|------|-------------|
|
|
2111
2729
|
| `open` | `(key: string) => void` | 특정 필터 편집 팝오버를 엽니다. |
|
|
2112
|
-
| `reset` | `() => void` | 모든 필터
|
|
2113
|
-
| `validate` | `() => boolean` |
|
|
2730
|
+
| `reset` | `() => void` | 바를 처음 상태로 되돌립니다 — 모든 필터 값이 기본값(또는 null)으로 돌아가고, "필터 추가"로 꺼내 놓은 칩도 함께 빠져 fixed·required 필드만 남습니다. 열려 있던 편집 팝오버는 닫힙니다 |
|
|
2731
|
+
| `validate` | `() => boolean` | 현재 값 기준으로 각 그룹의 rule을 만족하는지, 그리고 required 필드가 모두 비어 있지 않은지 반환합니다. rule을 준 그룹이 없고 required 필드도 모두 값이 있으면 항상 true. 경고 툴팁은 이 호출과 무관하게 rule 위반 상태인 동안 항상 실시간으로 떠 있으므로, 이 메서드는 그 상태를 그대로 읽어오는 용도다(예: 검색 버튼을 눌러도 되는지 사전 확인) |
|
|
2114
2732
|
|
|
2115
|
-
##
|
|
2733
|
+
## Types
|
|
2116
2734
|
|
|
2117
|
-
###
|
|
2735
|
+
### SChipFilterGroup
|
|
2118
2736
|
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2737
|
+
```ts
|
|
2738
|
+
/** fields를 이루는 단위. 묶을 것이 없어도 한 그룹으로 감싸 넘긴다 — `[{ fields: [...] }]` */
|
|
2739
|
+
export interface SChipFilterGroup {
|
|
2740
|
+
fields: SChipFilterField[];
|
|
2741
|
+
/** 지정하면 이 규칙으로 그룹을 검증한다. values가 바뀔 때마다 즉시 재평가되는 실시간 검증이라 —
|
|
2742
|
+
* 그룹이 rule을 만족하지 못하면 검색 시도 여부와 무관하게 그 즉시 그룹 중앙에 경고 툴팁이 뜬다.
|
|
2743
|
+
* 지정 안 하면 검증하지 않는다. */
|
|
2744
|
+
rule?: SChipFilterGroupRule;
|
|
2745
|
+
/** rule을 만족하지 않을 때 그룹 중앙에 띄울 툴팁 메시지. 지정 안 하면 rule 종류에 따른 기본 문구를 쓴다 */
|
|
2746
|
+
tooltipMessage?: string;
|
|
2747
|
+
/** 이 그룹 앞에 구분선을 넣을지. 검증(rule)과 구분선은 별개라 — 묶어서 검증만 하고 싶으면
|
|
2748
|
+
* 주지 않는다. 첫 그룹에는 앞에 가를 것이 없으므로 무시된다(label을 지정했다면 그 구분선이
|
|
2749
|
+
* 이미 있다) */
|
|
2750
|
+
divider?: boolean;
|
|
2751
|
+
}
|
|
2752
|
+
```
|
|
2753
|
+
|
|
2754
|
+
### SChipFilterValueMap
|
|
2755
|
+
|
|
2756
|
+
```ts
|
|
2757
|
+
export type SChipFilterValueMap = Record<string, SChipFilterValue>;
|
|
2758
|
+
```
|
|
2759
|
+
|
|
2760
|
+
### SChipFilterChangeDetail
|
|
2761
|
+
|
|
2762
|
+
```ts
|
|
2763
|
+
export interface SChipFilterChangeDetail {
|
|
2764
|
+
key: string;
|
|
2765
|
+
value: SChipFilterValue;
|
|
2766
|
+
values: SChipFilterValueMap;
|
|
2767
|
+
}
|
|
2768
|
+
```
|
|
2769
|
+
|
|
2770
|
+
### SChipFilterField
|
|
2771
|
+
|
|
2772
|
+
```ts
|
|
2773
|
+
/** 필터 하나의 정의. type에 따라 쓸 수 있는 속성이 달라진다 —
|
|
2774
|
+
* options는 select·select-multi·keyword, presets·maxRange·radioButton은
|
|
2775
|
+
* datepicker-range·datepicker-statistics, render는 custom 에만 있다 */
|
|
2776
|
+
export type SChipFilterField =
|
|
2777
|
+
| SChipFilterSelectField
|
|
2778
|
+
| SChipFilterSelectMultiField
|
|
2779
|
+
| SChipFilterDatePickerDayField
|
|
2780
|
+
| SChipFilterDatePickerRangeField
|
|
2781
|
+
| SChipFilterDatePickerStatisticsField
|
|
2782
|
+
| SChipFilterKeywordField
|
|
2783
|
+
| SChipFilterCustomField;
|
|
2784
|
+
```
|
|
2785
|
+
|
|
2786
|
+
### SChipFilterGroupRule
|
|
2787
|
+
|
|
2788
|
+
```ts
|
|
2789
|
+
/** 필터 그룹 검증 규칙 */
|
|
2790
|
+
export type SChipFilterGroupRule =
|
|
2791
|
+
| { type: 'requireKey'; key: string }
|
|
2792
|
+
| { type: 'requireAll' }
|
|
2793
|
+
| { type: 'requireAny'; dataGroupName?: string };
|
|
2794
|
+
```
|
|
2795
|
+
|
|
2796
|
+
### SChipFilterValue
|
|
2797
|
+
|
|
2798
|
+
```ts
|
|
2799
|
+
export type SChipFilterValue =
|
|
2800
|
+
| SChipFilterOptionValue
|
|
2801
|
+
| SChipFilterOptionValue[]
|
|
2802
|
+
| SDateRangeValue
|
|
2803
|
+
| SChipFilterKeywordValue
|
|
2804
|
+
| SChipFilterPeriodValue
|
|
2805
|
+
| SChipFilterCustomValue
|
|
2806
|
+
| null
|
|
2807
|
+
| undefined;
|
|
2808
|
+
```
|
|
2809
|
+
|
|
2810
|
+
### SChipFilterSelectField
|
|
2811
|
+
|
|
2812
|
+
```ts
|
|
2813
|
+
/** 후보 하나를 고른다 */
|
|
2814
|
+
export interface SChipFilterSelectField extends SChipFilterOptionsField {
|
|
2815
|
+
type: 'select';
|
|
2816
|
+
}
|
|
2817
|
+
```
|
|
2818
|
+
|
|
2819
|
+
### SChipFilterSelectMultiField
|
|
2820
|
+
|
|
2821
|
+
```ts
|
|
2822
|
+
/** 후보 여럿을 고른다 */
|
|
2823
|
+
export interface SChipFilterSelectMultiField extends SChipFilterOptionsField {
|
|
2824
|
+
type: 'select-multi';
|
|
2825
|
+
}
|
|
2826
|
+
```
|
|
2827
|
+
|
|
2828
|
+
### SChipFilterDatePickerDayField
|
|
2829
|
+
|
|
2830
|
+
```ts
|
|
2831
|
+
/** 날짜 하나를 고른다 — 캘린더 트리거 하나만 놓인다. 프리셋으로 고르게 하거나 시작~종료를
|
|
2832
|
+
* 받아야 하면 datepicker-range 다 */
|
|
2833
|
+
export interface SChipFilterDatePickerDayField extends SChipFilterFieldBase {
|
|
2834
|
+
type: 'datepicker-day';
|
|
2835
|
+
/** 캘린더 트리거의 placeholder */
|
|
2836
|
+
placeholder?: string;
|
|
2837
|
+
/** 선택 가능 범위 */
|
|
2838
|
+
selectable?: [string, string];
|
|
2839
|
+
}
|
|
2840
|
+
```
|
|
2841
|
+
|
|
2842
|
+
### SChipFilterDatePickerRangeField
|
|
2843
|
+
|
|
2844
|
+
```ts
|
|
2845
|
+
/** 기간(시작~종료)을 고른다. presets를 주면 프리셋 라디오 목록으로 고르고,
|
|
2846
|
+
* 주지 않으면 기간 피커 하나만 놓인다 */
|
|
2847
|
+
export interface SChipFilterDatePickerRangeField extends SChipFilterPresetsField {
|
|
2848
|
+
type: 'datepicker-range';
|
|
2849
|
+
/** presets를 필터 바에 세그먼트 라디오로 펼쳐 놓는다(팝오버 없음).
|
|
2850
|
+
* 기본 false — 칩 클릭 시 팝오버 안에 세로 라디오 목록(+사용자 지정 선택 시 기간 피커) */
|
|
2851
|
+
radioButton?: boolean;
|
|
2852
|
+
}
|
|
2853
|
+
```
|
|
2854
|
+
|
|
2855
|
+
### SChipFilterDatePickerStatisticsField
|
|
2856
|
+
|
|
2857
|
+
```ts
|
|
2858
|
+
/** 집계 단위(일·월·분기·반기·연)와 그 단위의 값을 함께 고른다 */
|
|
2859
|
+
export interface SChipFilterDatePickerStatisticsField extends SChipFilterPresetsField {
|
|
2860
|
+
type: 'datepicker-statistics';
|
|
2861
|
+
}
|
|
2862
|
+
```
|
|
2863
|
+
|
|
2864
|
+
### SChipFilterKeywordField
|
|
2865
|
+
|
|
2866
|
+
```ts
|
|
2867
|
+
/** 키워드를 입력해 누적한다. options는 입력 중 후보로만 뜬다 */
|
|
2868
|
+
export interface SChipFilterKeywordField extends SChipFilterOptionsField {
|
|
2869
|
+
type: 'keyword';
|
|
2870
|
+
/** 입력 placeholder */
|
|
2871
|
+
placeholder?: string;
|
|
2872
|
+
/** 입력 방식. 기본 'tag' — Enter 로 하나씩 추가한다.
|
|
2873
|
+
* 'csv' 는 쉼표도 구분자로 인정해, 쉼표를 치거나 쉼표가 섞인 텍스트를 붙여넣으면 그 자리에서
|
|
2874
|
+
* 여러 개로 쪼개져 목록에 쌓인다(엑셀에서 복사한 코드 목록을 한 번에 넣는 용도).
|
|
2875
|
+
* 쌓이는 목록도 값 형태도 'tag' 와 같다 — 구분자만 늘어난다. */
|
|
2876
|
+
input?: SChipFilterKeywordInput;
|
|
2877
|
+
/** 검색조건(포함/일치) 토글 표시 여부 */
|
|
2878
|
+
matchModes?: boolean;
|
|
2879
|
+
/** matchModes 활성 시 "미포함"까지 포함해 3개(포함/일치/미포함)로 노출할지.
|
|
2880
|
+
* 기본 false — 2개(포함/일치)만 */
|
|
2881
|
+
excludeMode?: boolean;
|
|
2882
|
+
}
|
|
2883
|
+
```
|
|
2884
|
+
|
|
2885
|
+
### SChipFilterCustomField
|
|
2886
|
+
|
|
2887
|
+
```ts
|
|
2888
|
+
/** 칩+팝오버를 거치지 않고 바에 놓을 노드를 앱이 직접 그린다 */
|
|
2889
|
+
export interface SChipFilterCustomField extends SChipFilterFieldBase {
|
|
2890
|
+
type: 'custom';
|
|
2891
|
+
/** 필터 바의 이 필드 자리에 놓일 노드를 직접 그린다. 반환한 노드가 그대로 바에 노출된다 —
|
|
2892
|
+
* SSelect를 그대로 놓거나 SInput을 바로 노출하는 식으로 렌더 방식을 자유롭게 구성한다. */
|
|
2893
|
+
render?: (ctx: {
|
|
2894
|
+
value: SChipFilterValue;
|
|
2895
|
+
disabled?: boolean;
|
|
2896
|
+
/** 속한 그룹이 rule을 위반하는 동안 true — 직접 그린 노드에도 경고 표시를 맞추라는 신호다 */
|
|
2897
|
+
warning?: boolean;
|
|
2898
|
+
onValueChange: (value: SChipFilterValue) => void;
|
|
2899
|
+
}) => ReactNode;
|
|
2900
|
+
}
|
|
2901
|
+
```
|
|
2902
|
+
|
|
2903
|
+
### SChipFilterOptionValue
|
|
2904
|
+
|
|
2905
|
+
```ts
|
|
2906
|
+
export type SChipFilterOptionValue = string | number;
|
|
2907
|
+
```
|
|
2908
|
+
|
|
2909
|
+
### SChipFilterKeywordValue
|
|
2910
|
+
|
|
2911
|
+
```ts
|
|
2912
|
+
/** keyword 필드에서 matchModes 활성 시 사용하는 값 형태 — 입력해 추가한 키워드 목록 */
|
|
2913
|
+
export interface SChipFilterKeywordValue {
|
|
2914
|
+
keywords: string[];
|
|
2915
|
+
mode: SChipFilterMatchMode;
|
|
2916
|
+
}
|
|
2917
|
+
```
|
|
2918
|
+
|
|
2919
|
+
### SChipFilterPeriodValue
|
|
2920
|
+
|
|
2921
|
+
```ts
|
|
2922
|
+
/** datepicker-statistics 필드 값 — 선택 단위(unit)와 그 단위의 입력값(value)을 함께 보관한다 */
|
|
2923
|
+
export interface SChipFilterPeriodValue {
|
|
2924
|
+
unit: SChipFilterPeriodUnit;
|
|
2925
|
+
value?: string | number | SDateRangeValue | null;
|
|
2926
|
+
}
|
|
2927
|
+
```
|
|
2928
|
+
|
|
2929
|
+
### SChipFilterCustomValue
|
|
2930
|
+
|
|
2931
|
+
```ts
|
|
2932
|
+
/** custom 필드가 자유롭게 담는 값. 형태를 강제하지 않는다 — render에서 직접 정의한 그대로 읽고 쓴다 */
|
|
2933
|
+
export type SChipFilterCustomValue = Record<string, unknown>;
|
|
2934
|
+
```
|
|
2935
|
+
|
|
2936
|
+
### SChipFilterOptionsField
|
|
2937
|
+
|
|
2938
|
+
```ts
|
|
2939
|
+
/** 후보 목록에서 고르는 필터 — select·select-multi·keyword */
|
|
2940
|
+
export interface SChipFilterOptionsField extends SChipFilterFieldBase {
|
|
2941
|
+
/** 고를 수 있는 후보 목록 */
|
|
2942
|
+
options?: SChipFilterOption[];
|
|
2943
|
+
}
|
|
2944
|
+
```
|
|
2945
|
+
|
|
2946
|
+
### SChipFilterFieldBase
|
|
2947
|
+
|
|
2948
|
+
```ts
|
|
2949
|
+
/** 타입과 무관하게 모든 필터가 갖는 속성 */
|
|
2950
|
+
export interface SChipFilterFieldBase {
|
|
2951
|
+
/** 필터 식별자 */
|
|
2952
|
+
key: string;
|
|
2953
|
+
/** 칩에 표시할 레이블 */
|
|
2954
|
+
label: string;
|
|
2955
|
+
/** 필수 필터 표시. true면 값이 비어 있을 때 기본값(defaultValue 또는 타입별 내장 기본값)이
|
|
2956
|
+
* 자동으로 채워지고, clearable은 현재 값이 기본값과 같을 땐 숨겨지며 클릭 시 기본값으로 되돌아간다.
|
|
2957
|
+
* 값이 채워졌다가 다시 비면(예: value를 제어하는 부모가 onReset에서 자기 상태를 비우는 경우)
|
|
2958
|
+
* 기본값이 한 번 더 채워진다 — required 필드가 빈 채로 남으면 onSearch가 막히기 때문이다.
|
|
2959
|
+
* 단 그 값을 부모가 value로 받아 주지 않으면 반복해서 채우지는 않는다 */
|
|
2960
|
+
required?: boolean;
|
|
2961
|
+
/** 고정 필터. true면 항상 노출되고 "필터 추가" 목록에는 나타나지 않으며 칩을 제거할 수 없다.
|
|
2962
|
+
* required도 같은 효과를 낸다. 나머지 필드는 "필터 추가"에서 골랐거나 value에 값이 들어
|
|
2963
|
+
* 있는 동안 노출된다 — 값으로 떠 있던 칩은 값이 비면 함께 빠지고, "필터 추가"로 꺼낸 칩은
|
|
2964
|
+
* 값이 없어도 세션 동안 자리를 지킨다 */
|
|
2965
|
+
fixed?: boolean;
|
|
2966
|
+
/** 초기값 및 clearable 클릭 시 되돌아갈 값.
|
|
2967
|
+
*
|
|
2968
|
+
* **`fixed` 또는 `required` 필드에서만 쓴다.** 그 외 필드에 지정하면 무시하고 콘솔에 알린다 —
|
|
2969
|
+
* 뺄 수 있는 필터는 처음에 바에 없는데, 값 맵에는 기본값이 채워져 **보이지도 지울 수도 없는
|
|
2970
|
+
* 조건이 검색에 걸린다.** 칩을 지워 값을 비워도 "검색 초기화"가 그 값을 되돌려 놓아 필터가
|
|
2971
|
+
* 조용히 되살아난다. 처음부터 값이 정해져 있어야 하는 필터라면 그것이 `fixed`·`required` 다.
|
|
2972
|
+
*
|
|
2973
|
+
* required인데 지정하지 않으면 타입별 내장 기본값(select: 첫 번째 옵션,
|
|
2974
|
+
* datepicker-day·datepicker-range: 오늘 날짜)을 대신 쓴다. 인라인(radioButton) datepicker-range
|
|
2975
|
+
* 필드는 팝오버 없이 바에 바로 노출되어 빈 상태로 둘 수 없으므로, 이 규칙과 무관하게 언제나
|
|
2976
|
+
* 오늘이 채워진다 */
|
|
2977
|
+
defaultValue?: SChipFilterValue;
|
|
2978
|
+
/** 이 필터만 비활성. 바에 남아 있되 팝오버가 열리지 않고 clearable도 눌리지 않는다.
|
|
2979
|
+
* 바 전체를 잠그려면 SChipFilterProps.disabled를 쓴다 — 둘은 OR로 합쳐진다 */
|
|
2980
|
+
disabled?: boolean;
|
|
2981
|
+
}
|
|
2982
|
+
```
|
|
2983
|
+
|
|
2984
|
+
### SChipFilterPresetsField
|
|
2985
|
+
|
|
2986
|
+
```ts
|
|
2987
|
+
/** 프리셋으로 기간을 고르는 필터 — datepicker-range·datepicker-statistics */
|
|
2988
|
+
export interface SChipFilterPresetsField extends SChipFilterFieldBase {
|
|
2989
|
+
/** 프리셋 라디오 목록. 지정하지 않으면 타입별 기본 목록을 쓴다 —
|
|
2990
|
+
* datepicker-range: 오늘·지난 7일·지난 30일·사용자 지정,
|
|
2991
|
+
* datepicker-statistics: 일별·월별·분기별·반기별·연도별·사용자 지정.
|
|
2992
|
+
* 프리셋 라디오 없이 캘린더(datepicker-range) 하나만 두려면 presets: [] 를 명시한다 */
|
|
2993
|
+
presets?: SChipFilterDatePreset[];
|
|
2994
|
+
/** 선택 가능 범위 */
|
|
2995
|
+
selectable?: [string, string];
|
|
2996
|
+
/** "사용자 지정" 프리셋으로 기간을 고를 때의 최대 선택 일수.
|
|
2997
|
+
* 지정하지 않아도 presets에 custom 프리셋이 있으면 기본 90일이 적용된다 — 무제한 기간
|
|
2998
|
+
* 집계·조회로 백엔드에 과도한 요청이 가는 것을 막기 위한 안전장치. 다른 상한이 필요하면
|
|
2999
|
+
* 이 값을 직접 지정한다 */
|
|
3000
|
+
maxRange?: number;
|
|
3001
|
+
}
|
|
3002
|
+
```
|
|
3003
|
+
|
|
3004
|
+
### SChipFilterKeywordInput
|
|
3005
|
+
|
|
3006
|
+
```ts
|
|
3007
|
+
/** keyword 필드의 입력 방식 — 무엇을 키워드 하나의 끝으로 볼지 */
|
|
3008
|
+
export type SChipFilterKeywordInput = 'tag' | 'csv';
|
|
3009
|
+
```
|
|
3010
|
+
|
|
3011
|
+
### SChipFilterMatchMode
|
|
3012
|
+
|
|
3013
|
+
```ts
|
|
3014
|
+
export type SChipFilterMatchMode = 'contains' | 'exact' | 'excludes';
|
|
3015
|
+
```
|
|
3016
|
+
|
|
3017
|
+
### SChipFilterPeriodUnit
|
|
3018
|
+
|
|
3019
|
+
```ts
|
|
3020
|
+
export type SChipFilterPeriodUnit = 'day' | 'month' | 'quarter' | 'half' | 'year' | 'custom';
|
|
3021
|
+
```
|
|
3022
|
+
|
|
3023
|
+
### SChipFilterOption
|
|
3024
|
+
|
|
3025
|
+
```ts
|
|
3026
|
+
export interface SChipFilterOption {
|
|
3027
|
+
value: SChipFilterOptionValue;
|
|
3028
|
+
label: string;
|
|
3029
|
+
disabled?: boolean;
|
|
3030
|
+
}
|
|
3031
|
+
```
|
|
3032
|
+
|
|
3033
|
+
### SChipFilterDatePreset
|
|
3034
|
+
|
|
3035
|
+
```ts
|
|
3036
|
+
/** datepicker-range·datepicker-statistics 필드의 프리셋 라디오 항목 (오늘/지난 7일/일별/월별/사용자 지정 등) */
|
|
3037
|
+
export interface SChipFilterDatePreset {
|
|
3038
|
+
/** 프리셋 식별자 */
|
|
3039
|
+
value: string;
|
|
3040
|
+
/** 라벨 */
|
|
3041
|
+
label: string;
|
|
3042
|
+
/** true면 "사용자 지정" — 선택 시 날짜/기간 피커가 추가로 노출된다. resolve는 무시된다. */
|
|
3043
|
+
custom?: boolean;
|
|
3044
|
+
/** custom이 아닐 때 실제 값을 계산한다. 단일 날짜(string) 또는 기간([start,end]) 모두 가능 —
|
|
3045
|
+
* datepicker-range 에서는 'd' 를 그 하루짜리 기간 ['d', 'd'] 와 같은 값으로 본다 */
|
|
3046
|
+
resolve?: () => string | SDateRangeValue;
|
|
3047
|
+
}
|
|
3048
|
+
```
|
|
3049
|
+
|
|
3050
|
+
## Dependencies
|
|
3051
|
+
|
|
3052
|
+
### Depends on
|
|
3053
|
+
|
|
3054
|
+
- [SDatePicker](../SDatePicker)
|
|
3055
|
+
- [SDateRangePicker](../SDateRangePicker)
|
|
3056
|
+
- [SIcon](../SIcon)
|
|
3057
|
+
- [SRadio](../SRadio)
|
|
3058
|
+
- [SRadioButton](../SRadioButton)
|
|
3059
|
+
- [SSearchInput](../SSearchInput)
|
|
3060
|
+
- [STag](../STag)
|
|
3061
|
+
- [STextLink](../STextLink)
|
|
2127
3062
|
- [STooltip](../STooltip)
|
|
2128
3063
|
|
|
2129
3064
|
### Graph
|
|
@@ -2162,7 +3097,7 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2162
3097
|
| `labelWidth?` | `number \| string` | — | |
|
|
2163
3098
|
| `hint?` | `string` | — | |
|
|
2164
3099
|
| `errorMessage?` | `string` | — | |
|
|
2165
|
-
| `width?` | `
|
|
3100
|
+
| `width?` | `SFieldWidth` | — | 컨트롤 너비 — 폭 등급(`'xs' | 'sm' | 'md' | 'lg' | 'xl'`) · 숫자=px · CSS 길이. 등급은 `--cmp-field-width-*` 토큰으로 풀리므로 토큰이 바뀌면 화면이 따라간다. 값 길이에 상한이 없으면 `"100%"` 로 두어 행 전체를 쓴다. |
|
|
2166
3101
|
| `status?` | `SFieldStatus` | — | |
|
|
2167
3102
|
| `icon?` | `SIconName` | — | |
|
|
2168
3103
|
| `iconColor?` | `SColor` | — | |
|
|
@@ -2186,6 +3121,20 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2186
3121
|
|--------|------|-------------|
|
|
2187
3122
|
| `focus` | `() => void` | 입력 필드에 포커스를 이동합니다. |
|
|
2188
3123
|
|
|
3124
|
+
## Types
|
|
3125
|
+
|
|
3126
|
+
### SChipInputSize
|
|
3127
|
+
|
|
3128
|
+
```ts
|
|
3129
|
+
export type SChipInputSize = SFieldSize;
|
|
3130
|
+
```
|
|
3131
|
+
|
|
3132
|
+
### SChipInputMetaPlacement
|
|
3133
|
+
|
|
3134
|
+
```ts
|
|
3135
|
+
export type SChipInputMetaPlacement = 'end' | 'inline';
|
|
3136
|
+
```
|
|
3137
|
+
|
|
2189
3138
|
## Dependencies
|
|
2190
3139
|
|
|
2191
3140
|
### Depends on
|
|
@@ -2219,6 +3168,14 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2219
3168
|
| `className?` | `string` | — | |
|
|
2220
3169
|
| `style?` | `CSSProperties` | — | |
|
|
2221
3170
|
|
|
3171
|
+
## Types
|
|
3172
|
+
|
|
3173
|
+
### SCircleProgressType
|
|
3174
|
+
|
|
3175
|
+
```ts
|
|
3176
|
+
export type SCircleProgressType = 'primary' | 'inverse' | 'error' | 'complete' | 'neutral';
|
|
3177
|
+
```
|
|
3178
|
+
|
|
2222
3179
|
## Dependencies
|
|
2223
3180
|
|
|
2224
3181
|
### Used by
|
|
@@ -2258,7 +3215,7 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2258
3215
|
| `optionSlot?` | `ReactNode` | — | 콘텐츠 박스에서 태그 오른쪽 커스텀 콘텐츠 (미지정 시 slotLabel 텍스트) |
|
|
2259
3216
|
| `contentSlot?` | `ReactNode` | — | 콘텐츠 박스 바로 아래에 통으로 들어가는 커스텀 콘텐츠 (전체 폭) |
|
|
2260
3217
|
| `mainButtonLabel?` | `string` | `'확인'` | 메인 버튼 |
|
|
2261
|
-
| `mainButtonName?` | `
|
|
3218
|
+
| `mainButtonName?` | `SConfirmModalMainButton` | — | 확인 버튼 프리셋 (없으면 type에 따라 자동 결정) |
|
|
2262
3219
|
| `subButtonLabel?` | `string` | — | 서브(취소) 버튼 |
|
|
2263
3220
|
|
|
2264
3221
|
#### Events
|
|
@@ -2270,6 +3227,22 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2270
3227
|
| `onCancel` | `() => void` | |
|
|
2271
3228
|
| `onClose` | `() => void` | 닫기(X) 버튼 클릭 (sdClose) |
|
|
2272
3229
|
|
|
3230
|
+
## Types
|
|
3231
|
+
|
|
3232
|
+
### SConfirmModalType
|
|
3233
|
+
|
|
3234
|
+
```ts
|
|
3235
|
+
export type SConfirmModalType = 'positive' | 'negative' | 'default';
|
|
3236
|
+
```
|
|
3237
|
+
|
|
3238
|
+
### SConfirmModalMainButton
|
|
3239
|
+
|
|
3240
|
+
```ts
|
|
3241
|
+
/** 확인 버튼 프리셋 (sd-confirm-modal ConfirmModalMainButton) */
|
|
3242
|
+
export type SConfirmModalMainButton =
|
|
3243
|
+
'primary_md' | 'primary_outline_md' | 'danger_md' | 'danger_outline_md' | 'neutral_outline_md';
|
|
3244
|
+
```
|
|
3245
|
+
|
|
2273
3246
|
## Dependencies
|
|
2274
3247
|
|
|
2275
3248
|
### Used by
|
|
@@ -2291,6 +3264,25 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2291
3264
|
|
|
2292
3265
|
> 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
|
|
2293
3266
|
|
|
3267
|
+
### SDatePickerMonthListbox
|
|
3268
|
+
|
|
3269
|
+
#### Props
|
|
3270
|
+
|
|
3271
|
+
| Prop | Type | Default | Description |
|
|
3272
|
+
|------|------|---------|-------------|
|
|
3273
|
+
| `value?` | `string \| null` | — | 선택 월 (YYYY-MM) |
|
|
3274
|
+
| `anchorYear?` | `number` | — | 연도 리스트가 처음 열릴 때 중앙에 둘 연도. 기본값은 올해 |
|
|
3275
|
+
| `selectable?` | `[string, string]` | — | 선택 가능 범위 [시작, 종료] |
|
|
3276
|
+
| `className?` | `string` | — | |
|
|
3277
|
+
| `style?` | `CSSProperties` | — | |
|
|
3278
|
+
|
|
3279
|
+
#### Events
|
|
3280
|
+
|
|
3281
|
+
| Event | Type | Description |
|
|
3282
|
+
|-------|------|-------------|
|
|
3283
|
+
| `onValueChange` | `(value: string) => void` | |
|
|
3284
|
+
| `onMonthSelect` | `(value: string) => void` | 월 컬럼에서 월을 선택했을 때 호출 |
|
|
3285
|
+
|
|
2294
3286
|
### SDatePicker
|
|
2295
3287
|
|
|
2296
3288
|
#### Props
|
|
@@ -2303,7 +3295,9 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2303
3295
|
| `placeholder?` | `string` | — | |
|
|
2304
3296
|
| `selectable?` | `[string, string]` | — | 선택 가능 범위 [시작, 종료] |
|
|
2305
3297
|
| `disabled?` | `boolean` | `false` | |
|
|
2306
|
-
| `
|
|
3298
|
+
| `clearable?` | `boolean` | `false` | 선택값 지우기 버튼. 값이 있을 때만 나타나고, 누르면 `onValueChange` 로 `null` 이 온다. **필수 입력 필드에는 켜지 않는다** — 지우면 다시 고르기 전까지 폼이 통과하지 못한다. `STimePicker` · `STimeRangePicker` · `SSelect` 의 `clearable` 과 같은 규칙이다. |
|
|
3299
|
+
| `width?` | `SFieldWidth` | — | 컨트롤 너비 — 폭 등급(`'xs' | 'sm' | 'md' | 'lg' | 'xl'`) · 숫자=px · CSS 길이. 등급은 `--cmp-field-width-*` 토큰으로 풀리므로 토큰이 바뀌면 화면이 따라간다. 값 길이에 상한이 없으면 `"100%"` 로 두어 행 전체를 쓴다. |
|
|
3300
|
+
| `maxWidth?` | `SFieldWidth` | — | 컨트롤 최대 너비 — 폭 등급 · 숫자=px · CSS 길이. 지정하지 않으면 값 길이(`YYYY-MM-DD`)에 맞춘 내장 상한(`size='md'` → `lg`, `'sm'` → `md`)이 걸려, `width="100%"` 를 받아도 행 전체로 늘어나지 않는다. 팝오버처럼 폭이 이미 좁게 정해진 자리에서 그 폭을 그대로 채워야 하면 `width="100%"` 와 함께 `maxWidth="100%"` 를 준다. `0` 은 상한을 아예 걸지 않는다 — 부모보다 넓어지는 것까지 허용해야 할 때만 쓴다. |
|
|
2307
3301
|
| `name?` | `string` | — | |
|
|
2308
3302
|
| `rules?` | `Rule[]` | — | |
|
|
2309
3303
|
| `status?` | `SFieldStatus` | — | |
|
|
@@ -2325,9 +3319,41 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2325
3319
|
|
|
2326
3320
|
| Event | Type | Description |
|
|
2327
3321
|
|-------|------|-------------|
|
|
2328
|
-
| `onValueChange` | `(value: string) => void` | 선택 변경 (sdUpdate) |
|
|
3322
|
+
| `onValueChange` | `(value: string \| null) => void` | 선택 변경 (sdUpdate). `clearable` 로 지우면 `null` 이 온다 |
|
|
2329
3323
|
| `onViewChange` | `(view: { year: number; month: number }) => void` | 보이는 연·월 변경 (sdViewChange) |
|
|
2330
3324
|
|
|
3325
|
+
### SDatePickerYearListbox
|
|
3326
|
+
|
|
3327
|
+
#### Props
|
|
3328
|
+
|
|
3329
|
+
| Prop | Type | Default | Description |
|
|
3330
|
+
|------|------|---------|-------------|
|
|
3331
|
+
| `value?` | `string \| null` | — | 선택 연도 (YYYY) |
|
|
3332
|
+
| `anchorYear?` | `number` | — | 리스트가 처음 열릴 때 중앙에 둘 연도. 기본값은 올해 |
|
|
3333
|
+
| `selectable?` | `[string, string]` | — | 선택 가능 범위 [시작, 종료] |
|
|
3334
|
+
| `className?` | `string` | — | |
|
|
3335
|
+
| `style?` | `CSSProperties` | — | |
|
|
3336
|
+
|
|
3337
|
+
#### Events
|
|
3338
|
+
|
|
3339
|
+
| Event | Type | Description |
|
|
3340
|
+
|-------|------|-------------|
|
|
3341
|
+
| `onValueChange` | `(year: string) => void` | |
|
|
3342
|
+
|
|
3343
|
+
## Types
|
|
3344
|
+
|
|
3345
|
+
### SDatePickerMode
|
|
3346
|
+
|
|
3347
|
+
```ts
|
|
3348
|
+
export type SDatePickerMode = 'date' | 'month' | 'year';
|
|
3349
|
+
```
|
|
3350
|
+
|
|
3351
|
+
### SDatePickerSize
|
|
3352
|
+
|
|
3353
|
+
```ts
|
|
3354
|
+
export type SDatePickerSize = SFieldSize;
|
|
3355
|
+
```
|
|
3356
|
+
|
|
2331
3357
|
## Dependencies
|
|
2332
3358
|
|
|
2333
3359
|
### Used by
|
|
@@ -2339,6 +3365,7 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2339
3365
|
|
|
2340
3366
|
- [SCalendar](../SCalendar)
|
|
2341
3367
|
- [SField](../SField)
|
|
3368
|
+
- [SGhostButton](../SGhostButton)
|
|
2342
3369
|
- [SIcon](../SIcon)
|
|
2343
3370
|
|
|
2344
3371
|
### Graph
|
|
@@ -2362,7 +3389,8 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2362
3389
|
| `maxRange?` | `number` | — | 최대 선택 일수 |
|
|
2363
3390
|
| `useTimePicker?` | `boolean` | `false` | 시간(시:분) 선택 푸터 사용 여부. true이면 value는 "YYYY-MM-DD HH:mm" 형식이 되고, 캘린더 하단에 시작·종료 시간 입력이 표시된다. |
|
|
2364
3391
|
| `disabled?` | `boolean` | `false` | |
|
|
2365
|
-
| `
|
|
3392
|
+
| `clearable?` | `boolean` | `false` | 선택값 지우기 버튼. 값이 있을 때만 나타나고, 누르면 `onValueChange` 로 `null` 이 온다. **필수 입력 필드에는 켜지 않는다** — 지우면 다시 고르기 전까지 폼이 통과하지 못한다. `SDatePicker` · `STimePicker` · `SSelect` 의 `clearable` 과 같은 규칙이다. |
|
|
3393
|
+
| `width?` | `SFieldWidth` | — | 컨트롤 너비 — 폭 등급(`'xs' | 'sm' | 'md' | 'lg' | 'xl'`) · 숫자=px · CSS 길이. 등급은 `--cmp-field-width-*` 토큰으로 풀리므로 토큰이 바뀌면 화면이 따라간다. 값 길이에 상한이 없으면 `"100%"` 로 두어 행 전체를 쓴다. |
|
|
2366
3394
|
| `name?` | `string` | — | |
|
|
2367
3395
|
| `rules?` | `Rule[]` | — | |
|
|
2368
3396
|
| `status?` | `SFieldStatus` | — | |
|
|
@@ -2384,7 +3412,7 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2384
3412
|
|
|
2385
3413
|
| Event | Type | Description |
|
|
2386
3414
|
|-------|------|-------------|
|
|
2387
|
-
| `onValueChange` | `(range: SDateRangeValue) => void` | 선택 변경 (sdUpdate) — [start, end] |
|
|
3415
|
+
| `onValueChange` | `(range: SDateRangeValue) => void` | 선택 변경 (sdUpdate) — [start, end]. `clearable` 로 지우면 `null` 이 온다 |
|
|
2388
3416
|
| `onViewChange` | `(view: { year: number; month: number }) => void` | 보이는 연·월 변경 (sdViewChange) |
|
|
2389
3417
|
|
|
2390
3418
|
### SRangeCalendar
|
|
@@ -2406,6 +3434,20 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2406
3434
|
| `onPendingStartChange` | `(start: string \| null) => void` | 시작일만 선택된(종료일 대기) 상태를 상위로 전달 — 트리거에 `start ~` 프리뷰 표시용 |
|
|
2407
3435
|
| `onViewChange` | `(view: { year: number; month: number }) => void` | |
|
|
2408
3436
|
|
|
3437
|
+
## Types
|
|
3438
|
+
|
|
3439
|
+
### SDateRangeValue
|
|
3440
|
+
|
|
3441
|
+
```ts
|
|
3442
|
+
export type SDateRangeValue = [string, string] | null;
|
|
3443
|
+
```
|
|
3444
|
+
|
|
3445
|
+
### SDateRangePickerSize
|
|
3446
|
+
|
|
3447
|
+
```ts
|
|
3448
|
+
export type SDateRangePickerSize = SFieldSize;
|
|
3449
|
+
```
|
|
3450
|
+
|
|
2409
3451
|
## Dependencies
|
|
2410
3452
|
|
|
2411
3453
|
### Used by
|
|
@@ -2435,6 +3477,7 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2435
3477
|
| Prop | Type | Default | Description |
|
|
2436
3478
|
|------|------|---------|-------------|
|
|
2437
3479
|
| `vertical?` | `boolean` | `false` | true면 수직 분할선, false면 수평 분할선 |
|
|
3480
|
+
| `height?` | `number \| string` | — | 수직 분할선의 길이. 숫자는 px, 문자열은 CSS 값(토큰 var 참조 등)으로 쓴다. 주지 않으면 `align-self: stretch` 로 부모 줄 높이를 그대로 채운다. 값을 주면 그 높이로 고정하고 교차축 가운데(`self-center`)에 맞춘다 — `align-self: stretch` 는 높이가 auto 일 때만 늘리므로, 높이를 정하는 순간 정렬을 함께 정해야 한다. 그래서 높이는 `className="h-*"` 가 아니라 이 prop 으로 준다. 수평 분할선에는 적용되지 않는다 (두께는 1px 로 고정). |
|
|
2438
3481
|
|
|
2439
3482
|
## Dependencies
|
|
2440
3483
|
|
|
@@ -2442,6 +3485,8 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2442
3485
|
|
|
2443
3486
|
- [SCalendar](../SCalendar)
|
|
2444
3487
|
- [SDateRangePicker](../SDateRangePicker)
|
|
3488
|
+
- [SList](../SList)
|
|
3489
|
+
- [STable](../STable)
|
|
2445
3490
|
- [STableBar](../STableBar)
|
|
2446
3491
|
|
|
2447
3492
|
### Graph
|
|
@@ -2462,6 +3507,7 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2462
3507
|
| `supportingText?` | `SDraggableItemSlot` | — | 제목을 보조하는 텍스트 |
|
|
2463
3508
|
| `supportingTextPosition?` | `SDraggableItemSupportingTextPosition` | `'right'` | 보조 텍스트 위치 |
|
|
2464
3509
|
| `trailing?` | `SDraggableItemSlot` | — | 타이틀 뒤에 표시할 태그/콘텐츠 |
|
|
3510
|
+
| `depth?` | `number` | `1` | 중첩 단계. `SListItem`·`SExpansionItem` 과 같은 들여쓰기 간격을 사용한다 |
|
|
2465
3511
|
| `accentStripe?` | `boolean` | `false` | 아이템 왼쪽 accent stripe 표시 여부 |
|
|
2466
3512
|
| `bordered?` | `boolean` | `false` | 외곽 테두리 사용 여부 |
|
|
2467
3513
|
| `selected?` | `boolean` | `false` | 선택 상태 여부 |
|
|
@@ -2479,6 +3525,42 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2479
3525
|
|-------|------|-------------|
|
|
2480
3526
|
| `onDragHandleMouseDown` | `(event: MouseEvent<HTMLDivElement>) => void` | 드래그 핸들 mouse down 이벤트 |
|
|
2481
3527
|
|
|
3528
|
+
## Types
|
|
3529
|
+
|
|
3530
|
+
### SDraggableItemSlot
|
|
3531
|
+
|
|
3532
|
+
```ts
|
|
3533
|
+
export type SDraggableItemSlot = ReactNode | SDraggableItemRenderProp;
|
|
3534
|
+
```
|
|
3535
|
+
|
|
3536
|
+
### SDraggableItemSupportingTextPosition
|
|
3537
|
+
|
|
3538
|
+
```ts
|
|
3539
|
+
export type SDraggableItemSupportingTextPosition = 'right' | 'bottom';
|
|
3540
|
+
```
|
|
3541
|
+
|
|
3542
|
+
### SDraggableItemSize
|
|
3543
|
+
|
|
3544
|
+
```ts
|
|
3545
|
+
export type SDraggableItemSize = 'sm' | 'md';
|
|
3546
|
+
```
|
|
3547
|
+
|
|
3548
|
+
### SDraggableItemRenderProp
|
|
3549
|
+
|
|
3550
|
+
```ts
|
|
3551
|
+
export type SDraggableItemRenderProp = (state: SDraggableItemRenderState) => ReactNode;
|
|
3552
|
+
```
|
|
3553
|
+
|
|
3554
|
+
### SDraggableItemRenderState
|
|
3555
|
+
|
|
3556
|
+
```ts
|
|
3557
|
+
export interface SDraggableItemRenderState {
|
|
3558
|
+
hovered: boolean;
|
|
3559
|
+
dragging: boolean;
|
|
3560
|
+
disabled: boolean;
|
|
3561
|
+
}
|
|
3562
|
+
```
|
|
3563
|
+
|
|
2482
3564
|
## Dependencies
|
|
2483
3565
|
|
|
2484
3566
|
### Depends on
|
|
@@ -2519,6 +3601,10 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2519
3601
|
| `selectedKey?` | `string` | — | 외부에서 제어하는 selected 아이템 key |
|
|
2520
3602
|
| `defaultSelectedKey?` | `string` | — | 초기 selected 아이템 key |
|
|
2521
3603
|
| `getDisabled?` | `(item: T, index: number) => boolean` | — | disabled 아이템은 선택 및 드래그에서 제외 |
|
|
3604
|
+
| `getDepth?` | `(item: T, index: number) => number` | — | 아이템의 중첩 단계. 기본값은 1 |
|
|
3605
|
+
| `setDepth?` | `(item: T, depth: number) => T` | — | depth 변경이 필요한 드롭에서 다음 아이템을 만드는 함수 |
|
|
3606
|
+
| `getCanHaveChildren?` | `(item: T, index: number) => boolean` | — | 하위 depth 를 가질 수 있는 아이템인지 판정 |
|
|
3607
|
+
| `maxDepth?` | `number` | `3` | 허용할 최대 depth |
|
|
2522
3608
|
| `listId?` | `string` | — | Provider 안에서 사용할 리스트 식별자 |
|
|
2523
3609
|
| `group?` | `string` | — | 같은 group 값을 가진 리스트끼리 드래그 이벤트를 공유 |
|
|
2524
3610
|
|
|
@@ -2537,6 +3623,32 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2537
3623
|
|------|------|---------|-------------|
|
|
2538
3624
|
| `children` | `ReactNode` | — | |
|
|
2539
3625
|
|
|
3626
|
+
## Types
|
|
3627
|
+
|
|
3628
|
+
### SDraggableGroupMoveEvent
|
|
3629
|
+
|
|
3630
|
+
```ts
|
|
3631
|
+
export interface SDraggableGroupMoveEvent {
|
|
3632
|
+
group: string;
|
|
3633
|
+
itemKey: string;
|
|
3634
|
+
fromListId: string;
|
|
3635
|
+
fromIndex: number;
|
|
3636
|
+
toListId: string;
|
|
3637
|
+
toIndex: number;
|
|
3638
|
+
}
|
|
3639
|
+
```
|
|
3640
|
+
|
|
3641
|
+
### SDraggableListRenderState
|
|
3642
|
+
|
|
3643
|
+
```ts
|
|
3644
|
+
export interface SDraggableListRenderState {
|
|
3645
|
+
onDragHandleMouseDown: (event: MouseEvent<HTMLDivElement>) => void;
|
|
3646
|
+
selected: boolean;
|
|
3647
|
+
disabled: boolean;
|
|
3648
|
+
depth: number;
|
|
3649
|
+
}
|
|
3650
|
+
```
|
|
3651
|
+
|
|
2540
3652
|
## Dependencies
|
|
2541
3653
|
|
|
2542
3654
|
### Depends on
|
|
@@ -2578,58 +3690,282 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2578
3690
|
| `onClose` | `() => void` | 닫기(X) 버튼 클릭 |
|
|
2579
3691
|
| `onWidthChange` | `(width: number) => void` | 너비가 확정될 때(드래그를 놓거나 방향키 조작). 드래그하는 동안에는 오지 않는다 |
|
|
2580
3692
|
|
|
2581
|
-
##
|
|
3693
|
+
## Types
|
|
3694
|
+
|
|
3695
|
+
### SDrawerButton
|
|
3696
|
+
|
|
3697
|
+
```ts
|
|
3698
|
+
export type SDrawerButton = SFooterButton;
|
|
3699
|
+
```
|
|
3700
|
+
|
|
3701
|
+
## Dependencies
|
|
3702
|
+
|
|
3703
|
+
### Depends on
|
|
3704
|
+
|
|
3705
|
+
- [SFooter](../SFooter)
|
|
3706
|
+
- [SOverlayHeader](../SOverlayHeader)
|
|
3707
|
+
|
|
3708
|
+
### Graph
|
|
3709
|
+
|
|
3710
|
+
---
|
|
3711
|
+
|
|
3712
|
+
# SDropdownButton
|
|
3713
|
+
|
|
3714
|
+
> 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
|
|
3715
|
+
|
|
3716
|
+
### SDropdownButton
|
|
3717
|
+
|
|
3718
|
+
#### Props
|
|
3719
|
+
|
|
3720
|
+
| Prop | Type | Default | Description |
|
|
3721
|
+
|------|------|---------|-------------|
|
|
3722
|
+
| `color?` | `SButtonColor` | `'primary'` | 버튼 색상 |
|
|
3723
|
+
| `outline?` | `boolean` | `false` | 아웃라인(테두리) 스타일 |
|
|
3724
|
+
| `size?` | `SDropdownButtonSize` | `'sm'` | 크기 (lg 미지원) |
|
|
3725
|
+
| `label?` | `string` | `''` | 버튼 레이블 |
|
|
3726
|
+
| `items?` | `SDropdownButtonItem[]` | `[]` | 메뉴 아이템 목록 |
|
|
3727
|
+
| `split?` | `boolean` | `false` | split 모드 — 레이블 버튼과 토글 분리 |
|
|
3728
|
+
| `disabled?` | `boolean` | `false` | 비활성 |
|
|
3729
|
+
| `className?` | `string` | — | |
|
|
3730
|
+
| `style?` | `CSSProperties` | — | |
|
|
3731
|
+
|
|
3732
|
+
#### Events
|
|
3733
|
+
|
|
3734
|
+
| Event | Type | Description |
|
|
3735
|
+
|-------|------|-------------|
|
|
3736
|
+
| `onItemClick` | `(value: string \| number) => void` | 메뉴 아이템 선택 (sdClick) |
|
|
3737
|
+
| `onLabelClick` | `() => void` | split 레이블 버튼 클릭 (sdButtonClick) |
|
|
3738
|
+
| `onOpenChange` | `(open: boolean) => void` | 열림/닫힘 변경 (sdDropDownShow) |
|
|
3739
|
+
|
|
3740
|
+
#### Methods (ref)
|
|
3741
|
+
|
|
3742
|
+
| Method | Type | Description |
|
|
3743
|
+
|--------|------|-------------|
|
|
3744
|
+
| `open` | `() => void` | 드롭다운 열기 (sdOpen) |
|
|
3745
|
+
| `close` | `() => void` | 드롭다운 닫기 (sdClose) |
|
|
3746
|
+
|
|
3747
|
+
## Types
|
|
3748
|
+
|
|
3749
|
+
### SDropdownButtonSize
|
|
3750
|
+
|
|
3751
|
+
```ts
|
|
3752
|
+
export type SDropdownButtonSize = 'xs' | 'sm' | 'md';
|
|
3753
|
+
```
|
|
3754
|
+
|
|
3755
|
+
### SDropdownButtonItem
|
|
3756
|
+
|
|
3757
|
+
```ts
|
|
3758
|
+
export interface SDropdownButtonItem {
|
|
3759
|
+
value: string | number;
|
|
3760
|
+
label: string;
|
|
3761
|
+
icon?: SIconName;
|
|
3762
|
+
disabled?: boolean;
|
|
3763
|
+
}
|
|
3764
|
+
```
|
|
3765
|
+
|
|
3766
|
+
## Dependencies
|
|
3767
|
+
|
|
3768
|
+
### Depends on
|
|
3769
|
+
|
|
3770
|
+
- [SButton](../SButton)
|
|
3771
|
+
- [SIcon](../SIcon)
|
|
3772
|
+
|
|
3773
|
+
### Graph
|
|
3774
|
+
|
|
3775
|
+
---
|
|
3776
|
+
|
|
3777
|
+
# SEditor
|
|
3778
|
+
|
|
3779
|
+
> 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
|
|
3780
|
+
|
|
3781
|
+
### EditorBody
|
|
3782
|
+
|
|
3783
|
+
#### Props
|
|
3784
|
+
|
|
3785
|
+
| Prop | Type | Default | Description |
|
|
3786
|
+
|------|------|---------|-------------|
|
|
3787
|
+
| `api` | `TiptapApi` | — | 다 불러온 tiptap — 이 컴포넌트는 준비된 뒤에만 마운트된다 |
|
|
3788
|
+
| `value?` | `string` | — | |
|
|
3789
|
+
| `defaultValue?` | `string` | — | |
|
|
3790
|
+
| `htmlRef` | `RefObject<string>` | — | 지금 화면에 있는 HTML. 껍데기(SEditor)가 규칙 검증·폼 제출에 쓴다 |
|
|
3791
|
+
| `placeholder` | `string` | — | |
|
|
3792
|
+
| `editable` | `boolean` | — | |
|
|
3793
|
+
| `disabled` | `boolean` | — | |
|
|
3794
|
+
| `minHeight?` | `number \| string` | — | |
|
|
3795
|
+
| `maxHeight?` | `number \| string` | — | |
|
|
3796
|
+
| `toolbar` | `SEditorToolbarItem[]` | — | |
|
|
3797
|
+
| `bubbleMenu` | `SEditorToolbarItem[]` | — | 선택 영역 위에 뜨는 서식 판 — 편집할 수 있으면 언제나 뜬다 (SEditor 주석 참고) |
|
|
3798
|
+
| `editorClass?` | `string` | — | |
|
|
3799
|
+
| `editorStyle?` | `CSSProperties` | — | |
|
|
3800
|
+
|
|
3801
|
+
#### Events
|
|
3802
|
+
|
|
3803
|
+
| Event | Type | Description |
|
|
3804
|
+
|-------|------|-------------|
|
|
3805
|
+
| `onInput` | `(html: string) => void` | 사용자가 고쳐서 값이 바뀌었다 (setHTML·clear 같은 프로그램 조작은 제외) |
|
|
3806
|
+
| `onFocusChange` | `(focused: boolean) => void` | |
|
|
3807
|
+
| `onReady` | `() => void` | 에디터 인스턴스가 생겼다 — 껍데기가 밀린 focus() 를 흘려보낸다 |
|
|
3808
|
+
| `onImageUpload` | `(file: File) => Promise<string>` | |
|
|
3809
|
+
|
|
3810
|
+
### EditorToolbarBar
|
|
3811
|
+
|
|
3812
|
+
#### Props
|
|
3813
|
+
|
|
3814
|
+
| Prop | Type | Default | Description |
|
|
3815
|
+
|------|------|---------|-------------|
|
|
3816
|
+
| `state` | `EditorToolbarState` | — | 눌림 표시 — 에디터가 아직 없으면 `IDLE_TOOLBAR_STATE` |
|
|
3817
|
+
| `editor` | `Editor \| null` | — | 없으면 버튼을 눌러도 아무 일도 하지 않는다 (그때는 disabled 로 함께 잠근다) |
|
|
3818
|
+
| `items` | `SEditorToolbarItem[]` | — | |
|
|
3819
|
+
| `disabled` | `boolean` | — | 편집 불가(비활성·읽기전용·엔진 로딩 중) — 모든 버튼을 잠근다 |
|
|
3820
|
+
| `variant?` | `'bar' \| 'bubble'` | `'bar'` | `'bar'` 는 편집 영역 위에 붙는 막대, `'bubble'` 은 선택 영역 위에 뜨는 판이다. 그리는 버튼은 같고 담는 상자와 줄바꿈만 다르다. |
|
|
3821
|
+
|
|
3822
|
+
#### Events
|
|
3823
|
+
|
|
3824
|
+
| Event | Type | Description |
|
|
3825
|
+
|-------|------|-------------|
|
|
3826
|
+
| `onImageUpload` | `(file: File) => Promise<string>` | 이미지 업로드 훅. 없으면 `image` 항목을 그리지 않는다 |
|
|
3827
|
+
|
|
3828
|
+
### SEditor
|
|
3829
|
+
|
|
3830
|
+
#### Props
|
|
3831
|
+
|
|
3832
|
+
| Prop | Type | Default | Description |
|
|
3833
|
+
|------|------|---------|-------------|
|
|
3834
|
+
| `value?` | `string` | — | 값 (제어) — HTML 문자열 |
|
|
3835
|
+
| `defaultValue?` | `string` | — | 초기값 (비제어) — HTML 문자열 |
|
|
3836
|
+
| `placeholder?` | `string` | `'내용을 입력해 주세요.'` | 빈 문서에 보일 안내 문구 |
|
|
3837
|
+
| `minHeight?` | `number \| string` | `200` | 편집 영역 최소 높이 (숫자=px) |
|
|
3838
|
+
| `maxHeight?` | `number \| string` | — | 편집 영역 최대 높이 (숫자=px). 넘으면 편집 영역 안에서만 스크롤한다 |
|
|
3839
|
+
| `simple?` | `boolean` | `false` | **글자에 거는 서식만** 남긴다 — 목록·정렬·인용·코드·링크·이미지·구분선이 빠진다. 막대와 버블 메뉴 양쪽에 함께 걸린다 (한쪽에만 남으면 아무것도 막지 못한다). 받은 글의 문단 구조까지 작성자를 따라가면 곤란한 자리 — 좁은 칸의 메모·사유·짧은 안내문 — 에 쓴다. 반대로 서식이 아예 필요 없다면 `SEditor` 가 아니라 `STextarea` 다. |
|
|
3840
|
+
| `rules?` | `Rule[]` | — | 유효성 규칙 — blur 시 자동 검증 |
|
|
3841
|
+
| `status?` | `SFieldStatus` | — | 필드 상태 ('default' | 'pass' | 'error') |
|
|
3842
|
+
| `focused?` | `boolean` | — | 포커스 상태 (제어/반영) |
|
|
3843
|
+
| `hovered?` | `boolean` | — | 호버 상태 (제어/반영) |
|
|
3844
|
+
| `name?` | `string` | — | 폼 전송용 name |
|
|
3845
|
+
| `editorClass?` | `string` | — | 편집 영역 className |
|
|
3846
|
+
| `editorStyle?` | `CSSProperties` | — | 편집 영역 style |
|
|
3847
|
+
| `label?` | `string` | — | |
|
|
3848
|
+
| `labelWidth?` | `number \| string` | — | |
|
|
3849
|
+
| `icon?` | `SIconName` | — | 레이블 영역 아이콘 |
|
|
3850
|
+
| `iconColor?` | `SColor` | — | |
|
|
3851
|
+
| `labelTooltip?` | `string` | — | 레이블 툴팁 텍스트 |
|
|
3852
|
+
| `labelTooltipProps?` | `Partial<STooltipProps>` | — | 레이블 툴팁 상세 옵션 |
|
|
3853
|
+
| `addonLabel?` | `string` | — | 우측 어드온 레이블 |
|
|
3854
|
+
| `addonAlign?` | `SFieldAddonAlign` | — | 어드온 정렬 |
|
|
3855
|
+
| `hint?` | `string` | — | |
|
|
3856
|
+
| `error?` | `boolean` | — | |
|
|
3857
|
+
| `errorMessage?` | `string` | — | |
|
|
3858
|
+
| `width?` | `SFieldWidth` | `'100%'` | 컨트롤 너비 — 폭 등급(`'xs' | 'sm' | 'md' | 'lg' | 'xl'`) · 숫자=px · CSS 길이. 본문 길이에 상한이 없으므로 기본은 `"100%"`(행 전체)다. |
|
|
3859
|
+
| `disabled?` | `boolean` | `false` | |
|
|
3860
|
+
| `readOnly?` | `boolean` | `false` | |
|
|
3861
|
+
| `className?` | `string` | — | |
|
|
3862
|
+
| `style?` | `CSSProperties` | — | |
|
|
3863
|
+
|
|
3864
|
+
#### Events
|
|
3865
|
+
|
|
3866
|
+
| Event | Type | Description |
|
|
3867
|
+
|-------|------|-------------|
|
|
3868
|
+
| `onValueChange` | `(html: string) => void` | 값 변경 (sdUpdate) — 빈 문서면 빈 문자열을 준다 |
|
|
3869
|
+
| `onImageUpload` | `(file: File) => Promise<string>` | 이미지 업로드 — 고른 파일을 저장하고 **표시할 URL 을 돌려준다.** 저장 위치는 앱마다 다르므로 DS 가 정하지 않는다. 이 훅이 없으면 툴바에서 이미지 항목이 빠진다 (본문에 base64 를 박지 않는다 — HTML 이 그대로 DB 로 실려 간다). |
|
|
3870
|
+
| `onFocus` | `() => void` | 포커스 진입 |
|
|
3871
|
+
| `onBlur` | `() => void` | 포커스 이탈 |
|
|
3872
|
+
|
|
3873
|
+
#### Methods (ref)
|
|
2582
3874
|
|
|
2583
|
-
|
|
3875
|
+
| Method | Type | Description |
|
|
3876
|
+
|--------|------|-------------|
|
|
3877
|
+
| `focus` | `() => void` | 편집 영역에 포커스 |
|
|
3878
|
+
| `blur` | `() => void` | 포커스 해제 |
|
|
3879
|
+
| `getHTML` | `() => string` | 현재 내용을 HTML 로 반환 (빈 문서면 빈 문자열) |
|
|
3880
|
+
| `getText` | `() => string` | 현재 내용을 서식 없는 텍스트로 반환 |
|
|
3881
|
+
| `setHTML` | `(html: string) => void` | 내용을 HTML 로 교체 (onValueChange 를 발생시키지 않는다) |
|
|
3882
|
+
| `clear` | `() => void` | 내용을 비운다 |
|
|
3883
|
+
| `editor` | `Editor \| null` | tiptap 에디터 인스턴스 — 확장 명령이 필요할 때만 쓴다 |
|
|
2584
3884
|
|
|
2585
|
-
|
|
2586
|
-
- [SOverlayHeader](../SOverlayHeader)
|
|
3885
|
+
## Types
|
|
2587
3886
|
|
|
2588
|
-
###
|
|
3887
|
+
### TiptapApi
|
|
2589
3888
|
|
|
2590
|
-
|
|
3889
|
+
```ts
|
|
3890
|
+
/** 동적으로 불러온 tiptap — 이 객체를 거치지 않고는 에디터 코드가 tiptap 을 만지지 않는다. */
|
|
3891
|
+
export interface TiptapApi {
|
|
3892
|
+
useEditor: TiptapReact['useEditor'];
|
|
3893
|
+
useEditorState: TiptapReact['useEditorState'];
|
|
3894
|
+
EditorContent: TiptapReact['EditorContent'];
|
|
3895
|
+
/** 선택 영역 위에 뜨는 판 — 자리는 floating-ui 가 잡는다 */
|
|
3896
|
+
BubbleMenu: TiptapMenus['BubbleMenu'];
|
|
3897
|
+
/** tiptap 확장 구성 — 아래 createExtensions 주석 참고 */
|
|
3898
|
+
createExtensions: (options: SEditorExtensionOptions) => AnyExtension[];
|
|
3899
|
+
}
|
|
3900
|
+
```
|
|
2591
3901
|
|
|
2592
|
-
|
|
3902
|
+
### SEditorToolbarItem
|
|
2593
3903
|
|
|
2594
|
-
|
|
3904
|
+
```ts
|
|
3905
|
+
export type SEditorToolbarItem = SEditorToolbarAction | '|';
|
|
3906
|
+
```
|
|
2595
3907
|
|
|
2596
|
-
###
|
|
3908
|
+
### EditorToolbarState
|
|
2597
3909
|
|
|
2598
|
-
|
|
3910
|
+
```ts
|
|
3911
|
+
export type EditorToolbarState = ReturnType<typeof readEditorState>;
|
|
3912
|
+
```
|
|
2599
3913
|
|
|
2600
|
-
|
|
2601
|
-
|------|------|---------|-------------|
|
|
2602
|
-
| `color?` | `SButtonColor` | `'primary'` | 버튼 색상 |
|
|
2603
|
-
| `outline?` | `boolean` | `false` | 아웃라인(테두리) 스타일 |
|
|
2604
|
-
| `size?` | `SDropdownButtonSize` | `'sm'` | 크기 (lg 미지원) |
|
|
2605
|
-
| `label?` | `string` | `''` | 버튼 레이블 |
|
|
2606
|
-
| `items?` | `SDropdownButtonItem[]` | `[]` | 메뉴 아이템 목록 |
|
|
2607
|
-
| `split?` | `boolean` | `false` | split 모드 — 레이블 버튼과 토글 분리 |
|
|
2608
|
-
| `disabled?` | `boolean` | `false` | 비활성 |
|
|
2609
|
-
| `className?` | `string` | — | |
|
|
2610
|
-
| `style?` | `CSSProperties` | — | |
|
|
3914
|
+
### SEditorExtensionOptions
|
|
2611
3915
|
|
|
2612
|
-
|
|
3916
|
+
```ts
|
|
3917
|
+
export interface SEditorExtensionOptions {
|
|
3918
|
+
/** 빈 문서에 보일 문구를 그때그때 읽어 오는 게터 */
|
|
3919
|
+
getPlaceholder: () => string;
|
|
3920
|
+
}
|
|
3921
|
+
```
|
|
2613
3922
|
|
|
2614
|
-
|
|
2615
|
-
|-------|------|-------------|
|
|
2616
|
-
| `onItemClick` | `(value: string \| number) => void` | 메뉴 아이템 선택 (sdClick) |
|
|
2617
|
-
| `onLabelClick` | `() => void` | split 레이블 버튼 클릭 (sdButtonClick) |
|
|
2618
|
-
| `onOpenChange` | `(open: boolean) => void` | 열림/닫힘 변경 (sdDropDownShow) |
|
|
3923
|
+
### SEditorToolbarAction
|
|
2619
3924
|
|
|
2620
|
-
|
|
3925
|
+
```ts
|
|
3926
|
+
export type SEditorToolbarAction = (typeof SEDITOR_TOOLBAR_ITEMS)[number];
|
|
3927
|
+
```
|
|
2621
3928
|
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
3929
|
+
### SEDITOR_TOOLBAR_ITEMS
|
|
3930
|
+
|
|
3931
|
+
```ts
|
|
3932
|
+
/** 툴바에 놓을 수 있는 항목. `'|'` 는 구분선이다. */
|
|
3933
|
+
export const SEDITOR_TOOLBAR_ITEMS = [
|
|
3934
|
+
'heading',
|
|
3935
|
+
'fontSize',
|
|
3936
|
+
'bold',
|
|
3937
|
+
'italic',
|
|
3938
|
+
'underline',
|
|
3939
|
+
'strike',
|
|
3940
|
+
'code',
|
|
3941
|
+
'color',
|
|
3942
|
+
'alignLeft',
|
|
3943
|
+
'alignCenter',
|
|
3944
|
+
'alignRight',
|
|
3945
|
+
'alignJustify',
|
|
3946
|
+
'bulletList',
|
|
3947
|
+
'orderedList',
|
|
3948
|
+
'taskList',
|
|
3949
|
+
'list',
|
|
3950
|
+
'blockquote',
|
|
3951
|
+
'codeBlock',
|
|
3952
|
+
'horizontalRule',
|
|
3953
|
+
'link',
|
|
3954
|
+
'image',
|
|
3955
|
+
'table',
|
|
3956
|
+
'undo',
|
|
3957
|
+
'redo',
|
|
3958
|
+
] as const;
|
|
3959
|
+
```
|
|
2626
3960
|
|
|
2627
3961
|
## Dependencies
|
|
2628
3962
|
|
|
2629
3963
|
### Depends on
|
|
2630
3964
|
|
|
2631
3965
|
- [SButton](../SButton)
|
|
3966
|
+
- [SField](../SField)
|
|
2632
3967
|
- [SIcon](../SIcon)
|
|
3968
|
+
- [SInput](../SInput)
|
|
2633
3969
|
|
|
2634
3970
|
### Graph
|
|
2635
3971
|
|
|
@@ -2668,6 +4004,42 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2668
4004
|
|-------|------|-------------|
|
|
2669
4005
|
| `onToggle` | `(expanded: boolean, event: MouseEvent<HTMLButtonElement>) => void` | |
|
|
2670
4006
|
|
|
4007
|
+
## Types
|
|
4008
|
+
|
|
4009
|
+
### SExpansionItemSupportingTextPosition
|
|
4010
|
+
|
|
4011
|
+
```ts
|
|
4012
|
+
export type SExpansionItemSupportingTextPosition = 'right' | 'bottom';
|
|
4013
|
+
```
|
|
4014
|
+
|
|
4015
|
+
### SExpansionItemRenderProp
|
|
4016
|
+
|
|
4017
|
+
```ts
|
|
4018
|
+
export type SExpansionItemRenderProp = (state: SExpansionItemRenderState) => ReactNode;
|
|
4019
|
+
```
|
|
4020
|
+
|
|
4021
|
+
### SExpansionItemInteraction
|
|
4022
|
+
|
|
4023
|
+
```ts
|
|
4024
|
+
export type SExpansionItemInteraction = 'chevron';
|
|
4025
|
+
```
|
|
4026
|
+
|
|
4027
|
+
### SExpansionItemSize
|
|
4028
|
+
|
|
4029
|
+
```ts
|
|
4030
|
+
export type SExpansionItemSize = 'sm' | 'md';
|
|
4031
|
+
```
|
|
4032
|
+
|
|
4033
|
+
### SExpansionItemRenderState
|
|
4034
|
+
|
|
4035
|
+
```ts
|
|
4036
|
+
export interface SExpansionItemRenderState {
|
|
4037
|
+
expanded: boolean;
|
|
4038
|
+
selected: boolean;
|
|
4039
|
+
disabled: boolean;
|
|
4040
|
+
}
|
|
4041
|
+
```
|
|
4042
|
+
|
|
2671
4043
|
## Dependencies
|
|
2672
4044
|
|
|
2673
4045
|
### Used by
|
|
@@ -2724,8 +4096,9 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2724
4096
|
| `hint?` | `string` | `''` | 하단 힌트 |
|
|
2725
4097
|
| `disabled?` | `boolean` | `false` | 비활성 |
|
|
2726
4098
|
| `readOnly?` | `boolean` | `false` | 읽기 전용 (회색 배경) |
|
|
2727
|
-
| `width?` | `
|
|
2728
|
-
| `minWidth?` | `
|
|
4099
|
+
| `width?` | `SFieldWidth` | — | 컨트롤 너비. 지정하면 필드가 부모 폭을 다 먹지 않고 (레이블 + width) 만큼만 차지한다. 다른 요소와 나란히 놓으려면 부모를 flex 로 두면 된다. **폭 등급(`'xs' | 'sm' | 'md' | 'lg' | 'xl'`)으로 준다** — `--cmp-field-width-*` 토큰으로 풀리므로 토큰이 바뀌면 화면이 따라간다. 등급은 `maxLength`(= 스키마 상한)로 고르고, 상한이 `xl` 을 넘거나 상한이 없으면 `"100%"` 로 둔다. (`sellmate/field-width-grade` 가 검사한다) 숫자는 px, 그 밖의 문자열은 CSS 길이 그대로다. |
|
|
4100
|
+
| `minWidth?` | `SFieldWidth` | — | 컨트롤 최소 너비 (등급 · 숫자=px · CSS 길이). 하한선만 지정하며 필드는 계속 부모 폭을 채운다 |
|
|
4101
|
+
| `maxWidth?` | `SFieldWidth` | — | 컨트롤 최대 너비 (등급 · 숫자=px · CSS 길이). 상한선만 지정하며 필드는 그 아래에서 부모 폭을 채운다. `width` 와 달리 hug 로 전환하지 않는다 — 부모가 좁으면 같이 좁아지고, 넓어도 여기서 멈춘다. 값 길이가 정해진 컨트롤(날짜·시간)이 `width="100%"` 를 받아 행 전체로 늘어나는 것을 막는 데 쓴다. **라벨은 상한에 들어가지 않는다** — 라벨은 컨트롤의 형제라 이 상한 밖이다. `addonLabel` 은 테두리 박스 안이라 상한을 나눠 먹으므로, `labelWidth`(= addon 폭)만큼 상한을 자동으로 늘린다. addon 이 있는데 `labelWidth` 가 없으면 폭을 알 수 없어 상한을 걸지 않는다 — 컨트롤이 잘리는 것보다 넓은 편이 낫다. |
|
|
2729
4102
|
| `multiline?` | `boolean` | `false` | 멀티라인(textarea) — 컨트롤 높이를 고정하지 않고 min-height만 적용 |
|
|
2730
4103
|
| `borderless?` | `boolean` | `false` | 테두리 박스 제거 (inline 컨트롤용) — border/배경/hover·focus 강조만 사라지고 label·hint·errorMessage 등 나머지 필드 구성은 그대로 동작한다. |
|
|
2731
4104
|
| `children?` | `ReactNode` | — | 실제 컨트롤 (input/select 등) — 테두리 없이 렌더, 테두리는 SField가 제공 |
|
|
@@ -2745,6 +4118,26 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2745
4118
|
|--------|------|-------------|
|
|
2746
4119
|
| `focus` | `() => void` | 내부 컨트롤에 포커스하고 필드를 화면에 스크롤합니다. |
|
|
2747
4120
|
|
|
4121
|
+
## Types
|
|
4122
|
+
|
|
4123
|
+
### SFieldSize
|
|
4124
|
+
|
|
4125
|
+
```ts
|
|
4126
|
+
export type SFieldSize = 'sm' | 'md';
|
|
4127
|
+
```
|
|
4128
|
+
|
|
4129
|
+
### SFieldAddonAlign
|
|
4130
|
+
|
|
4131
|
+
```ts
|
|
4132
|
+
export type SFieldAddonAlign = 'start' | 'center' | 'end';
|
|
4133
|
+
```
|
|
4134
|
+
|
|
4135
|
+
### SFieldStatus
|
|
4136
|
+
|
|
4137
|
+
```ts
|
|
4138
|
+
export type SFieldStatus = 'default' | 'pass' | 'error';
|
|
4139
|
+
```
|
|
4140
|
+
|
|
2748
4141
|
## Dependencies
|
|
2749
4142
|
|
|
2750
4143
|
### Used by
|
|
@@ -2753,9 +4146,11 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2753
4146
|
- [SChipInput](../SChipInput)
|
|
2754
4147
|
- [SDatePicker](../SDatePicker)
|
|
2755
4148
|
- [SDateRangePicker](../SDateRangePicker)
|
|
4149
|
+
- [SEditor](../SEditor)
|
|
2756
4150
|
- [SFilePicker](../SFilePicker)
|
|
2757
4151
|
- [SInput](../SInput)
|
|
2758
4152
|
- [SNumberInput](../SNumberInput)
|
|
4153
|
+
- [SSearchInput](../SSearchInput)
|
|
2759
4154
|
- [SSelect](../SSelect)
|
|
2760
4155
|
- [STextarea](../STextarea)
|
|
2761
4156
|
- [STimePicker](../STimePicker)
|
|
@@ -2807,7 +4202,7 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2807
4202
|
| `hint?` | `string` | — | |
|
|
2808
4203
|
| `error?` | `boolean` | — | |
|
|
2809
4204
|
| `errorMessage?` | `string` | — | |
|
|
2810
|
-
| `width?` | `
|
|
4205
|
+
| `width?` | `SFieldWidth` | — | 컨트롤 너비 — 폭 등급(`'xs' | 'sm' | 'md' | 'lg' | 'xl'`) · 숫자=px · CSS 길이. 등급은 `--cmp-field-width-*` 토큰으로 풀리므로 토큰이 바뀌면 화면이 따라간다. 값 길이에 상한이 없으면 `"100%"` 로 두어 행 전체를 쓴다. |
|
|
2811
4206
|
| `className?` | `string` | — | |
|
|
2812
4207
|
| `style?` | `CSSProperties` | — | |
|
|
2813
4208
|
|
|
@@ -2818,6 +4213,20 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2818
4213
|
| `onValueChange` | `(value: SFilePickerValue) => void` | 파일 변경 (sdUpdate) |
|
|
2819
4214
|
| `onReject` | `(detail: { files: File[]; reason: SFilePickerRejectReason }) => void` | 제한 초과 거부 (sdReject) |
|
|
2820
4215
|
|
|
4216
|
+
## Types
|
|
4217
|
+
|
|
4218
|
+
### SFilePickerValue
|
|
4219
|
+
|
|
4220
|
+
```ts
|
|
4221
|
+
export type SFilePickerValue = File[] | File | null;
|
|
4222
|
+
```
|
|
4223
|
+
|
|
4224
|
+
### SFilePickerRejectReason
|
|
4225
|
+
|
|
4226
|
+
```ts
|
|
4227
|
+
export type SFilePickerRejectReason = 'max-file-size' | 'max-total-size' | 'max-files';
|
|
4228
|
+
```
|
|
4229
|
+
|
|
2821
4230
|
## Dependencies
|
|
2822
4231
|
|
|
2823
4232
|
### Used by
|
|
@@ -2853,13 +4262,36 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2853
4262
|
| `leftClassName?` | `string` | — | |
|
|
2854
4263
|
| `style?` | `CSSProperties` | — | |
|
|
2855
4264
|
|
|
4265
|
+
## Types
|
|
4266
|
+
|
|
4267
|
+
### SFooterBg
|
|
4268
|
+
|
|
4269
|
+
```ts
|
|
4270
|
+
export type SFooterBg = 'white' | 'grey';
|
|
4271
|
+
```
|
|
4272
|
+
|
|
4273
|
+
### SFooterButton
|
|
4274
|
+
|
|
4275
|
+
```ts
|
|
4276
|
+
export interface SFooterButton {
|
|
4277
|
+
label?: string;
|
|
4278
|
+
color?: SButtonColor;
|
|
4279
|
+
outline?: boolean;
|
|
4280
|
+
size?: SButtonSize;
|
|
4281
|
+
disabled?: boolean;
|
|
4282
|
+
onClick?: () => void;
|
|
4283
|
+
}
|
|
4284
|
+
```
|
|
4285
|
+
|
|
2856
4286
|
## Dependencies
|
|
2857
4287
|
|
|
2858
4288
|
### Used by
|
|
2859
4289
|
|
|
2860
4290
|
- [SActionModal](../SActionModal)
|
|
4291
|
+
- [SCard](../SCard)
|
|
2861
4292
|
- [SDrawer](../SDrawer)
|
|
2862
4293
|
- [SPopup](../SPopup)
|
|
4294
|
+
- [SSectionHeaderCard](../SSectionHeaderCard)
|
|
2863
4295
|
|
|
2864
4296
|
### Depends on
|
|
2865
4297
|
|
|
@@ -2900,6 +4332,17 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2900
4332
|
| `reset` | `() => void` | resetValidation 별칭 — sd-form sdReset 대응. 값 리셋은 소비자(value/onChange) 책임 |
|
|
2901
4333
|
| `focusFirstInvalid` | `() => void` | 첫 번째 실패 필드로 포커스 이동 |
|
|
2902
4334
|
|
|
4335
|
+
## Types
|
|
4336
|
+
|
|
4337
|
+
### SFormValidationError
|
|
4338
|
+
|
|
4339
|
+
```ts
|
|
4340
|
+
export interface SFormValidationError {
|
|
4341
|
+
/** 검증에 실패한 필드 name 목록 (DOM 순서) */
|
|
4342
|
+
names: string[];
|
|
4343
|
+
}
|
|
4344
|
+
```
|
|
4345
|
+
|
|
2903
4346
|
---
|
|
2904
4347
|
|
|
2905
4348
|
# SGhostButton
|
|
@@ -2931,6 +4374,20 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2931
4374
|
|-------|------|-------------|
|
|
2932
4375
|
| `onClick` | `(e: MouseEvent<HTMLButtonElement>) => void` | 클릭 (sdClick) |
|
|
2933
4376
|
|
|
4377
|
+
## Types
|
|
4378
|
+
|
|
4379
|
+
### SGhostButtonSize
|
|
4380
|
+
|
|
4381
|
+
```ts
|
|
4382
|
+
export type SGhostButtonSize = 'xxs' | 'xs' | 'sm' | 'md' | 'lg';
|
|
4383
|
+
```
|
|
4384
|
+
|
|
4385
|
+
### SGhostButtonIntent
|
|
4386
|
+
|
|
4387
|
+
```ts
|
|
4388
|
+
export type SGhostButtonIntent = 'default' | 'danger' | 'action' | 'subAction' | 'inverse';
|
|
4389
|
+
```
|
|
4390
|
+
|
|
2934
4391
|
## Dependencies
|
|
2935
4392
|
|
|
2936
4393
|
### Used by
|
|
@@ -2938,7 +4395,7 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2938
4395
|
- [SBarcodeInput](../SBarcodeInput)
|
|
2939
4396
|
- [SCalendar](../SCalendar)
|
|
2940
4397
|
- [SChip](../SChip)
|
|
2941
|
-
- [
|
|
4398
|
+
- [SDatePicker](../SDatePicker)
|
|
2942
4399
|
- [SDateRangePicker](../SDateRangePicker)
|
|
2943
4400
|
- [SFilePicker](../SFilePicker)
|
|
2944
4401
|
- [SGnb](../SGnb)
|
|
@@ -2948,7 +4405,8 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2948
4405
|
- [SOverlayHeader](../SOverlayHeader)
|
|
2949
4406
|
- [SPage](../SPage)
|
|
2950
4407
|
- [SPopover](../SPopover)
|
|
2951
|
-
- [
|
|
4408
|
+
- [SSearchInput](../SSearchInput)
|
|
4409
|
+
- [STable](../STable)
|
|
2952
4410
|
- [STimePicker](../STimePicker)
|
|
2953
4411
|
- [STimeRangePicker](../STimeRangePicker)
|
|
2954
4412
|
- [SToast](../SToast)
|
|
@@ -2992,6 +4450,7 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
2992
4450
|
| `resizable?` | `boolean` | `false` | 메뉴 폭을 드래그로 조절할 수 있게 한다. 레일 폭은 고정이고 **메뉴 컬럼만** 늘고 준다. 조절선은 GNB 컬럼의 오른쪽 경계 전체다 — fix 는 상단바 높이까지, full 은 상단바가 전폭이라 본문 높이까지. 접혀 있거나 깔 메뉴가 없으면(레일 리프가 활성) 조절선이 나오지 않는다. |
|
|
2993
4451
|
| `menuWidth?` | `number` | — | 메뉴 폭(px). 주면 controlled — onMenuWidthChange 로 직접 갱신해야 움직인다 |
|
|
2994
4452
|
| `defaultMenuWidth?` | `number` | — | 메뉴 초기 폭(px). uncontrolled |
|
|
4453
|
+
| `system?` | `ReactNode` | — | GNB 맨 아래에 붙는 system 패널(`SGnbSystem`) 슬롯. 펼치면 메뉴 컬럼 바닥에, 접히면(header="fix" 레일) `foldedFooter` 아래 레일 바닥에 여백 없이 붙는다. 색·형태(펼침/접힘)는 GNB 가 내려주므로 SGnbSystem 에 다시 넘기지 않아도 된다. useRail 에서 메뉴가 없는 레일 아이템이 활성이면 메뉴 컬럼째 사라지므로 이 슬롯도 함께 빠진다. |
|
|
2995
4454
|
| `ariaLabel?` | `string` | `'global navigation'` | 메뉴 landmark(nav) 의 접근성 레이블. 한 화면에 nav 가 여럿일 때 구분한다. |
|
|
2996
4455
|
|
|
2997
4456
|
#### Events
|
|
@@ -3004,8 +4463,57 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
3004
4463
|
| `onLauncherClick` | `() => void` | 앱런처(그리드) 버튼 클릭. 미지정 시 런처 버튼을 렌더하지 않는다. 접힘 레일(fix)에는 폴드 버튼만 남으므로 표시되지 않는다. |
|
|
3005
4464
|
| `onMenuWidthChange` | `(width: number) => void` | 메뉴 폭이 확정될 때(드래그를 놓거나 방향키 조작). 드래그하는 동안에는 오지 않는다 |
|
|
3006
4465
|
|
|
4466
|
+
## Types
|
|
4467
|
+
|
|
4468
|
+
### SGnbType
|
|
4469
|
+
|
|
4470
|
+
```ts
|
|
4471
|
+
/** 메뉴 스타일: box = 라운드/좁은 들여쓰기, belt = 풀폭 행/넓은 들여쓰기 */
|
|
4472
|
+
export type SGnbType = 'box' | 'belt';
|
|
4473
|
+
```
|
|
4474
|
+
|
|
4475
|
+
### SGnbHeader
|
|
4476
|
+
|
|
4477
|
+
```ts
|
|
4478
|
+
/** 상단바 구조: fix = GNB 폭에 고정, full = 레이아웃 전폭 */
|
|
4479
|
+
export type SGnbHeader = 'fix' | 'full';
|
|
4480
|
+
```
|
|
4481
|
+
|
|
4482
|
+
### SGnbColor
|
|
4483
|
+
|
|
4484
|
+
```ts
|
|
4485
|
+
/** 색상(테마): light / dark */
|
|
4486
|
+
export type SGnbColor = 'light' | 'dark';
|
|
4487
|
+
```
|
|
4488
|
+
|
|
4489
|
+
### SGnbMenuItem
|
|
4490
|
+
|
|
4491
|
+
```ts
|
|
4492
|
+
/** GNB 메뉴 아이템 (재귀 트리). depth1 → depth2 → depth3. */
|
|
4493
|
+
export interface SGnbMenuItem {
|
|
4494
|
+
/** 표시 텍스트 */
|
|
4495
|
+
label: string;
|
|
4496
|
+
/** 고유 식별값 (선택 상태 비교 기준) */
|
|
4497
|
+
value: string;
|
|
4498
|
+
/** depth1 전용 아이콘명 */
|
|
4499
|
+
icon?: SIconName;
|
|
4500
|
+
/** 우측 Tag 뱃지 텍스트 */
|
|
4501
|
+
tag?: string;
|
|
4502
|
+
/** 뱃지 색. 미지정 시 blue. */
|
|
4503
|
+
tagColor?: STagColor;
|
|
4504
|
+
/** 비활성 여부 */
|
|
4505
|
+
disabled?: boolean;
|
|
4506
|
+
/** 하위 아이템 (있으면 펼침/접힘 대상) */
|
|
4507
|
+
children?: SGnbMenuItem[];
|
|
4508
|
+
}
|
|
4509
|
+
```
|
|
4510
|
+
|
|
3007
4511
|
## Dependencies
|
|
3008
4512
|
|
|
4513
|
+
### Used by
|
|
4514
|
+
|
|
4515
|
+
- [SGnbSystem](../SGnbSystem)
|
|
4516
|
+
|
|
3009
4517
|
### Depends on
|
|
3010
4518
|
|
|
3011
4519
|
- [SGhostButton](../SGhostButton)
|
|
@@ -3017,6 +4525,122 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
3017
4525
|
|
|
3018
4526
|
---
|
|
3019
4527
|
|
|
4528
|
+
# SGnbSystem
|
|
4529
|
+
|
|
4530
|
+
> 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
|
|
4531
|
+
|
|
4532
|
+
### SGnbSystem
|
|
4533
|
+
|
|
4534
|
+
#### Props
|
|
4535
|
+
|
|
4536
|
+
| Prop | Type | Default | Description |
|
|
4537
|
+
|------|------|---------|-------------|
|
|
4538
|
+
| `color?` | `SGnbSystemColor` | — | 색상(패널 표면). light = 흰 면, dark = 오션블루 면. 미지정 시 자기를 담은 SGnb 의 color 를 따른다(단독으로 쓰면 `'light'`). |
|
|
4539
|
+
| `folded?` | `boolean` | — | 접힌(min) 형태 — 48px 레일 폭에 맞춰 알림·계정 아이콘만 남는다. 미지정 시 자기를 담은 SGnb 의 접힘 상태를 따른다. |
|
|
4540
|
+
| `actions?` | `SGnbSystemAction[]` | — | 패널 상단에 쌓는 행 목록. 각 원소는 `SSystemActionButton` 의 props 다(`color` 제외). 아래에 다른 조각이 있으면 그 사이에 구분선이 그어진다. 접히면 자리가 없어 렌더되지 않는다. |
|
|
4541
|
+
| `domain?` | `string` | — | 도메인 태그 텍스트. 접히면 자리가 없어 렌더되지 않는다 |
|
|
4542
|
+
| `alert?` | `SGnbSystemAlert` | — | 알림 버튼. 없으면 렌더하지 않는다. 설정과 나란히 서면 남는 폭을 먹고, 혼자면 폭을 다 먹는다 |
|
|
4543
|
+
| `setting?` | `SGnbSystemSetting` | — | 설정 버튼. 없으면 렌더하지 않는다. 알림과 나란히 서면 제 폭만 갖고, 혼자면 폭을 다 먹는다. 접히면 자리가 없어 렌더되지 않는다 — 접힌 상태에서도 닿아야 하면 SGnb 의 `foldedFooter` 에 둔다. |
|
|
4544
|
+
| `account?` | `SGnbSystemAccount` | — | 계정 행. 없으면 렌더하지 않는다 |
|
|
4545
|
+
|
|
4546
|
+
## Types
|
|
4547
|
+
|
|
4548
|
+
### SGnbSystemColor
|
|
4549
|
+
|
|
4550
|
+
```ts
|
|
4551
|
+
export type SGnbSystemColor = (typeof GNB_SYSTEM_COLORS)[number];
|
|
4552
|
+
```
|
|
4553
|
+
|
|
4554
|
+
### SGnbSystemAction
|
|
4555
|
+
|
|
4556
|
+
```ts
|
|
4557
|
+
/**
|
|
4558
|
+
* 패널 상단에 쌓이는 행 하나. `SSystemActionButton` 의 props 를 그대로 받는다 —
|
|
4559
|
+
* 단 `color` 는 패널이 정하므로 받지 않고, 목록 key 로 쓸 `value` 만 더한다.
|
|
4560
|
+
*/
|
|
4561
|
+
export type SGnbSystemAction = DistributiveOmit<SSystemActionButtonProps, 'color'> & {
|
|
4562
|
+
/** 목록 key. 없으면 순서를 key 로 쓴다 */
|
|
4563
|
+
value?: string;
|
|
4564
|
+
};
|
|
4565
|
+
```
|
|
4566
|
+
|
|
4567
|
+
### SGnbSystemAlert
|
|
4568
|
+
|
|
4569
|
+
```ts
|
|
4570
|
+
/** 알림 버튼 */
|
|
4571
|
+
export interface SGnbSystemAlert {
|
|
4572
|
+
/** 라벨 텍스트. 기본 `'알림'` */
|
|
4573
|
+
label?: string;
|
|
4574
|
+
/**
|
|
4575
|
+
* 읽지 않은 알림 개수. 1 이상이면 벨이 울리는 아이콘·강조색으로 바뀌고 개수(접히면 점)가 붙는다.
|
|
4576
|
+
* 표기는 컴포넌트가 맡는다 — 세 자리마다 콤마를 넣어 그리므로 앱이 포맷한 문자열을 넘기지 않는다.
|
|
4577
|
+
*/
|
|
4578
|
+
count?: number;
|
|
4579
|
+
/** 클릭 */
|
|
4580
|
+
onClick?: () => void;
|
|
4581
|
+
}
|
|
4582
|
+
```
|
|
4583
|
+
|
|
4584
|
+
### SGnbSystemSetting
|
|
4585
|
+
|
|
4586
|
+
```ts
|
|
4587
|
+
/** 설정 버튼 */
|
|
4588
|
+
export interface SGnbSystemSetting {
|
|
4589
|
+
/** 라벨 텍스트. 기본 `'설정'` */
|
|
4590
|
+
label?: string;
|
|
4591
|
+
/** 클릭 */
|
|
4592
|
+
onClick?: () => void;
|
|
4593
|
+
}
|
|
4594
|
+
```
|
|
4595
|
+
|
|
4596
|
+
### SGnbSystemAccount
|
|
4597
|
+
|
|
4598
|
+
```ts
|
|
4599
|
+
/** 계정 행 */
|
|
4600
|
+
export interface SGnbSystemAccount {
|
|
4601
|
+
/** 계정 이름 */
|
|
4602
|
+
label?: string;
|
|
4603
|
+
/** 클릭 */
|
|
4604
|
+
onClick?: () => void;
|
|
4605
|
+
}
|
|
4606
|
+
```
|
|
4607
|
+
|
|
4608
|
+
### GNB_SYSTEM_COLORS
|
|
4609
|
+
|
|
4610
|
+
```ts
|
|
4611
|
+
/**
|
|
4612
|
+
* SGnbSystem 설정.
|
|
4613
|
+
*
|
|
4614
|
+
* 토큰은 GNB system 패널 한 벌(`--cmp-gnb-system-*`)이다.
|
|
4615
|
+
* 패널(배경·경계·구분선·간격) → --cmp-gnb-system-panel-*
|
|
4616
|
+
* 도메인 태그 → --cmp-gnb-system-domainTag-*
|
|
4617
|
+
* 행 공통(높이·라운드·아이콘 프레임) → --cmp-gnb-system-item-* (SSystemActionButton 과 공유)
|
|
4618
|
+
* 알림 버튼 → --cmp-gnb-system-alertButton-*
|
|
4619
|
+
* 설정 버튼 → --cmp-gnb-system-setting-*
|
|
4620
|
+
* 펼침·접힘 패널 여백 → --cmp-gnb-system-project-padding · --cmp-gnb-system-min-padding
|
|
4621
|
+
*
|
|
4622
|
+
* 타이포그래피는 CSS 변수가 아니라 `.typo-*` 프리셋으로 나온다.
|
|
4623
|
+
* domainTag.typography → typo-body-sm-medium (12/500/20)
|
|
4624
|
+
* item.label.typography → typo-body-sm-semibold (12/600/20)
|
|
4625
|
+
* alertButton.count → typo-body-md-bold (13/700/22)
|
|
4626
|
+
*/
|
|
4627
|
+
|
|
4628
|
+
/** 색상(표면). light = 흰 면, dark = 오션블루 면. GNB 의 color 를 그대로 따라간다 */
|
|
4629
|
+
export const GNB_SYSTEM_COLORS = ['light', 'dark'] as const;
|
|
4630
|
+
```
|
|
4631
|
+
|
|
4632
|
+
## Dependencies
|
|
4633
|
+
|
|
4634
|
+
### Depends on
|
|
4635
|
+
|
|
4636
|
+
- [SGnb](../SGnb)
|
|
4637
|
+
- [SIcon](../SIcon)
|
|
4638
|
+
- [SSystemActionButton](../SSystemActionButton)
|
|
4639
|
+
|
|
4640
|
+
### Graph
|
|
4641
|
+
|
|
4642
|
+
---
|
|
4643
|
+
|
|
3020
4644
|
# SGuide
|
|
3021
4645
|
|
|
3022
4646
|
> 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
|
|
@@ -3036,6 +4660,21 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
3036
4660
|
| `className?` | `string` | — | |
|
|
3037
4661
|
| `style?` | `CSSProperties` | — | |
|
|
3038
4662
|
|
|
4663
|
+
## Types
|
|
4664
|
+
|
|
4665
|
+
### SGuideType
|
|
4666
|
+
|
|
4667
|
+
```ts
|
|
4668
|
+
export type SGuideType = 'tip' | 'notion';
|
|
4669
|
+
```
|
|
4670
|
+
|
|
4671
|
+
### SGuideMessage
|
|
4672
|
+
|
|
4673
|
+
```ts
|
|
4674
|
+
/** 중첩 배열로 depth 표현 (원본 renderListItem 대응). 예: ['상위', ['하위1', '하위2']] */
|
|
4675
|
+
export type SGuideMessage = string | SGuideMessage[];
|
|
4676
|
+
```
|
|
4677
|
+
|
|
3039
4678
|
## Dependencies
|
|
3040
4679
|
|
|
3041
4680
|
### Depends on
|
|
@@ -3079,11 +4718,13 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
3079
4718
|
- [SDateRangePicker](../SDateRangePicker)
|
|
3080
4719
|
- [SDraggableItem](../SDraggableItem)
|
|
3081
4720
|
- [SDropdownButton](../SDropdownButton)
|
|
4721
|
+
- [SEditor](../SEditor)
|
|
3082
4722
|
- [SExpansionItem](../SExpansionItem)
|
|
3083
4723
|
- [SField](../SField)
|
|
3084
4724
|
- [SFilePicker](../SFilePicker)
|
|
3085
4725
|
- [SGhostButton](../SGhostButton)
|
|
3086
4726
|
- [SGnb](../SGnb)
|
|
4727
|
+
- [SGnbSystem](../SGnbSystem)
|
|
3087
4728
|
- [SGuide](../SGuide)
|
|
3088
4729
|
- [SImage](../SImage)
|
|
3089
4730
|
- [SKeyValueTable](../SKeyValueTable)
|
|
@@ -3092,9 +4733,11 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
3092
4733
|
- [SNumberInput](../SNumberInput)
|
|
3093
4734
|
- [SPagination](../SPagination)
|
|
3094
4735
|
- [SPopover](../SPopover)
|
|
4736
|
+
- [SSearchInput](../SSearchInput)
|
|
3095
4737
|
- [SSectionHeaderCard](../SSectionHeaderCard)
|
|
3096
4738
|
- [SSelect](../SSelect)
|
|
3097
4739
|
- [SStepper](../SStepper)
|
|
4740
|
+
- [SSystemActionButton](../SSystemActionButton)
|
|
3098
4741
|
- [STable](../STable)
|
|
3099
4742
|
- [STag](../STag)
|
|
3100
4743
|
- [STextLink](../STextLink)
|
|
@@ -3136,6 +4779,20 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
3136
4779
|
| `onLoad` | `(src: string) => void` | 로드 완료. 로드된 src 를 넘긴다 |
|
|
3137
4780
|
| `onError` | `(event: SyntheticEvent<HTMLImageElement>) => void` | 로드 실패 |
|
|
3138
4781
|
|
|
4782
|
+
## Types
|
|
4783
|
+
|
|
4784
|
+
### SImageFit
|
|
4785
|
+
|
|
4786
|
+
```ts
|
|
4787
|
+
export type SImageFit = (typeof IMAGE_FITS)[number];
|
|
4788
|
+
```
|
|
4789
|
+
|
|
4790
|
+
### IMAGE_FITS
|
|
4791
|
+
|
|
4792
|
+
```ts
|
|
4793
|
+
export const IMAGE_FITS = ['cover', 'contain', 'fill', 'none', 'scale-down'] as const;
|
|
4794
|
+
```
|
|
4795
|
+
|
|
3139
4796
|
## Dependencies
|
|
3140
4797
|
|
|
3141
4798
|
### Depends on
|
|
@@ -3180,7 +4837,7 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
3180
4837
|
| `errorMessage?` | `string` | — | |
|
|
3181
4838
|
| `addonLabel?` | `string` | — | |
|
|
3182
4839
|
| `addonAlign?` | `SFieldAddonAlign` | — | 어드온 정렬 |
|
|
3183
|
-
| `width?` | `
|
|
4840
|
+
| `width?` | `SFieldWidth` | — | 컨트롤 너비 — 폭 등급(`'xs' | 'sm' | 'md' | 'lg' | 'xl'`) · 숫자=px · CSS 길이. 등급은 `--cmp-field-width-*` 토큰으로 풀리며 `maxLength`(= 스키마 상한)로 고른다. 상한이 `xl` 을 넘거나 상한이 없으면 `"100%"`. (`sellmate/field-width-grade` 가 검사한다) |
|
|
3184
4841
|
| `disabled?` | `boolean` | `false` | |
|
|
3185
4842
|
| `readOnly?` | `boolean` | `false` | |
|
|
3186
4843
|
| `className?` | `string` | — | |
|
|
@@ -3197,6 +4854,7 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
3197
4854
|
|
|
3198
4855
|
### Used by
|
|
3199
4856
|
|
|
4857
|
+
- [SEditor](../SEditor)
|
|
3200
4858
|
- [SKeyValueTable](../SKeyValueTable)
|
|
3201
4859
|
|
|
3202
4860
|
### Depends on
|
|
@@ -3222,6 +4880,7 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
3222
4880
|
| `values?` | `Record<string, unknown>` | `{}` | field name을 key로 하는 값 객체 (`{ [name]: value }`). 지정 시 해당 field의 값으로 사용되며, field별 `options.value`보다 우선합니다. `onChange`의 `detail.values`와 함께 controlled 패턴으로 사용합니다. |
|
|
3223
4881
|
| `search?` | `boolean` | `false` | 우측 검색 패널 |
|
|
3224
4882
|
| `radius?` | `'default' \| 'useTop' \| 'full'` | `'default'` | border-radius 제어 |
|
|
4883
|
+
| `bordered?` | `boolean` | `true` | 바깥 테두리. 기본은 `true`. `SSectionHeaderCard` 의 `padding="none"` 안에 넣어 카드 가장자리까지 채울 때 `false` 로 끈다 — 카드가 이미 바깥 테두리를 그리므로, 켜 두면 1px 두 개가 나란히 놓여 그 변만 2px 로 보인다. |
|
|
3225
4884
|
| `className?` | `string` | — | |
|
|
3226
4885
|
| `style?` | `CSSProperties` | — | |
|
|
3227
4886
|
|
|
@@ -3232,6 +4891,90 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
3232
4891
|
| `onChange` | `(detail: SKeyValueChangeDetail) => void` | 값 변경 (sdChange) |
|
|
3233
4892
|
| `onSearch` | `() => void` | 검색 클릭 (sdSearch) |
|
|
3234
4893
|
|
|
4894
|
+
## Types
|
|
4895
|
+
|
|
4896
|
+
### SKeyValueField
|
|
4897
|
+
|
|
4898
|
+
```ts
|
|
4899
|
+
export type SKeyValueField = SKeyValueFieldBase & SKeyValueFieldByType;
|
|
4900
|
+
```
|
|
4901
|
+
|
|
4902
|
+
### SKeyValueChangeDetail
|
|
4903
|
+
|
|
4904
|
+
```ts
|
|
4905
|
+
/**
|
|
4906
|
+
* `onChange` 콜백 detail.
|
|
4907
|
+
* 제네릭 `V`에 폼 값 형태(`{ [name]: value }`)를 넘기면 `name`/`value`/`values`가 정밀하게 타이핑됩니다.
|
|
4908
|
+
* 미지정 시 `value`는 {@link SKeyValueFieldValue} 유니온으로 잡힙니다.
|
|
4909
|
+
*/
|
|
4910
|
+
export interface SKeyValueChangeDetail<
|
|
4911
|
+
V extends Record<string, SKeyValueFieldValue> = Record<string, SKeyValueFieldValue>,
|
|
4912
|
+
> {
|
|
4913
|
+
/** 변경된 field의 name */
|
|
4914
|
+
name: Extract<keyof V, string>;
|
|
4915
|
+
/** 변경된 field의 새 값 */
|
|
4916
|
+
value: V[Extract<keyof V, string>];
|
|
4917
|
+
/** 변경이 반영된 전체 값 객체 (`{ [name]: value }`). controlled 패턴에서 그대로 상태에 반영하면 됩니다. */
|
|
4918
|
+
values: V;
|
|
4919
|
+
}
|
|
4920
|
+
```
|
|
4921
|
+
|
|
4922
|
+
### SKeyValueFieldBase
|
|
4923
|
+
|
|
4924
|
+
```ts
|
|
4925
|
+
/** field type과 무관한 공통 속성 (레이아웃·레이블·스팬 등) */
|
|
4926
|
+
export interface SKeyValueFieldBase {
|
|
4927
|
+
name: string;
|
|
4928
|
+
label?: string;
|
|
4929
|
+
required?: boolean;
|
|
4930
|
+
helpText?: string[];
|
|
4931
|
+
hideTh?: boolean;
|
|
4932
|
+
thRowSpan?: number;
|
|
4933
|
+
thColSpan?: number;
|
|
4934
|
+
tdRowSpan?: number;
|
|
4935
|
+
tdColSpan?: number;
|
|
4936
|
+
thWidth?: number | string;
|
|
4937
|
+
thClass?: string;
|
|
4938
|
+
tdClass?: string;
|
|
4939
|
+
/** 셀 커스텀 렌더 (React 확장). 지정 시 type/options는 무시되고 이 노드가 그대로 렌더됩니다. */
|
|
4940
|
+
render?: ReactNode;
|
|
4941
|
+
}
|
|
4942
|
+
```
|
|
4943
|
+
|
|
4944
|
+
### SKeyValueFieldByType
|
|
4945
|
+
|
|
4946
|
+
```ts
|
|
4947
|
+
/**
|
|
4948
|
+
* `type`에 따라 `options`가 해당 컴포넌트의 props로 좁혀지는 discriminated union.
|
|
4949
|
+
* `type` 생략 시 `text`로 동작합니다.
|
|
4950
|
+
*/
|
|
4951
|
+
export type SKeyValueFieldByType =
|
|
4952
|
+
| { type?: 'text'; options?: { value?: ReactNode } }
|
|
4953
|
+
| { type: 'input'; options?: FieldOptions<SInputProps> }
|
|
4954
|
+
| { type: 'textarea'; options?: FieldOptions<STextareaProps> }
|
|
4955
|
+
| { type: 'number-input'; options?: FieldOptions<SNumberInputProps> }
|
|
4956
|
+
| { type: 'select'; options?: FieldOptions<SSelectProps> }
|
|
4957
|
+
| { type: 'radio'; options?: FieldOptions<SRadioGroupProps> }
|
|
4958
|
+
| { type: 'checkbox'; options?: FieldOptions<SCheckboxProps> }
|
|
4959
|
+
| { type: 'switch'; options?: FieldOptions<SSwitchProps> }
|
|
4960
|
+
| { type: 'date-picker'; options?: FieldOptions<SDatePickerProps> }
|
|
4961
|
+
| { type: 'date-range-picker'; options?: FieldOptions<SDateRangePickerProps> }
|
|
4962
|
+
| { type: 'file-picker'; options?: FieldOptions<SFilePickerProps> };
|
|
4963
|
+
```
|
|
4964
|
+
|
|
4965
|
+
### SKeyValueFieldValue
|
|
4966
|
+
|
|
4967
|
+
```ts
|
|
4968
|
+
/**
|
|
4969
|
+
* field type별 `onChange`로 올라오는 값 타입.
|
|
4970
|
+
* - `string`: input · textarea · select · radio · date-picker
|
|
4971
|
+
* - `number`: number-input
|
|
4972
|
+
* - `boolean`: checkbox · switch
|
|
4973
|
+
* - `string[]`: date-range-picker
|
|
4974
|
+
*/
|
|
4975
|
+
export type SKeyValueFieldValue = string | number | boolean | string[];
|
|
4976
|
+
```
|
|
4977
|
+
|
|
3235
4978
|
## Dependencies
|
|
3236
4979
|
|
|
3237
4980
|
### Depends on
|
|
@@ -3246,6 +4989,7 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
3246
4989
|
- [SNumberInput](../SNumberInput)
|
|
3247
4990
|
- [SRadio](../SRadio)
|
|
3248
4991
|
- [SSelect](../SSelect)
|
|
4992
|
+
- [SSwitch](../SSwitch)
|
|
3249
4993
|
- [STextarea](../STextarea)
|
|
3250
4994
|
- [STooltip](../STooltip)
|
|
3251
4995
|
|
|
@@ -3275,6 +5019,22 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
3275
5019
|
|-------|------|-------------|
|
|
3276
5020
|
| `onFoldedChange` | `(folded: boolean) => void` | 접힘 상태 변경 |
|
|
3277
5021
|
|
|
5022
|
+
## Types
|
|
5023
|
+
|
|
5024
|
+
### SLayoutType
|
|
5025
|
+
|
|
5026
|
+
```ts
|
|
5027
|
+
/** 메뉴 스타일: box(라운드) / belt(풀폭 행) — 자식 SGnb 의 메뉴 모양 */
|
|
5028
|
+
export type SLayoutType = SGnbType;
|
|
5029
|
+
```
|
|
5030
|
+
|
|
5031
|
+
### SLayoutHeader
|
|
5032
|
+
|
|
5033
|
+
```ts
|
|
5034
|
+
/** 레이아웃 구조: fix(좌측 GNB + 페이지 가로 분할) / full(풀폭 상단바 + 아래에 메뉴|페이지) */
|
|
5035
|
+
export type SLayoutHeader = SGnbHeader;
|
|
5036
|
+
```
|
|
5037
|
+
|
|
3278
5038
|
## Dependencies
|
|
3279
5039
|
|
|
3280
5040
|
### Used by
|
|
@@ -3298,11 +5058,26 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
3298
5058
|
|------|------|---------|-------------|
|
|
3299
5059
|
| `value?` | `number` | `0` | 진행률 (0–100) |
|
|
3300
5060
|
| `type?` | `SLinearProgressType` | `'primary'` | 색상 타입 |
|
|
5061
|
+
| `size?` | `SLinearProgressSize` | `'sm'` | 바 높이. xs 는 바 안에 퍼센트 텍스트를 넣지 않는다 |
|
|
3301
5062
|
| `indeterminate?` | `boolean` | `false` | 진행률 없이 무한 애니메이션 |
|
|
3302
5063
|
| `label?` | `string` | — | 하단 레이블 |
|
|
3303
5064
|
| `className?` | `string` | — | |
|
|
3304
5065
|
| `style?` | `CSSProperties` | — | |
|
|
3305
5066
|
|
|
5067
|
+
## Types
|
|
5068
|
+
|
|
5069
|
+
### SLinearProgressType
|
|
5070
|
+
|
|
5071
|
+
```ts
|
|
5072
|
+
export type SLinearProgressType = 'primary' | 'error' | 'complete';
|
|
5073
|
+
```
|
|
5074
|
+
|
|
5075
|
+
### SLinearProgressSize
|
|
5076
|
+
|
|
5077
|
+
```ts
|
|
5078
|
+
export type SLinearProgressSize = 'xs' | 'sm' | 'md';
|
|
5079
|
+
```
|
|
5080
|
+
|
|
3306
5081
|
---
|
|
3307
5082
|
|
|
3308
5083
|
# SList
|
|
@@ -3318,7 +5093,6 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
3318
5093
|
| `children?` | `ReactNode` | — | 리스트 컨테이너 내부에 렌더링할 내용 |
|
|
3319
5094
|
| `useGap?` | `boolean` | `false` | 리스트 아이템 사이 gap 토큰 적용 여부 |
|
|
3320
5095
|
| `usePadding?` | `boolean` | `false` | 리스트 컨테이너 padding 토큰 적용 여부 |
|
|
3321
|
-
| `separator?` | `boolean` | `false` | 아이템 하단 구분선 표시 여부. 테두리를 가진 아이템(`bordered`)에는 쓰지 않는다 |
|
|
3322
5096
|
|
|
3323
5097
|
## Dependencies
|
|
3324
5098
|
|
|
@@ -3327,6 +5101,10 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
3327
5101
|
- [SDraggableList](../SDraggableList)
|
|
3328
5102
|
- [SExpansionList](../SExpansionList)
|
|
3329
5103
|
|
|
5104
|
+
### Depends on
|
|
5105
|
+
|
|
5106
|
+
- [SDivider](../SDivider)
|
|
5107
|
+
|
|
3330
5108
|
### Graph
|
|
3331
5109
|
|
|
3332
5110
|
---
|
|
@@ -3356,6 +5134,47 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
3356
5134
|
| `size?` | `SListItemSize` | `'sm'` | 타이포그래피 크기 |
|
|
3357
5135
|
| `disabled?` | `boolean` | `false` | 비활성 상태 여부 |
|
|
3358
5136
|
|
|
5137
|
+
## Types
|
|
5138
|
+
|
|
5139
|
+
### SListItemSlot
|
|
5140
|
+
|
|
5141
|
+
```ts
|
|
5142
|
+
export type SListItemSlot = ReactNode | SListItemRenderProp;
|
|
5143
|
+
```
|
|
5144
|
+
|
|
5145
|
+
### SListItemSupportingTextPosition
|
|
5146
|
+
|
|
5147
|
+
```ts
|
|
5148
|
+
export type SListItemSupportingTextPosition = 'right' | 'bottom';
|
|
5149
|
+
```
|
|
5150
|
+
|
|
5151
|
+
### SListItemInteraction
|
|
5152
|
+
|
|
5153
|
+
```ts
|
|
5154
|
+
export type SListItemInteraction = 'chevron';
|
|
5155
|
+
```
|
|
5156
|
+
|
|
5157
|
+
### SListItemSize
|
|
5158
|
+
|
|
5159
|
+
```ts
|
|
5160
|
+
export type SListItemSize = 'sm' | 'md';
|
|
5161
|
+
```
|
|
5162
|
+
|
|
5163
|
+
### SListItemRenderProp
|
|
5164
|
+
|
|
5165
|
+
```ts
|
|
5166
|
+
export type SListItemRenderProp = (state: SListItemRenderState) => ReactNode;
|
|
5167
|
+
```
|
|
5168
|
+
|
|
5169
|
+
### SListItemRenderState
|
|
5170
|
+
|
|
5171
|
+
```ts
|
|
5172
|
+
export interface SListItemRenderState {
|
|
5173
|
+
hovered: boolean;
|
|
5174
|
+
disabled: boolean;
|
|
5175
|
+
}
|
|
5176
|
+
```
|
|
5177
|
+
|
|
3359
5178
|
## Dependencies
|
|
3360
5179
|
|
|
3361
5180
|
### Used by
|
|
@@ -3408,7 +5227,7 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
3408
5227
|
|------|------|---------|-------------|
|
|
3409
5228
|
| `open?` | `boolean` | — | |
|
|
3410
5229
|
| `persistent?` | `boolean` | — | 백드롭·ESC로 안 닫히고 흔들림 (sd-modal-container persistent). **기본값 true** |
|
|
3411
|
-
| `state?` | `
|
|
5230
|
+
| `state?` | `SLoadingModalState` | `'loading'` | 상태 (loading: 스피너, error: 경고 아이콘) |
|
|
3412
5231
|
| `progress?` | `number` | — | 진행률 (0–100) — 지정 시 determinate 스피너 |
|
|
3413
5232
|
| `message?` | `string \| string[]` | — | 메시지 (없으면 state 기본값) |
|
|
3414
5233
|
| `useButton?` | `boolean` | `false` | 하단 버튼 표시 |
|
|
@@ -3424,6 +5243,14 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
3424
5243
|
| `onClose` | `() => void` | 닫기(X) 버튼 클릭 (sdClose) — error 상태에서만 노출 |
|
|
3425
5244
|
| `onButtonClick` | `() => void` | 버튼 클릭 (sdClick) |
|
|
3426
5245
|
|
|
5246
|
+
## Types
|
|
5247
|
+
|
|
5248
|
+
### SLoadingModalState
|
|
5249
|
+
|
|
5250
|
+
```ts
|
|
5251
|
+
export type SLoadingModalState = 'loading' | 'error';
|
|
5252
|
+
```
|
|
5253
|
+
|
|
3427
5254
|
## Dependencies
|
|
3428
5255
|
|
|
3429
5256
|
### Used by
|
|
@@ -3756,7 +5583,7 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
3756
5583
|
| `hint?` | `string` | — | |
|
|
3757
5584
|
| `error?` | `boolean` | — | |
|
|
3758
5585
|
| `errorMessage?` | `string` | — | |
|
|
3759
|
-
| `width?` | `
|
|
5586
|
+
| `width?` | `SFieldWidth` | — | 컨트롤 너비 — 폭 등급(`'xs' | 'sm' | 'md' | 'lg' | 'xl'`) · 숫자=px · CSS 길이. 등급은 `--cmp-field-width-*` 토큰으로 풀리며 `max`(= 스키마 상한)로 고른다. 상한이 `xl` 을 넘거나 상한이 없으면 `"100%"`. (`sellmate/field-width-grade` 가 검사한다) |
|
|
3760
5587
|
| `className?` | `string` | — | |
|
|
3761
5588
|
| `style?` | `CSSProperties` | — | |
|
|
3762
5589
|
|
|
@@ -3768,6 +5595,14 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
3768
5595
|
| `onFocus` | `(e: React.FocusEvent<HTMLInputElement>) => void` | 포커스 이벤트 (sdFocus) |
|
|
3769
5596
|
| `onBlur` | `(e: React.FocusEvent<HTMLInputElement>) => void` | 블러 이벤트 (sdBlur) |
|
|
3770
5597
|
|
|
5598
|
+
## Types
|
|
5599
|
+
|
|
5600
|
+
### SNumberInputSize
|
|
5601
|
+
|
|
5602
|
+
```ts
|
|
5603
|
+
export type SNumberInputSize = SFieldSize;
|
|
5604
|
+
```
|
|
5605
|
+
|
|
3771
5606
|
## Dependencies
|
|
3772
5607
|
|
|
3773
5608
|
### Used by
|
|
@@ -3852,9 +5687,49 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
3852
5687
|
| Prop | Type | Default | Description |
|
|
3853
5688
|
|------|------|---------|-------------|
|
|
3854
5689
|
| `background?` | `SPageBackground` | `'frame'` | 페이지 배경 표면. frame=흰 콘텐츠 면, neutral=옅은 회색 면, screen=앱 바탕. 스크롤바 처리도 여기 묶여 있다 — 셋 다 구분선+트랙 배경이고, 트랙 색만 neutral 에서 흰색이 된다. |
|
|
3855
|
-
| `scrollEndSpacing?` | `boolean` | `
|
|
5690
|
+
| `scrollEndSpacing?` | `boolean` | `false` | 스크롤 끝 여백. 마지막 항목이 창 하단에 붙어 "여기서 끝"이 안 읽히는 것을 막는다. **기본은 꺼져 있다.** 페이지 스크롤 자체가 예외이기 때문이다 — 대부분의 화면은 본문이 창을 꽉 채우고 스크롤은 각 영역 안에서 일어난다. 여백을 조건 없이 붙이면 내용이 화면에 거의 딱 맞는 페이지까지 그 여백 때문에 스크롤되게 만든다. 페이지가 실제로 스크롤되는 화면(`contentHeight="auto"` + 내용이 창보다 김)에서만 켠다. 페이지네이션처럼 끝을 알려주는 것이 이미 있으면 켜지 않는다. |
|
|
5691
|
+
| `overlayScrollbar?` | `boolean` | `false` | true면 네이티브 스크롤바를 숨기고 SPage 위에 오버레이 스크롤바를 얹는다. 스크롤바가 레이아웃 폭을 차지하지 않아 내부 콘텐츠 폭이 줄어들지 않는다. |
|
|
5692
|
+
| `contentHeight?` | `SPageContentHeight` | `'auto'` | 본문 높이 모드. 기본은 `auto` — 콘텐츠가 흐르고 넘치면 페이지가 스크롤한다. `fill` 은 본문이 남은 높이를 정확히 채우고 페이지는 스크롤하지 않는다. 표가 자기 안에서 스크롤하고 페이지네이션이 하단에 고정되는 목록 화면용이다. 본문 루트에 `h-full` 이 먹으므로 `<div className="flex h-full min-h-0 flex-col">` + `<STable className="min-h-0 flex-1" />` 구성이 성립한다. `fill` 에서는 페이지가 스크롤하지 않으므로 `scrollEndSpacing` 이 무시된다. |
|
|
3856
5693
|
| `header?` | `SPageHeaderProps` | — | 페이지 타이틀 영역(pageHeader 포팅). 주면 스크롤·페이지 패딩 밖으로 빼내 상단에 고정 배치한다. `children` 은 항상 본문이다 — header 유무와 무관하게 같은 자리에 같은 뜻으로 들어간다. |
|
|
3857
5694
|
|
|
5695
|
+
## Types
|
|
5696
|
+
|
|
5697
|
+
### SPageBackground
|
|
5698
|
+
|
|
5699
|
+
```ts
|
|
5700
|
+
export type SPageBackground = (typeof PAGE_BACKGROUNDS)[number];
|
|
5701
|
+
```
|
|
5702
|
+
|
|
5703
|
+
### SPageContentHeight
|
|
5704
|
+
|
|
5705
|
+
```ts
|
|
5706
|
+
export type SPageContentHeight = (typeof PAGE_CONTENT_HEIGHTS)[number];
|
|
5707
|
+
```
|
|
5708
|
+
|
|
5709
|
+
### PAGE_BACKGROUNDS
|
|
5710
|
+
|
|
5711
|
+
```ts
|
|
5712
|
+
/**
|
|
5713
|
+
* 페이지 배경 — 표면의 역할로 이름을 붙인다(리터럴 색이 아니다).
|
|
5714
|
+
* - frame: 콘텐츠를 얹는 흰 표면 (sys.color.bg.frame)
|
|
5715
|
+
* - neutral: 한 단계 눌러앉은 회색 표면 (sys.color.bg.neutralLight)
|
|
5716
|
+
* - screen: 카드·패널이 떠 있는 앱 바탕 (sys.color.bg.screen)
|
|
5717
|
+
*/
|
|
5718
|
+
export const PAGE_BACKGROUNDS = ['frame', 'neutral', 'screen'] as const;
|
|
5719
|
+
```
|
|
5720
|
+
|
|
5721
|
+
### PAGE_CONTENT_HEIGHTS
|
|
5722
|
+
|
|
5723
|
+
```ts
|
|
5724
|
+
/**
|
|
5725
|
+
* 본문 높이 모드 — 페이지가 스크롤할지, 본문이 남은 높이를 채울지.
|
|
5726
|
+
* - auto: 콘텐츠가 흐르고, 넘치면 페이지가 스크롤한다
|
|
5727
|
+
* - fill: 본문이 남은 높이를 정확히 채우고 페이지는 스크롤하지 않는다.
|
|
5728
|
+
* 표가 자기 안에서 스크롤하고 페이지네이션이 하단에 고정되는 목록 화면용.
|
|
5729
|
+
*/
|
|
5730
|
+
export const PAGE_CONTENT_HEIGHTS = ['auto', 'fill'] as const;
|
|
5731
|
+
```
|
|
5732
|
+
|
|
3858
5733
|
## Dependencies
|
|
3859
5734
|
|
|
3860
5735
|
### Depends on
|
|
@@ -3937,6 +5812,21 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
3937
5812
|
| `onLeftLinkClick` | `() => void` | 하단 좌측 링크 클릭 |
|
|
3938
5813
|
| `onButtonClick` | `() => void` | 하단 우측 버튼 클릭 |
|
|
3939
5814
|
|
|
5815
|
+
## Types
|
|
5816
|
+
|
|
5817
|
+
### SPopoverPlacement
|
|
5818
|
+
|
|
5819
|
+
```ts
|
|
5820
|
+
export type SPopoverPlacement = 'top' | 'bottom' | 'left' | 'right';
|
|
5821
|
+
```
|
|
5822
|
+
|
|
5823
|
+
### SPopoverType
|
|
5824
|
+
|
|
5825
|
+
```ts
|
|
5826
|
+
/** 색상 타입 — default: 다크 배경 / danger·warning·accent: 라이트 배경 (component.popover 토큰) */
|
|
5827
|
+
export type SPopoverType = 'default' | 'danger' | 'warning' | 'accent';
|
|
5828
|
+
```
|
|
5829
|
+
|
|
3940
5830
|
## Dependencies
|
|
3941
5831
|
|
|
3942
5832
|
### Depends on
|
|
@@ -3976,6 +5866,25 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
3976
5866
|
|-------|------|-------------|
|
|
3977
5867
|
| `onSubmit` | `() => void` | 확인 버튼 클릭 (sdSubmit) |
|
|
3978
5868
|
|
|
5869
|
+
## Types
|
|
5870
|
+
|
|
5871
|
+
### SPopupType
|
|
5872
|
+
|
|
5873
|
+
```ts
|
|
5874
|
+
export type SPopupType = 'default' | 'light';
|
|
5875
|
+
```
|
|
5876
|
+
|
|
5877
|
+
### SPopupSubmitButton
|
|
5878
|
+
|
|
5879
|
+
```ts
|
|
5880
|
+
export interface SPopupSubmitButton {
|
|
5881
|
+
label?: string;
|
|
5882
|
+
color?: SButtonColor;
|
|
5883
|
+
outline?: boolean;
|
|
5884
|
+
size?: SButtonSize;
|
|
5885
|
+
}
|
|
5886
|
+
```
|
|
5887
|
+
|
|
3979
5888
|
## Dependencies
|
|
3980
5889
|
|
|
3981
5890
|
### Depends on
|
|
@@ -4022,6 +5931,20 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
4022
5931
|
| `onCloseAutoFocus` | `(event: Event) => void` | 닫힐 때 포커스 복귀 처리 (기본은 Radix가 앵커로 포커스를 되돌림). e.preventDefault()로 막을 수 있다. |
|
|
4023
5932
|
| `onPointerDownOutside` | `(event: Event) => void` | 바깥 영역 pointerdown 으로 닫힘이 시작될 때 (앵커 위 클릭·persistent 는 제외). |
|
|
4024
5933
|
|
|
5934
|
+
## Types
|
|
5935
|
+
|
|
5936
|
+
### SPortalPlacement
|
|
5937
|
+
|
|
5938
|
+
```ts
|
|
5939
|
+
export type SPortalPlacement = 'top' | 'bottom' | 'left' | 'right';
|
|
5940
|
+
```
|
|
5941
|
+
|
|
5942
|
+
### SPortalAlign
|
|
5943
|
+
|
|
5944
|
+
```ts
|
|
5945
|
+
export type SPortalAlign = 'start' | 'center' | 'end';
|
|
5946
|
+
```
|
|
5947
|
+
|
|
4025
5948
|
## Dependencies
|
|
4026
5949
|
|
|
4027
5950
|
### Used by
|
|
@@ -4077,6 +6000,26 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
4077
6000
|
|-------|------|-------------|
|
|
4078
6001
|
| `onValueChange` | `(val: SRadioValue) => void` | 선택 변경 (sdUpdate) — 선택된 val 전달 |
|
|
4079
6002
|
|
|
6003
|
+
## Types
|
|
6004
|
+
|
|
6005
|
+
### SRadioOption
|
|
6006
|
+
|
|
6007
|
+
```ts
|
|
6008
|
+
export interface SRadioOption {
|
|
6009
|
+
/** 옵션 값 (sd-radio-group SRadioOption.value) */
|
|
6010
|
+
value: SRadioValue;
|
|
6011
|
+
label: string;
|
|
6012
|
+
disabled?: boolean;
|
|
6013
|
+
}
|
|
6014
|
+
```
|
|
6015
|
+
|
|
6016
|
+
### SRadioValue
|
|
6017
|
+
|
|
6018
|
+
```ts
|
|
6019
|
+
/** 라디오 값 타입 (sd-radio SRadioValue) */
|
|
6020
|
+
export type SRadioValue = string | number | boolean;
|
|
6021
|
+
```
|
|
6022
|
+
|
|
4080
6023
|
## Dependencies
|
|
4081
6024
|
|
|
4082
6025
|
### Used by
|
|
@@ -4112,6 +6055,24 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
4112
6055
|
|-------|------|-------------|
|
|
4113
6056
|
| `onValueChange` | `(value: string \| number) => void` | 변경 (sdUpdate) |
|
|
4114
6057
|
|
|
6058
|
+
## Types
|
|
6059
|
+
|
|
6060
|
+
### SRadioButtonOption
|
|
6061
|
+
|
|
6062
|
+
```ts
|
|
6063
|
+
export interface SRadioButtonOption {
|
|
6064
|
+
value: string | number;
|
|
6065
|
+
label: string;
|
|
6066
|
+
disabled?: boolean;
|
|
6067
|
+
}
|
|
6068
|
+
```
|
|
6069
|
+
|
|
6070
|
+
### SRadioButtonSize
|
|
6071
|
+
|
|
6072
|
+
```ts
|
|
6073
|
+
export type SRadioButtonSize = 'xs' | 'sm' | 'md';
|
|
6074
|
+
```
|
|
6075
|
+
|
|
4115
6076
|
## Dependencies
|
|
4116
6077
|
|
|
4117
6078
|
### Used by
|
|
@@ -4122,6 +6083,81 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
4122
6083
|
|
|
4123
6084
|
---
|
|
4124
6085
|
|
|
6086
|
+
# SRadioCard
|
|
6087
|
+
|
|
6088
|
+
> 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
|
|
6089
|
+
|
|
6090
|
+
### SRadioCardGroup
|
|
6091
|
+
|
|
6092
|
+
#### Props
|
|
6093
|
+
|
|
6094
|
+
| Prop | Type | Default | Description |
|
|
6095
|
+
|------|------|---------|-------------|
|
|
6096
|
+
| `options?` | `SRadioCardOption[]` | `[]` | 옵션 목록 |
|
|
6097
|
+
| `value?` | `SRadioValue \| null` | — | 현재 선택 값 |
|
|
6098
|
+
| `direction?` | `'vertical' \| 'horizontal'` | `'vertical'` | 배치 방향 |
|
|
6099
|
+
| `disabled?` | `boolean` | `false` | 전체 비활성 |
|
|
6100
|
+
| `groupName?` | `string` | — | 라디오 name 속성 (form-agnostic 그룹핑) |
|
|
6101
|
+
| `className?` | `string` | — | |
|
|
6102
|
+
| `style?` | `CSSProperties` | — | |
|
|
6103
|
+
|
|
6104
|
+
#### Events
|
|
6105
|
+
|
|
6106
|
+
| Event | Type | Description |
|
|
6107
|
+
|-------|------|-------------|
|
|
6108
|
+
| `onValueChange` | `(val: SRadioValue) => void` | 선택 변경 (sdUpdate) |
|
|
6109
|
+
|
|
6110
|
+
### SRadioCard
|
|
6111
|
+
|
|
6112
|
+
#### Props
|
|
6113
|
+
|
|
6114
|
+
| Prop | Type | Default | Description |
|
|
6115
|
+
|------|------|---------|-------------|
|
|
6116
|
+
| `val` | `SRadioValue` | — | 이 카드의 값 |
|
|
6117
|
+
| `value?` | `SRadioValue \| null` | — | 그룹의 현재 선택 값 |
|
|
6118
|
+
| `label` | `string` | — | 라벨 (첫 줄) |
|
|
6119
|
+
| `subtitle?` | `string` | — | 서브타이틀 (둘째 줄). 라벨 왼쪽 끝에 맞춰 들여쓴다 |
|
|
6120
|
+
| `tag?` | `Omit<STagProps, 'size'>` | — | 라벨 오른쪽에 붙일 태그. `STag` 의 props 를 그대로 받되 크기는 `xs` 로 고정된다 |
|
|
6121
|
+
| `disabled?` | `boolean` | `false` | 비활성 |
|
|
6122
|
+
| `name?` | `string` | — | native radio name (form-agnostic 그룹핑) |
|
|
6123
|
+
| `className?` | `string` | — | |
|
|
6124
|
+
| `style?` | `CSSProperties` | — | |
|
|
6125
|
+
|
|
6126
|
+
#### Events
|
|
6127
|
+
|
|
6128
|
+
| Event | Type | Description |
|
|
6129
|
+
|-------|------|-------------|
|
|
6130
|
+
| `onValueChange` | `(val: SRadioValue) => void` | 선택 변경 (sdUpdate) — 선택된 val 전달 |
|
|
6131
|
+
|
|
6132
|
+
## Types
|
|
6133
|
+
|
|
6134
|
+
### SRadioCardOption
|
|
6135
|
+
|
|
6136
|
+
```ts
|
|
6137
|
+
export interface SRadioCardOption {
|
|
6138
|
+
/** 옵션 값 */
|
|
6139
|
+
value: SRadioValue;
|
|
6140
|
+
/** 라벨 (첫 줄) */
|
|
6141
|
+
label: string;
|
|
6142
|
+
/** 서브타이틀 (둘째 줄) */
|
|
6143
|
+
subtitle?: string;
|
|
6144
|
+
/** 라벨 오른쪽에 붙일 태그 (`STag` props, 크기는 `xs` 고정) */
|
|
6145
|
+
tag?: Omit<STagProps, 'size'>;
|
|
6146
|
+
/** 이 옵션만 비활성 */
|
|
6147
|
+
disabled?: boolean;
|
|
6148
|
+
}
|
|
6149
|
+
```
|
|
6150
|
+
|
|
6151
|
+
## Dependencies
|
|
6152
|
+
|
|
6153
|
+
### Depends on
|
|
6154
|
+
|
|
6155
|
+
- [STag](../STag)
|
|
6156
|
+
|
|
6157
|
+
### Graph
|
|
6158
|
+
|
|
6159
|
+
---
|
|
6160
|
+
|
|
4125
6161
|
# SScrollArea
|
|
4126
6162
|
|
|
4127
6163
|
> 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
|
|
@@ -4138,49 +6174,142 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
4138
6174
|
| `maxHeight?` | `string` | — | viewport 최대 높이 (예: '400px'). 비우면 부모 크기를 따른다. |
|
|
4139
6175
|
| `maxWidth?` | `string` | — | viewport 최대 너비 (예: '480px'). 비우면 부모 크기를 따른다. |
|
|
4140
6176
|
|
|
6177
|
+
## Types
|
|
6178
|
+
|
|
6179
|
+
### SScrollAreaAxis
|
|
6180
|
+
|
|
6181
|
+
```ts
|
|
6182
|
+
export type SScrollAreaAxis = (typeof SCROLL_AREA_AXES)[number];
|
|
6183
|
+
```
|
|
6184
|
+
|
|
6185
|
+
### SCROLL_AREA_AXES
|
|
6186
|
+
|
|
6187
|
+
```ts
|
|
6188
|
+
export const SCROLL_AREA_AXES = ['vertical', 'horizontal', 'both'] as const;
|
|
6189
|
+
```
|
|
6190
|
+
|
|
4141
6191
|
---
|
|
4142
6192
|
|
|
4143
|
-
#
|
|
6193
|
+
# SSearchInput
|
|
4144
6194
|
|
|
4145
6195
|
> 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
|
|
4146
6196
|
|
|
4147
|
-
###
|
|
6197
|
+
### SSearchInput
|
|
4148
6198
|
|
|
4149
6199
|
#### Props
|
|
4150
6200
|
|
|
4151
6201
|
| Prop | Type | Default | Description |
|
|
4152
6202
|
|------|------|---------|-------------|
|
|
4153
|
-
| `
|
|
4154
|
-
| `
|
|
6203
|
+
| `value?` | `string \| number` | — | 값 (제어) |
|
|
6204
|
+
| `size?` | `SSearchInputSize` | `'sm'` | 크기 |
|
|
6205
|
+
| `placeholder?` | `string` | `'결과 내 검색'` | 플레이스홀더 |
|
|
6206
|
+
| `clearable?` | `boolean` | `false` | 지우기 버튼 — 값이 있을 때만 나타난다 |
|
|
6207
|
+
| `disabled?` | `boolean` | `false` | 비활성 |
|
|
6208
|
+
| `width?` | `SFieldWidth` | — | 컨트롤 너비 — 폭 등급(`'xs' | 'sm' | 'md' | 'lg' | 'xl'`) · 숫자=px · CSS 길이. 등급은 `--cmp-field-width-*` 토큰으로 풀리므로 토큰이 바뀌면 화면이 따라간다. 값 길이에 상한이 없으면 `"100%"` 로 두어 행 전체를 쓴다. 미지정 시 부모 폭을 채운다. |
|
|
6209
|
+
| `focused?` | `boolean` | — | 포커스 상태 (제어/반영) |
|
|
6210
|
+
| `hovered?` | `boolean` | — | 호버 상태 (제어/반영) |
|
|
6211
|
+
| `inputClass?` | `string` | — | 내부 input 요소 className |
|
|
6212
|
+
| `inputStyle?` | `CSSProperties` | — | 내부 input 요소 style |
|
|
6213
|
+
| `className?` | `string` | — | |
|
|
6214
|
+
| `style?` | `CSSProperties` | — | |
|
|
6215
|
+
|
|
6216
|
+
#### Events
|
|
6217
|
+
|
|
6218
|
+
| Event | Type | Description |
|
|
6219
|
+
|-------|------|-------------|
|
|
6220
|
+
| `onValueChange` | `(value: string) => void` | 값 변경 — 문자열 전달 |
|
|
6221
|
+
| `onChange` | `InputHTMLAttributes<HTMLInputElement>['onChange']` | 네이티브 onChange (form-agnostic 연동용, RHF 등) |
|
|
6222
|
+
| `onSearch` | `(value: string) => void` | 검색 실행 — Enter 키에서 현재 값과 함께 호출된다. 한글 조합 중의 Enter(IME 확정)는 검색으로 치지 않는다. |
|
|
6223
|
+
|
|
6224
|
+
## Types
|
|
6225
|
+
|
|
6226
|
+
### SSearchInputSize
|
|
6227
|
+
|
|
6228
|
+
```ts
|
|
6229
|
+
export type SSearchInputSize = SFieldSize;
|
|
6230
|
+
```
|
|
6231
|
+
|
|
6232
|
+
## Dependencies
|
|
6233
|
+
|
|
6234
|
+
### Used by
|
|
6235
|
+
|
|
6236
|
+
- [SChipFilter](../SChipFilter)
|
|
6237
|
+
- [SSelect](../SSelect)
|
|
6238
|
+
|
|
6239
|
+
### Depends on
|
|
6240
|
+
|
|
6241
|
+
- [SField](../SField)
|
|
6242
|
+
- [SGhostButton](../SGhostButton)
|
|
6243
|
+
- [SIcon](../SIcon)
|
|
6244
|
+
|
|
6245
|
+
### Graph
|
|
6246
|
+
|
|
6247
|
+
---
|
|
6248
|
+
|
|
6249
|
+
# SSectionHeaderCard
|
|
4155
6250
|
|
|
4156
|
-
|
|
6251
|
+
> 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
|
|
6252
|
+
|
|
6253
|
+
### SSectionHeaderCard
|
|
4157
6254
|
|
|
4158
6255
|
#### Props
|
|
4159
6256
|
|
|
4160
6257
|
| Prop | Type | Default | Description |
|
|
4161
6258
|
|------|------|---------|-------------|
|
|
4162
|
-
| `title
|
|
4163
|
-
| `titleSize?` | `SSectionHeaderCardTitleSize` |
|
|
4164
|
-
| `marker?` | `boolean` |
|
|
4165
|
-
| `required?` | `boolean` |
|
|
6259
|
+
| `title?` | `ReactNode` | — | 헤더 제목 |
|
|
6260
|
+
| `titleSize?` | `SSectionHeaderCardTitleSize` | — | 제목 크기 |
|
|
6261
|
+
| `marker?` | `boolean` | — | 제목 앞 점 표시 여부 |
|
|
6262
|
+
| `required?` | `boolean` | — | 제목 뒤 필수 별 표시 여부 |
|
|
4166
6263
|
| `helpText?` | `string[]` | — | 도움말 툴팁 메시지 |
|
|
4167
6264
|
| `subtitle?` | `ReactNode` | — | 부제 |
|
|
4168
6265
|
| `slot?` | `ReactNode` | — | 헤더 우측 슬롯 |
|
|
4169
|
-
| `thickness?` | `SSectionHeaderCardThickness` |
|
|
6266
|
+
| `thickness?` | `SSectionHeaderCardThickness` | — | 상단 border 색상 타입. false면 표시하지 않습니다. |
|
|
6267
|
+
| `headerClassName?` | `string` | — | 헤더 영역 클래스 |
|
|
6268
|
+
| `padding?` | `SSectionHeaderCardBodyPadding` | — | 바디 안쪽 여백. 기본은 `default`. 성격이 다른 요소가 세 종류 이상 섞인 영역에만 `wide`, 표를 가장자리까지 채울 때만 `none`. |
|
|
6269
|
+
| `background?` | `SSectionHeaderCardBodyBackground` | — | 본문 배경. 기본은 `frame` — 카드가 깐 흰 면을 그대로 쓴다. `neutral` 은 바탕을 한 단계 눌러앉혀, 흰 면 덩어리(표·리스트)가 여럿일 때 그것들이 **"면 위에 놓인 객체"로 읽히게** 한다. 위계를 한 단계 더 주고 싶을 때 고르는 선택지이며, **기본값이 틀린 것은 아니다** — 표·리스트는 테두리·라운드·헤더 줄과 간격을 이미 갖고 있어 흰 바탕에서도 경계가 읽힌다. 깔아도 **효과가 없는** 자리가 있다 — 덩어리가 가장자리까지 차는 경우(`padding="none"` 으로 표를 채우면 바탕이 완전히 가려진다), 덩어리에 회색 면이 섞인 경우(그 덩어리가 바탕에 묻힌다), 맨 텍스트나 폼 컨트롤만 있는 본문(떠오를 흰 면이 없다). |
|
|
6270
|
+
| `footerLeft?` | `ReactNode` | — | 하단 액션 영역 좌측 슬롯. 보조 버튼(취소·목록 등)이 오는 자리다. `footerLeft` 또는 `button` 중 하나라도 있으면 하단 액션 영역이 렌더된다. |
|
|
6271
|
+
| `button?` | `SFooterButton` | — | 하단 액션 영역 우측 주 액션 버튼 |
|
|
6272
|
+
| `footerClassName?` | `string` | — | 하단 액션 영역 클래스 |
|
|
6273
|
+
| `children?` | `SSectionHeaderCardChildren` | — | 바디 콘텐츠. 특정 컴포넌트 타입으로 제한하지 않습니다. |
|
|
4170
6274
|
|
|
4171
|
-
|
|
6275
|
+
## Types
|
|
4172
6276
|
|
|
4173
|
-
|
|
6277
|
+
### SSectionHeaderCardTitleSize
|
|
4174
6278
|
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
6279
|
+
```ts
|
|
6280
|
+
export type SSectionHeaderCardTitleSize = 'xs' | 'sm';
|
|
6281
|
+
```
|
|
6282
|
+
|
|
6283
|
+
### SSectionHeaderCardThickness
|
|
6284
|
+
|
|
6285
|
+
```ts
|
|
6286
|
+
export type SSectionHeaderCardThickness = false | 'default' | 'accent';
|
|
6287
|
+
```
|
|
6288
|
+
|
|
6289
|
+
### SSectionHeaderCardBodyPadding
|
|
6290
|
+
|
|
6291
|
+
```ts
|
|
6292
|
+
export type SSectionHeaderCardBodyPadding = 'default' | 'wide' | 'none';
|
|
6293
|
+
```
|
|
6294
|
+
|
|
6295
|
+
### SSectionHeaderCardBodyBackground
|
|
6296
|
+
|
|
6297
|
+
```ts
|
|
6298
|
+
export type SSectionHeaderCardBodyBackground = 'frame' | 'neutral';
|
|
6299
|
+
```
|
|
6300
|
+
|
|
6301
|
+
### SSectionHeaderCardChildren
|
|
6302
|
+
|
|
6303
|
+
```ts
|
|
6304
|
+
export type SSectionHeaderCardChildren = ReactNode;
|
|
6305
|
+
```
|
|
4178
6306
|
|
|
4179
6307
|
## Dependencies
|
|
4180
6308
|
|
|
4181
6309
|
### Depends on
|
|
4182
6310
|
|
|
4183
6311
|
- [SBadge](../SBadge)
|
|
6312
|
+
- [SFooter](../SFooter)
|
|
4184
6313
|
- [SIcon](../SIcon)
|
|
4185
6314
|
- [STooltip](../STooltip)
|
|
4186
6315
|
|
|
@@ -4208,10 +6337,11 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
4208
6337
|
| `allSelectedLabel?` | `string` | `'전체'` | 전체선택 레이블 |
|
|
4209
6338
|
| `placeholder?` | `string` | `'선택'` | placeholder |
|
|
4210
6339
|
| `disabled?` | `boolean` | `false` | 비활성 |
|
|
6340
|
+
| `clearable?` | `boolean` | `false` | 선택값 지우기(×) 버튼 노출. 선택값이 있고 비활성이 아닐 때만 나타나며, 누르면 드롭다운을 열지 않고 값만 비운다. |
|
|
4211
6341
|
| `error?` | `boolean` | `false` | 에러 상태 |
|
|
4212
6342
|
| `rules?` | `Rule[]` | — | 유효성 규칙 — 닫힐 때 자동 검증 |
|
|
4213
6343
|
| `labelTooltipProps?` | `Partial<STooltipProps>` | — | 레이블 툴팁 상세 옵션 |
|
|
4214
|
-
| `dropdownHeight?` | `string` | `'
|
|
6344
|
+
| `dropdownHeight?` | `string` | `'640px'` | 드롭다운 최대 높이. 기본 640px — 창이 이보다 작으면 창 안에 남는 만큼까지만 채워진다 |
|
|
4215
6345
|
| `dropdownWidth?` | `string` | — | 드롭다운 너비 (없으면 트리거 너비). 창보다 넓게 줘도 창 폭까지만 벌어진다. |
|
|
4216
6346
|
| `maxDropdownWidth?` | `string` | `'640px'` | 드롭다운 최대 너비. 창이 이보다 좁으면 창 폭이 상한이 된다. |
|
|
4217
6347
|
| `label?` | `string` | — | |
|
|
@@ -4223,7 +6353,7 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
4223
6353
|
| `addonAlign?` | `SFieldAddonAlign` | — | 어드온 정렬 |
|
|
4224
6354
|
| `hint?` | `string` | — | |
|
|
4225
6355
|
| `errorMessage?` | `string` | — | |
|
|
4226
|
-
| `width?` | `
|
|
6356
|
+
| `width?` | `SFieldWidth` | — | 컨트롤 너비 — 폭 등급(`'xs' | 'sm' | 'md' | 'lg' | 'xl'`) · 숫자=px · CSS 길이. 등급은 `--cmp-field-width-*` 토큰으로 풀리므로 토큰이 바뀌면 화면이 따라간다. 값 길이에 상한이 없으면 `"100%"` 로 두어 행 전체를 쓴다. 셀렉트는 `maxLength` 개념이 없어 목록 최장값을 담는 등급으로 고른다. |
|
|
4227
6357
|
| `className?` | `string` | — | |
|
|
4228
6358
|
| `style?` | `CSSProperties` | — | |
|
|
4229
6359
|
|
|
@@ -4241,6 +6371,25 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
4241
6371
|
| `focus` | `() => void` | 트리거 버튼에 포커스 (sdFocus) |
|
|
4242
6372
|
| `open` | `() => void` | 드롭다운 열기 (sdOpen) |
|
|
4243
6373
|
|
|
6374
|
+
## Types
|
|
6375
|
+
|
|
6376
|
+
### SSelectOption
|
|
6377
|
+
|
|
6378
|
+
```ts
|
|
6379
|
+
export interface SSelectOption {
|
|
6380
|
+
value: string | number;
|
|
6381
|
+
label: string;
|
|
6382
|
+
disabled?: boolean;
|
|
6383
|
+
children?: SSelectOption[];
|
|
6384
|
+
}
|
|
6385
|
+
```
|
|
6386
|
+
|
|
6387
|
+
### SSelectType
|
|
6388
|
+
|
|
6389
|
+
```ts
|
|
6390
|
+
export type SSelectType = 'default' | 'multi' | 'default_depth' | 'multi_depth';
|
|
6391
|
+
```
|
|
6392
|
+
|
|
4244
6393
|
## Dependencies
|
|
4245
6394
|
|
|
4246
6395
|
### Used by
|
|
@@ -4251,9 +6400,9 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
4251
6400
|
### Depends on
|
|
4252
6401
|
|
|
4253
6402
|
- [SField](../SField)
|
|
4254
|
-
- [SGhostButton](../SGhostButton)
|
|
4255
6403
|
- [SIcon](../SIcon)
|
|
4256
6404
|
- [SPortal](../SPortal)
|
|
6405
|
+
- [SSearchInput](../SSearchInput)
|
|
4257
6406
|
|
|
4258
6407
|
### Graph
|
|
4259
6408
|
|
|
@@ -4280,11 +6429,26 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
4280
6429
|
| `dividerStyle?` | `CSSProperties` | — | 구분선에 얹을 인라인 스타일 |
|
|
4281
6430
|
| `children?` | `ReactNode` | — | SSplitter.Before 와 SSplitter.After 둘 |
|
|
4282
6431
|
|
|
4283
|
-
#### Events
|
|
6432
|
+
#### Events
|
|
6433
|
+
|
|
6434
|
+
| Event | Type | Description |
|
|
6435
|
+
|-------|------|-------------|
|
|
6436
|
+
| `onValueChange` | `(value: number) => void` | 크기가 확정될 때. emitImmediately 가 아니면 드래그를 놓는 순간 한 번만 온다 |
|
|
6437
|
+
|
|
6438
|
+
## Types
|
|
6439
|
+
|
|
6440
|
+
### SSplitterUnit
|
|
6441
|
+
|
|
6442
|
+
```ts
|
|
6443
|
+
/** 모델·limits 를 읽는 단위. Quasar QSplitter 의 `unit` 과 같다. */
|
|
6444
|
+
export type SSplitterUnit = (typeof SSPLITTER_UNITS)[number];
|
|
6445
|
+
```
|
|
6446
|
+
|
|
6447
|
+
### SSPLITTER_UNITS
|
|
4284
6448
|
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
6449
|
+
```ts
|
|
6450
|
+
export const SSPLITTER_UNITS = ['%', 'px'] as const;
|
|
6451
|
+
```
|
|
4288
6452
|
|
|
4289
6453
|
---
|
|
4290
6454
|
|
|
@@ -4320,6 +6484,32 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
4320
6484
|
| `showItemTooltip` | `() => void` | error 아이템 중 첫 번째에 툴팁을 연다. error 아이템이 없으면 아무 일도 일어나지 않는다. |
|
|
4321
6485
|
| `hideItemTooltip` | `() => void` | 열려있는 커스텀 툴팁을 닫는다. 활성 단계(value prop)가 바뀌면 별도 호출 없이도 자동으로 닫힌다. |
|
|
4322
6486
|
|
|
6487
|
+
## Types
|
|
6488
|
+
|
|
6489
|
+
### SStepperItem
|
|
6490
|
+
|
|
6491
|
+
```ts
|
|
6492
|
+
export type SStepperItem = {
|
|
6493
|
+
label: string;
|
|
6494
|
+
value?: string;
|
|
6495
|
+
group?: string;
|
|
6496
|
+
completed?: boolean;
|
|
6497
|
+
error?: boolean;
|
|
6498
|
+
};
|
|
6499
|
+
```
|
|
6500
|
+
|
|
6501
|
+
### SStepperSize
|
|
6502
|
+
|
|
6503
|
+
```ts
|
|
6504
|
+
export type SStepperSize = (typeof STEPPER_SIZES)[number];
|
|
6505
|
+
```
|
|
6506
|
+
|
|
6507
|
+
### STEPPER_SIZES
|
|
6508
|
+
|
|
6509
|
+
```ts
|
|
6510
|
+
export const STEPPER_SIZES = ['sm', 'lg'] as const;
|
|
6511
|
+
```
|
|
6512
|
+
|
|
4323
6513
|
## Dependencies
|
|
4324
6514
|
|
|
4325
6515
|
### Depends on
|
|
@@ -4355,6 +6545,143 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
4355
6545
|
|-------|------|-------------|
|
|
4356
6546
|
| `onValueChange` | `(value: boolean) => void` | 변경 (sdUpdate) |
|
|
4357
6547
|
|
|
6548
|
+
## Dependencies
|
|
6549
|
+
|
|
6550
|
+
### Used by
|
|
6551
|
+
|
|
6552
|
+
- [SKeyValueTable](../SKeyValueTable)
|
|
6553
|
+
|
|
6554
|
+
### Graph
|
|
6555
|
+
|
|
6556
|
+
---
|
|
6557
|
+
|
|
6558
|
+
# SSystemActionButton
|
|
6559
|
+
|
|
6560
|
+
> 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
|
|
6561
|
+
|
|
6562
|
+
### SSystemActionButtonAccount
|
|
6563
|
+
|
|
6564
|
+
#### Props
|
|
6565
|
+
|
|
6566
|
+
| Prop | Type | Default | Description |
|
|
6567
|
+
|------|------|---------|-------------|
|
|
6568
|
+
| `type` | `'account'` | — | 계정 행 |
|
|
6569
|
+
| `label?` | `string` | — | 라벨 텍스트 |
|
|
6570
|
+
| `option?` | `never` | — | 아이콘이 `user` 로 고정이라 받을 것이 없다 |
|
|
6571
|
+
|
|
6572
|
+
### SSystemActionButtonBase
|
|
6573
|
+
|
|
6574
|
+
#### Props
|
|
6575
|
+
|
|
6576
|
+
| Prop | Type | Default | Description |
|
|
6577
|
+
|------|------|---------|-------------|
|
|
6578
|
+
| `color?` | `SSystemActionButtonColor` | — | 색상(놓이는 표면). light = 흰 면, dark = 오션블루 면, darker = 네이비 면. 기본 `'light'` |
|
|
6579
|
+
| `disabled?` | `boolean` | — | 비활성 여부. 기본 `false` |
|
|
6580
|
+
|
|
6581
|
+
### SSystemActionButtonSelect
|
|
6582
|
+
|
|
6583
|
+
#### Props
|
|
6584
|
+
|
|
6585
|
+
| Prop | Type | Default | Description |
|
|
6586
|
+
|------|------|---------|-------------|
|
|
6587
|
+
| `type` | `'select'` | — | 화살표만 붙는 행 |
|
|
6588
|
+
| `label?` | `string` | — | 라벨 텍스트 |
|
|
6589
|
+
| `option` | `SSystemActionButtonIconOption` | — | 선행 아이콘 |
|
|
6590
|
+
|
|
6591
|
+
### SSystemActionButtonStatus
|
|
6592
|
+
|
|
6593
|
+
#### Props
|
|
6594
|
+
|
|
6595
|
+
| Prop | Type | Default | Description |
|
|
6596
|
+
|------|------|---------|-------------|
|
|
6597
|
+
| `type` | `'status'` | — | 라벨 자리에 상태 태그가 들어가는 행 |
|
|
6598
|
+
| `label?` | `never` | — | 태그가 라벨 자리를 차지하므로 받지 않는다 |
|
|
6599
|
+
| `option?` | `SSystemActionButtonStatusOption` | — | 상태 태그 |
|
|
6600
|
+
|
|
6601
|
+
### SSystemActionButtonText
|
|
6602
|
+
|
|
6603
|
+
#### Props
|
|
6604
|
+
|
|
6605
|
+
| Prop | Type | Default | Description |
|
|
6606
|
+
|------|------|---------|-------------|
|
|
6607
|
+
| `type` | `'switch' \| 'subSelect'` | — | 우측에 문구가 붙는 행. switch = 보조 조작 문구, subSelect = 지금 선택된 값 |
|
|
6608
|
+
| `label?` | `string` | — | 라벨 텍스트 |
|
|
6609
|
+
| `option` | `SSystemActionButtonTextOption` | — | 선행 아이콘 + 우측 액션 텍스트 |
|
|
6610
|
+
|
|
6611
|
+
## Types
|
|
6612
|
+
|
|
6613
|
+
### SSystemActionButtonColor
|
|
6614
|
+
|
|
6615
|
+
```ts
|
|
6616
|
+
export type SSystemActionButtonColor = (typeof SYSTEM_ACTION_BUTTON_COLORS)[number];
|
|
6617
|
+
```
|
|
6618
|
+
|
|
6619
|
+
### SSystemActionButtonIconOption
|
|
6620
|
+
|
|
6621
|
+
```ts
|
|
6622
|
+
/** 선행 아이콘만 받는 타입(`select`)의 option */
|
|
6623
|
+
export interface SSystemActionButtonIconOption {
|
|
6624
|
+
/** 선행 아이콘명 */
|
|
6625
|
+
icon: SIconName;
|
|
6626
|
+
}
|
|
6627
|
+
```
|
|
6628
|
+
|
|
6629
|
+
### SSystemActionButtonStatusOption
|
|
6630
|
+
|
|
6631
|
+
```ts
|
|
6632
|
+
/** `status` 타입의 option — 라벨 자리에 들어가는 상태 태그 */
|
|
6633
|
+
export interface SSystemActionButtonStatusOption {
|
|
6634
|
+
/** 상태 태그 색 */
|
|
6635
|
+
color?: STagColor;
|
|
6636
|
+
/** 상태 태그 텍스트 */
|
|
6637
|
+
label?: string;
|
|
6638
|
+
}
|
|
6639
|
+
```
|
|
6640
|
+
|
|
6641
|
+
### SSystemActionButtonTextOption
|
|
6642
|
+
|
|
6643
|
+
```ts
|
|
6644
|
+
/** 우측 액션 텍스트까지 받는 타입(`switch` · `subSelect`)의 option */
|
|
6645
|
+
export interface SSystemActionButtonTextOption extends SSystemActionButtonIconOption {
|
|
6646
|
+
/** 우측 액션 텍스트 */
|
|
6647
|
+
text?: string;
|
|
6648
|
+
}
|
|
6649
|
+
```
|
|
6650
|
+
|
|
6651
|
+
### SYSTEM_ACTION_BUTTON_COLORS
|
|
6652
|
+
|
|
6653
|
+
```ts
|
|
6654
|
+
/**
|
|
6655
|
+
* SSystemActionButton 설정.
|
|
6656
|
+
*
|
|
6657
|
+
* 토큰은 GNB system 패널 한 벌(`--cmp-gnb-system-*`)을 그대로 쓴다.
|
|
6658
|
+
* 행 공통(높이·라운드·아이콘 프레임·라벨) → --cmp-gnb-system-item-*
|
|
6659
|
+
* 색(color 축) light / dark / darker → --cmp-gnb-system-item-<color>-*
|
|
6660
|
+
* 우측 액션(패딩·간격·아이콘·텍스트색) → --cmp-gnb-system-actionButton-*
|
|
6661
|
+
*
|
|
6662
|
+
* 타이포그래피는 CSS 변수가 아니라 `.typo-*` 프리셋으로 나온다.
|
|
6663
|
+
* item.label → typo-body-sm-semibold (12/600/20)
|
|
6664
|
+
* actionButton.switch → typo-field-sm-default (12/400/20)
|
|
6665
|
+
* actionButton.selectData → typo-control-xs-medium (12/500/20)
|
|
6666
|
+
*/
|
|
6667
|
+
|
|
6668
|
+
/** 색상(표면). light = 흰 면, dark = 오션블루 면, darker = 네이비 면 */
|
|
6669
|
+
export const SYSTEM_ACTION_BUTTON_COLORS = ['light', 'dark', 'darker'] as const;
|
|
6670
|
+
```
|
|
6671
|
+
|
|
6672
|
+
## Dependencies
|
|
6673
|
+
|
|
6674
|
+
### Used by
|
|
6675
|
+
|
|
6676
|
+
- [SGnbSystem](../SGnbSystem)
|
|
6677
|
+
|
|
6678
|
+
### Depends on
|
|
6679
|
+
|
|
6680
|
+
- [SIcon](../SIcon)
|
|
6681
|
+
- [STag](../STag)
|
|
6682
|
+
|
|
6683
|
+
### Graph
|
|
6684
|
+
|
|
4358
6685
|
---
|
|
4359
6686
|
|
|
4360
6687
|
# STable
|
|
@@ -4368,20 +6695,23 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
4368
6695
|
| Prop | Type | Default | Description |
|
|
4369
6696
|
|------|------|---------|-------------|
|
|
4370
6697
|
| `columns?` | `STableColumn[]` | `[]` | |
|
|
6698
|
+
| `headerGroups?` | `STableHeaderGroup[]` | — | 상위 그룹 헤더. 지정하면 기존 컬럼 헤더가 두 번째 행에 렌더링된다. |
|
|
4371
6699
|
| `rows?` | `SRow[]` | `[]` | |
|
|
4372
6700
|
| `rowKey?` | `string` | `'id'` | 행 식별 필드 |
|
|
4373
6701
|
| `selectable?` | `boolean` | `false` | 행 선택 체크박스 |
|
|
4374
6702
|
| `selected?` | `SRow[]` | `[]` | |
|
|
6703
|
+
| `sort?` | `STableSort \| null` | `null` | 정렬 상태 (controlled). `null`·미지정이면 정렬 없음. 컴포넌트는 정렬 상태를 갖지 않는다 — 서버 정렬이면 이 값이 곧 조회 조건이고, 뒤로가기·새로고침·링크 공유로 복원돼야 하므로 진실은 URL·store 쪽에 있어야 한다. 행을 실제로 정렬하는 것도 소비 앱 몫이다 (`STable` 은 받은 순서대로 그린다). |
|
|
4375
6704
|
| `resizable?` | `boolean` | `false` | 컬럼 너비 조절 |
|
|
4376
6705
|
| `width?` | `string` | — | |
|
|
4377
6706
|
| `height?` | `string` | — | |
|
|
4378
6707
|
| `stickyHeader?` | `boolean` | — | |
|
|
4379
6708
|
| `stickyColumn?` | `STableStickyColumn` | — | 고정할 좌/우 컬럼 수 |
|
|
4380
6709
|
| `radius?` | `'default' \| 'useTop' \| 'full'` | `'default'` | border-radius 제어 |
|
|
6710
|
+
| `bordered?` | `boolean` | `true` | 바깥 테두리. 기본은 `true`. `SSectionHeaderCard` 의 `padding="none"` 안에 넣어 카드 가장자리까지 채울 때 `false` 로 끈다 — 카드가 이미 바깥 테두리를 그리므로, 켜 두면 1px 두 개가 나란히 놓여 그 변만 2px 로 보인다. 페이지네이션 바의 테두리와 1px 겹침(`-mt-px`)도 함께 꺼진다. 본문 테두리가 없으면 겹칠 대상이 없어, 그대로 두면 페이지네이션만 테두리를 갖고 1px 어긋난다. |
|
|
4381
6711
|
| `noDataLabel?` | `string` | `'데이터가 없습니다.'` | |
|
|
4382
6712
|
| `noDataSlot?` | `ReactNode` | — | 데이터가 없을 때 body 영역 전체를 대체하는 슬롯. 지정하면 `noDataLabel` 대신 이 콘텐츠가 헤더 아래 영역을 채우며, 버튼 등 인터랙션도 동작한다. |
|
|
4383
6713
|
| `isLoading?` | `boolean` | `false` | |
|
|
4384
|
-
| `dense?` | `boolean` | `false` |
|
|
6714
|
+
| `dense?` | `boolean` | `false` | 행 높이를 좁게 (세로 여백만 줄인다 — 좌우 패딩은 그대로). **시작 밀도이자 밀도 토글의 스위치다.** 켜면 하단 바 우측에 `좁게 보기` · `넓게 보기` 토글이 붙는다 — 페이지네이션이 없으면 이 바를 토글만 담아 그린다. 누른 뒤의 밀도는 테이블이 내부 상태로 들고 가므로 `onDenseChange` 를 받지 않아도 토글은 동작하고, 넓게 본 뒤에도 토글은 그대로 남는다(붙일지는 이 prop 이 정한다). 이 prop 값이 바뀌면 내부 밀도도 그 값으로 맞춰진다. |
|
|
4385
6715
|
| `noHover?` | `boolean` | `false` | true면 행에 마우스를 올려도 hover 배경(grey_05)을 표시하지 않는다 |
|
|
4386
6716
|
| `pagination?` | `STablePagination` | — | 페이지네이션 (있으면 하단 표시) |
|
|
4387
6717
|
| `useInternalPagination?` | `boolean` | `false` | 테이블 내부에서 페이지네이션을 직접 관리 (rows를 내부 슬라이싱) |
|
|
@@ -4400,6 +6730,8 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
4400
6730
|
| Event | Type | Description |
|
|
4401
6731
|
|-------|------|-------------|
|
|
4402
6732
|
| `onSelectedChange` | `(rows: SRow[]) => void` | |
|
|
6733
|
+
| `onSortChange` | `(sort: STableSort \| null) => void` | 정렬 헤더 클릭 (`asc → desc → 해제` 3단). 해제되면 `null` 이 온다. 다중 정렬은 1차 안에서 지원하지 않는다. |
|
|
6734
|
+
| `onDenseChange` | `(dense: boolean) => void` | 밀도 변경 (하단 바의 밀도 토글을 눌렀을 때). 표시는 테이블이 알아서 바꾸므로 받지 않아도 되고, 사용자가 고른 밀도를 다음 방문까지 기억해 두려는(로컬 저장 등) 페이지만 받으면 된다. |
|
|
4403
6735
|
| `onPageChange` | `(page: number) => void` | |
|
|
4404
6736
|
| `onRowsPerPageChange` | `(perPage: number) => void` | |
|
|
4405
6737
|
| `onVirtualUpdate` | `(range: { from: number; to: number }) => void` | |
|
|
@@ -4418,15 +6750,92 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
4418
6750
|
| `updateRowSelect` | `(row: SRow) => void` | 단일 행 선택 토글 (sd-table updateRowSelect) — onSelectedChange 발생 |
|
|
4419
6751
|
| `toggleSelectAll` | `(checked: boolean, rows: SRow[]) => void` | 주어진 행들 전체 선택/해제 (sd-table toggleSelectAll) — onSelectedChange 발생 |
|
|
4420
6752
|
|
|
6753
|
+
## Types
|
|
6754
|
+
|
|
6755
|
+
### STableColumn
|
|
6756
|
+
|
|
6757
|
+
```ts
|
|
6758
|
+
/**
|
|
6759
|
+
* `autoWidth` 스페이서 열은 값을 그리지 않으므로 `field` 를 생략할 수 있다.
|
|
6760
|
+
* 그 밖의 열은 값 접근자가 있어야 한다.
|
|
6761
|
+
*/
|
|
6762
|
+
export type STableColumn = STableColumnBase &
|
|
6763
|
+
(
|
|
6764
|
+
{ autoWidth: true; field?: STableColumnField } | { autoWidth?: false; field: STableColumnField }
|
|
6765
|
+
);
|
|
6766
|
+
```
|
|
6767
|
+
|
|
6768
|
+
### STableHeaderGroup
|
|
6769
|
+
|
|
6770
|
+
```ts
|
|
6771
|
+
/** 2단 헤더의 상위 셀. columnNames는 visible 컬럼 기준으로 연속되어야 한다. */
|
|
6772
|
+
export interface STableHeaderGroup {
|
|
6773
|
+
label?: ReactNode;
|
|
6774
|
+
columnNames: string[];
|
|
6775
|
+
rowSpan?: 1 | 2;
|
|
6776
|
+
align?: 'left' | 'center' | 'right';
|
|
6777
|
+
className?: string;
|
|
6778
|
+
style?: CSSProperties;
|
|
6779
|
+
render?: () => ReactNode;
|
|
6780
|
+
}
|
|
6781
|
+
```
|
|
6782
|
+
|
|
6783
|
+
### SRow
|
|
6784
|
+
|
|
6785
|
+
```ts
|
|
6786
|
+
export type SRow = Record<string, any>;
|
|
6787
|
+
```
|
|
6788
|
+
|
|
6789
|
+
### STableSort
|
|
6790
|
+
|
|
6791
|
+
```ts
|
|
6792
|
+
/** 정렬 상태 — 어느 열을 어느 방향으로 정렬했는가 */
|
|
6793
|
+
export interface STableSort {
|
|
6794
|
+
/** 정렬 기준 컬럼의 `name` */
|
|
6795
|
+
name: string;
|
|
6796
|
+
dir: 'asc' | 'desc';
|
|
6797
|
+
}
|
|
6798
|
+
```
|
|
6799
|
+
|
|
6800
|
+
### STableStickyColumn
|
|
6801
|
+
|
|
6802
|
+
```ts
|
|
6803
|
+
export interface STableStickyColumn {
|
|
6804
|
+
left?: number;
|
|
6805
|
+
right?: number;
|
|
6806
|
+
}
|
|
6807
|
+
```
|
|
6808
|
+
|
|
6809
|
+
### STablePagination
|
|
6810
|
+
|
|
6811
|
+
```ts
|
|
6812
|
+
export interface STablePagination {
|
|
6813
|
+
page: number;
|
|
6814
|
+
rowsPerPage: number;
|
|
6815
|
+
lastPage?: number;
|
|
6816
|
+
}
|
|
6817
|
+
```
|
|
6818
|
+
|
|
6819
|
+
### STableColumnField
|
|
6820
|
+
|
|
6821
|
+
```ts
|
|
6822
|
+
/** 값 접근: 필드명 또는 접근 함수 */
|
|
6823
|
+
export type STableColumnField = string | ((row: SRow) => any);
|
|
6824
|
+
```
|
|
6825
|
+
|
|
4421
6826
|
## Dependencies
|
|
4422
6827
|
|
|
4423
6828
|
### Depends on
|
|
4424
6829
|
|
|
4425
6830
|
- [SCheckbox](../SCheckbox)
|
|
4426
6831
|
- [SCircleProgress](../SCircleProgress)
|
|
6832
|
+
- [SDivider](../SDivider)
|
|
6833
|
+
- [SGhostButton](../SGhostButton)
|
|
4427
6834
|
- [SIcon](../SIcon)
|
|
4428
6835
|
- [SPagination](../SPagination)
|
|
4429
6836
|
- [SSelect](../SSelect)
|
|
6837
|
+
- [STextLink](../STextLink)
|
|
6838
|
+
- [STooltip](../STooltip)
|
|
4430
6839
|
|
|
4431
6840
|
### Graph
|
|
4432
6841
|
|
|
@@ -4469,7 +6878,6 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
4469
6878
|
|------|------|---------|-------------|
|
|
4470
6879
|
| `value` | `string` | — | 현재 선택된 탭 value |
|
|
4471
6880
|
| `tabs` | `STabOption[]` | — | 탭 옵션 목록 |
|
|
4472
|
-
| `size?` | `STabSize` | `'md'` | 탭 크기 (main 전용) |
|
|
4473
6881
|
| `isSub?` | `boolean` | `false` | 서브 탭(밑줄형) 스타일 |
|
|
4474
6882
|
| `vertical?` | `boolean` | `false` | 세로 배치 (sub 전용 — main 폴더형은 항상 가로) |
|
|
4475
6883
|
| `className?` | `string` | — | |
|
|
@@ -4481,6 +6889,18 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
4481
6889
|
|-------|------|-------------|
|
|
4482
6890
|
| `onValueChange` | `(value: string) => void` | 탭 변경 (sdUpdate) |
|
|
4483
6891
|
|
|
6892
|
+
## Types
|
|
6893
|
+
|
|
6894
|
+
### STabOption
|
|
6895
|
+
|
|
6896
|
+
```ts
|
|
6897
|
+
export interface STabOption {
|
|
6898
|
+
label: string;
|
|
6899
|
+
value: string;
|
|
6900
|
+
badge?: string | number;
|
|
6901
|
+
}
|
|
6902
|
+
```
|
|
6903
|
+
|
|
4484
6904
|
## Dependencies
|
|
4485
6905
|
|
|
4486
6906
|
### Depends on
|
|
@@ -4504,11 +6924,59 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
4504
6924
|
| `shape?` | `STagShape` | `'square'` | 모양 |
|
|
4505
6925
|
| `size?` | `STagSize` | `'sm'` | 크기 |
|
|
4506
6926
|
| `color?` | `STagColor` | `'grey'` | 색상 |
|
|
6927
|
+
| `deep?` | `boolean` | `false` | 진한 배경(deep) 버전. 배경에 강조색을 채우고 텍스트·아이콘을 반전색으로 쓴다 |
|
|
4507
6928
|
| `label?` | `string` | `''` | 레이블 텍스트 |
|
|
4508
6929
|
| `icon?` | `SIconName` | — | 아이콘명 |
|
|
4509
6930
|
| `iconLeft?` | `boolean` | `true` | 아이콘을 레이블 왼쪽에 배치 |
|
|
4510
6931
|
| `width?` | `string \| number` | — | 태그 너비 (숫자면 px, 문자열이면 그대로 적용). 미지정 시 콘텐츠 크기 |
|
|
4511
6932
|
|
|
6933
|
+
## Types
|
|
6934
|
+
|
|
6935
|
+
### STagShape
|
|
6936
|
+
|
|
6937
|
+
```ts
|
|
6938
|
+
export type STagShape = (typeof TAG_SHAPES)[number];
|
|
6939
|
+
```
|
|
6940
|
+
|
|
6941
|
+
### STagSize
|
|
6942
|
+
|
|
6943
|
+
```ts
|
|
6944
|
+
export type STagSize = (typeof TAG_SIZES)[number];
|
|
6945
|
+
```
|
|
6946
|
+
|
|
6947
|
+
### STagColor
|
|
6948
|
+
|
|
6949
|
+
```ts
|
|
6950
|
+
export type STagColor = (typeof TAG_COLORS)[number];
|
|
6951
|
+
```
|
|
6952
|
+
|
|
6953
|
+
### TAG_SHAPES
|
|
6954
|
+
|
|
6955
|
+
```ts
|
|
6956
|
+
export const TAG_SHAPES = ['square', 'pill'] as const;
|
|
6957
|
+
```
|
|
6958
|
+
|
|
6959
|
+
### TAG_SIZES
|
|
6960
|
+
|
|
6961
|
+
```ts
|
|
6962
|
+
export const TAG_SIZES = ['xs', 'sm', 'md'] as const;
|
|
6963
|
+
```
|
|
6964
|
+
|
|
6965
|
+
### TAG_COLORS
|
|
6966
|
+
|
|
6967
|
+
```ts
|
|
6968
|
+
export const TAG_COLORS = [
|
|
6969
|
+
'grey',
|
|
6970
|
+
'red',
|
|
6971
|
+
'orange',
|
|
6972
|
+
'yellow',
|
|
6973
|
+
'green',
|
|
6974
|
+
'blue',
|
|
6975
|
+
'darkblue',
|
|
6976
|
+
'indigo',
|
|
6977
|
+
] as const;
|
|
6978
|
+
```
|
|
6979
|
+
|
|
4512
6980
|
## Dependencies
|
|
4513
6981
|
|
|
4514
6982
|
### Used by
|
|
@@ -4517,6 +6985,8 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
4517
6985
|
- [SConfirmModal](../SConfirmModal)
|
|
4518
6986
|
- [SGhostButton](../SGhostButton)
|
|
4519
6987
|
- [SGnb](../SGnb)
|
|
6988
|
+
- [SRadioCard](../SRadioCard)
|
|
6989
|
+
- [SSystemActionButton](../SSystemActionButton)
|
|
4520
6990
|
- [STabs](../STabs)
|
|
4521
6991
|
- [STextLink](../STextLink)
|
|
4522
6992
|
|
|
@@ -4541,6 +7011,7 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
4541
7011
|
| `label?` | `string` | `''` | 레이블 |
|
|
4542
7012
|
| `icon?` | `SIconName` | — | 좌측 아이콘 |
|
|
4543
7013
|
| `iconColor?` | `SColor` | — | 좌측 아이콘 색상. 팔레트 키(`grey_65`, `red_95` …) 또는 임의 CSS 색상 |
|
|
7014
|
+
| `iconRotate?` | `0 \| 90 \| 180 \| 270` | — | 좌측 아이콘 회전 각도. 같은 아이콘을 방향만 바꿔 쓸 때 (`SIcon` 의 `rotate` 로 그대로 간다) |
|
|
4544
7015
|
| `labelClass?` | `string` | — | 레이블 span에 추가할 클래스 |
|
|
4545
7016
|
| `rightArrow?` | `STextLinkArrow` | `'none'` | 우측 화살표 |
|
|
4546
7017
|
| `underline?` | `boolean` | `false` | 밑줄 여부 |
|
|
@@ -4554,6 +7025,20 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
4554
7025
|
|-------|------|-------------|
|
|
4555
7026
|
| `onClick` | `() => void` | |
|
|
4556
7027
|
|
|
7028
|
+
## Types
|
|
7029
|
+
|
|
7030
|
+
### STextLinkArrow
|
|
7031
|
+
|
|
7032
|
+
```ts
|
|
7033
|
+
export type STextLinkArrow = 'none' | 'chevron' | 'caret';
|
|
7034
|
+
```
|
|
7035
|
+
|
|
7036
|
+
### STextLinkSize
|
|
7037
|
+
|
|
7038
|
+
```ts
|
|
7039
|
+
export type STextLinkSize = 'sm' | 'md' | 'lg';
|
|
7040
|
+
```
|
|
7041
|
+
|
|
4557
7042
|
## Dependencies
|
|
4558
7043
|
|
|
4559
7044
|
### Used by
|
|
@@ -4561,6 +7046,7 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
4561
7046
|
- [SChipFilter](../SChipFilter)
|
|
4562
7047
|
- [SChipInput](../SChipInput)
|
|
4563
7048
|
- [SPopover](../SPopover)
|
|
7049
|
+
- [STable](../STable)
|
|
4564
7050
|
|
|
4565
7051
|
### Depends on
|
|
4566
7052
|
|
|
@@ -4600,7 +7086,7 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
4600
7086
|
| `hint?` | `string` | — | |
|
|
4601
7087
|
| `error?` | `boolean` | — | |
|
|
4602
7088
|
| `errorMessage?` | `string` | — | |
|
|
4603
|
-
| `width?` | `
|
|
7089
|
+
| `width?` | `SFieldWidth` | — | 컨트롤 너비 — 폭 등급(`'xs' | 'sm' | 'md' | 'lg' | 'xl'`) · 숫자=px · CSS 길이. 등급은 `--cmp-field-width-*` 토큰으로 풀리므로 토큰이 바뀌면 화면이 따라간다. 값 길이에 상한이 없으면 `"100%"` 로 두어 행 전체를 쓴다. |
|
|
4604
7090
|
| `disabled?` | `boolean` | `false` | |
|
|
4605
7091
|
| `readOnly?` | `boolean` | `false` | |
|
|
4606
7092
|
| `className?` | `string` | — | |
|
|
@@ -4645,7 +7131,7 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
4645
7131
|
| `clearable?` | `boolean` | `false` | |
|
|
4646
7132
|
| `useMeridiem?` | `boolean` | — | 오전/오후 선택 표시 여부. 지정하지 않으면 type="midday"일 때만 켜집니다. |
|
|
4647
7133
|
| `minuteStep?` | `number` | `1` | |
|
|
4648
|
-
| `width?` | `
|
|
7134
|
+
| `width?` | `SFieldWidth` | — | 컨트롤 너비 — 폭 등급(`'xs' | 'sm' | 'md' | 'lg' | 'xl'`) · 숫자=px · CSS 길이. 등급은 `--cmp-field-width-*` 토큰으로 풀리므로 토큰이 바뀌면 화면이 따라간다. 값 길이에 상한이 없으면 `"100%"` 로 두어 행 전체를 쓴다. |
|
|
4649
7135
|
| `name?` | `string` | — | |
|
|
4650
7136
|
| `rules?` | `Rule[]` | — | |
|
|
4651
7137
|
| `status?` | `SFieldStatus` | — | |
|
|
@@ -4670,6 +7156,20 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
4670
7156
|
| `onValueChange` | `(time: string \| null) => void` | 선택 변경 (sdUpdate) |
|
|
4671
7157
|
| `onOpenChange` | `(open: boolean) => void` | 열림/닫힘 변경 (sdDropDownShow) |
|
|
4672
7158
|
|
|
7159
|
+
## Types
|
|
7160
|
+
|
|
7161
|
+
### STimePickerType
|
|
7162
|
+
|
|
7163
|
+
```ts
|
|
7164
|
+
export type STimePickerType = 'default' | 'midday';
|
|
7165
|
+
```
|
|
7166
|
+
|
|
7167
|
+
### STimePickerSize
|
|
7168
|
+
|
|
7169
|
+
```ts
|
|
7170
|
+
export type STimePickerSize = 'sm' | 'md';
|
|
7171
|
+
```
|
|
7172
|
+
|
|
4673
7173
|
## Dependencies
|
|
4674
7174
|
|
|
4675
7175
|
### Used by
|
|
@@ -4705,7 +7205,7 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
4705
7205
|
| `useMeridiem?` | `boolean` | — | 오전/오후 선택 표시 여부. 지정하지 않으면 type="midday"일 때만 켜집니다. |
|
|
4706
7206
|
| `minuteStep?` | `number` | `1` | |
|
|
4707
7207
|
| `rangeOrder?` | `STimeRangePickerRangeOrder` | `'strict'` | 범위 순서 정책. strict는 시작 시간이 종료 시간보다 늦어지지 않도록 보정합니다. |
|
|
4708
|
-
| `width?` | `
|
|
7208
|
+
| `width?` | `SFieldWidth` | — | 컨트롤 너비 — 폭 등급(`'xs' | 'sm' | 'md' | 'lg' | 'xl'`) · 숫자=px · CSS 길이. 등급은 `--cmp-field-width-*` 토큰으로 풀리므로 토큰이 바뀌면 화면이 따라간다. 값 길이에 상한이 없으면 `"100%"` 로 두어 행 전체를 쓴다. |
|
|
4709
7209
|
| `name?` | `string` | — | |
|
|
4710
7210
|
| `rules?` | `Rule[]` | — | |
|
|
4711
7211
|
| `status?` | `SFieldStatus` | — | |
|
|
@@ -4730,6 +7230,32 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
4730
7230
|
| `onValueChange` | `(range: STimeRangeValue) => void` | 선택 변경 (sdUpdate) |
|
|
4731
7231
|
| `onOpenChange` | `(open: boolean) => void` | 열림/닫힘 변경 (sdDropDownShow) |
|
|
4732
7232
|
|
|
7233
|
+
## Types
|
|
7234
|
+
|
|
7235
|
+
### STimeRangeValue
|
|
7236
|
+
|
|
7237
|
+
```ts
|
|
7238
|
+
export type STimeRangeValue = [string, string] | null;
|
|
7239
|
+
```
|
|
7240
|
+
|
|
7241
|
+
### STimeRangePickerType
|
|
7242
|
+
|
|
7243
|
+
```ts
|
|
7244
|
+
export type STimeRangePickerType = STimePickerType;
|
|
7245
|
+
```
|
|
7246
|
+
|
|
7247
|
+
### STimeRangePickerSize
|
|
7248
|
+
|
|
7249
|
+
```ts
|
|
7250
|
+
export type STimeRangePickerSize = STimePickerSize;
|
|
7251
|
+
```
|
|
7252
|
+
|
|
7253
|
+
### STimeRangePickerRangeOrder
|
|
7254
|
+
|
|
7255
|
+
```ts
|
|
7256
|
+
export type STimeRangePickerRangeOrder = 'strict' | 'allow-cross-day';
|
|
7257
|
+
```
|
|
7258
|
+
|
|
4733
7259
|
## Dependencies
|
|
4734
7260
|
|
|
4735
7261
|
### Depends on
|
|
@@ -4796,6 +7322,30 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
4796
7322
|
| `onClose` | `() => void` | 닫기 (sdClose) |
|
|
4797
7323
|
| `onButtonClick` | `(e: MouseEvent) => void` | 버튼 클릭 (sdButtonClick) |
|
|
4798
7324
|
|
|
7325
|
+
## Types
|
|
7326
|
+
|
|
7327
|
+
### SToastPosition
|
|
7328
|
+
|
|
7329
|
+
```ts
|
|
7330
|
+
export type SToastPosition =
|
|
7331
|
+
'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';
|
|
7332
|
+
```
|
|
7333
|
+
|
|
7334
|
+
### SToastNotifyOptions
|
|
7335
|
+
|
|
7336
|
+
```ts
|
|
7337
|
+
export interface SToastNotifyOptions extends Omit<SToastProps, 'onClose'> {
|
|
7338
|
+
/** 자동 닫힘 지연(ms). 0이면 자동 닫힘 없음 (없으면 defaultDuration) */
|
|
7339
|
+
duration?: number;
|
|
7340
|
+
}
|
|
7341
|
+
```
|
|
7342
|
+
|
|
7343
|
+
### SToastType
|
|
7344
|
+
|
|
7345
|
+
```ts
|
|
7346
|
+
export type SToastType = 'default' | 'danger' | 'caution' | 'complete' | 'accent' | 'info';
|
|
7347
|
+
```
|
|
7348
|
+
|
|
4799
7349
|
## Dependencies
|
|
4800
7350
|
|
|
4801
7351
|
### Depends on
|
|
@@ -4831,6 +7381,14 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
4831
7381
|
|-------|------|-------------|
|
|
4832
7382
|
| `onValueChange` | `(value: boolean) => void` | 변경 (sdUpdate) |
|
|
4833
7383
|
|
|
7384
|
+
## Types
|
|
7385
|
+
|
|
7386
|
+
### SToggleSize
|
|
7387
|
+
|
|
7388
|
+
```ts
|
|
7389
|
+
export type SToggleSize = 'xs' | 'sm';
|
|
7390
|
+
```
|
|
7391
|
+
|
|
4834
7392
|
---
|
|
4835
7393
|
|
|
4836
7394
|
# STooltip
|
|
@@ -4875,6 +7433,26 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
4875
7433
|
| `show` | `() => void` | 툴팁을 표시합니다. |
|
|
4876
7434
|
| `hide` | `() => void` | 툴팁을 숨깁니다. |
|
|
4877
7435
|
|
|
7436
|
+
## Types
|
|
7437
|
+
|
|
7438
|
+
### STooltipTrigger
|
|
7439
|
+
|
|
7440
|
+
```ts
|
|
7441
|
+
export type STooltipTrigger = 'hover' | 'click' | 'none';
|
|
7442
|
+
```
|
|
7443
|
+
|
|
7444
|
+
### STooltipPlacement
|
|
7445
|
+
|
|
7446
|
+
```ts
|
|
7447
|
+
export type STooltipPlacement = 'top' | 'bottom' | 'left' | 'right';
|
|
7448
|
+
```
|
|
7449
|
+
|
|
7450
|
+
### STooltipType
|
|
7451
|
+
|
|
7452
|
+
```ts
|
|
7453
|
+
export type STooltipType = 'default' | 'danger' | 'warning' | 'accent';
|
|
7454
|
+
```
|
|
7455
|
+
|
|
4878
7456
|
## Dependencies
|
|
4879
7457
|
|
|
4880
7458
|
### Used by
|
|
@@ -4884,6 +7462,7 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
4884
7462
|
- [SKeyValueTable](../SKeyValueTable)
|
|
4885
7463
|
- [SSectionHeaderCard](../SSectionHeaderCard)
|
|
4886
7464
|
- [SStepper](../SStepper)
|
|
7465
|
+
- [STable](../STable)
|
|
4887
7466
|
|
|
4888
7467
|
### Depends on
|
|
4889
7468
|
|
|
@@ -4914,6 +7493,7 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
4914
7493
|
| `border?` | `boolean` | `true` | 아이템 하단 border 표시 여부 |
|
|
4915
7494
|
| `indeterminate?` | `boolean` | `false` | 일부 선택 상태 |
|
|
4916
7495
|
| `selectable?` | `boolean` | `true` | 체크박스 표시 여부 |
|
|
7496
|
+
| `size?` | `STreeSize` | `'sm'` | 타이포그래피 크기 |
|
|
4917
7497
|
| `leading?` | `ReactNode \| ((state: STreeItemRenderState) => ReactNode)` | — | 사용자 지정 leading 콘텐츠 |
|
|
4918
7498
|
| `trailing?` | `ReactNode \| ((state: STreeItemRenderState) => ReactNode)` | — | 사용자 지정 trailing 콘텐츠 |
|
|
4919
7499
|
|
|
@@ -4936,6 +7516,8 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
4936
7516
|
| `expandedIds?` | `string[]` | — | 펼쳐진 노드 ID 목록 |
|
|
4937
7517
|
| `defaultExpandedIds?` | `string[]` | — | 비제어 펼침 초기값 |
|
|
4938
7518
|
| `selectable?` | `boolean` | `true` | 체크박스 표시 여부 |
|
|
7519
|
+
| `size?` | `STreeSize` | `'sm'` | 타이포그래피 크기 |
|
|
7520
|
+
| `useAll?` | `boolean` | `false` | 체크박스 사용 시 최상단 전체 선택 아이템 표시 여부 |
|
|
4939
7521
|
| `guideline?` | `boolean` | `true` | depth 연결선 표시 여부 |
|
|
4940
7522
|
| `border?` | `boolean` | `true` | 아이템 하단 border 표시 여부 |
|
|
4941
7523
|
| `cascadeSelection?` | `boolean` | `true` | 부모 선택 시 하위 노드까지 함께 토글 |
|
|
@@ -4949,6 +7531,44 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
|
|
|
4949
7531
|
| `onValueChange` | `(value: string[], node: STreeNode) => void` | 선택 변경 |
|
|
4950
7532
|
| `onExpandedChange` | `(expandedIds: string[], node: STreeNode) => void` | 펼침 변경 |
|
|
4951
7533
|
|
|
7534
|
+
## Types
|
|
7535
|
+
|
|
7536
|
+
### STreeNode
|
|
7537
|
+
|
|
7538
|
+
```ts
|
|
7539
|
+
export interface STreeNode {
|
|
7540
|
+
/** 노드 고유 ID */
|
|
7541
|
+
id: string;
|
|
7542
|
+
/** 노드 라벨 */
|
|
7543
|
+
label: ReactNode;
|
|
7544
|
+
/** 하위 노드 */
|
|
7545
|
+
children?: STreeNode[];
|
|
7546
|
+
/** 비활성 상태 */
|
|
7547
|
+
disabled?: boolean;
|
|
7548
|
+
}
|
|
7549
|
+
```
|
|
7550
|
+
|
|
7551
|
+
### STreeSize
|
|
7552
|
+
|
|
7553
|
+
```ts
|
|
7554
|
+
export type STreeSize = 'sm' | 'md';
|
|
7555
|
+
```
|
|
7556
|
+
|
|
7557
|
+
### STreeItemRenderState
|
|
7558
|
+
|
|
7559
|
+
```ts
|
|
7560
|
+
export interface STreeItemRenderState {
|
|
7561
|
+
node: STreeNode;
|
|
7562
|
+
depth: number;
|
|
7563
|
+
size: STreeSize;
|
|
7564
|
+
expanded: boolean;
|
|
7565
|
+
selected: boolean;
|
|
7566
|
+
indeterminate: boolean;
|
|
7567
|
+
disabled: boolean;
|
|
7568
|
+
hasChildren: boolean;
|
|
7569
|
+
}
|
|
7570
|
+
```
|
|
7571
|
+
|
|
4952
7572
|
## Dependencies
|
|
4953
7573
|
|
|
4954
7574
|
### Depends on
|