sellmate-design-system-react 9.0.0-beta.24 → 9.0.0-beta.26

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 CHANGED
@@ -26,7 +26,7 @@
26
26
 
27
27
  | 분류 | 컴포넌트 |
28
28
  | --- | --- |
29
- | **버튼·링크** | `SButton` `SGhostButton` `SDropdownButton` `STextLink` `SSwitch` `SToggle` |
29
+ | **버튼·링크** | `SButton` `SGhostButton` `SDropdownButton` `SSystemActionButton` `STextLink` `SSwitch` `SToggle` |
30
30
  | **입력 (폼)** | `SForm` `SField` `SInput` `SSearchInput` `SNumberInput` `STextarea` `SEditor` `SSelect` `SCheckbox` `SRadio` `SRadioGroup` `SRadioButton` `SChip` `SChipInput` `SBarcodeInput` `SFilePicker` |
31
31
  | **날짜·시간** | `SCalendar` `SDatePicker` `SDatePickerYearListbox` `SDatePickerMonthListbox` `SDateRangePicker` `STimePicker` `STimeRangePicker` |
32
32
  | **표·목록** | `STable` `STableBar` `SChipFilter` `SKeyValueTable` `SList` `SListItem` `SExpansionList` `SDraggableList` `SDraggableItem` `STree` |
@@ -152,7 +152,7 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
152
152
  | --- | --- | --- |
153
153
  | **셸** | 앱 전체 뼈대. 페이지가 바뀌어도 남는다 | `SLayout` `SGnb` `SPage`(제목 영역은 `header` prop) |
154
154
  | **블록** | `SPage` 의 직계 자식. 페이지를 세로로 쌓는 단위 | `SSectionHeaderCard` `SCard` `SForm` `SSplitter` `SScrollArea` `STable` `STableBar` `SChipFilter` `SKeyValueTable` `SList` `SExpansionList` `SDraggableList` `STree` `SCallout` `STabs` `SStepper` `SPagination` `SDivider` |
155
- | **요소** | 블록 **안에** 놓이는 컨트롤. 혼자 페이지에 서지 않는다 | `SButton` `SGhostButton` `SDropdownButton` `SField` `SInput` `SSearchInput` `SNumberInput` `STextarea` `SEditor` `SSelect` `SCheckbox` `SRadio` `SRadioGroup` `SRadioButton` `SSwitch` `SToggle` `SChipInput` `SBarcodeInput` `SFilePicker` `SDatePicker` `SDatePickerYearListbox` `SDatePickerMonthListbox` `SDateRangePicker` `STimePicker` `STimeRangePicker` `SCalendar` `SListItem` `SExpansionItem` `SDraggableItem` `SImage` `SLinearProgress` `SCircleProgress` |
155
+ | **요소** | 블록 **안에** 놓이는 컨트롤. 혼자 페이지에 서지 않는다 | `SButton` `SGhostButton` `SDropdownButton` `SSystemActionButton` `SField` `SInput` `SSearchInput` `SNumberInput` `STextarea` `SEditor` `SSelect` `SCheckbox` `SRadio` `SRadioGroup` `SRadioButton` `SSwitch` `SToggle` `SChipInput` `SBarcodeInput` `SFilePicker` `SDatePicker` `SDatePickerYearListbox` `SDatePickerMonthListbox` `SDateRangePicker` `STimePicker` `STimeRangePicker` `SCalendar` `SListItem` `SExpansionItem` `SDraggableItem` `SImage` `SLinearProgress` `SCircleProgress` |
156
156
  | **인라인** | 텍스트 흐름·셀·라벨 안에 섞인다. 혼자 블록이 되지 않는다 | `STag` `SBadge` `SIcon` `STextLink` `SChip` |
157
157
  | **레이어** | 문서 흐름 **밖**에 떠서 그려진다. 어느 층에서 띄우든 레이아웃에 영향이 없다 | `SModal` `SActionModal` `SConfirmModal` `SPopup` `SDrawer` `SPopover` `STooltip` `SPortal` `SToast` `SLoadingModal` `SLoadingContainer` `SGuide` |
158
158
 
@@ -523,6 +523,7 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
523
523
  | 라벨이 있는 일반 액션을 준다 | `SButton` | §3-5 |
524
524
  | 아이콘 하나로 뜻이 통하는 부가 조작을 준다 | `SGhostButton` | §3-5-5 |
525
525
  | 한 버튼에 여러 선택지를 매단다 | `SDropdownButton` | §3-5-4 |
526
+ | GNB system 패널에서 한 줄짜리 행 액션을 준다 | `SSystemActionButton` | §3-5-7 |
526
527
 
527
528
  #### D. 화면을 담고 나눈다
528
529
 
@@ -1012,6 +1013,7 @@ const columns: STableColumn[] = [
1012
1013
  | **아이콘 하나로 뜻이 통하는 부가 조작** | **`SGhostButton`** — 라벨이 없는 아이콘 전용 버튼 (아래 3-5-5) |
1013
1014
  | 본문 속 이동 링크 | `STextLink` |
1014
1015
  | 메인 액션 + 부가 메뉴 | `SDropdownButton` |
1016
+ | **GNB system 패널의 한 줄 액션** | **`SSystemActionButton`** — 행 전체가 버튼인 표면형 (아래 3-5-7) |
1015
1017
 
1016
1018
  #### 3-5-1. 채움 버튼은 페이지당 개수 제한이 있다
1017
1019
 
@@ -1144,6 +1146,34 @@ const columns: STableColumn[] = [
1144
1146
  </div>
1145
1147
  ```
1146
1148
 
1149
+ #### 3-5-7. `SSystemActionButton` — GNB system 패널의 한 줄 액션
1150
+
1151
+ **행 전체가 하나의 버튼**인 표면형 컨트롤이다. 아이콘 프레임 + 라벨 + 우측 액션이 한 줄에 붙고, hover 하면 표면색이 한 단계 바뀐다.
1152
+
1153
+ | 상황 | 사용 |
1154
+ | --- | --- |
1155
+ | 페이지·모달의 일반 액션 | `SButton` — 라벨 폭만큼만 차지한다 |
1156
+ | 목록에서 항목 하나를 고르게 한다 | `SListItem` · `STree` (§3-7-6·§3-7-7) |
1157
+ | 좌측 내비게이션의 메뉴 | `SGnb` 의 `menus` (§4-1) |
1158
+ | **GNB system 패널에서 설정·연결 상태를 한 줄씩 쌓고, 그 줄을 눌러 들어가게 한다** | **`SSystemActionButton`** |
1159
+
1160
+ **무엇이 그려지는지는 `type` 이 정한다.** 타입마다 다른 값만 `option` 으로 받으므로, 타입에 맞지 않는 `option` 은 타입 에러가 된다.
1161
+
1162
+ | `type` | 선행 아이콘 | 라벨 | 우측 | 언제 |
1163
+ | --- | --- | --- | --- | --- |
1164
+ | `account` | `user` 고정 | `label` | 없음 | 로그인한 계정을 보여주고 계정 화면으로 보낼 때 |
1165
+ | `select` | `option.icon` (필수) | `label` | 화살표 | 눌러서 하위 화면으로 들어갈 때 |
1166
+ | `switch` | `option.icon` (필수) | `label` | `option.text` + 새로고침 | 지금 붙어 있는 대상을 갈아 끼울 때 |
1167
+ | `subSelect` | `option.icon` (필수) | `label` | `option.text` + 화살표 | 지금 선택돼 있는 값을 함께 보여줄 때 |
1168
+ | `status` | 없음 | 상태 태그(`option.color`·`option.label`) | 화살표 | 라벨 대신 상태 자체가 제목일 때 |
1169
+
1170
+ - **`SPage` 본문에 직접 놓지 않는다.** 폭을 가득 채우는 행이라 페이지에 바로 놓으면 표처럼 보인다. GNB system 패널·`SDrawer`·`SPopover` 처럼 **좁은 폭이 정해진 자리**에 쌓는다. 행 사이 간격은 `--cmp-gnb-system-panel-gap` 이다.
1171
+ - **`color` 는 놓이는 표면을 따라간다** — 흰 면이면 `light`, 오션블루 면이면 `dark`, 네이비 면이면 `darker`. 배경과 다른 색을 고르면 버튼만 떠 보인다.
1172
+ - **상태는 default·hover 둘뿐이다.** 토큰에 selected 가 없으므로 "지금 열려 있는 항목"을 이 버튼으로 표시하지 않는다. 선택 상태가 필요한 자리면 `SGnb` 메뉴나 `SListItem`(§3-7-6) 이다.
1173
+ - **선행 아이콘이 비는 행은 없다.** `account` 는 `user` 고정, `status` 는 태그가 자리를 차지하고, 나머지는 `option.icon` 이 필수다.
1174
+ - **우측 아이콘은 `type` 이 정하므로 밖에서 바꾸지 않는다.** 화살표가 필요한데 문구가 없으면 `select`, 문구가 함께 필요하면 `subSelect` 다.
1175
+ - 높이·라운드·아이콘 크기·좌우 여백은 전부 토큰이 넣는다. 직접 주지 않는다.
1176
+
1147
1177
  ### 3-6. 영역 나누기 — SDivider vs SSplitter
1148
1178
 
1149
1179
  | 상황 | 사용 |
@@ -11,8 +11,7 @@
11
11
  | `fields?` | `SChipFilterGroup[]` | — | 필터 정의 목록. 묶을 것이 없어도 한 그룹으로 감싸 넘긴다 — `[{ fields: [...] }]`. 그룹은 rule로 함께 검증하거나 divider로 갈라 놓을 때 나눈다. |
12
12
  | `value?` | `SChipFilterValueMap` | — | 필터 값 맵 |
13
13
  | `activeKeys?` | `string[]` | — | 노출 필터 key 목록. 지정하면 제어 컴포넌트로 동작 — "필터 추가"로 고른 필드를 이 배열에 직접 넣어줘야 칩이 나타난다(onActiveKeysChange에서 받은 keys로 갱신). 지정하지 않으면 fixed·required 필드만 노출된 상태로 시작해 컴포넌트가 내부 상태로 관리하는 비제어 방식으로 동작한다. fixed·required 필드는 이 배열에 없어도 항상 노출된다 |
14
- | `label?` | `string` | `'검색 필터'` | 좌측 태그 텍스트 |
15
- | `showLabel?` | `boolean` | `false` | 좌측 태그(label)·구분선 표시 여부 |
14
+ | `label?` | `string` | | 좌측 태그 텍스트. 지정하면 태그와 그 뒤 구분선이 함께 노출되고, 지정하지 않으면(undefined) 둘 다 생략된다 |
16
15
  | `showReset?` | `boolean` | `true` | 검색 초기화 링크 표시 여부 |
17
16
  | `disabled?` | `boolean` | `false` | 바 비활성 상태 |
18
17
  | `className?` | `string` | — | |
@@ -24,7 +23,7 @@
24
23
  |-------|------|-------------|
25
24
  | `onValueChange` | `(value: SChipFilterValueMap) => void` | 전체 값 변경 — 편집 중인 값이 바뀔 때마다(선택할 때마다) 호출된다. 실제 검색 실행은 onSearch를 쓴다 |
26
25
  | `onFilterChange` | `(detail: SChipFilterChangeDetail) => void` | 개별 필터 값 변경 |
27
- | `onSearch` | `(value: SChipFilterValueMap) => void` | 실제 검색을 실행할 시점 — 편집 팝오버의 "검색" 버튼을 누르거나 팝오버가 닫힐 때(바깥 클릭·Esc·다른 칩으로 전환 포함) 그 시점의 전체 값 맵과 함께 호출된다. 팝오버가 없는 필드(인라인 date 프리셋·custom, clearable ×, 검색 초기화)는 값이 바뀌는 즉시 호출된다. keyword 필터에서 Enter 로 키워드를 추가할 때도 그 즉시 호출된다 — 팝오버는 열린 채라 키워드를 이어서 더 넣을 수 있고, 넣을 때마다 조회가 갱신된다. dirty 체크가 기본 적용되어 있어 — 마지막으로 실제 검색이 실행된 값 맵과 비교해 하나라도 달라진 게 없으면(예: 팝오버를 열었다 아무것도 안 바꾸고 닫는 경우) 호출되지 않는다. fields를 그룹으로 넘겼다면 rule을 만족하지 못한 그룹이 있는 동안엔 onSearch가 호출되지 않는다 — 해당 그룹의 경고 툴팁은 이 시점과 무관하게 값이 비어 있는 동안 항상 실시간으로 떠 있다(별도로 validate()를 호출할 필요 없음) |
26
+ | `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로 비울 수 있어 이때의 안전장치다 |
28
27
  | `onActiveKeysChange` | `(keys: string[]) => void` | 노출 필터 key 변경(칩 추가·제거) — 비제어 방식에서도 참고용으로 호출된다. activeKeys를 직접 제어할 때는 이 값을 그대로 activeKeys에 반영해야 한다 |
29
28
  | `onReset` | `() => void` | "검색 초기화" 클릭 — 모든 필드가 기본값(또는 null)으로 리셋된 뒤 호출된다 |
30
29
  | `onAddFilter` | `(key: string) => void` | "필터 추가" 목록에서 항목을 골랐을 때 — activeKeys를 직접 제어 중이면 이 콜백에서 (또는 onActiveKeysChange에서) key를 activeKeys에 추가해줘야 칩이 실제로 나타난다. activeKeys를 넘기지 않았다면(비제어) 별도 처리 없이도 컴포넌트가 알아서 칩을 노출한다 |
@@ -35,7 +34,7 @@
35
34
  |--------|------|-------------|
36
35
  | `open` | `(key: string) => void` | 특정 필터 편집 팝오버를 엽니다. |
37
36
  | `reset` | `() => void` | 모든 필터 값을 초기화합니다. |
38
- | `validate` | `() => boolean` | 현재 값 기준으로 각 그룹의 rule을 만족하는지 반환합니다. rule을 준 그룹이 없으면 항상 true. 경고 툴팁은 이 호출과 무관하게 rule 위반 상태인 동안 항상 실시간으로 떠 있으므로, 이 메서드는 그 상태를 그대로 읽어오는 용도다(예: 검색 버튼을 눌러도 되는지 사전 확인) |
37
+ | `validate` | `() => boolean` | 현재 값 기준으로 각 그룹의 rule을 만족하는지, 그리고 required 필드가 모두 비어 있지 않은지 반환합니다. rule을 준 그룹이 없고 required 필드도 모두 값이 있으면 항상 true. 경고 툴팁은 이 호출과 무관하게 rule 위반 상태인 동안 항상 실시간으로 떠 있으므로, 이 메서드는 그 상태를 그대로 읽어오는 용도다(예: 검색 버튼을 눌러도 되는지 사전 확인) |
39
38
 
40
39
  ## Types
41
40
 
@@ -52,7 +51,8 @@ export interface SChipFilterGroup {
52
51
  /** rule을 만족하지 않을 때 그룹 중앙에 띄울 툴팁 메시지. 지정 안 하면 rule 종류에 따른 기본 문구를 쓴다 */
53
52
  tooltipMessage?: string;
54
53
  /** 이 그룹 앞에 구분선을 넣을지. 검증(rule)과 구분선은 별개라 — 묶어서 검증만 하고 싶으면
55
- * 주지 않는다. 첫 그룹에는 앞에 가를 것이 없으므로 무시된다(showLabel의 구분선이 이미 있다) */
54
+ * 주지 않는다. 첫 그룹에는 앞에 가를 것이 없으므로 무시된다(label을 지정했다면 구분선이
55
+ * 이미 있다) */
56
56
  divider?: boolean;
57
57
  }
58
58
  ```
@@ -172,7 +172,8 @@ export interface SChipFilterGroup {
172
172
  /** rule을 만족하지 않을 때 그룹 중앙에 띄울 툴팁 메시지. 지정 안 하면 rule 종류에 따른 기본 문구를 쓴다 */
173
173
  tooltipMessage?: string;
174
174
  /** 이 그룹 앞에 구분선을 넣을지. 검증(rule)과 구분선은 별개라 — 묶어서 검증만 하고 싶으면
175
- * 주지 않는다. 첫 그룹에는 앞에 가를 것이 없으므로 무시된다(showLabel의 구분선이 이미 있다) */
175
+ * 주지 않는다. 첫 그룹에는 앞에 가를 것이 없으므로 무시된다(label을 지정했다면 구분선이
176
+ * 이미 있다) */
176
177
  divider?: boolean;
177
178
  }
178
179
  export interface SChipFilterHandle {
@@ -180,7 +181,8 @@ export interface SChipFilterHandle {
180
181
  open: (key: string) => void;
181
182
  /** 모든 필터 값을 초기화합니다. */
182
183
  reset: () => void;
183
- /** 현재 값 기준으로 각 그룹의 rule을 만족하는지 반환합니다. rule을 그룹이 없으면 항상 true.
184
+ /** 현재 값 기준으로 각 그룹의 rule을 만족하는지, 그리고 required 필드가 모두 비어 있지 않은지
185
+ * 반환합니다. rule을 준 그룹이 없고 required 필드도 모두 값이 있으면 항상 true.
184
186
  * 경고 툴팁은 이 호출과 무관하게 rule 위반 상태인 동안 항상 실시간으로 떠 있으므로,
185
187
  * 이 메서드는 그 상태를 그대로 읽어오는 용도다(예: 검색 버튼을 눌러도 되는지 사전 확인) */
186
188
  validate: () => boolean;
@@ -202,7 +204,10 @@ export interface SChipFilterProps {
202
204
  * 실제 검색이 실행된 값 맵과 비교해 하나라도 달라진 게 없으면(예: 팝오버를 열었다 아무것도 안 바꾸고
203
205
  * 닫는 경우) 호출되지 않는다. fields를 그룹으로 넘겼다면 rule을 만족하지 못한 그룹이 있는 동안엔
204
206
  * onSearch가 호출되지 않는다 — 해당 그룹의 경고 툴팁은 이 시점과 무관하게 값이 비어 있는 동안 항상
205
- * 실시간으로 떠 있다(별도로 validate()를 호출할 필요 없음) */
207
+ * 실시간으로 떠 있다(별도로 validate()를 호출할 필요 없음). required 필드가 비어 있는 동안에도
208
+ * 마찬가지로 호출되지 않는다 — 정상 설정(내장 기본값 또는 defaultValue)이라면 원래 비워지지
209
+ * 않지만, defaultValue 없는 select-multi·keyword·custom에 required만 준 오설정에서는 "검색
210
+ * 초기화"·clearable이 값을 null로 비울 수 있어 이때의 안전장치다 */
206
211
  onSearch?: (value: SChipFilterValueMap) => void;
207
212
  /** 노출 필터 key 목록. 지정하면 제어 컴포넌트로 동작 — "필터 추가"로 고른 필드를 이 배열에 직접
208
213
  * 넣어줘야 칩이 나타난다(onActiveKeysChange에서 받은 keys로 갱신). 지정하지 않으면
@@ -218,10 +223,9 @@ export interface SChipFilterProps {
218
223
  * (또는 onActiveKeysChange에서) key를 activeKeys에 추가해줘야 칩이 실제로 나타난다.
219
224
  * activeKeys를 넘기지 않았다면(비제어) 별도 처리 없이도 컴포넌트가 알아서 칩을 노출한다 */
220
225
  onAddFilter?: (key: string) => void;
221
- /** 좌측 태그 텍스트 */
226
+ /** 좌측 태그 텍스트. 지정하면 태그와 그 뒤 구분선이 함께 노출되고, 지정하지 않으면(undefined)
227
+ * 둘 다 생략된다 */
222
228
  label?: string;
223
- /** 좌측 태그(label)·구분선 표시 여부 */
224
- showLabel?: boolean;
225
229
  /** 검색 초기화 링크 표시 여부 */
226
230
  showReset?: boolean;
227
231
  /** 바 비활성 상태 */
@@ -48,6 +48,7 @@
48
48
  - [SSectionHeaderCard](../SSectionHeaderCard)
49
49
  - [SSelect](../SSelect)
50
50
  - [SStepper](../SStepper)
51
+ - [SSystemActionButton](../SSystemActionButton)
51
52
  - [STable](../STable)
52
53
  - [STag](../STag)
53
54
  - [STextLink](../STextLink)
@@ -88,6 +89,7 @@ graph TD;
88
89
  SSectionHeaderCard --> SIcon
89
90
  SSelect --> SIcon
90
91
  SStepper --> SIcon
92
+ SSystemActionButton --> SIcon
91
93
  STable --> SIcon
92
94
  STag --> SIcon
93
95
  STextLink --> SIcon
@@ -0,0 +1,129 @@
1
+ # SSystemActionButton
2
+
3
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
4
+
5
+ ### SSystemActionButtonAccount
6
+
7
+ #### Props
8
+
9
+ | Prop | Type | Default | Description |
10
+ |------|------|---------|-------------|
11
+ | `type` | `'account'` | — | 계정 행 |
12
+ | `label?` | `string` | — | 라벨 텍스트 |
13
+ | `option?` | `never` | — | 아이콘이 `user` 로 고정이라 받을 것이 없다 |
14
+
15
+ ### SSystemActionButtonBase
16
+
17
+ #### Props
18
+
19
+ | Prop | Type | Default | Description |
20
+ |------|------|---------|-------------|
21
+ | `color?` | `SSystemActionButtonColor` | — | 색상(놓이는 표면). light = 흰 면, dark = 오션블루 면, darker = 네이비 면. 기본 `'light'` |
22
+ | `disabled?` | `boolean` | — | 비활성 여부. 기본 `false` |
23
+
24
+ ### SSystemActionButtonSelect
25
+
26
+ #### Props
27
+
28
+ | Prop | Type | Default | Description |
29
+ |------|------|---------|-------------|
30
+ | `type` | `'select'` | — | 화살표만 붙는 행 |
31
+ | `label?` | `string` | — | 라벨 텍스트 |
32
+ | `option` | `SSystemActionButtonIconOption` | — | 선행 아이콘 |
33
+
34
+ ### SSystemActionButtonStatus
35
+
36
+ #### Props
37
+
38
+ | Prop | Type | Default | Description |
39
+ |------|------|---------|-------------|
40
+ | `type` | `'status'` | — | 라벨 자리에 상태 태그가 들어가는 행 |
41
+ | `label?` | `never` | — | 태그가 라벨 자리를 차지하므로 받지 않는다 |
42
+ | `option?` | `SSystemActionButtonStatusOption` | — | 상태 태그 |
43
+
44
+ ### SSystemActionButtonText
45
+
46
+ #### Props
47
+
48
+ | Prop | Type | Default | Description |
49
+ |------|------|---------|-------------|
50
+ | `type` | `'switch' \| 'subSelect'` | — | 우측에 문구가 붙는 행. switch = 보조 조작 문구, subSelect = 지금 선택된 값 |
51
+ | `label?` | `string` | — | 라벨 텍스트 |
52
+ | `option` | `SSystemActionButtonTextOption` | — | 선행 아이콘 + 우측 액션 텍스트 |
53
+
54
+ ## Types
55
+
56
+ ### SSystemActionButtonColor
57
+
58
+ ```ts
59
+ export type SSystemActionButtonColor = (typeof SYSTEM_ACTION_BUTTON_COLORS)[number];
60
+ ```
61
+
62
+ ### SSystemActionButtonIconOption
63
+
64
+ ```ts
65
+ /** 선행 아이콘만 받는 타입(`select`)의 option */
66
+ export interface SSystemActionButtonIconOption {
67
+ /** 선행 아이콘명 */
68
+ icon: SIconName;
69
+ }
70
+ ```
71
+
72
+ ### SSystemActionButtonStatusOption
73
+
74
+ ```ts
75
+ /** `status` 타입의 option — 라벨 자리에 들어가는 상태 태그 */
76
+ export interface SSystemActionButtonStatusOption {
77
+ /** 상태 태그 색 */
78
+ color?: STagColor;
79
+ /** 상태 태그 텍스트 */
80
+ label?: string;
81
+ }
82
+ ```
83
+
84
+ ### SSystemActionButtonTextOption
85
+
86
+ ```ts
87
+ /** 우측 액션 텍스트까지 받는 타입(`switch` · `subSelect`)의 option */
88
+ export interface SSystemActionButtonTextOption extends SSystemActionButtonIconOption {
89
+ /** 우측 액션 텍스트 */
90
+ text?: string;
91
+ }
92
+ ```
93
+
94
+ ### SYSTEM_ACTION_BUTTON_COLORS
95
+
96
+ ```ts
97
+ /**
98
+ * SSystemActionButton 설정.
99
+ *
100
+ * 토큰은 GNB system 패널 한 벌(`--cmp-gnb-system-*`)을 그대로 쓴다.
101
+ * 행 공통(높이·라운드·아이콘 프레임·라벨) → --cmp-gnb-system-item-*
102
+ * 색(color 축) light / dark / darker → --cmp-gnb-system-item-<color>-*
103
+ * 우측 액션(패딩·간격·아이콘·텍스트색) → --cmp-gnb-system-actionButton-*
104
+ *
105
+ * 타이포그래피는 CSS 변수가 아니라 `.typo-*` 프리셋으로 나온다.
106
+ * item.label → typo-body-sm-semibold (12/600/20)
107
+ * actionButton.switch → typo-field-sm-default (12/400/20)
108
+ * actionButton.selectData → typo-control-xs-medium (12/500/20)
109
+ */
110
+
111
+ /** 색상(표면). light = 흰 면, dark = 오션블루 면, darker = 네이비 면 */
112
+ export const SYSTEM_ACTION_BUTTON_COLORS = ['light', 'dark', 'darker'] as const;
113
+ ```
114
+
115
+ ## Dependencies
116
+
117
+ ### Depends on
118
+
119
+ - [SIcon](../SIcon)
120
+ - [STag](../STag)
121
+
122
+ ### Graph
123
+
124
+ ```mermaid
125
+ graph TD;
126
+ SSystemActionButton --> SIcon
127
+ SSystemActionButton --> STag
128
+ style SSystemActionButton fill:#f9f,stroke:#333,stroke-width:4px
129
+ ```
@@ -0,0 +1,79 @@
1
+ import { type ButtonHTMLAttributes } from 'react';
2
+ import { type SIconName } from '../SIcon';
3
+ import { type STagColor } from '../STag';
4
+ import { type SSystemActionButtonColor } from './systemActionButton.config';
5
+ /** 선행 아이콘만 받는 타입(`select`)의 option */
6
+ export interface SSystemActionButtonIconOption {
7
+ /** 선행 아이콘명 */
8
+ icon: SIconName;
9
+ }
10
+ /** 우측 액션 텍스트까지 받는 타입(`switch` · `subSelect`)의 option */
11
+ export interface SSystemActionButtonTextOption extends SSystemActionButtonIconOption {
12
+ /** 우측 액션 텍스트 */
13
+ text?: string;
14
+ }
15
+ /** `status` 타입의 option — 라벨 자리에 들어가는 상태 태그 */
16
+ export interface SSystemActionButtonStatusOption {
17
+ /** 상태 태그 색 */
18
+ color?: STagColor;
19
+ /** 상태 태그 텍스트 */
20
+ label?: string;
21
+ }
22
+ /** 모든 타입이 공통으로 받는 props */
23
+ export interface SSystemActionButtonBaseProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'color' | 'type' | 'children'> {
24
+ /** 색상(놓이는 표면). light = 흰 면, dark = 오션블루 면, darker = 네이비 면. 기본 `'light'` */
25
+ color?: SSystemActionButtonColor;
26
+ /** 비활성 여부. 기본 `false` */
27
+ disabled?: boolean;
28
+ }
29
+ /** 계정 행 — 아이콘이 `user` 로 고정이고 우측 액션이 없다 */
30
+ export interface SSystemActionButtonAccountProps extends SSystemActionButtonBaseProps {
31
+ /** 계정 행 */
32
+ type: 'account';
33
+ /** 라벨 텍스트 */
34
+ label?: string;
35
+ /** 아이콘이 `user` 로 고정이라 받을 것이 없다 */
36
+ option?: never;
37
+ }
38
+ /** 화살표만 붙는 행 — 눌러 들어간다는 뜻 */
39
+ export interface SSystemActionButtonSelectProps extends SSystemActionButtonBaseProps {
40
+ /** 화살표만 붙는 행 */
41
+ type: 'select';
42
+ /** 라벨 텍스트 */
43
+ label?: string;
44
+ /** 선행 아이콘 */
45
+ option: SSystemActionButtonIconOption;
46
+ }
47
+ /** 우측에 문구가 붙는 행 — `switch` 는 보조 조작 문구, `subSelect` 는 지금 선택된 값 */
48
+ export interface SSystemActionButtonTextProps extends SSystemActionButtonBaseProps {
49
+ /** 우측에 문구가 붙는 행. switch = 보조 조작 문구, subSelect = 지금 선택된 값 */
50
+ type: 'switch' | 'subSelect';
51
+ /** 라벨 텍스트 */
52
+ label?: string;
53
+ /** 선행 아이콘 + 우측 액션 텍스트 */
54
+ option: SSystemActionButtonTextOption;
55
+ }
56
+ /** 라벨 자리에 상태 태그가 들어가는 행 */
57
+ export interface SSystemActionButtonStatusProps extends SSystemActionButtonBaseProps {
58
+ /** 라벨 자리에 상태 태그가 들어가는 행 */
59
+ type: 'status';
60
+ /** 태그가 라벨 자리를 차지하므로 받지 않는다 */
61
+ label?: never;
62
+ /** 상태 태그 */
63
+ option?: SSystemActionButtonStatusOption;
64
+ }
65
+ /** `type` 으로 갈리는 판별 유니온. 타입에 맞지 않는 option 은 컴파일 단계에서 막힌다. */
66
+ export type SSystemActionButtonProps = SSystemActionButtonAccountProps | SSystemActionButtonSelectProps | SSystemActionButtonTextProps | SSystemActionButtonStatusProps;
67
+ /**
68
+ * SSystemActionButton — GNB system 패널의 한 줄짜리 액션 버튼.
69
+ *
70
+ * 아이콘 프레임 + 라벨 + 우측 액션이 한 행에 붙고, 행 전체가 버튼이다. 무엇이 그려지는지는
71
+ * `type` 이 정하고(`account` `switch` `select` `subSelect` `status`), 타입마다 다른 값만
72
+ * `option` 으로 받는다. 선행 아이콘은 항상 있다 — `account` 는 `user` 고정, `status` 는 태그가
73
+ * 자리를 차지해 아이콘이 없고, 나머지는 `option.icon` 이 필수다.
74
+ *
75
+ * hover 하면 표면색이 한 단계 바뀌고, 어두운 색에서는 아이콘 프레임이 흰색으로 떠오른다.
76
+ * 상태는 default·hover 둘뿐이다 — 토큰에 selected 가 없다.
77
+ * 토큰은 `--cmp-gnb-system-item-*` · `--cmp-gnb-system-actionButton-*`.
78
+ */
79
+ export declare const SSystemActionButton: import("react").ForwardRefExoticComponent<SSystemActionButtonProps & import("react").RefAttributes<HTMLButtonElement>>;
@@ -0,0 +1,2 @@
1
+ export { SSystemActionButton, type SSystemActionButtonProps, type SSystemActionButtonBaseProps, type SSystemActionButtonAccountProps, type SSystemActionButtonSelectProps, type SSystemActionButtonTextProps, type SSystemActionButtonStatusProps, type SSystemActionButtonIconOption, type SSystemActionButtonTextOption, type SSystemActionButtonStatusOption, } from './SSystemActionButton';
2
+ export { SYSTEM_ACTION_BUTTON_COLORS, SYSTEM_ACTION_BUTTON_TYPES, SYSTEM_ACTION_BUTTON_COLOR_CONFIG, SYSTEM_ACTION_BUTTON_TYPE_CONFIG, SYSTEM_ACTION_BUTTON_LAYOUT, type SSystemActionButtonColor, type SSystemActionButtonType, } from './systemActionButton.config';
@@ -0,0 +1,87 @@
1
+ import type { SIconName } from '../SIcon';
2
+ /**
3
+ * SSystemActionButton 설정.
4
+ *
5
+ * 토큰은 GNB system 패널 한 벌(`--cmp-gnb-system-*`)을 그대로 쓴다.
6
+ * 행 공통(높이·라운드·아이콘 프레임·라벨) → --cmp-gnb-system-item-*
7
+ * 색(color 축) light / dark / darker → --cmp-gnb-system-item-<color>-*
8
+ * 우측 액션(패딩·간격·아이콘·텍스트색) → --cmp-gnb-system-actionButton-*
9
+ *
10
+ * 타이포그래피는 CSS 변수가 아니라 `.typo-*` 프리셋으로 나온다.
11
+ * item.label → typo-body-sm-semibold (12/600/20)
12
+ * actionButton.switch → typo-field-sm-default (12/400/20)
13
+ * actionButton.selectData → typo-control-xs-medium (12/500/20)
14
+ */
15
+ /** 색상(표면). light = 흰 면, dark = 오션블루 면, darker = 네이비 면 */
16
+ export declare const SYSTEM_ACTION_BUTTON_COLORS: readonly ["light", "dark", "darker"];
17
+ export type SSystemActionButtonColor = (typeof SYSTEM_ACTION_BUTTON_COLORS)[number];
18
+ /**
19
+ * 행 구성.
20
+ * - `account` — 계정 행. 아이콘이 `user` 로 고정이고 우측 액션이 없다
21
+ * - `switch` — 보조 조작 문구 + 새로고침 아이콘
22
+ * - `select` — 화살표만. 눌러 들어간다는 뜻
23
+ * - `subSelect` — 지금 선택돼 있는 값 + 화살표
24
+ * - `status` — 라벨 자리에 상태 태그가 들어가고 화살표가 붙는다
25
+ */
26
+ export declare const SYSTEM_ACTION_BUTTON_TYPES: readonly ["account", "switch", "select", "subSelect", "status"];
27
+ export type SSystemActionButtonType = (typeof SYSTEM_ACTION_BUTTON_TYPES)[number];
28
+ /** 우측 액션 텍스트의 색·타이포가 어느 토큰 그룹을 쓰는지 (토큰의 하위 그룹명과 같다) */
29
+ export type SystemActionButtonActionTextToken = 'switch' | 'selectData';
30
+ export interface SystemActionButtonColorTokens {
31
+ /** 행 배경 (기본) */
32
+ bg: string;
33
+ /** 행 배경 (hover·selected) */
34
+ bgHover: string;
35
+ /** 아이콘 프레임 배경 (기본) */
36
+ iconBg: string;
37
+ /** 아이콘 프레임 배경 (hover·selected) */
38
+ iconBgHover: string;
39
+ /** 아이콘 글리프색 (기본) */
40
+ iconGlyph: string;
41
+ /** 아이콘 글리프색 (hover·selected) */
42
+ iconGlyphHover: string;
43
+ /** 라벨 텍스트색 */
44
+ text: string;
45
+ /** 우측 액션 텍스트색 (토큰 그룹별) */
46
+ actionText: Record<SystemActionButtonActionTextToken, string>;
47
+ /** 우측 액션 아이콘색 */
48
+ actionIcon: string;
49
+ }
50
+ export declare const SYSTEM_ACTION_BUTTON_COLOR_CONFIG: Record<SSystemActionButtonColor, SystemActionButtonColorTokens>;
51
+ export interface SystemActionButtonTypeSpec {
52
+ /** 선행 아이콘 고정값. 있으면 `option.icon` 을 받지 않는다 */
53
+ fixedIcon?: SIconName;
54
+ /** 우측 액션 텍스트의 색·타이포. 없으면 텍스트를 그리지 않는다 */
55
+ actionText?: {
56
+ token: SystemActionButtonActionTextToken;
57
+ typo: string;
58
+ };
59
+ /** 우측 액션 아이콘. 타입이 정하므로 밖에서 바꾸지 않는다 */
60
+ actionIcon?: {
61
+ name: SIconName;
62
+ size: string;
63
+ };
64
+ }
65
+ /** 타입이 정하는 고정 구성 — 무엇이 그려지는가 */
66
+ export declare const SYSTEM_ACTION_BUTTON_TYPE_CONFIG: Record<SSystemActionButtonType, SystemActionButtonTypeSpec>;
67
+ /** 치수 — 전부 토큰 참조 */
68
+ export declare const SYSTEM_ACTION_BUTTON_LAYOUT: {
69
+ /** 행 높이 */
70
+ readonly height: string;
71
+ /** 행 라운드 */
72
+ readonly radius: string;
73
+ /** 아이콘 프레임 한 변 */
74
+ readonly iconFrame: string;
75
+ /** 아이콘 글리프 크기 */
76
+ readonly iconGlyph: string;
77
+ /** 아이콘 프레임 라운드 */
78
+ readonly iconRadius: string;
79
+ /** 아이콘 프레임 ↔ 라벨 간격 */
80
+ readonly iconLabelGap: string;
81
+ /** 좌측 여백 */
82
+ readonly paddingLeft: string;
83
+ /** 우측 여백 */
84
+ readonly paddingRight: string;
85
+ /** 액션 텍스트 ↔ 액션 아이콘 간격 */
86
+ readonly actionGap: string;
87
+ };
@@ -9,6 +9,7 @@
9
9
  | Prop | Type | Default | Description |
10
10
  |------|------|---------|-------------|
11
11
  | `columns?` | `STableColumn[]` | `[]` | |
12
+ | `headerGroups?` | `STableHeaderGroup[]` | — | 상위 그룹 헤더. 지정하면 기존 컬럼 헤더가 두 번째 행에 렌더링된다. |
12
13
  | `rows?` | `SRow[]` | `[]` | |
13
14
  | `rowKey?` | `string` | `'id'` | 행 식별 필드 |
14
15
  | `selectable?` | `boolean` | `false` | 행 선택 체크박스 |
@@ -78,6 +79,21 @@ export type STableColumn = STableColumnBase &
78
79
  );
79
80
  ```
80
81
 
82
+ ### STableHeaderGroup
83
+
84
+ ```ts
85
+ /** 2단 헤더의 상위 셀. columnNames는 visible 컬럼 기준으로 연속되어야 한다. */
86
+ export interface STableHeaderGroup {
87
+ label?: ReactNode;
88
+ columnNames: string[];
89
+ rowSpan?: 1 | 2;
90
+ align?: 'left' | 'center' | 'right';
91
+ className?: string;
92
+ style?: CSSProperties;
93
+ render?: () => ReactNode;
94
+ }
95
+ ```
96
+
81
97
  ### SRow
82
98
 
83
99
  ```ts
@@ -147,6 +147,16 @@ export interface STableSort {
147
147
  name: string;
148
148
  dir: 'asc' | 'desc';
149
149
  }
150
+ /** 2단 헤더의 상위 셀. columnNames는 visible 컬럼 기준으로 연속되어야 한다. */
151
+ export interface STableHeaderGroup {
152
+ label?: ReactNode;
153
+ columnNames: string[];
154
+ rowSpan?: 1 | 2;
155
+ align?: 'left' | 'center' | 'right';
156
+ className?: string;
157
+ style?: CSSProperties;
158
+ render?: () => ReactNode;
159
+ }
150
160
  /** STable ref 명령형 API (sd-table @Method 대응) */
151
161
  export interface STableHandle {
152
162
  /** 행 선택 여부 */
@@ -171,6 +181,8 @@ export interface STableHandle {
171
181
  }
172
182
  export interface STableProps {
173
183
  columns?: STableColumn[];
184
+ /** 상위 그룹 헤더. 지정하면 기존 컬럼 헤더가 두 번째 행에 렌더링된다. */
185
+ headerGroups?: STableHeaderGroup[];
174
186
  rows?: SRow[];
175
187
  /** 행 식별 필드 */
176
188
  rowKey?: string;
@@ -1 +1 @@
1
- export { STable, type STableProps, type STableColumn, type STableColumnField, type STableSort, type STableCellContext, type STablePagination, type STableStickyColumn, type STableHandle, type SRow, } from './STable';
1
+ export { STable, type STableProps, type STableColumn, type STableColumnField, type STableSort, type STableHeaderGroup, type STableCellContext, type STablePagination, type STableStickyColumn, type STableHandle, type SRow, } from './STable';
@@ -71,6 +71,7 @@ export const TAG_COLORS = [
71
71
  - [SConfirmModal](../SConfirmModal)
72
72
  - [SGhostButton](../SGhostButton)
73
73
  - [SGnb](../SGnb)
74
+ - [SSystemActionButton](../SSystemActionButton)
74
75
  - [STabs](../STabs)
75
76
  - [STextLink](../STextLink)
76
77
 
@@ -87,6 +88,7 @@ graph TD;
87
88
  SConfirmModal --> STag
88
89
  SGhostButton --> STag
89
90
  SGnb --> STag
91
+ SSystemActionButton --> STag
90
92
  STabs --> STag
91
93
  STextLink --> STag
92
94
  style STag fill:#f9f,stroke:#333,stroke-width:4px