sellmate-design-system-react 9.0.0-beta.29 → 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.
Files changed (35) hide show
  1. package/AGENTS.md +39 -2
  2. package/dist/components/SChipFilter/README.md +7 -8
  3. package/dist/components/SChipFilter/SChipFilter.d.ts +10 -16
  4. package/dist/components/SChipFilter/index.d.ts +1 -1
  5. package/dist/components/SChipInput/index.d.ts +1 -1
  6. package/dist/components/SConfirmModal/README.md +3 -3
  7. package/dist/components/SConfirmModal/SConfirmModal.d.ts +2 -2
  8. package/dist/components/SConfirmModal/index.d.ts +1 -1
  9. package/dist/components/SDraggableItem/index.d.ts +1 -1
  10. package/dist/components/SExpansionItem/index.d.ts +1 -1
  11. package/dist/components/SGnb/README.md +6 -0
  12. package/dist/components/SGnb/SGnb.d.ts +22 -0
  13. package/dist/components/SGnb/index.d.ts +1 -1
  14. package/dist/components/SGnbSystem/README.md +121 -0
  15. package/dist/components/SGnbSystem/SGnbSystem.d.ts +93 -0
  16. package/dist/components/SGnbSystem/gnbSystem.config.d.ts +89 -0
  17. package/dist/components/SGnbSystem/index.d.ts +2 -0
  18. package/dist/components/SIcon/README.md +2 -0
  19. package/dist/components/SKeyValueTable/index.d.ts +1 -1
  20. package/dist/components/SLayout/index.d.ts +1 -1
  21. package/dist/components/SListItem/index.d.ts +1 -1
  22. package/dist/components/SLoadingModal/README.md +3 -3
  23. package/dist/components/SLoadingModal/SLoadingModal.d.ts +2 -2
  24. package/dist/components/SLoadingModal/index.d.ts +1 -1
  25. package/dist/components/SSystemActionButton/README.md +5 -0
  26. package/dist/components/SToggle/index.d.ts +1 -1
  27. package/dist/index.cjs +530 -203
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.d.ts +1 -0
  30. package/dist/index.js +526 -204
  31. package/dist/index.js.map +1 -1
  32. package/dist/llms-full.txt +178 -16
  33. package/dist/llms.txt +41 -4
  34. package/dist/styles.css +81 -0
  35. package/package.json +1 -1
package/AGENTS.md CHANGED
@@ -30,7 +30,7 @@
30
30
  | **입력 (폼)** | `SForm` `SField` `SInput` `SSearchInput` `SNumberInput` `STextarea` `SEditor` `SSelect` `SCheckbox` `SRadio` `SRadioGroup` `SRadioButton` `SRadioCard` `SRadioCardGroup` `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` |
33
- | **레이아웃** | `SLayout` `SGnb` `SPage`(제목 영역은 `header` prop) `SSectionHeaderCard` `SCard` `SDivider` `SSplitter` `SScrollArea` `SExpansionItem` |
33
+ | **레이아웃** | `SLayout` `SGnb` `SGnbSystem` `SPage`(제목 영역은 `header` prop) `SSectionHeaderCard` `SCard` `SDivider` `SSplitter` `SScrollArea` `SExpansionItem` |
34
34
  | **내비게이션** | `STabs` `SPagination` `SStepper` |
35
35
  | **표시·상태** | `STag` `SBadge` `SIcon` `SImage` `SCallout` `SGuide` |
36
36
  | **진행·로딩** | `SLinearProgress` `SCircleProgress` `SLoadingContainer` `SLoadingModal` |
@@ -150,7 +150,7 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
150
150
 
151
151
  | 층 | 무엇인가 | 컴포넌트 |
152
152
  | --- | --- | --- |
153
- | **셸** | 앱 전체 뼈대. 페이지가 바뀌어도 남는다 | `SLayout` `SGnb` `SPage`(제목 영역은 `header` prop) |
153
+ | **셸** | 앱 전체 뼈대. 페이지가 바뀌어도 남는다 | `SLayout` `SGnb` `SGnbSystem`(GNB 맨 아래 패널) `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
155
  | **요소** | 블록 **안에** 놓이는 컨트롤. 혼자 페이지에 서지 않는다 | `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` |
156
156
  | **인라인** | 텍스트 흐름·셀·라벨 안에 섞인다. 혼자 블록이 되지 않는다 | `STag` `SBadge` `SIcon` `STextLink` `SChip` |
@@ -534,6 +534,7 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
534
534
  | --- | --- | --- |
535
535
  | 앱 셸(상단바 + 내비 + 본문)을 세운다 | `SLayout` | §4-1 |
536
536
  | 좌측 내비게이션을 만든다 | `SGnb` | §4-1 |
537
+ | 좌측 내비게이션 맨 아래에 서비스·도메인·알림·설정·계정 묶음을 붙인다 | `SGnbSystem` | §4-1 |
537
538
  | 페이지 본문을 담는다 (패딩·스크롤) | `SPage` | §4-1 |
538
539
  | 페이지 제목(+ 서브 텍스트·뒤로가기·우측 슬롯)을 만든다 | `SPage` 의 `header` prop | §4-1 |
539
540
  | 제목 있는 섹션으로 묶는다 | `SSectionHeaderCard` | §3-7-8 |
@@ -1171,6 +1172,7 @@ const columns: STableColumn[] = [
1171
1172
  | `status` | 없음 | 상태 태그(`option.color`·`option.label`) | 화살표 | 라벨 대신 상태 자체가 제목일 때 |
1172
1173
 
1173
1174
  - **`SPage` 본문에 직접 놓지 않는다.** 폭을 가득 채우는 행이라 페이지에 바로 놓으면 표처럼 보인다. GNB system 패널·`SDrawer`·`SPopover` 처럼 **좁은 폭이 정해진 자리**에 쌓는다. 행 사이 간격은 `--cmp-gnb-system-panel-gap` 이다.
1175
+ - **GNB 맨 아래에 쌓는 것이라면 이 버튼을 직접 배치하지 말고 `SGnbSystem` 의 `actions` 에 넘긴다** (§4-1). 패널의 배경·구분선·도메인 태그·알림·계정까지 한 벌로 그려주므로, 조각을 손으로 조립할 이유가 없다.
1174
1176
  - **`color` 는 놓이는 표면을 따라간다** — 흰 면이면 `light`, 오션블루 면이면 `dark`, 네이비 면이면 `darker`. 배경과 다른 색을 고르면 버튼만 떠 보인다.
1175
1177
  - **상태는 default·hover 둘뿐이다.** 토큰에 selected 가 없으므로 "지금 열려 있는 항목"을 이 버튼으로 표시하지 않는다. 선택 상태가 필요한 자리면 `SGnb` 메뉴나 `SListItem`(§3-7-6) 이다.
1176
1178
  - **선행 아이콘이 비는 행은 없다.** `account` 는 `user` 고정, `status` 는 태그가 자리를 차지하고, 나머지는 `option.icon` 이 필수다.
@@ -1287,8 +1289,11 @@ const columns: STableColumn[] = [
1287
1289
  - **`SRadio` 를 직접 나열하지 않는다.** 그룹 간격은 `SRadioGroup` 이 맞춘다 (§2-2). `SRadio` 단독은 `SRadioGroup` 이 만들 수 없는 배치 — 표 셀 안에 행마다 하나씩 놓는 경우 — 에만 쓴다.
1288
1290
  - `SRadioButton` 은 `options` 를 통째로 받는 세그먼트 컨트롤이라 `SRadio` 를 여러 개 넣는 게 아니다.
1289
1291
  - **`SRadioGroup` 과 `SRadioCardGroup` 의 갈림길은 "설명이 필요한가" 하나다.** 라벨만으로 뜻이 통하면 `SRadioGroup`, 고르기 전에 한 줄 설명을 읽어야 하면 `SRadioCardGroup` 이다. 라벨 옆에 설명을 괄호로 덧붙여 늘이지 않는다.
1292
+ - **`SRadioCardGroup` 은 `SRadioGroup` 의 대체가 아니라 격상판이다.** 라벨이 한 단계 크고 그 아래 설명 줄까지 들어가 카드 한 장이 자리를 넉넉히 쓴다 — 선택지가 조밀하게 반복되는 자리에는 과하다.
1293
+ - **쓰는 자리는 설정 화면·마법사(스텝)의 소수 결정이다.** 고르고 나면 뒤 화면이 통째로 바뀌거나 되돌리기 어려운 선택 — 평가 대상자 범위(전체/특정 구성원), 상담 자동 배정 방식 같은 것. 목록 필터·표 안 조건처럼 같은 선택지가 화면마다 반복 노출되는 자리에는 `SRadioGroup` 을 쓴다.
1290
1294
  - `SRadioCard` 단독은 `SRadioCardGroup` 이 만들 수 없는 배치에만 쓴다 — 나열은 그룹이 맡는다 (§2-2).
1291
1295
  - 카드 안에 상태 배지를 넣으려면 `tag` 슬롯에 `STag` 를 준다. 라벨 문자열에 "(추천)" 처럼 섞어 쓰지 않는다.
1296
+ - **카드처럼 생겼다고 `SCard`/`SSectionHeaderCard` 로 감싸지 않는다.** `SRadioCard` 자체가 완결된 요소이고, 나열 간격은 `SRadioCardGroup` 의 `direction` 이 맞춘다 (§2-2).
1292
1297
 
1293
1298
  #### 3-7-3. 켜고 끄는 셋 — SCheckbox vs SSwitch vs SToggle
1294
1299
 
@@ -1459,6 +1464,7 @@ const [selectedId, setSelectedId] = useState<string>();
1459
1464
  - **기본은 `SKeyValueTable` 이다** (§4-2). 조건이 대여섯 개 이하로 고정이면 표로 펼쳐 두는 편이 한눈에 읽힌다.
1460
1465
  - `SChipFilter` 는 조건을 **칩 한 줄**로 접고, "필터 추가" 로 필요한 것만 꺼내 쓰게 한다. 칩을 누르면 편집 팝오버가 열리고, 날짜는 프리셋(오늘·지난 7일·사용자 지정)으로 고른다. 조건 후보가 많은 목록 화면에서 필터가 화면을 세로로 잡아먹는 것을 막는 용도다.
1461
1466
  - 검색 실행 시점이 다르다 — `SKeyValueTable` 필터는 앱이 검색 버튼을 직접 놓지만, `SChipFilter` 는 편집 팝오버가 닫히거나 "검색" 을 누를 때 `onSearch` 가 값 맵과 함께 호출된다. 값이 바뀌지 않았으면 호출되지 않는다.
1467
+ - **노출할 칩을 앱이 계산하지 않는다.** 바에 놓이는 것은 `fixed`·`required` 필드와 **`value` 에 값이 들어 있는 필드**다. 노출 목록을 밖에서 넘기는 prop 은 없다. 쿼리스트링·서버 상태에서 조건을 복원하는 목록 화면도 **`value` 만 넘기면 칩이 함께 살아나고**, 그 조건을 빼면 칩도 함께 빠진다 — 칩과 조회 조건이 어긋날 자리가 없다. "필터 추가" 로 꺼낸 필터는 값을 넣기 전에도 자리를 지키지만, "검색 초기화" 를 누르거나 화면을 다시 그리면 사라진다. 걸린 조건이 없으니 문제되지 않는다.
1462
1468
  - **`fields` 는 항상 그룹 배열이다.** 묶을 것이 없어도 `[{ fields: [...] }]` 로 한 겹 감싼다. 함께 걸어야 하는 조건(예: 기간 중 하나는 필수)이 있으면 그 필드들만 별도 그룹으로 떼어 `rule` 을 준다 — 규칙을 못 채운 동안 경고 툴팁이 떠 있고 `onSearch` 가 막힌다. 그룹 앞 구분선은 `divider` 로 켠다. 검증 단위와 구분선은 별개라, 묶어서 검증만 하고 싶으면 `divider` 를 주지 않는다.
1463
1469
 
1464
1470
  #### 3-7-12. 이미지 — SImage
@@ -1615,6 +1621,37 @@ import { SModalOutlet } from 'sellmate-design-system-react';
1615
1621
 
1616
1622
  슬롯 안쪽 여백은 **슬롯 내용이 직접 갖는다** — 컴포넌트는 자리만 잡는다(폴드 슬롯만 좁은 폭에 맞춰 가운데 정렬한다). 메뉴 폭은 `resizable` 로 바뀔 수 있으므로 슬롯 내용은 고정 폭 대신 `w-full` 로 따라가게 둔다.
1617
1623
 
1624
+ #### GNB 맨 아래의 system 패널
1625
+
1626
+ **서비스 전환·도메인·알림·설정·계정처럼 "지금 어떤 자격으로 보고 있는가" 를 다루는 묶음은 `SGnbSystem` 을 `SGnb` 의 `system` 슬롯에 넣는다.** 이 조각들을 `menuFooter` 에 손으로 조립하지 않는다 — 패널의 배경·위쪽 경계·구분선·행 간격이 전부 토큰으로 정해져 있어 직접 만들면 어긋난다.
1627
+
1628
+ `system` 은 다른 슬롯과 달리 **펼침·접힘 양쪽을 혼자 맡는다** — 펼치면 메뉴 컬럼 바닥에, 접히면(`header="fix"`) `foldedFooter` 아래 레일 바닥에 여백 없이 붙는다. 그래서 `menuFooter`/`foldedFooter` 로 나눠 줄 필요가 없다.
1629
+
1630
+ ```tsx
1631
+ <SGnb
1632
+ items={MENU} value={current} onValueChange={navigate} logo={<Logo />}
1633
+ system={
1634
+ <SGnbSystem
1635
+ /* 상단에 쌓는 행 — SSystemActionButton 의 props 배열이다 (§3-5). color 는 넘기지 않는다 */
1636
+ actions={[{ type: 'select', label: '서비스 전환', option: { icon: 'robot' }, onClick: openServices }]}
1637
+ domain={domainName}
1638
+ alert={{ count: unreadCount, onClick: openAlerts }}
1639
+ setting={{ onClick: openSettings }}
1640
+ account={{ label: userName, onClick: openAccount }}
1641
+ />
1642
+ }
1643
+ />
1644
+ ```
1645
+
1646
+ - **`color` 와 `folded` 를 주지 않는다.** `system` 슬롯에 있으면 GNB 가 자기 색과 접힘 형태를 내려준다. 두 prop 은 GNB 밖에서 이 패널을 단독으로 쓸 때만 준다.
1647
+ - **각 조각은 해당 prop 을 줄 때만 나타난다 — 어떤 조합이어도 그것만으로 판이 성립한다.** 계정만, 알림만, 설정만, 알림+계정 … 서비스가 쓰는 것만 넘긴다. 조각을 빼려고 빈 문자열이나 빈 객체를 넣지 않는다(`setting={{}}` 은 "설정 버튼을 쓴다" 는 뜻이다).
1648
+ - 구분선은 **위아래 양쪽에 내용이 있을 때만** 그어진다 — `actions` 만 넘겨도 바닥에 뜻 없는 선이 남지 않는다.
1649
+ - 알림·설정은 **혼자 서면 폭을 다 먹고**, 둘이 나란히 서면 알림이 남는 폭을·설정이 제 폭을 갖는다. 어느 쪽이 빠져도 왼쪽 끝은 위아래 행과 맞으므로 앱이 정렬을 맞출 일이 없다.
1650
+ - **켜진 조각이 하나도 없으면 아무것도 렌더되지 않는다.** 조건부로 조각이 다 빠지는 화면에서도 GNB 바닥에 빈 판이 남지 않는다.
1651
+ - **접히면 알림·계정 아이콘만 남는다** — `actions`·`domain`·`setting` 은 48px 폭에 놓을 자리가 없어 렌더되지 않고, 알림 개수는 점 배지가 된다. **알림도 계정도 안 쓰는 조합이면 접힌 패널은 아예 렌더되지 않는다** — 접힌 상태에서도 반드시 눌러야 하는 것이 그 둘 밖에 있으면 `foldedTop`/`foldedFooter` 로 따로 준다.
1652
+ - **알림 개수는 `alert.count` 하나로 표현한다.** 1 이상이면 벨이 울리는 아이콘과 강조색으로 바뀐다 — 색은 GNB 색이 정하므로 앱이 직접 칠하지 않는다.
1653
+ - **`useRail` 에서 하위 메뉴가 없는 레일 아이템이 활성이면 메뉴 컬럼째 사라지므로 이 패널도 함께 빠진다.** 항상 보여야 하는 계정·알림이라면 그런 레일 리프를 두지 않는다.
1654
+
1618
1655
  ### 4-2. 목록 페이지 (필터 + 테이블)
1619
1656
 
1620
1657
  구조: **페이지 헤더(`AppShell` 의 `header` prop, 가이드 등 부가 액션은 slot) → 필터(`SKeyValueTable`) → `STableBar` → `STable`**
@@ -10,7 +10,6 @@
10
10
  |------|------|---------|-------------|
11
11
  | `fields?` | `SChipFilterGroup[]` | — | 필터 정의 목록. 묶을 것이 없어도 한 그룹으로 감싸 넘긴다 — `[{ fields: [...] }]`. 그룹은 rule로 함께 검증하거나 divider로 갈라 놓을 때 나눈다. |
12
12
  | `value?` | `SChipFilterValueMap` | — | 필터 값 맵 |
13
- | `activeKeys?` | `string[]` | — | 노출 필터 key 목록. 지정하면 제어 컴포넌트로 동작 — "필터 추가"로 고른 필드를 이 배열에 직접 넣어줘야 칩이 나타난다(onActiveKeysChange에서 받은 keys로 갱신). 지정하지 않으면 fixed·required 필드만 노출된 상태로 시작해 컴포넌트가 내부 상태로 관리하는 비제어 방식으로 동작한다. fixed·required 필드는 이 배열에 없어도 항상 노출된다 |
14
13
  | `label?` | `string` | — | 좌측 태그 텍스트. 지정하면 태그와 그 뒤 구분선이 함께 노출되고, 지정하지 않으면(undefined) 둘 다 생략된다 |
15
14
  | `showReset?` | `boolean` | `true` | 검색 초기화 링크 표시 여부 |
16
15
  | `disabled?` | `boolean` | `false` | 바 비활성 상태 |
@@ -24,16 +23,15 @@
24
23
  | `onValueChange` | `(value: SChipFilterValueMap) => void` | 전체 값 변경 — 편집 중인 값이 바뀔 때마다(선택할 때마다) 호출된다. 실제 검색 실행은 onSearch를 쓴다 |
25
24
  | `onFilterChange` | `(detail: SChipFilterChangeDetail) => void` | 개별 필터 값 변경 |
26
25
  | `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로 비울 수 있어 이때의 안전장치다 |
27
- | `onActiveKeysChange` | `(keys: string[]) => void` | 노출 필터 key 변경(칩 추가·제거) 비제어 방식에서도 참고용으로 호출된다. activeKeys를 직접 제어할 때는 값을 그대로 activeKeys에 반영해야 한다 |
28
- | `onReset` | `() => void` | "검색 초기화" 클릭 모든 필드가 기본값(또는 null)으로 리셋된 호출된다 |
29
- | `onAddFilter` | `(key: string) => void` | "필터 추가" 목록에서 항목을 골랐을 때 — activeKeys를 직접 제어 중이면 이 콜백에서 (또는 onActiveKeysChange에서) key를 activeKeys에 추가해줘야 칩이 실제로 나타난다. activeKeys를 넘기지 않았다면(비제어) 별도 처리 없이도 컴포넌트가 알아서 칩을 노출한다 |
26
+ | `onReset` | `() => void` | "검색 초기화" 클릭모든 필드가 기본값(또는 null)으로 리셋되고 "필터 추가"로 꺼낸 칩이 바에서 빠진 호출된다 |
27
+ | `onAddFilter` | `(key: string) => void` | "필터 추가" 목록에서 항목을 골랐을 호출된다(알림). 노출은 컴포넌트가 알아서 하므로 이 콜백에서 따로 해줄 일은 없다 |
30
28
 
31
29
  #### Methods (ref)
32
30
 
33
31
  | Method | Type | Description |
34
32
  |--------|------|-------------|
35
33
  | `open` | `(key: string) => void` | 특정 필터 편집 팝오버를 엽니다. |
36
- | `reset` | `() => void` | 모든 필터 값을 초기화합니다. |
34
+ | `reset` | `() => void` | 바를 처음 상태로 되돌립니다 — 모든 필터 값이 기본값(또는 null)으로 돌아가고, "필터 추가"로 꺼내 놓은 칩도 함께 빠져 fixed·required 필드만 남습니다. 열려 있던 편집 팝오버는 닫힙니다 |
37
35
  | `validate` | `() => boolean` | 현재 값 기준으로 각 그룹의 rule을 만족하는지, 그리고 required 필드가 모두 비어 있지 않은지 반환합니다. rule을 준 그룹이 없고 required 필드도 모두 값이 있으면 항상 true. 경고 툴팁은 이 호출과 무관하게 rule 위반 상태인 동안 항상 실시간으로 떠 있으므로, 이 메서드는 그 상태를 그대로 읽어오는 용도다(예: 검색 버튼을 눌러도 되는지 사전 확인) |
38
36
 
39
37
  ## Types
@@ -264,9 +262,10 @@ export interface SChipFilterFieldBase {
264
262
  * 기본값이 한 번 더 채워진다 — required 필드가 빈 채로 남으면 onSearch가 막히기 때문이다.
265
263
  * 단 그 값을 부모가 value로 받아 주지 않으면 반복해서 채우지는 않는다 */
266
264
  required?: boolean;
267
- /** 고정 필터. true면 activeKeys와 무관하게 항상 노출되고 "필터 추가" 목록에는 나타나지 않는다.
268
- * required도 같은 효과를 낸다 처음부터 바에 보이는 것은 fixed이거나 required인 필드뿐이고,
269
- * 나머지는 전부 "필터 추가"에서 골라야 나타난다 */
265
+ /** 고정 필터. true면 항상 노출되고 "필터 추가" 목록에는 나타나지 않으며 칩을 제거할 수 없다.
266
+ * required도 같은 효과를 낸다. 나머지 필드는 "필터 추가"에서 골랐거나 value에 값이 들어
267
+ * 있는 동안 노출된다 — 값으로 떠 있던 칩은 값이 비면 함께 빠지고, "필터 추가" 꺼낸 칩은
268
+ * 값이 없어도 세션 동안 자리를 지킨다 */
270
269
  fixed?: boolean;
271
270
  /** 초기값 및 clearable 클릭 시 되돌아갈 값.
272
271
  *
@@ -51,9 +51,10 @@ export interface SChipFilterFieldBase {
51
51
  * 기본값이 한 번 더 채워진다 — required 필드가 빈 채로 남으면 onSearch가 막히기 때문이다.
52
52
  * 단 그 값을 부모가 value로 받아 주지 않으면 반복해서 채우지는 않는다 */
53
53
  required?: boolean;
54
- /** 고정 필터. true면 activeKeys와 무관하게 항상 노출되고 "필터 추가" 목록에는 나타나지 않는다.
55
- * required도 같은 효과를 낸다 처음부터 바에 보이는 것은 fixed이거나 required인 필드뿐이고,
56
- * 나머지는 전부 "필터 추가"에서 골라야 나타난다 */
54
+ /** 고정 필터. true면 항상 노출되고 "필터 추가" 목록에는 나타나지 않으며 칩을 제거할 수 없다.
55
+ * required도 같은 효과를 낸다. 나머지 필드는 "필터 추가"에서 골랐거나 value에 값이 들어
56
+ * 있는 동안 노출된다 — 값으로 떠 있던 칩은 값이 비면 함께 빠지고, "필터 추가" 꺼낸 칩은
57
+ * 값이 없어도 세션 동안 자리를 지킨다 */
57
58
  fixed?: boolean;
58
59
  /** 초기값 및 clearable 클릭 시 되돌아갈 값.
59
60
  *
@@ -187,7 +188,8 @@ export interface SChipFilterGroup {
187
188
  export interface SChipFilterHandle {
188
189
  /** 특정 필터 편집 팝오버를 엽니다. */
189
190
  open: (key: string) => void;
190
- /** 모든 필터 값을 초기화합니다. */
191
+ /** 바를 처음 상태로 되돌립니다 — 모든 필터 값이 기본값(또는 null)으로 돌아가고, "필터 추가"로
192
+ * 꺼내 놓은 칩도 함께 빠져 fixed·required 필드만 남습니다. 열려 있던 편집 팝오버는 닫힙니다 */
191
193
  reset: () => void;
192
194
  /** 현재 값 기준으로 각 그룹의 rule을 만족하는지, 그리고 required 필드가 모두 비어 있지 않은지
193
195
  * 반환합니다. rule을 준 그룹이 없고 required 필드도 모두 값이 있으면 항상 true.
@@ -217,19 +219,11 @@ export interface SChipFilterProps {
217
219
  * 않지만, defaultValue 없는 select-multi·keyword·custom에 required만 준 오설정에서는 "검색
218
220
  * 초기화"·clearable이 값을 null로 비울 수 있어 이때의 안전장치다 */
219
221
  onSearch?: (value: SChipFilterValueMap) => void;
220
- /** 노출 필터 key 목록. 지정하면 제어 컴포넌트로 동작 "필터 추가"로 고른 필드를 이 배열에 직접
221
- * 넣어줘야 칩이 나타난다(onActiveKeysChange에서 받은 keys로 갱신). 지정하지 않으면
222
- * fixed·required 필드만 노출된 상태로 시작해 컴포넌트가 내부 상태로 관리하는 비제어 방식으로
223
- * 동작한다. fixed·required 필드는 이 배열에 없어도 항상 노출된다 */
224
- activeKeys?: string[];
225
- /** 노출 필터 key 변경(칩 추가·제거) — 비제어 방식에서도 참고용으로 호출된다.
226
- * activeKeys를 직접 제어할 때는 이 값을 그대로 activeKeys에 반영해야 한다 */
227
- onActiveKeysChange?: (keys: string[]) => void;
228
- /** "검색 초기화" 클릭 — 모든 필드가 기본값(또는 null)으로 리셋된 뒤 호출된다 */
222
+ /** "검색 초기화" 클릭 모든 필드가 기본값(또는 null)으로 리셋되고 "필터 추가"로 꺼낸 칩이
223
+ * 바에서 빠진 호출된다 */
229
224
  onReset?: () => void;
230
- /** "필터 추가" 목록에서 항목을 골랐을 때 activeKeys를 직접 제어 중이면 이 콜백에서
231
- * (또는 onActiveKeysChange에서) key를 activeKeys에 추가해줘야 칩이 실제로 나타난다.
232
- * activeKeys를 넘기지 않았다면(비제어) 별도 처리 없이도 컴포넌트가 알아서 칩을 노출한다 */
225
+ /** "필터 추가" 목록에서 항목을 골랐을 때 호출된다(알림). 노출은 컴포넌트가 알아서 하므로
226
+ * 콜백에서 따로 해줄 일은 없다 */
233
227
  onAddFilter?: (key: string) => void;
234
228
  /** 좌측 태그 텍스트. 지정하면 태그와 그 뒤 구분선이 함께 노출되고, 지정하지 않으면(undefined)
235
229
  * 둘 다 생략된다 */
@@ -1 +1 @@
1
- export { SChipFilter, type SChipFilterChangeDetail, type SChipFilterCustomField, type SChipFilterDatePickerDayField, type SChipFilterDatePickerRangeField, type SChipFilterDatePickerStatisticsField, type SChipFilterDatePreset, type SChipFilterField, type SChipFilterFieldBase, type SChipFilterGroup, type SChipFilterGroupRule, type SChipFilterHandle, type SChipFilterKeywordField, type SChipFilterKeywordInput, type SChipFilterKeywordValue, type SChipFilterMatchMode, type SChipFilterOption, type SChipFilterOptionValue, type SChipFilterOptionsField, type SChipFilterPresetsField, type SChipFilterProps, type SChipFilterSelectField, type SChipFilterSelectMultiField, type SChipFilterType, type SChipFilterValue, type SChipFilterValueMap, } from './SChipFilter';
1
+ export { SChipFilter, type SChipFilterChangeDetail, type SChipFilterCustomField, type SChipFilterCustomValue, type SChipFilterDatePickerDayField, type SChipFilterDatePickerRangeField, type SChipFilterDatePickerStatisticsField, type SChipFilterDatePreset, type SChipFilterField, type SChipFilterFieldBase, type SChipFilterGroup, type SChipFilterGroupRule, type SChipFilterHandle, type SChipFilterKeywordField, type SChipFilterKeywordInput, type SChipFilterKeywordValue, type SChipFilterMatchMode, type SChipFilterOption, type SChipFilterOptionValue, type SChipFilterOptionsField, type SChipFilterPeriodUnit, type SChipFilterPeriodValue, type SChipFilterPresetsField, type SChipFilterProps, type SChipFilterSelectField, type SChipFilterSelectMultiField, type SChipFilterType, type SChipFilterValue, type SChipFilterValueMap, } from './SChipFilter';
@@ -1 +1 @@
1
- export { SChipInput, type SChipInputMetaPlacement, type SChipInputProps, type SChipInputSize, } from './SChipInput';
1
+ export { SChipInput, type SChipInputHandle, type SChipInputMetaPlacement, type SChipInputProps, type SChipInputSize, } from './SChipInput';
@@ -24,7 +24,7 @@
24
24
  | `optionSlot?` | `ReactNode` | — | 콘텐츠 박스에서 태그 오른쪽 커스텀 콘텐츠 (미지정 시 slotLabel 텍스트) |
25
25
  | `contentSlot?` | `ReactNode` | — | 콘텐츠 박스 바로 아래에 통으로 들어가는 커스텀 콘텐츠 (전체 폭) |
26
26
  | `mainButtonLabel?` | `string` | `'확인'` | 메인 버튼 |
27
- | `mainButtonName?` | `ConfirmModalMainButton` | — | 확인 버튼 프리셋 (없으면 type에 따라 자동 결정) |
27
+ | `mainButtonName?` | `SConfirmModalMainButton` | — | 확인 버튼 프리셋 (없으면 type에 따라 자동 결정) |
28
28
  | `subButtonLabel?` | `string` | — | 서브(취소) 버튼 |
29
29
 
30
30
  #### Events
@@ -44,11 +44,11 @@
44
44
  export type SConfirmModalType = 'positive' | 'negative' | 'default';
45
45
  ```
46
46
 
47
- ### ConfirmModalMainButton
47
+ ### SConfirmModalMainButton
48
48
 
49
49
  ```ts
50
50
  /** 확인 버튼 프리셋 (sd-confirm-modal ConfirmModalMainButton) */
51
- export type ConfirmModalMainButton =
51
+ export type SConfirmModalMainButton =
52
52
  'primary_md' | 'primary_outline_md' | 'danger_md' | 'danger_outline_md' | 'neutral_outline_md';
53
53
  ```
54
54
 
@@ -2,7 +2,7 @@ import { type ReactNode } from 'react';
2
2
  import { type STagColor, type STagShape, type STagSize } from '../STag';
3
3
  export type SConfirmModalType = 'positive' | 'negative' | 'default';
4
4
  /** 확인 버튼 프리셋 (sd-confirm-modal ConfirmModalMainButton) */
5
- export type ConfirmModalMainButton = 'primary_md' | 'primary_outline_md' | 'danger_md' | 'danger_outline_md' | 'neutral_outline_md';
5
+ export type SConfirmModalMainButton = 'primary_md' | 'primary_outline_md' | 'danger_md' | 'danger_outline_md' | 'neutral_outline_md';
6
6
  export interface SConfirmModalProps {
7
7
  open?: boolean;
8
8
  onOpenChange?: (open: boolean) => void;
@@ -34,7 +34,7 @@ export interface SConfirmModalProps {
34
34
  /** 메인 버튼 */
35
35
  mainButtonLabel?: string;
36
36
  /** 확인 버튼 프리셋 (없으면 type에 따라 자동 결정) */
37
- mainButtonName?: ConfirmModalMainButton;
37
+ mainButtonName?: SConfirmModalMainButton;
38
38
  /** 서브(취소) 버튼 */
39
39
  subButtonLabel?: string;
40
40
  /** 확인 버튼 클릭 (sdOk) */
@@ -1 +1 @@
1
- export { SConfirmModal, type SConfirmModalProps, type SConfirmModalType } from './SConfirmModal';
1
+ export { SConfirmModal, type SConfirmModalMainButton, type SConfirmModalProps, type SConfirmModalType, } from './SConfirmModal';
@@ -1 +1 @@
1
- export { SDraggableItem, type SDraggableItemProps, type SDraggableItemRenderProp, type SDraggableItemRenderState, type SDraggableItemSlot, type SDraggableItemSupportingTextPosition, } from './SDraggableItem';
1
+ export { SDraggableItem, type SDraggableItemProps, type SDraggableItemRenderProp, type SDraggableItemRenderState, type SDraggableItemSize, type SDraggableItemSlot, type SDraggableItemSupportingTextPosition, } from './SDraggableItem';
@@ -1 +1 @@
1
- export { SExpansionItem, type SExpansionItemProps, type SExpansionItemRenderProp, type SExpansionItemRenderState, type SExpansionItemSupportingTextPosition, } from './SExpansionItem';
1
+ export { SExpansionItem, type SExpansionItemInteraction, type SExpansionItemProps, type SExpansionItemRenderProp, type SExpansionItemRenderState, type SExpansionItemSize, type SExpansionItemSupportingTextPosition, } from './SExpansionItem';
@@ -26,6 +26,7 @@
26
26
  | `resizable?` | `boolean` | `false` | 메뉴 폭을 드래그로 조절할 수 있게 한다. 레일 폭은 고정이고 **메뉴 컬럼만** 늘고 준다. 조절선은 GNB 컬럼의 오른쪽 경계 전체다 — fix 는 상단바 높이까지, full 은 상단바가 전폭이라 본문 높이까지. 접혀 있거나 깔 메뉴가 없으면(레일 리프가 활성) 조절선이 나오지 않는다. |
27
27
  | `menuWidth?` | `number` | — | 메뉴 폭(px). 주면 controlled — onMenuWidthChange 로 직접 갱신해야 움직인다 |
28
28
  | `defaultMenuWidth?` | `number` | — | 메뉴 초기 폭(px). uncontrolled |
29
+ | `system?` | `ReactNode` | — | GNB 맨 아래에 붙는 system 패널(`SGnbSystem`) 슬롯. 펼치면 메뉴 컬럼 바닥에, 접히면(header="fix" 레일) `foldedFooter` 아래 레일 바닥에 여백 없이 붙는다. 색·형태(펼침/접힘)는 GNB 가 내려주므로 SGnbSystem 에 다시 넘기지 않아도 된다. useRail 에서 메뉴가 없는 레일 아이템이 활성이면 메뉴 컬럼째 사라지므로 이 슬롯도 함께 빠진다. |
29
30
  | `ariaLabel?` | `string` | `'global navigation'` | 메뉴 landmark(nav) 의 접근성 레이블. 한 화면에 nav 가 여럿일 때 구분한다. |
30
31
 
31
32
  #### Events
@@ -85,6 +86,10 @@ export interface SGnbMenuItem {
85
86
 
86
87
  ## Dependencies
87
88
 
89
+ ### Used by
90
+
91
+ - [SGnbSystem](../SGnbSystem)
92
+
88
93
  ### Depends on
89
94
 
90
95
  - [SGhostButton](../SGhostButton)
@@ -100,5 +105,6 @@ graph TD;
100
105
  SGnb --> SIcon
101
106
  SGnb --> SLayout
102
107
  SGnb --> STag
108
+ SGnbSystem --> SGnb
103
109
  style SGnb fill:#f9f,stroke:#333,stroke-width:4px
104
110
  ```
@@ -1,5 +1,20 @@
1
1
  import { type HTMLAttributes, type ReactNode } from 'react';
2
2
  import { type SGnbType, type SGnbHeader, type SGnbColor, type SGnbMenuItem } from './gnb.config';
3
+ /**
4
+ * SGnb 가 `system` 슬롯에 내려주는 값. 슬롯에 놓인 SGnbSystem 이 이걸 읽어
5
+ * 색과 형태(펼침/접힘)를 GNB 에 맞춘다 — 앱이 같은 값을 두 번 넘기지 않게 한다.
6
+ *
7
+ * `folded` 는 GNB 의 접힘 상태가 아니라 **그 자리가 접힌 레일인지**다. 슬롯은 두 곳에 놓이고
8
+ * (메뉴 컬럼 바닥 · 접힘 레일 바닥) 각 자리가 자기 형태를 못 박으므로, 접는 동안 아직 미끄러져
9
+ * 나가는 중인 메뉴 컬럼의 패널이 min 형태로 튀지 않는다.
10
+ */
11
+ export interface SGnbSystemContextValue {
12
+ /** GNB 색 */
13
+ color: SGnbColor;
14
+ /** 이 자리가 접힌(48px) 레일인지 */
15
+ folded: boolean;
16
+ }
17
+ export declare const SGnbSystemContext: import("react").Context<SGnbSystemContextValue | null>;
3
18
  export interface SGnbProps extends Omit<HTMLAttributes<HTMLDivElement>, 'color'> {
4
19
  /** 메뉴 스타일: box(라운드) / belt(풀폭 행). 미지정 시 SLayout 의 type 을 따른다. */
5
20
  type?: SGnbType;
@@ -80,6 +95,13 @@ export interface SGnbProps extends Omit<HTMLAttributes<HTMLDivElement>, 'color'>
80
95
  defaultMenuWidth?: number;
81
96
  /** 메뉴 폭이 확정될 때(드래그를 놓거나 방향키 조작). 드래그하는 동안에는 오지 않는다 */
82
97
  onMenuWidthChange?: (width: number) => void;
98
+ /**
99
+ * GNB 맨 아래에 붙는 system 패널(`SGnbSystem`) 슬롯. 펼치면 메뉴 컬럼 바닥에,
100
+ * 접히면(header="fix" 레일) `foldedFooter` 아래 레일 바닥에 여백 없이 붙는다.
101
+ * 색·형태(펼침/접힘)는 GNB 가 내려주므로 SGnbSystem 에 다시 넘기지 않아도 된다.
102
+ * useRail 에서 메뉴가 없는 레일 아이템이 활성이면 메뉴 컬럼째 사라지므로 이 슬롯도 함께 빠진다.
103
+ */
104
+ system?: ReactNode;
83
105
  /** 메뉴 landmark(nav) 의 접근성 레이블. 한 화면에 nav 가 여럿일 때 구분한다. */
84
106
  ariaLabel?: string;
85
107
  }
@@ -1,2 +1,2 @@
1
- export { SGnb, type SGnbProps } from './SGnb';
1
+ export { SGnb, SGnbSystemContext, type SGnbProps, type SGnbSystemContextValue } from './SGnb';
2
2
  export { GNB_HEADER, GNB_MENU, GNB_COLORS, GNB_COMMON, GNB_FOLD_MS, GNB_FULL_LOGO_WIDTH, GNB_MENU_WIDTH, GNB_RAIL, GNB_RAIL_WIDTH, findGnbAncestors, findGnbRailValue, type SGnbType, type SGnbHeader, type SGnbColor, type SGnbMenuItem, } from './gnb.config';
@@ -0,0 +1,121 @@
1
+ # SGnbSystem
2
+
3
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
4
+
5
+ ### SGnbSystem
6
+
7
+ #### Props
8
+
9
+ | Prop | Type | Default | Description |
10
+ |------|------|---------|-------------|
11
+ | `color?` | `SGnbSystemColor` | — | 색상(패널 표면). light = 흰 면, dark = 오션블루 면. 미지정 시 자기를 담은 SGnb 의 color 를 따른다(단독으로 쓰면 `'light'`). |
12
+ | `folded?` | `boolean` | — | 접힌(min) 형태 — 48px 레일 폭에 맞춰 알림·계정 아이콘만 남는다. 미지정 시 자기를 담은 SGnb 의 접힘 상태를 따른다. |
13
+ | `actions?` | `SGnbSystemAction[]` | — | 패널 상단에 쌓는 행 목록. 각 원소는 `SSystemActionButton` 의 props 다(`color` 제외). 아래에 다른 조각이 있으면 그 사이에 구분선이 그어진다. 접히면 자리가 없어 렌더되지 않는다. |
14
+ | `domain?` | `string` | — | 도메인 태그 텍스트. 접히면 자리가 없어 렌더되지 않는다 |
15
+ | `alert?` | `SGnbSystemAlert` | — | 알림 버튼. 없으면 렌더하지 않는다. 설정과 나란히 서면 남는 폭을 먹고, 혼자면 폭을 다 먹는다 |
16
+ | `setting?` | `SGnbSystemSetting` | — | 설정 버튼. 없으면 렌더하지 않는다. 알림과 나란히 서면 제 폭만 갖고, 혼자면 폭을 다 먹는다. 접히면 자리가 없어 렌더되지 않는다 — 접힌 상태에서도 닿아야 하면 SGnb 의 `foldedFooter` 에 둔다. |
17
+ | `account?` | `SGnbSystemAccount` | — | 계정 행. 없으면 렌더하지 않는다 |
18
+
19
+ ## Types
20
+
21
+ ### SGnbSystemColor
22
+
23
+ ```ts
24
+ export type SGnbSystemColor = (typeof GNB_SYSTEM_COLORS)[number];
25
+ ```
26
+
27
+ ### SGnbSystemAction
28
+
29
+ ```ts
30
+ /**
31
+ * 패널 상단에 쌓이는 행 하나. `SSystemActionButton` 의 props 를 그대로 받는다 —
32
+ * 단 `color` 는 패널이 정하므로 받지 않고, 목록 key 로 쓸 `value` 만 더한다.
33
+ */
34
+ export type SGnbSystemAction = DistributiveOmit<SSystemActionButtonProps, 'color'> & {
35
+ /** 목록 key. 없으면 순서를 key 로 쓴다 */
36
+ value?: string;
37
+ };
38
+ ```
39
+
40
+ ### SGnbSystemAlert
41
+
42
+ ```ts
43
+ /** 알림 버튼 */
44
+ export interface SGnbSystemAlert {
45
+ /** 라벨 텍스트. 기본 `'알림'` */
46
+ label?: string;
47
+ /**
48
+ * 읽지 않은 알림 개수. 1 이상이면 벨이 울리는 아이콘·강조색으로 바뀌고 개수(접히면 점)가 붙는다.
49
+ * 표기는 컴포넌트가 맡는다 — 세 자리마다 콤마를 넣어 그리므로 앱이 포맷한 문자열을 넘기지 않는다.
50
+ */
51
+ count?: number;
52
+ /** 클릭 */
53
+ onClick?: () => void;
54
+ }
55
+ ```
56
+
57
+ ### SGnbSystemSetting
58
+
59
+ ```ts
60
+ /** 설정 버튼 */
61
+ export interface SGnbSystemSetting {
62
+ /** 라벨 텍스트. 기본 `'설정'` */
63
+ label?: string;
64
+ /** 클릭 */
65
+ onClick?: () => void;
66
+ }
67
+ ```
68
+
69
+ ### SGnbSystemAccount
70
+
71
+ ```ts
72
+ /** 계정 행 */
73
+ export interface SGnbSystemAccount {
74
+ /** 계정 이름 */
75
+ label?: string;
76
+ /** 클릭 */
77
+ onClick?: () => void;
78
+ }
79
+ ```
80
+
81
+ ### GNB_SYSTEM_COLORS
82
+
83
+ ```ts
84
+ /**
85
+ * SGnbSystem 설정.
86
+ *
87
+ * 토큰은 GNB system 패널 한 벌(`--cmp-gnb-system-*`)이다.
88
+ * 패널(배경·경계·구분선·간격) → --cmp-gnb-system-panel-*
89
+ * 도메인 태그 → --cmp-gnb-system-domainTag-*
90
+ * 행 공통(높이·라운드·아이콘 프레임) → --cmp-gnb-system-item-* (SSystemActionButton 과 공유)
91
+ * 알림 버튼 → --cmp-gnb-system-alertButton-*
92
+ * 설정 버튼 → --cmp-gnb-system-setting-*
93
+ * 펼침·접힘 패널 여백 → --cmp-gnb-system-project-padding · --cmp-gnb-system-min-padding
94
+ *
95
+ * 타이포그래피는 CSS 변수가 아니라 `.typo-*` 프리셋으로 나온다.
96
+ * domainTag.typography → typo-body-sm-medium (12/500/20)
97
+ * item.label.typography → typo-body-sm-semibold (12/600/20)
98
+ * alertButton.count → typo-body-md-bold (13/700/22)
99
+ */
100
+
101
+ /** 색상(표면). light = 흰 면, dark = 오션블루 면. GNB 의 color 를 그대로 따라간다 */
102
+ export const GNB_SYSTEM_COLORS = ['light', 'dark'] as const;
103
+ ```
104
+
105
+ ## Dependencies
106
+
107
+ ### Depends on
108
+
109
+ - [SGnb](../SGnb)
110
+ - [SIcon](../SIcon)
111
+ - [SSystemActionButton](../SSystemActionButton)
112
+
113
+ ### Graph
114
+
115
+ ```mermaid
116
+ graph TD;
117
+ SGnbSystem --> SGnb
118
+ SGnbSystem --> SIcon
119
+ SGnbSystem --> SSystemActionButton
120
+ style SGnbSystem fill:#f9f,stroke:#333,stroke-width:4px
121
+ ```
@@ -0,0 +1,93 @@
1
+ import { type HTMLAttributes } from 'react';
2
+ import { type SSystemActionButtonProps } from '../SSystemActionButton';
3
+ import { type SGnbSystemColor } from './gnbSystem.config';
4
+ /** 유니온을 유지한 채 키를 걷어낸다 — 그냥 Omit 하면 판별 유니온이 한 덩어리로 뭉개진다. */
5
+ type DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;
6
+ /**
7
+ * 패널 상단에 쌓이는 행 하나. `SSystemActionButton` 의 props 를 그대로 받는다 —
8
+ * 단 `color` 는 패널이 정하므로 받지 않고, 목록 key 로 쓸 `value` 만 더한다.
9
+ */
10
+ export type SGnbSystemAction = DistributiveOmit<SSystemActionButtonProps, 'color'> & {
11
+ /** 목록 key. 없으면 순서를 key 로 쓴다 */
12
+ value?: string;
13
+ };
14
+ /** 알림 버튼 */
15
+ export interface SGnbSystemAlert {
16
+ /** 라벨 텍스트. 기본 `'알림'` */
17
+ label?: string;
18
+ /**
19
+ * 읽지 않은 알림 개수. 1 이상이면 벨이 울리는 아이콘·강조색으로 바뀌고 개수(접히면 점)가 붙는다.
20
+ * 표기는 컴포넌트가 맡는다 — 세 자리마다 콤마를 넣어 그리므로 앱이 포맷한 문자열을 넘기지 않는다.
21
+ */
22
+ count?: number;
23
+ /** 클릭 */
24
+ onClick?: () => void;
25
+ }
26
+ /** 설정 버튼 */
27
+ export interface SGnbSystemSetting {
28
+ /** 라벨 텍스트. 기본 `'설정'` */
29
+ label?: string;
30
+ /** 클릭 */
31
+ onClick?: () => void;
32
+ }
33
+ /** 계정 행 */
34
+ export interface SGnbSystemAccount {
35
+ /** 계정 이름 */
36
+ label?: string;
37
+ /** 클릭 */
38
+ onClick?: () => void;
39
+ }
40
+ export interface SGnbSystemProps extends Omit<HTMLAttributes<HTMLDivElement>, 'color'> {
41
+ /**
42
+ * 색상(패널 표면). light = 흰 면, dark = 오션블루 면.
43
+ * 미지정 시 자기를 담은 SGnb 의 color 를 따른다(단독으로 쓰면 `'light'`).
44
+ */
45
+ color?: SGnbSystemColor;
46
+ /**
47
+ * 접힌(min) 형태 — 48px 레일 폭에 맞춰 알림·계정 아이콘만 남는다.
48
+ * 미지정 시 자기를 담은 SGnb 의 접힘 상태를 따른다.
49
+ */
50
+ folded?: boolean;
51
+ /**
52
+ * 패널 상단에 쌓는 행 목록. 각 원소는 `SSystemActionButton` 의 props 다(`color` 제외).
53
+ * 아래에 다른 조각이 있으면 그 사이에 구분선이 그어진다. 접히면 자리가 없어 렌더되지 않는다.
54
+ */
55
+ actions?: SGnbSystemAction[];
56
+ /** 도메인 태그 텍스트. 접히면 자리가 없어 렌더되지 않는다 */
57
+ domain?: string;
58
+ /** 알림 버튼. 없으면 렌더하지 않는다. 설정과 나란히 서면 남는 폭을 먹고, 혼자면 폭을 다 먹는다 */
59
+ alert?: SGnbSystemAlert;
60
+ /**
61
+ * 설정 버튼. 없으면 렌더하지 않는다. 알림과 나란히 서면 제 폭만 갖고, 혼자면 폭을 다 먹는다.
62
+ * 접히면 자리가 없어 렌더되지 않는다 — 접힌 상태에서도 닿아야 하면 SGnb 의 `foldedFooter` 에 둔다.
63
+ */
64
+ setting?: SGnbSystemSetting;
65
+ /** 계정 행. 없으면 렌더하지 않는다 */
66
+ account?: SGnbSystemAccount;
67
+ }
68
+ /**
69
+ * SGnbSystem — GNB 맨 아래에 붙는 system 패널.
70
+ *
71
+ * 위에서부터 [행 목록(`actions`)] · 구분선 · [도메인 태그] · [알림 | 설정] · [계정] 을 쌓는다.
72
+ * 각 조각은 해당 prop 을 줄 때만 나타나므로, 서비스마다 필요한 만큼만 켜서 쓴다 — 계정만, 알림만,
73
+ * 설정만, 알림+계정 … 어떤 부분집합이어도 그 조합만으로 판이 성립하도록 다음을 지킨다.
74
+ *
75
+ * - **구분선은 위아래 양쪽에 내용이 있을 때만** 긋는다. `actions` 만 주면 바닥에 뜻 없는 선이 남지 않는다.
76
+ * - **알림·설정은 혼자 서면 폭을 다 먹는다.** 둘이 나란히 서면 알림이 남는 폭을, 설정이 제 폭을 갖고
77
+ * 사이에 세로선이 놓인다. 어느 쪽이 빠져도 왼쪽 끝은 위아래 행과 맞는다.
78
+ * - **켜진 조각이 하나도 없으면 아무것도 렌더하지 않는다** — 빈 판은 GNB 바닥에 정체 모를 경계선만 남긴다.
79
+ *
80
+ * 알림·설정·계정은 늘 함께 붙어 다니는 한 덩어리라 사이를 띄우지 않고, 그 위 조각들만 간격을 갖는다.
81
+ *
82
+ * `folded` 면 48px 레일 폭에 맞춘 min 형태가 된다 — **알림·계정 아이콘만** 남고 라벨·태그·행 목록·설정은
83
+ * 빠진다(시안). 알림 개수는 적을 자리가 없어 점 배지로 바뀐다. 둘 다 없는 조합(설정만 쓰는 앱 등)이면
84
+ * 접힌 패널은 렌더되지 않으므로, 접힌 상태에서도 닿아야 하는 것은 SGnb 의 `foldedTop`·`foldedFooter` 에 둔다.
85
+ *
86
+ * `alert.count` 가 1 이상이면 벨이 울리는 아이콘(`alert`)과 강조색으로 바뀐다 — light 는 파랑,
87
+ * dark 는 노랑(토큰이 정한다). 0 이거나 없으면 조용한 벨(`bell`)에 기본 색이다.
88
+ *
89
+ * SGnb 의 `system` 슬롯에 넣으면 `color` 와 `folded` 를 GNB 에서 받아오므로 앱이 다시 넘길 필요가 없다.
90
+ * 단독으로 쓰면 두 prop 을 직접 준다. 폭은 늘 부모를 가득 채운다 — 좁은 GNB 컬럼에 놓이는 패널이다.
91
+ */
92
+ export declare const SGnbSystem: import("react").ForwardRefExoticComponent<SGnbSystemProps & import("react").RefAttributes<HTMLDivElement>>;
93
+ export {};