sellmate-design-system-react 7.0.0 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -28,12 +28,12 @@
28
28
  | **입력 (폼)** | `SForm` `SField` `SInput` `SNumberInput` `STextarea` `SSelect` `SCheckbox` `SRadio` `SRadioButton` `SChip` `SChipInput` `SBarcodeInput` `SFilePicker` |
29
29
  | **날짜·시간** | `SCalendar` `SDatePicker` `SDateRangePicker` `STimePicker` `STimeRangePicker` |
30
30
  | **표·목록** | `STable` `STableBar` `SKeyValueTable` `SList` `SListItem` `SDraggableItem` |
31
- | **레이아웃** | `SLayout` `SGnb` `SPage` `SSectionHeaderCard` `SCard` `SDivider` `SScrollArea` `SExpansionItem` |
31
+ | **레이아웃** | `SLayout` `SGnb` `SPage` `SSectionHeaderCard` `SCard` `SDivider` `SSplitter` `SScrollArea` `SExpansionItem` |
32
32
  | **내비게이션** | `STabs` `SPagination` `SStepper` |
33
33
  | **표시·상태** | `STag` `SBadge` `SIcon` `SCallout` `SGuide` |
34
34
  | **진행·로딩** | `SLinearProgress` `SCircleProgress` `SLoadingContainer` `SLoadingModal` |
35
35
  | **오버레이** | `STooltip` `SPopover` `SPopup` `SPortal` |
36
- | **모달** | `SModal.confirm()` `SModal.create()` + `SActionModal` `SConfirmModal` |
36
+ | **모달** | `SModal.confirm()` `SModal.create()` + `SActionModal` `SConfirmModal` `SModalOutlet`(앱 루트 1회) |
37
37
  | **알림** | `SToast` `SToastContainer` |
38
38
 
39
39
  표에 없는 UI 를 만들어야 할 때만 `div` 로 직접 조립하고, 그때도 §1-2 · §2 의 토큰 규칙을 지킨다.
@@ -452,7 +452,7 @@ function OrderModal({ orderId, open, onOpenChange, onClose, modalRef }: OrderMod
452
452
  width={720}
453
453
  // 주 액션은 button(단수), 보조 버튼은 footerLeft — 하단 버튼 양끝 분리 규칙과 같다
454
454
  button={{ label: '접수', onClick: () => modalRef.ok() }}
455
- footerLeft={<SButton color="neutral" outline label="취소" onClick={() => modalRef.cancel()} />}
455
+ footerLeft={<SButton color="neutral" outline size="md" label="취소" onClick={() => modalRef.cancel()} />}
456
456
  >
457
457
  <SKeyValueTable fields={orderFields} values={order} />
458
458
  </SActionModal>
@@ -465,6 +465,10 @@ SModal.create({ component: OrderModal, componentProps: { orderId } })
465
465
  .onDismissed(() => {});
466
466
  ```
467
467
 
468
+ **하단 버튼을 본문(children)에 직접 두지 않는다.** 주 액션은 `button`, 보조 버튼은 `footerLeft` 로 넘긴다 — 푸터 배경·여백·양끝 분리가 컴포넌트 규칙대로 잡히는 자리다. `button` 은 클릭해도 **모달을 닫지 않으므로**(`onClick` 만 발화) 저장 API 응답을 보고 `modalRef.ok()` 로 닫으면 되고, 그 때문에 본문에 버튼을 따로 둘 이유가 없다. `footerLeft` 는 슬롯이라 `SButton` 을 직접 배치하며 `size="md"` 를 명시한다(§3-5-2).
469
+
470
+ **모달 안에서도 앱의 훅을 그냥 쓴다 — 단, 앱 루트에 `SModalOutlet` 이 있어야 한다 (§4-1).** outlet 이 있으면 명령형 모달이 앱 렌더 트리의 자식으로 그려지므로 `useQuery`·`useNavigate`·`useTheme` 같은 Context 기반 훅이 페이지에서와 똑같이 동작한다. **모달 컴포넌트를 Provider 로 다시 감싸지 않는다.** outlet 없이 띄우면 모달이 별도 React 루트로 떠서 Provider 가 하나도 닿지 않고, `No QueryClient set` 처럼 모달을 여는 순간에만 터진다.
471
+
468
472
  #### 3-3-5. 닫기 경로 — `persistent` 기본값은 컴포넌트마다 다르다
469
473
 
470
474
  **입력을 담는 모달은 백드롭 클릭·ESC 로 닫히지 않는 것이 기본**이다. 닫기 시도는 흔들림(shake)으로만 반응한다. 작성 중인 내용을 실수로 잃지 않게 하기 위한 것이다. 반면 `SConfirmModal` 은 잃을 입력이 없으므로 **백드롭·ESC 로 닫히는 것이 기본**이다.
@@ -715,6 +719,41 @@ const columns: STableColumn[] = [
715
719
  </div>
716
720
  ```
717
721
 
722
+ ### 3-6. 영역 나누기 — SDivider vs SSplitter
723
+
724
+ | 상황 | 사용 |
725
+ | --- | --- |
726
+ | 두 영역 사이에 **선만** 그을 때 | `SDivider` — 위치·두께가 고정된 구분선이다 |
727
+ | 사용자가 **경계를 끌어 넓이를 바꿀 수 있어야** 할 때 | `SSplitter` — 두 패널을 감싸고 경계 위치를 소유한다 |
728
+
729
+ `SSplitter` 의 구분선은 평소 자리만 잡고 칠해지지 않다가, 경계에 커서를 올리거나 포커스를 주면 그때 드러난다 — 조절 가능한 자리라는 신호다. **항상 보이는 선이 필요하면 `SDivider` 를 쓴다.** 선 색·두께·주변 여백은 토큰이 정하므로 직접 주지 않는다.
730
+
731
+ ```tsx
732
+ <SSplitter defaultValue={30} limits={[20, 60]}>
733
+ <SSplitter.Before>내비게이션</SSplitter.Before>
734
+ <SSplitter.After>본문</SSplitter.After>
735
+ </SSplitter>
736
+ ```
737
+
738
+ - 자식은 `SSplitter.Before` 와 `SSplitter.After` **둘뿐**이고, 루트의 직접 자식이어야 한다.
739
+ - 크기 단위는 `unit` 이 정한다. 기본 `'%'` 는 창이 바뀌어도 비율을 유지하고, `'px'` 는 폭을 유지한다. **사이드바처럼 폭이 고정돼야 하는 자리는 `'px'`**, 화면을 비율로 나누는 자리는 기본값 그대로 둔다.
740
+ - 본문이 읽을 수 없을 만큼 좁아지지 않도록 `limits={[최소, 최대]}` 를 준다. 생략하면 `'%'` 는 `[10, 90]`, `'px'` 는 `[50, Infinity]`.
741
+ - 각 패널은 넘치는 만큼 **스스로 스크롤한다.** 패널 안에 `SScrollArea` 를 겹쳐 넣지 않는다.
742
+ - 모델은 항상 **첫 패널**(`SSplitter.Before`) 크기다. 사이드가 기준인 화면이면 사이드를 `Before` 에 둔다.
743
+ - 앱 셸의 GNB 폭은 `SGnb` 가 소유한다. `SLayout`/`SGnb` 를 `SSplitter` 로 감싸지 않는다 — **GNB 폭을 끌 수 있게 하려면 `SGnb` 에 `resizable` 을 준다**(§4-1).
744
+
745
+ **Quasar `q-splitter` 에서 옮겨올 때** — `unit` · `limits` · `emitImmediately` 는 이름과 뜻이 같고, 나머지는 아래처럼 바뀐다. `emitImmediately` 를 주지 않으면 **드래그를 놓는 순간 한 번만** `onValueChange` 가 온다.
746
+
747
+ | q-splitter | SSplitter |
748
+ | --- | --- |
749
+ | `horizontal` (상/하 분할) | **`vertical`** — 이 저장소는 `STabs`·`SStepper` 와 같이 "세로 **배치**" 로 읽는다 |
750
+ | `v-model` | `value` + `onValueChange` (또는 `defaultValue`) |
751
+ | `disable` | `disabled` |
752
+ | `before` / `after` 슬롯 | `SSplitter.Before` / `SSplitter.After` |
753
+ | `before-class` / `after-class` | 각 슬롯에 `className` 을 직접 |
754
+ | `separator-class` / `separator-style` | `dividerClassName` / `dividerStyle` |
755
+ | `reverse` · `dark` | 없음 — 모델은 항상 첫 패널 크기다 |
756
+
718
757
  ---
719
758
 
720
759
  ## 4. 페이지 레시피 — 표준 골격
@@ -750,6 +789,30 @@ export default function AppShell({
750
789
  }
751
790
  ```
752
791
 
792
+ **GNB 폭을 사용자가 조절하게 하려면 `SGnb` 에 `resizable` 을 준다.** 메뉴 오른쪽 경계가 조절선이 되고, 레일 폭은 고정된 채 메뉴 컬럼만 늘고 준다. 범위는 컴포넌트가 정하므로 숫자를 직접 주지 않는다.
793
+
794
+ ```tsx
795
+ {/* 폭을 기억해야 하면 menuWidth 를 앱이 쥐고 onMenuWidthChange 로 되받아 저장한다.
796
+ 초기값만 정하면 되면 defaultMenuWidth 하나로 끝난다. */}
797
+ <SGnb items={MENU} value={current} onValueChange={navigate} resizable defaultMenuWidth={240} />
798
+ ```
799
+
800
+ `onMenuWidthChange` 는 **드래그를 놓는 순간**(또는 방향키 조작) 한 번만 온다 — 저장 로직을 그대로 붙여도 프레임마다 쓰이지 않는다. 접혀 있거나 레일 리프가 활성이라 깔 메뉴가 없으면 조절선은 나오지 않는다.
801
+
802
+ **앱 부트스트랩에는 `<SModalOutlet />` 을 한 번 렌더한다 — Provider 안쪽에 둔다.**
803
+
804
+ ```tsx
805
+ import { SModalOutlet } from 'sellmate-design-system-react';
806
+
807
+ // 앱 진입점 (main.tsx / App.tsx) — 앱 전체에 하나. 위치는 Provider 안쪽이기만 하면 된다
808
+ <QueryClientProvider client={queryClient}>
809
+ <RouterProvider router={router} />
810
+ <SModalOutlet />
811
+ </QueryClientProvider>;
812
+ ```
813
+
814
+ `SModal.confirm/loading/create` 로 띄운 모달이 그려지는 자리다. outlet 이 앱 트리 안에 있어야 모달이 앱의 Context(QueryClient·Router·Theme 등)를 상속한다 — outlet 이 없으면 모달은 뜨지만 별도 React 루트라 Provider 가 닿지 않는다(§3-3-4). 모달은 언제나 `body` 로 portal 되므로 outlet 을 어디에 두든 레이아웃에는 영향이 없고, **셸 안에 넣을 필요도 없다.** 토스트를 쓴다면 `SToastContainer` 도 같은 자리에 둔다.
815
+
753
816
  **최소 너비는 `SLayout` 이 보장한다 — 앱이 `min-w-*` 를 직접 주지 않는다.** 창이 최소 너비(`SLAYOUT_MIN_WIDTH`, GNB 포함한 전체 기준)보다 좁아지면 GNB·상단바는 제자리에 남고 **`SPage` 안에만 가로 스크롤이 생긴다.** 문서(브라우저 창)에는 가로 스크롤이 생기지 않는다. 그래서 셸을 감싸는 요소에 `min-width` 나 `overflow-x` 를 걸지 않는다 — 걸면 창 전체가 스크롤되어 GNB 가 화면 밖으로 밀린다.
754
817
 
755
818
  **셸의 `SPage` 는 모든 페이지가 공유하므로, 스크롤 끝 여백을 끄려면 페이지가 셸에 알려야 한다.** 위처럼 프레임 컴포넌트가 `scrollEndSpacing` 을 받아 그대로 넘기고, 페이지네이션이 있는 목록 페이지만 `false` 를 준다 (§4-2). 나머지 페이지는 넘기지 않으면 기본값(켬)이 적용된다.
@@ -1069,6 +1132,8 @@ export default function ProductDetailPage() {
1069
1132
  - [ ] `SGhostButton` 의 `intent` 가 조작 성격과 맞는가 (되돌릴 수 없는 삭제만 `danger`, 진입·추가는 `action`, 나머지는 `default`)
1070
1133
  - [ ] 창을 띄울 때 §3-3-1 판별 순서를 따랐는가 (그 자체가 화면 → `SPopup` / 실행 여부만 확정 → `SModal.confirm` / 모달 안에서 작성 → `SActionModal`)
1071
1134
  - [ ] 작업용 모달을 `SActionModal` + `SModal.create` 로 만들었는가 (직접 오버레이 ❌)
1135
+ - [ ] 모달·드로어의 하단 버튼을 본문이 아니라 `button` · `footerLeft` prop 으로 넘겼는가 (§3-3-4)
1136
+ - [ ] 앱 부트스트랩의 Provider 안쪽에 `<SModalOutlet />` 이 한 번 렌더되어 있는가 (§4-1 — 없으면 모달 안에서 앱 훅이 죽는다), 그 대신으로 모달 컴포넌트를 Provider 로 다시 감싸지 않았는가
1072
1137
  - [ ] 상태 표시·알림·확인 다이얼로그가 §3의 선택 규칙을 따르는가
1073
1138
 
1074
1139
  ---
package/README.md CHANGED
@@ -123,6 +123,21 @@ export function Example() {
123
123
 
124
124
  `SButton` 은 텍스트를 **`label`(문자열)로만** 받습니다 — `children` 은 받지 않으며, 아이콘은 `icon`/`rightIcon` 을 씁니다. 색상 prop 은 `color` 입니다(`variant` 아님).
125
125
 
126
+ ### 앱 부트스트랩 — `SModalOutlet`
127
+
128
+ 명령형 모달(`SModal.confirm` / `loading` / `create`)을 쓴다면 앱 진입점의 **Provider 안쪽에 `<SModalOutlet />` 을 한 번** 렌더하세요.
129
+
130
+ ```tsx
131
+ import { SModalOutlet } from 'sellmate-design-system-react';
132
+
133
+ <QueryClientProvider client={queryClient}>
134
+ <RouterProvider router={router} />
135
+ <SModalOutlet />
136
+ </QueryClientProvider>;
137
+ ```
138
+
139
+ outlet 이 있으면 모달이 앱 렌더 트리의 자식으로 그려져 **QueryClient·Router·Theme 등 Context 를 그대로 상속**합니다(DOM 상으로는 `body` 로 portal 되므로 레이아웃에는 영향이 없습니다). 렌더하지 않아도 모달은 뜨지만 별도 React 루트로 마운트되어 앱의 Provider 가 닿지 않고, 개발 모드에서 경고가 찍힙니다. 자세한 내용은 [`SModal` 문서](./src/components/SModal/README.md)를 보세요.
140
+
126
141
  ## 화면 작성 규칙 (AI 에이전트 포함)
127
142
 
128
143
  컴포넌트 조합·간격·타이포 등 **화면을 만들 때 지켜야 할 규칙**은 패키지에 함께 배포됩니다.
@@ -281,7 +296,7 @@ export default [...sellmate.configs.strict];
281
296
  | Atoms | `SIcon` · `SBadge` · `SCard` · `SDraggableItem` · `SDraggableList` · `SExpansionItem` · `SExpansionList` · `SList` · `SListItem` · `STree` · `SSectionHeaderCard` · `SDivider` · `SScrollArea` · `STag` · `SButton` · `SCallout` · `SLinearProgress` · `SCircleProgress` · `SLoadingContainer` |
282
297
  | Control | `STextLink` · `SDropdownButton` · `SGhostButton` · `SSwitch` · `SToggle` |
283
298
  | Overlays | `STooltip` · `SPopover` · `SPortal` · `SPopup` · `SGuide` |
284
- | Modals | `SConfirmModal` · `SActionModal` · `SLoadingModal` · `SDrawer` · `SModal` |
299
+ | Modals | `SConfirmModal` · `SActionModal` · `SLoadingModal` · `SDrawer` · `SModal` · `SModalOutlet` |
285
300
  | Layout | `SGnb` · `SLayout` · `SPage` |
286
301
  | Navigation | `STabs` · `SPagination` · `SStepper` |
287
302
  | Data | `STable` · `STableBar` · `SKeyValueTable` |
@@ -20,6 +20,9 @@
20
20
  | `railFooter?` | `ReactNode` | — | 레일 하단 고정 슬롯. 레일 아이템이 많아 넘치면 아이템 목록(ul)만 스크롤되고 이 슬롯은 레일 하단에 붙어 고정된다. useRail 일 때만 렌더된다. |
21
21
  | `menuFooter?` | `ReactNode` | — | 메뉴 하단 고정 슬롯. 메뉴 아이템이 많아 넘치면 메뉴 목록(ul)만 스크롤되고 이 슬롯은 메뉴 하단에 붙어 고정된다. 메뉴가 렌더될 때만(showMenu) 나타난다. |
22
22
  | `foldedFooter?` | `ReactNode` | — | 접힘(fix 레일) 하단 고정 슬롯. 접으면 본문이 빠져나가며 rail/menu footer 도 사라지므로, 48px 폴드 레일 바닥에 붙는 별도 슬롯이다. header="fix" 로 접혔을 때만 나타난다. |
23
+ | `resizable?` | `boolean` | `false` | 메뉴 폭을 드래그로 조절할 수 있게 한다. 레일 폭은 고정이고 **메뉴 컬럼만** 늘고 준다. 조절선은 GNB 컬럼의 오른쪽 경계 전체다 — fix 는 상단바 높이까지, full 은 상단바가 전폭이라 본문 높이까지. 접혀 있거나 깔 메뉴가 없으면(레일 리프가 활성) 조절선이 나오지 않는다. |
24
+ | `menuWidth?` | `number` | — | 메뉴 폭(px). 주면 controlled — onMenuWidthChange 로 직접 갱신해야 움직인다 |
25
+ | `defaultMenuWidth?` | `number` | — | 메뉴 초기 폭(px). uncontrolled |
23
26
  | `ariaLabel?` | `string` | `'global navigation'` | 메뉴 landmark(nav) 의 접근성 레이블. 한 화면에 nav 가 여럿일 때 구분한다. |
24
27
 
25
28
  #### Events
@@ -30,6 +33,7 @@
30
33
  | `onRailChange` | `(value: string) => void` | 레일 선택 변경. 레일 아이템을 눌러 패널이 바뀔 때 알린다(선택 상태는 SGnb 가 자체 관리). |
31
34
  | `onFoldChange` | `(folded: boolean) => void` | 접힘 토글 (sdFoldChange) |
32
35
  | `onLauncherClick` | `() => void` | 앱런처(그리드) 버튼 클릭. 미지정 시 런처 버튼을 렌더하지 않는다. 접힘 레일(fix)에는 폴드 버튼만 남으므로 표시되지 않는다. |
36
+ | `onMenuWidthChange` | `(width: number) => void` | 메뉴 폭이 확정될 때(드래그를 놓거나 방향키 조작). 드래그하는 동안에는 오지 않는다 |
33
37
 
34
38
  ## Dependencies
35
39
 
@@ -53,6 +53,18 @@ export interface SGnbProps extends Omit<HTMLAttributes<HTMLDivElement>, 'color'>
53
53
  * 48px 폴드 레일 바닥에 붙는 별도 슬롯이다. header="fix" 로 접혔을 때만 나타난다.
54
54
  */
55
55
  foldedFooter?: ReactNode;
56
+ /**
57
+ * 메뉴 폭을 드래그로 조절할 수 있게 한다. 레일 폭은 고정이고 **메뉴 컬럼만** 늘고 준다.
58
+ * 조절선은 GNB 컬럼의 오른쪽 경계 전체다 — fix 는 상단바 높이까지, full 은 상단바가 전폭이라 본문 높이까지.
59
+ * 접혀 있거나 깔 메뉴가 없으면(레일 리프가 활성) 조절선이 나오지 않는다.
60
+ */
61
+ resizable?: boolean;
62
+ /** 메뉴 폭(px). 주면 controlled — onMenuWidthChange 로 직접 갱신해야 움직인다 */
63
+ menuWidth?: number;
64
+ /** 메뉴 초기 폭(px). uncontrolled */
65
+ defaultMenuWidth?: number;
66
+ /** 메뉴 폭이 확정될 때(드래그를 놓거나 방향키 조작). 드래그하는 동안에는 오지 않는다 */
67
+ onMenuWidthChange?: (width: number) => void;
56
68
  /** 메뉴 landmark(nav) 의 접근성 레이블. 한 화면에 nav 가 여럿일 때 구분한다. */
57
69
  ariaLabel?: string;
58
70
  }
@@ -115,6 +115,19 @@ export declare const GNB_EXPAND_MS = 200;
115
115
  * header="full" 에서 SLayout 이 "메뉴 오른쪽 = 페이지" 영역을 잡을 때도 이 값을 쓴다.
116
116
  */
117
117
  export declare const GNB_MENU_WIDTH = 240;
118
+ /**
119
+ * resizable 일 때 메뉴 폭이 움직일 수 있는 범위(px). 레일 폭은 고정이고 메뉴 컬럼만 늘고 준다.
120
+ * 디자인이 아직 확정한 값이 아니라 임시다 — 확정되면 토큰으로 내려온다.
121
+ */
122
+ export declare const GNB_MENU_MIN_WIDTH = 200;
123
+ export declare const GNB_MENU_MAX_WIDTH = 300;
124
+ /** 메뉴 폭 조절에서 방향키 한 번에 움직이는 양(px). Shift 를 함께 누르면 large. */
125
+ export declare const GNB_MENU_RESIZE_KEY_STEP: {
126
+ normal: number;
127
+ large: number;
128
+ };
129
+ /** 메뉴 폭을 허용 범위 안으로 자른다. */
130
+ export declare const clampGnbMenuWidth: (width: number) => number;
118
131
  /**
119
132
  * header="full" 상단바의 로고 슬롯 고정 폭(토큰 없음 → 상수).
120
133
  * 전폭 상단바에서는 로고 오른쪽에 topContent 슬롯이 이어지므로, 로고 내용이 바뀌어도
@@ -17,6 +17,6 @@ export interface SIconProps {
17
17
  }
18
18
  /**
19
19
  * SIcon — sd-icon 포팅.
20
- * 89개 SVG(icons.gen.tsx)를 currentColor 기반으로 렌더한다.
20
+ * 94개 SVG(icons.gen.tsx)를 currentColor 기반으로 렌더한다.
21
21
  */
22
22
  export declare const SIcon: import("react").ForwardRefExoticComponent<SIconProps & import("react").RefAttributes<HTMLSpanElement>>;
@@ -11,6 +11,7 @@ export declare const ICONS: {
11
11
  readonly arrowLeft: (p: IconRenderProps) => import("react").JSX.Element;
12
12
  readonly arrowRight: (p: IconRenderProps) => import("react").JSX.Element;
13
13
  readonly arrowUp: (p: IconRenderProps) => import("react").JSX.Element;
14
+ readonly asterisk: (p: IconRenderProps) => import("react").JSX.Element;
14
15
  readonly attachFile: (p: IconRenderProps) => import("react").JSX.Element;
15
16
  readonly barcode: (p: IconRenderProps) => import("react").JSX.Element;
16
17
  readonly bell: (p: IconRenderProps) => import("react").JSX.Element;
@@ -53,10 +54,10 @@ export declare const ICONS: {
53
54
  readonly helpFill: (p: IconRenderProps) => import("react").JSX.Element;
54
55
  readonly helpOutline: (p: IconRenderProps) => import("react").JSX.Element;
55
56
  readonly history: (p: IconRenderProps) => import("react").JSX.Element;
56
- readonly imageFill: (p: IconRenderProps) => import("react").JSX.Element;
57
- readonly imageOutline: (p: IconRenderProps) => import("react").JSX.Element;
57
+ readonly image: (p: IconRenderProps) => import("react").JSX.Element;
58
58
  readonly inline: (p: IconRenderProps) => import("react").JSX.Element;
59
59
  readonly launcher: (p: IconRenderProps) => import("react").JSX.Element;
60
+ readonly leftRight: (p: IconRenderProps) => import("react").JSX.Element;
60
61
  readonly line: (p: IconRenderProps) => import("react").JSX.Element;
61
62
  readonly location: (p: IconRenderProps) => import("react").JSX.Element;
62
63
  readonly logout: (p: IconRenderProps) => import("react").JSX.Element;
@@ -77,14 +78,18 @@ export declare const ICONS: {
77
78
  readonly row: (p: IconRenderProps) => import("react").JSX.Element;
78
79
  readonly save: (p: IconRenderProps) => import("react").JSX.Element;
79
80
  readonly search: (p: IconRenderProps) => import("react").JSX.Element;
81
+ readonly send: (p: IconRenderProps) => import("react").JSX.Element;
80
82
  readonly setting: (p: IconRenderProps) => import("react").JSX.Element;
83
+ readonly settingOutline: (p: IconRenderProps) => import("react").JSX.Element;
81
84
  readonly shield: (p: IconRenderProps) => import("react").JSX.Element;
82
85
  readonly sidebar: (p: IconRenderProps) => import("react").JSX.Element;
83
- readonly star: (p: IconRenderProps) => import("react").JSX.Element;
84
86
  readonly store: (p: IconRenderProps) => import("react").JSX.Element;
85
87
  readonly synchronize: (p: IconRenderProps) => import("react").JSX.Element;
88
+ readonly thumbsDown: (p: IconRenderProps) => import("react").JSX.Element;
89
+ readonly thumbsUp: (p: IconRenderProps) => import("react").JSX.Element;
86
90
  readonly updown: (p: IconRenderProps) => import("react").JSX.Element;
87
91
  readonly user: (p: IconRenderProps) => import("react").JSX.Element;
92
+ readonly users: (p: IconRenderProps) => import("react").JSX.Element;
88
93
  readonly visibilityOff: (p: IconRenderProps) => import("react").JSX.Element;
89
94
  readonly visibilityOn: (p: IconRenderProps) => import("react").JSX.Element;
90
95
  readonly warehouseFill: (p: IconRenderProps) => import("react").JSX.Element;
@@ -26,6 +26,12 @@ export interface SLayoutNavState {
26
26
  * true 면 header prop 과 무관하게 full 로 선다.
27
27
  */
28
28
  requireFullHeader: boolean;
29
+ /**
30
+ * GNB 폭을 지금 드래그로 조절하는 중인지.
31
+ * 참이면 메뉴 열의 폭 전환을 끈다 — 접힘용 전환(foldMs)이 그대로 걸려 있으면
32
+ * 열이 손보다 그 길이만큼 늦게 따라와 끌리는 느낌이 사라진다.
33
+ */
34
+ resizing?: boolean;
29
35
  }
30
36
  export interface SLayoutContextValue {
31
37
  /** 메뉴 스타일 — 자식 SGnb 가 스타일을 맞추는 기준 */
@@ -6,6 +6,8 @@
6
6
 
7
7
  호출 시마다 `document.body` 에 컨테이너를 만들어 모달을 렌더하고, 닫힘 애니메이션이 끝나면 자동으로 언마운트한다. 모든 메서드는 체이닝 핸들 [`SModalRef`](#smodalref) 를 반환한다.
8
8
 
9
+ > **앱 부트스트랩에 [`<SModalOutlet />`](#smodaloutlet) 을 한 번 렌더한다.** 그래야 명령형 모달이 앱 렌더 트리의 자식으로 그려져 QueryClient·Router·Theme 등 Context 를 상속한다. 없으면 예전처럼 별도 React 루트로 떠서 **앱의 Provider 가 하나도 닿지 않는다.**
10
+
9
11
  | 메서드 | 띄우는 모달 | 용도 | 주요 콜백/제어 |
10
12
  |---|---|---|---|
11
13
  | [`SModal.confirm(options)`](#smodalconfirm) | `SConfirmModal` | 확인/취소 | `onOk` / `onCancel` / `onClose` |
@@ -20,6 +22,29 @@ import { SModal } from 'sellmate-design-system-react';
20
22
 
21
23
  ---
22
24
 
25
+ ## SModalOutlet
26
+
27
+ 명령형 모달이 **그려지는 자리**. 앱 부트스트랩에서 Provider 안쪽에 **한 번만** 렌더한다. props 는 없다.
28
+
29
+ ```tsx
30
+ import { SModalOutlet } from 'sellmate-design-system-react';
31
+
32
+ <QueryClientProvider client={queryClient}>
33
+ <RouterProvider router={router} />
34
+ <SModalOutlet /> {/* 앱 전체에 하나 */}
35
+ </QueryClientProvider>;
36
+ ```
37
+
38
+ `SModal.confirm/loading/create` 는 전역 스토어에 모달을 넣기만 하고, outlet 이 그것을 `createPortal` 로 `body` 에 그린다. **DOM 위치·쌓임 순서는 outlet 유무와 무관하게 같고**, 달라지는 것은 렌더 트리다 — outlet 이 있으면 모달이 앱 트리의 자식이 되어 Context 를 상속한다.
39
+
40
+ - outlet 을 어디에 두든(Provider 안쪽이기만 하면) 모달은 `body` 로 portal 되므로 레이아웃·`overflow`·`transform` 의 영향을 받지 않는다.
41
+ - **호출부 API 는 그대로다.** outlet 도입 전 코드를 고칠 필요가 없다.
42
+ - outlet 이 없으면 예전처럼 별도 React 루트(`createRoot`)로 마운트되어 모달은 뜨지만 앱의 Provider 가 닿지 않는다 (개발 모드에서 1회 `console.warn`).
43
+ - 모달 본문이 렌더 중 예외를 던지면 모달만 닫히고 앱 트리는 유지된다 — 원인을 지목하는 `console.error` 가 함께 찍힌다.
44
+ - 모달이 떠 있는 채로 outlet 이 언마운트되면(앱 언마운트·Provider 교체) 그 모달은 정리되고 `onDismissed` 가 발화한다 — 남아서 되살아나지 않는다.
45
+
46
+ ---
47
+
23
48
  ## SModal.confirm
24
49
 
25
50
  아이콘 + 제목 + 메시지 + 확인/취소 버튼. `type` 에 따라 아이콘·메인 버튼 색이 결정된다.
@@ -121,30 +146,40 @@ SModal.create({ component: OrderModal, componentProps: { orderId: 'ORD-001' } })
121
146
 
122
147
  ### 비동기 제출 — 응답 보고 닫기
123
148
 
124
- SActionModal `button` 푸터 버튼은 클릭 **즉시 닫힌다**. 저장 API 응답에 따라 닫힘 여부를 정해야 하면 푸터 버튼 대신 **본문에 버튼을 두고** `modalRef` 로 닫힘 시점을 직접 제어한다.
149
+ **하단 버튼은 본문에 직접 두지 않는다.** 액션은 `button`(의도적으로 단수), 보조 버튼은 `footerLeft` 슬롯에 넣는다 그래야 푸터 배경·여백·양끝 분리가 컴포넌트 규칙대로 잡힌다.
150
+
151
+ `button` 은 클릭해도 **모달을 닫지 않는다.** `onClick` 만 발화하므로 저장 API 응답을 보고 `modalRef.ok()` 로 닫으면 된다.
125
152
 
126
153
  ```tsx
127
154
  function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderModalProps) {
128
155
  const [error, setError] = useState('');
156
+ const [saving, setSaving] = useState(false);
129
157
  const handleSubmit = async () => {
158
+ setSaving(true);
130
159
  try {
131
160
  await save(orderId);
132
161
  modalRef.ok(); // 성공 → onOk + 닫기
133
162
  } catch {
134
163
  setError('저장 실패'); // 실패 → 모달 유지
164
+ } finally {
165
+ setSaving(false);
135
166
  }
136
167
  };
137
168
  return (
138
- // button 주지 않으면 푸터가 렌더되지 않는다
139
- <SActionModal open={open} onOpenChange={onOpenChange} onClose={onClose} persistent modalTitle="주문 처리">
169
+ // button footerLeft 도 주지 않으면 푸터가 렌더되지 않는다
170
+ <SActionModal
171
+ open={open} onOpenChange={onOpenChange} onClose={onClose} persistent modalTitle="주문 처리"
172
+ button={{ label: saving ? '저장 중...' : '저장', disabled: saving, onClick: handleSubmit }}
173
+ footerLeft={<SButton color="neutral" outline size="md" label="취소" disabled={saving} onClick={() => modalRef.cancel()} />}
174
+ >
140
175
  {error && <p>{error}</p>}
141
- <SButton label="저장" onClick={handleSubmit} />
142
- <SButton label="취소" onClick={() => modalRef.cancel()} />
143
176
  </SActionModal>
144
177
  );
145
178
  }
146
179
  ```
147
180
 
181
+ `button` 은 `label` · `color`(기본 `primary`) · `outline` · `size`(기본 `md`) · `disabled` · `onClick` 을 받는다. `footerLeft` 는 슬롯이라 `SButton` 을 직접 배치하며, 푸터 규칙상 `size="md"` 를 명시한다.
182
+
148
183
  ---
149
184
 
150
185
  ## SModalRef
@@ -180,7 +215,7 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
180
215
 
181
216
  - **선언형과 공존**: 서비스는 추가 API다. open 상태가 앱 상태/라우트에 묶인 경우엔 선언형 `<SConfirmModal open>` / `<SLoadingModal open>` 이 더 적합하다.
182
217
  - **백드롭·ESC = 중립적 닫힘**: 특정 콜백(onClose 등) 없이 `onDismissed` 만 발화한다. 명시적 버튼·메서드만 onOk/onCancel/onClose 를 발화한다.
183
- - **Context 미상속**: `create` 의 커스텀 컴포넌트는 React 트리(createRoot)에서 렌더되어 부모의 Context Provider(Theme·Store 등)를 상속하지 않는다. 필요하면 컴포넌트 내부에서 직접 Provider 로 감싸라. (confirm/loading 은 토큰이 `:root` CSS 변수라 무관)
218
+ - **Context outlet 이 있어야 상속된다**: [`<SModalOutlet />`](#smodaloutlet) 을 앱 부트스트랩에 렌더하면 `create` 의 커스텀 컴포넌트(와 `confirm/loading` `contentSlot`) 트리의 자식으로 렌더되어 QueryClient·Router·Theme 등을 그대로 쓴다. outlet 없으면 별도 React 트리에서 렌더되어 Provider 가 닿지 않는다 — `useQuery` 는 `No QueryClient set`, `useNavigate` 는 `may be used only in the context of a <Router>` 죽는다. (토큰은 `:root` CSS 변수라 어느 쪽이든 무관)
184
219
  - **`create` 의 `component` 는 SActionModal 을 루트로**: `create` 는 컨테이너를 덧씌우지 않으므로, 본문만 렌더하는 컴포넌트를 넘기면 딤·카드 없이 콘텐츠가 그대로 화면에 붙는다. TypeScript 는 이를 막지 못한다(`component` 타입이 아무 컴포넌트나 허용). 개발 모드에서는 마운트 직후 렌더 결과로 이를 감지해 `console.warn` 으로 경고한다 — 세 모달은 모두 Portal 로 `body` 에 렌더되므로 `create` 가 만든 host 는 비어 있어야 하는데, host 에 엘리먼트가 남아 있으면 모달이 아닌 것으로 판정한다.
185
220
 
186
221
  ## Dependencies
@@ -0,0 +1,18 @@
1
+ /**
2
+ * SModalOutlet — 명령형 모달(`SModal.confirm` / `loading` / `create`)이 그려지는 자리.
3
+ *
4
+ * **앱 부트스트랩에서 Provider 안쪽에 한 번만 렌더한다.** 이 outlet 이 있으면 명령형 모달이
5
+ * 앱 렌더 트리의 자식으로 그려져 QueryClient·Router·Theme 등 앱의 Context 를 그대로 상속한다.
6
+ * DOM 상으로는 예전과 같이 `body` 직속으로 portal 되므로 위치·쌓임 순서는 달라지지 않는다.
7
+ *
8
+ * outlet 을 렌더하지 않아도 모달은 뜨지만, 예전처럼 별도 React 루트로 마운트되어 앱의
9
+ * Context 가 닿지 않는다(개발 모드에서 1회 경고).
10
+ *
11
+ * @example
12
+ * // 앱 부트스트랩 — 한 번만. Provider 안쪽이어야 모달이 상속받는다.
13
+ * <QueryClientProvider client={queryClient}>
14
+ * <RouterProvider router={router} />
15
+ * <SModalOutlet />
16
+ * </QueryClientProvider>
17
+ */
18
+ export declare function SModalOutlet(): import("react").JSX.Element;
@@ -1 +1,2 @@
1
1
  export { SModal, type SModalRef, type SConfirmOptions, type SLoadingOptions, type SCreateOptions, type SModalCreateComponentProps, } from './SModal';
2
+ export { SModalOutlet } from './SModalOutlet';
@@ -0,0 +1,27 @@
1
+ # SSplitter
2
+
3
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
4
+
5
+ ### SSplitter
6
+
7
+ #### Props
8
+
9
+ | Prop | Type | Default | Description |
10
+ |------|------|---------|-------------|
11
+ | `value?` | `number` | — | 첫 패널 크기. unit 단위. 주면 controlled |
12
+ | `defaultValue?` | `number` | — | 첫 패널 초기 크기. uncontrolled. 기본은 '%' 면 50, 'px' 면 240 |
13
+ | `unit?` | `SSplitterUnit` | `'%'` | 모델·limits 를 읽는 단위 |
14
+ | `limits?` | `readonly [number, number]` | — | [최소, 최대]. 생략하면 '%' 는 [10, 90], 'px' 는 [50, Infinity] |
15
+ | `emitImmediately?` | `boolean` | `false` | 드래그하는 동안에도 onValueChange 를 계속 보낸다 |
16
+ | `vertical?` | `boolean` | `false` | true면 패널을 위아래로 쌓는다 (Quasar q-splitter 의 horizontal 에 해당) |
17
+ | `disabled?` | `boolean` | `false` | true면 크기를 바꿀 수 없다. 커서도 구분선도 나오지 않는다 |
18
+ | `dividerClassName?` | `string` | — | 구분선에 얹을 클래스 |
19
+ | `dividerStyle?` | `CSSProperties` | — | 구분선에 얹을 인라인 스타일 |
20
+ | `children?` | `ReactNode` | — | SSplitter.Before 와 SSplitter.After 둘 |
21
+
22
+ #### Events
23
+
24
+ | Event | Type | Description |
25
+ |-------|------|-------------|
26
+ | `onValueChange` | `(value: number) => void` | 크기가 확정될 때. emitImmediately 가 아니면 드래그를 놓는 순간 한 번만 온다 |
27
+
@@ -0,0 +1,32 @@
1
+ import { type CSSProperties, type HTMLAttributes, type ReactNode } from 'react';
2
+ import { type SSplitterUnit } from './splitter.config';
3
+ /** 패널은 순수 슬롯이다 — 크기·제약은 전부 루트가 소유한다. */
4
+ export type SSplitterPaneProps = HTMLAttributes<HTMLDivElement>;
5
+ export interface SSplitterProps extends Omit<HTMLAttributes<HTMLDivElement>, 'defaultValue'> {
6
+ /** 첫 패널 크기. unit 단위. 주면 controlled */
7
+ value?: number;
8
+ /** 첫 패널 초기 크기. uncontrolled. 기본은 '%' 면 50, 'px' 면 240 */
9
+ defaultValue?: number;
10
+ /** 크기가 확정될 때. emitImmediately 가 아니면 드래그를 놓는 순간 한 번만 온다 */
11
+ onValueChange?: (value: number) => void;
12
+ /** 모델·limits 를 읽는 단위 */
13
+ unit?: SSplitterUnit;
14
+ /** [최소, 최대]. 생략하면 '%' 는 [10, 90], 'px' 는 [50, Infinity] */
15
+ limits?: readonly [number, number];
16
+ /** 드래그하는 동안에도 onValueChange 를 계속 보낸다 */
17
+ emitImmediately?: boolean;
18
+ /** true면 패널을 위아래로 쌓는다 (Quasar q-splitter 의 horizontal 에 해당) */
19
+ vertical?: boolean;
20
+ /** true면 크기를 바꿀 수 없다. 커서도 구분선도 나오지 않는다 */
21
+ disabled?: boolean;
22
+ /** 구분선에 얹을 클래스 */
23
+ dividerClassName?: string;
24
+ /** 구분선에 얹을 인라인 스타일 */
25
+ dividerStyle?: CSSProperties;
26
+ /** SSplitter.Before 와 SSplitter.After 둘 */
27
+ children?: ReactNode;
28
+ }
29
+ export declare const SSplitter: import("react").ForwardRefExoticComponent<SSplitterProps & import("react").RefAttributes<HTMLDivElement>> & {
30
+ Before: import("react").ForwardRefExoticComponent<SSplitterPaneProps & import("react").RefAttributes<HTMLDivElement>>;
31
+ After: import("react").ForwardRefExoticComponent<SSplitterPaneProps & import("react").RefAttributes<HTMLDivElement>>;
32
+ };
@@ -0,0 +1,2 @@
1
+ export { SSplitter, type SSplitterPaneProps, type SSplitterProps } from './SSplitter';
2
+ export { SSPLITTER_UNITS, type SSplitterUnit } from './splitter.config';
@@ -0,0 +1,15 @@
1
+ export declare const SSPLITTER_UNITS: readonly ["%", "px"];
2
+ /** 모델·limits 를 읽는 단위. Quasar QSplitter 의 `unit` 과 같다. */
3
+ export type SSplitterUnit = (typeof SSPLITTER_UNITS)[number];
4
+ /** limits 를 주지 않았을 때의 기본 범위. Quasar 와 같은 값이다. */
5
+ export declare const SSPLITTER_DEFAULT_LIMITS: Record<SSplitterUnit, readonly [number, number]>;
6
+ /** value·defaultValue 를 주지 않았을 때의 시작값. */
7
+ export declare const SSPLITTER_DEFAULT_VALUE: Record<SSplitterUnit, number>;
8
+ /**
9
+ * 방향키 한 번에 움직이는 양. 단위가 다르면 체감도 달라야 하므로 단위별로 둔다.
10
+ * 두께·색과 달리 대응 토큰이 없어 상수다(SLAYOUT_MIN_WIDTH 와 같은 이유).
11
+ */
12
+ export declare const SSPLITTER_KEY_STEP: Record<SSplitterUnit, {
13
+ normal: number;
14
+ large: number;
15
+ }>;
@@ -11,20 +11,37 @@
11
11
  | `items` | `SStepperItem[]` | — | 단계 목록 |
12
12
  | `value?` | `string` | — | 현재 활성 단계 value |
13
13
  | `size?` | `SStepperSize` | `'sm'` | 크기 |
14
+ | `vertical?` | `boolean` | `false` | 세로형 여부 |
15
+ | `clickable?` | `boolean` | — | 단계 클릭 가능 여부. 미지정 시 가로형=false, 세로형=true |
14
16
  | `ariaLabel?` | `string` | `'진행 단계'` | 접근성 레이블 |
15
17
  | `className?` | `string` | — | |
16
18
  | `style?` | `CSSProperties` | — | |
17
19
 
20
+ #### Events
21
+
22
+ | Event | Type | Description |
23
+ |-------|------|-------------|
24
+ | `onValueChange` | `(value: string, item: SStepperItem, index: number) => void` | 단계 선택 시 호출 |
25
+
26
+ #### Methods (ref)
27
+
28
+ | Method | Type | Description |
29
+ |--------|------|-------------|
30
+ | `showItemTooltip` | `() => void` | error 아이템 중 첫 번째에 툴팁을 연다. error 아이템이 없으면 아무 일도 일어나지 않는다. |
31
+ | `hideItemTooltip` | `() => void` | 열려있는 커스텀 툴팁을 닫는다. 활성 단계(value prop)가 바뀌면 별도 호출 없이도 자동으로 닫힌다. |
32
+
18
33
  ## Dependencies
19
34
 
20
35
  ### Depends on
21
36
 
22
37
  - [SIcon](../SIcon)
38
+ - [STooltip](../STooltip)
23
39
 
24
40
  ### Graph
25
41
 
26
42
  ```mermaid
27
43
  graph TD;
28
44
  SStepper --> SIcon
45
+ SStepper --> STooltip
29
46
  style SStepper fill:#f9f,stroke:#333,stroke-width:4px
30
47
  ```
@@ -7,10 +7,30 @@ export interface SStepperProps {
7
7
  value?: string;
8
8
  /** 크기 */
9
9
  size?: SStepperSize;
10
+ /** 세로형 여부 */
11
+ vertical?: boolean;
12
+ /** 단계 클릭 가능 여부. 미지정 시 가로형=false, 세로형=true */
13
+ clickable?: boolean;
14
+ /** 단계 선택 시 호출 */
15
+ onValueChange?: (value: string, item: SStepperItem, index: number) => void;
10
16
  /** 접근성 레이블 */
11
17
  ariaLabel?: string;
12
18
  className?: string;
13
19
  style?: CSSProperties;
14
20
  }
21
+ /**
22
+ * ref 로 노출되는 커스텀 툴팁 제어 API. 세로형 전용 — items 중 error 가 true 인 첫 번째
23
+ * 아이템의 텍스트+뱃지 영역에 표시된다.
24
+ *
25
+ * 지금은 대상(첫 번째 error 아이템 하나)·문구·스타일이 hover 툴팁(ERROR_TOOLTIP_MESSAGE, danger)과
26
+ * 동일하게 고정되어 있다 — 열고 닫는 타이밍만 소비자가 제어한다. 대상 아이템을 지정하거나
27
+ * content·type 을 커스터마이즈하는 기능은 필요해지면 추가한다(지금은 만들지 않음).
28
+ */
29
+ export interface SStepperHandle {
30
+ /** error 아이템 중 첫 번째에 툴팁을 연다. error 아이템이 없으면 아무 일도 일어나지 않는다. */
31
+ showItemTooltip: () => void;
32
+ /** 열려있는 커스텀 툴팁을 닫는다. 활성 단계(value prop)가 바뀌면 별도 호출 없이도 자동으로 닫힌다. */
33
+ hideItemTooltip: () => void;
34
+ }
15
35
  /** SStepper — sd-stepper 포팅. 단계 진행 상태 표시 전용. */
16
- export declare function SStepper({ items, value, size, ariaLabel, className, style, }: SStepperProps): import("react").JSX.Element;
36
+ export declare const SStepper: import("react").ForwardRefExoticComponent<SStepperProps & import("react").RefAttributes<SStepperHandle>>;
@@ -1,2 +1,2 @@
1
- export { SStepper, type SStepperProps } from './SStepper';
1
+ export { SStepper, type SStepperHandle, type SStepperProps } from './SStepper';
2
2
  export { STEPPER_SIZES, type SStepperItem, type SStepperSize } from './stepper.config';
@@ -3,6 +3,9 @@ export type SStepperSize = (typeof STEPPER_SIZES)[number];
3
3
  export type SStepperItem = {
4
4
  label: string;
5
5
  value?: string;
6
+ group?: string;
7
+ completed?: boolean;
8
+ error?: boolean;
6
9
  };
7
10
  export declare const STEPPER_SIZE_CONFIG: Record<SStepperSize, {
8
11
  sequenceSize: string;
@@ -34,6 +37,7 @@ export declare const STEPPER_COLORS: {
34
37
  sequenceText: {
35
38
  active: string;
36
39
  default: string;
40
+ completed: string;
37
41
  };
38
42
  text: {
39
43
  active: string;
@@ -41,3 +45,39 @@ export declare const STEPPER_COLORS: {
41
45
  completed: string;
42
46
  };
43
47
  };
48
+ export declare const STEPPER_HORIZONTAL_COMPLETED_COLORS: {
49
+ sequenceBorder: string;
50
+ sequenceBg: string;
51
+ sequenceIcon: string;
52
+ text: string;
53
+ };
54
+ export declare const STEPPER_VERTICAL_LAYOUT: {
55
+ width: string;
56
+ itemGap: string;
57
+ itemPaddingX: string;
58
+ itemPaddingY: string;
59
+ titlePaddingTop: string;
60
+ titlePaddingBottom: string;
61
+ titleFontSize: string;
62
+ titleLineHeight: string;
63
+ accentStripeWidth: string;
64
+ accentStripeColor: string;
65
+ };
66
+ export declare const STEPPER_VERTICAL_COLORS: {
67
+ itemBg: {
68
+ default: string;
69
+ hover: string;
70
+ selected: string;
71
+ };
72
+ titleText: string;
73
+ textDefault: string;
74
+ textCompleted: string;
75
+ sequenceBorderActive: string;
76
+ sequenceBgActive: string;
77
+ sequenceTextActive: string;
78
+ sequenceTextDefault: string;
79
+ sequenceBorderCompleted: string;
80
+ sequenceBgCompleted: string;
81
+ sequenceIconCompleted: string;
82
+ errorBadge: string;
83
+ };
@@ -47,6 +47,7 @@
47
47
  - [SField](../SField)
48
48
  - [SKeyValueTable](../SKeyValueTable)
49
49
  - [SSectionHeaderCard](../SSectionHeaderCard)
50
+ - [SStepper](../SStepper)
50
51
 
51
52
  ### Depends on
52
53
 
@@ -62,5 +63,6 @@ graph TD;
62
63
  SField --> STooltip
63
64
  SKeyValueTable --> STooltip
64
65
  SSectionHeaderCard --> STooltip
66
+ SStepper --> STooltip
65
67
  style STooltip fill:#f9f,stroke:#333,stroke-width:4px
66
68
  ```