sellmate-design-system-react 9.0.0-beta.109 → 9.0.0-beta.111

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 (118) hide show
  1. package/AGENTS.md +49 -41
  2. package/README.md +8 -8
  3. package/dist/components/SActionModal/README.md +5 -4
  4. package/dist/components/SActionModal/SActionModal.d.ts +8 -6
  5. package/dist/components/SBadge/SBadge.d.ts +1 -1
  6. package/dist/components/SBarcodeInput/README.md +1 -1
  7. package/dist/components/SBarcodeInput/SBarcodeInput.d.ts +2 -2
  8. package/dist/components/SButton/SButton.d.ts +2 -3
  9. package/dist/components/SCalendar/README.md +2 -2
  10. package/dist/components/SCalendar/SCalendar.d.ts +3 -3
  11. package/dist/components/SCalendarBoard/CalendarDayCard.d.ts +8 -0
  12. package/dist/components/SCallout/SCallout.d.ts +2 -3
  13. package/dist/components/SCard/SCard.d.ts +1 -1
  14. package/dist/components/SCheckbox/README.md +1 -1
  15. package/dist/components/SCheckbox/SCheckbox.d.ts +2 -2
  16. package/dist/components/SChip/README.md +4 -4
  17. package/dist/components/SChip/SChip.d.ts +5 -5
  18. package/dist/components/SChipInput/README.md +2 -2
  19. package/dist/components/SChipInput/SChipInput.d.ts +4 -4
  20. package/dist/components/SCircleProgress/SCircleProgress.d.ts +1 -1
  21. package/dist/components/SConfirmModal/README.md +10 -9
  22. package/dist/components/SConfirmModal/SConfirmModal.d.ts +15 -13
  23. package/dist/components/SDatePicker/README.md +2 -2
  24. package/dist/components/SDatePicker/SDatePicker.d.ts +3 -3
  25. package/dist/components/SDateRangePicker/README.md +2 -2
  26. package/dist/components/SDateRangePicker/SDateRangePicker.d.ts +3 -3
  27. package/dist/components/SDivider/SDivider.d.ts +1 -1
  28. package/dist/components/SDrawer/README.md +2 -1
  29. package/dist/components/SDrawer/SDrawer.d.ts +10 -5
  30. package/dist/components/SDropdownButton/README.md +5 -5
  31. package/dist/components/SDropdownButton/SDropdownButton.d.ts +5 -5
  32. package/dist/components/SEditor/README.md +1 -1
  33. package/dist/components/SEditor/SEditor.d.ts +1 -1
  34. package/dist/components/SField/SField.d.ts +3 -3
  35. package/dist/components/SFilePicker/README.md +2 -2
  36. package/dist/components/SFilePicker/SFilePicker.d.ts +3 -3
  37. package/dist/components/SForm/README.md +4 -4
  38. package/dist/components/SForm/SForm.d.ts +6 -6
  39. package/dist/components/SGhostButton/README.md +1 -1
  40. package/dist/components/SGhostButton/SGhostButton.d.ts +2 -2
  41. package/dist/components/SGnb/README.md +2 -2
  42. package/dist/components/SGnb/SGnb.d.ts +2 -2
  43. package/dist/components/SGuide/README.md +1 -1
  44. package/dist/components/SGuide/SGuide.d.ts +2 -2
  45. package/dist/components/SIcon/SIcon.d.ts +1 -2
  46. package/dist/components/SInput/README.md +3 -3
  47. package/dist/components/SInput/SInput.d.ts +4 -4
  48. package/dist/components/SKeyValueTable/README.md +2 -2
  49. package/dist/components/SKeyValueTable/SKeyValueTable.d.ts +3 -3
  50. package/dist/components/SLinearProgress/SLinearProgress.d.ts +1 -1
  51. package/dist/components/SLoadingContainer/README.md +1 -1
  52. package/dist/components/SLoadingContainer/SLoadingContainer.d.ts +3 -3
  53. package/dist/components/SLoadingModal/README.md +4 -3
  54. package/dist/components/SLoadingModal/SLoadingModal.d.ts +7 -5
  55. package/dist/components/SModal/README.md +7 -7
  56. package/dist/components/SModal/SModal.d.ts +4 -4
  57. package/dist/components/SModalContainer/README.md +3 -2
  58. package/dist/components/SModalContainer/SModalContainer.d.ts +12 -7
  59. package/dist/components/SNumberInput/README.md +3 -3
  60. package/dist/components/SNumberInput/SNumberInput.d.ts +4 -4
  61. package/dist/components/SPage/SPageHeader.d.ts +1 -1
  62. package/dist/components/SPagination/README.md +11 -1
  63. package/dist/components/SPagination/SPagination.d.ts +9 -3
  64. package/dist/components/SPagination/index.d.ts +1 -1
  65. package/dist/components/SPopover/README.md +1 -1
  66. package/dist/components/SPopover/SPopover.d.ts +2 -2
  67. package/dist/components/SPopup/README.md +1 -1
  68. package/dist/components/SPopup/SPopup.d.ts +2 -2
  69. package/dist/components/SPortal/README.md +6 -5
  70. package/dist/components/SPortal/SPortal.d.ts +11 -9
  71. package/dist/components/SRadio/README.md +4 -4
  72. package/dist/components/SRadio/SRadio.d.ts +3 -3
  73. package/dist/components/SRadio/SRadioGroup.d.ts +3 -3
  74. package/dist/components/SRadioButton/README.md +1 -1
  75. package/dist/components/SRadioButton/SRadioButton.d.ts +2 -2
  76. package/dist/components/SRadioCard/README.md +2 -2
  77. package/dist/components/SRadioCard/SRadioCard.d.ts +1 -1
  78. package/dist/components/SRadioCard/SRadioCardGroup.d.ts +1 -1
  79. package/dist/components/SScrollArea/SScrollArea.d.ts +1 -2
  80. package/dist/components/SSelect/README.md +4 -4
  81. package/dist/components/SSelect/SSelect.d.ts +4 -4
  82. package/dist/components/SSelect/select.config.d.ts +2 -2
  83. package/dist/components/SSlider/README.md +1 -1
  84. package/dist/components/SSlider/SSlider.d.ts +1 -1
  85. package/dist/components/SStepper/SStepper.d.ts +1 -1
  86. package/dist/components/SSwitch/README.md +1 -1
  87. package/dist/components/SSwitch/SSwitch.d.ts +2 -2
  88. package/dist/components/STable/README.md +2 -2
  89. package/dist/components/STable/STable.d.ts +4 -4
  90. package/dist/components/STabs/README.md +1 -1
  91. package/dist/components/STabs/STabs.d.ts +3 -4
  92. package/dist/components/STag/STag.d.ts +1 -2
  93. package/dist/components/STextLink/STextLink.d.ts +1 -1
  94. package/dist/components/STextarea/README.md +1 -1
  95. package/dist/components/STextarea/STextarea.d.ts +2 -2
  96. package/dist/components/STimePicker/README.md +2 -2
  97. package/dist/components/STimePicker/STimePicker.d.ts +3 -3
  98. package/dist/components/STimeRangePicker/README.md +2 -2
  99. package/dist/components/STimeRangePicker/STimeRangePicker.d.ts +3 -3
  100. package/dist/components/SToast/README.md +4 -4
  101. package/dist/components/SToast/SToast.d.ts +3 -3
  102. package/dist/components/SToast/SToastContainer.d.ts +3 -3
  103. package/dist/components/SToggle/README.md +1 -1
  104. package/dist/components/SToggle/SToggle.d.ts +2 -2
  105. package/dist/components/STooltip/STooltip.d.ts +1 -1
  106. package/dist/index.cjs +224 -134
  107. package/dist/index.cjs.map +1 -1
  108. package/dist/index.js +224 -134
  109. package/dist/index.js.map +1 -1
  110. package/dist/lib/color.d.ts +1 -1
  111. package/dist/lib/floating-anim.d.ts +2 -2
  112. package/dist/lib/floating-width.d.ts +1 -2
  113. package/dist/lib/form.d.ts +1 -1
  114. package/dist/llms-full.txt +167 -145
  115. package/dist/llms.txt +54 -56
  116. package/dist/styles.css +19 -0
  117. package/dist/theme.css +8 -5
  118. package/package.json +5 -1
package/AGENTS.md CHANGED
@@ -860,7 +860,9 @@ SModal.create({ component: OrderModal, componentProps: { orderId } })
860
860
 
861
861
  **`button` 은 `SButton` 의 prop 을 그대로 받는다** — `icon`·`rightIcon`·`outline`·`disabled` 등을 함께 넘길 수 있다. 단 **`size` 는 타입에 없다**: 버튼 크기는 놓이는 자리가 정하므로(§3-5-2) 푸터가 준다. 모달·드로어·팝업의 푸터는 **언제나 `md`** 라 받을 것이 없다 — 크기를 고를 수 있는 것은 카드 둘뿐이다(§3-7-8 `footerSize`). **`color` 는 주지 않는 것이 기본**이다 — 주지 않으면 주 액션 규칙대로 `primary` 가 되고, 그 버튼이 파괴적 액션일 때만 §3-5-3 에 따라 `danger` 를 준다. 카드(§3-7-8)의 `button` 도 같다.
862
862
 
863
- **확인 모달은 확인 버튼 하나로 세우는 것이 기본이다 — `subButtonLabel` 을 주지 않는다.** 비워 두면 서브 버튼이 아예 서지 않는다. 답하지 않고 나가는 길은 **X 버튼이 이미 맡고**(늘 서 있다), `persistent` 를 켜지 않았으면 백드롭·ESC 로도 닫힌다(§3-3-5).
863
+ **`type` 은 모달이 말하는 성격으로 고른다.** `positive` 는 알림, `negative` 는 되돌릴 수 없거나 위험한 일의 확인, `complete` 는 작업·절차가 **끝났음**(등록·처리 완료), `default` 는 중립 확인이다. 아이콘과 확인 버튼 색은 `type` 이 정하므로 앱이 따로 고르지 않는다 — 저장·변경 같은 단순 알림은 `positive`, 상품 등록·주문 처리처럼 한 건의 작업이나 절차가 마무리됐음을 알릴 때는 `complete` 다.
864
+
865
+ **확인 모달은 확인 버튼 하나로 세우는 것이 기본이다 — `subButtonLabel` 을 주지 않는다.** 비워 두면 서브 버튼이 아예 서지 않는다. 답하지 않고 나가는 길은 **X 버튼이 이미 맡고**(늘 서 있다), `persistent` 를 켜지 않았으면 백드롭으로, `closeOnEscape` 를 끄지 않았으면 ESC 로도 닫힌다(§3-3-5).
864
866
 
865
867
  **서브 버튼은 `onCancel` 이 닫는 것 말고 따로 할 일이 있을 때만 둔다.** 눌러도 창만 닫힌다면 X 와 똑같은 일을 하는 버튼이 하나 더 서는 것이라, 어느 것을 눌러야 하는지가 오히려 흐려진다. **되묻는 모달이라고 예외가 아니다** — "삭제할까요?" 에 답하지 않고 나가는 것은 X 로 충분하다.
866
868
 
@@ -873,6 +875,8 @@ SModal.create({ component: OrderModal, componentProps: { orderId } })
873
875
  mainButtonLabel: '자동 취소', subButtonLabel: '수동 처리' })
874
876
  .onOk(() => cancelShortOrders()).onCancel(() => openManualFlow())
875
877
  // 서브가 창을 닫는 것이 아니라 다른 흐름을 시작한다
878
+ ✅ SModal.confirm({ type: 'complete', modalTitle: '상품 등록이 완료되었습니다', mainButtonLabel: '확인' })
879
+ // 한 건의 작업·절차가 끝났음을 알린다 — 저장·변경 같은 단순 알림은 'positive'
876
880
 
877
881
  ❌ SModal.confirm({ …, mainButtonLabel: '삭제', subButtonLabel: '취소' }).onCancel(() => {})
878
882
  // 눌러도 닫히기만 한다 — X 가 이미 하는 일
@@ -888,28 +892,32 @@ SModal.create({ component: OrderModal, componentProps: { orderId } })
888
892
 
889
893
  **모달 안에서도 앱의 훅을 그냥 쓴다 — 단, 앱 루트에 `SModalOutlet` 이 있어야 한다 (§4-1).** outlet 이 있으면 명령형 모달이 앱 렌더 트리의 자식으로 그려지므로 `useQuery`·`useNavigate`·`useTheme` 같은 Context 기반 훅이 페이지에서와 똑같이 동작한다. **모달 컴포넌트를 Provider 로 다시 감싸지 않는다.** outlet 없이 띄우면 모달이 별도 React 루트로 떠서 Provider 가 하나도 닿지 않고, `No QueryClient set` 처럼 모달을 여는 순간에만 터진다.
890
894
 
891
- #### 3-3-5. 닫기 경로 — `persistent` 기본값은 컴포넌트마다 다르다
895
+ #### 3-3-5. 닫기 경로 — `persistent`(백드롭)와 `closeOnEscape`(ESC)는 따로 제어한다
892
896
 
893
- **입력을 담는 모달은 백드롭 클릭·ESC 로 닫히지 않는 것이 기본**이다. 닫기 시도는 흔들림(shake)으로만 반응한다. 작성 중인 내용을 실수로 잃지 않게 하기 위한 것이다. 반면 `SConfirmModal` 은 잃을 입력이 없으므로 **백드롭·ESC 로 닫히는 것이 기본**이다.
897
+ **`persistent` 는 백드롭(바깥) 클릭만, `closeOnEscape` 는 ESC 만 제어한다.** 두 prop 은 서로 영향을 주지 않는다. **ESC 는 로딩 모달을 뺀 모든 모달·드로어에서 기본으로 닫힌다**(`closeOnEscape` 기본 `true`, `SLoadingModal` 만 `false`). **입력을 담는 모달은 백드롭 클릭으로 닫히지 않는 것이 기본**이다 — 닫기 시도는 흔들림(shake)으로만 반응한다. 작성 중인 내용을 실수로 잃지 않게 하기 위한 것이다. 반면 `SConfirmModal` 은 잃을 입력이 없으므로 **백드롭으로도 닫히는 것이 기본**이다.
894
898
 
895
- | | `persistent` 기본값 | 기본 닫기 경로 | 백드롭·ESC |
896
- | --- | --- | --- | --- |
897
- | `SActionModal` · `SLoadingModal` | `true` | X 버튼, 모달 안의 버튼 | 막힘 (흔들림) |
898
- | ↳ `SActionModal` 에 `showClose={false}` | `true` | **모달 안의 버튼만** | 막힘 (흔들림) |
899
- | `SDrawer` | `true` | X 버튼, footer 버튼 | 막힘 (흔들림) |
900
- | `SConfirmModal` | `false` | X 버튼, 확인 버튼 | **닫힌다** |
901
- | `SPopover` · `STooltip` · `SSelect` 등 floating | — | 바깥 클릭·ESC | **막지 않는다** — 이 규칙의 대상이 아니다 |
899
+ | | `persistent` 기본값 | `closeOnEscape` 기본값 | 기본 닫기 경로 | 백드롭 | ESC |
900
+ | --- | --- | --- | --- | --- | --- |
901
+ | `SActionModal` | `true` | `true` | X 버튼, 모달 안의 버튼, ESC | 막힘 (흔들림) | **닫힌다** |
902
+ | `SLoadingModal` | `true` | **`false`** | (error 상태) X 버튼, 모달 안의 버튼 | 막힘 (흔들림) | 막힘 (흔들림) |
903
+ | ↳ `SActionModal` 에 `showClose={false}` | `true` | `true` | 모달 안의 버튼, ESC | 막힘 (흔들림) | **닫힌다** |
904
+ | `SDrawer` | `true` | `true` | X 버튼, footer 버튼, ESC | 막힘 (흔들림) | **닫힌다** |
905
+ | `SConfirmModal` | `false` | `true` | X 버튼, 확인 버튼, 백드롭, ESC | **닫힌다** | **닫힌다** |
906
+ | `SPopover` · `STooltip` · `SSelect` 등 floating | — | — | 바깥 클릭·ESC | **막지 않는다** — 이 규칙의 대상이 아니다 | **막지 않는다** |
907
+
908
+ `SLoadingModal`(과 `SModal.loading()`)은 로딩 중 임의로 닫히지 않게 `persistent` 가 `true`, `closeOnEscape` 가 `false` 가 기본이다. error 상태에서는 모달 안의 버튼이 필수로 서므로 갇히지 않는다.
902
909
 
903
910
  따라서 다음을 지킨다.
904
911
 
905
912
  - **닫을 수단을 반드시 하나는 둔다.** X 버튼은 기본으로 서 있으므로, 이 규칙이 걸리는 것은 `SActionModal` 에 `showClose={false}` 를 준 경우다. 그때 `button` 도 `footerLeft` 도 없으면 사용자가 모달을 닫을 방법이 없다 — 백드롭이 더 이상 탈출구가 아니기 때문이다. 개발 중 콘솔 경고가 뜬다.
906
- - **`SActionModal` 의 `showClose={false}` 는 액션을 끝내야만 나갈 수 있는 모달에만.** 필수 약관 동의, 필수 정보 입력처럼 **중간에 빠져나가면 앱이 정의되지 않은 상태로 남는 경우**로 한정한다. 그 외에는 기본값(X 가 있는 상태)을 그대로 둔다 — "집중시키고 싶다"는 이유로 X 를 없애지 않는다. `persistent` 기본값(true)과 합쳐져 나가는 길이 버튼 하나뿐이 되므로, 그 버튼은 **비활성 상태로 시작하더라도 사용자가 스스로 풀 수 있는 조건**이어야 한다(동의 체크 등). 서버 응답을 기다려야 푸는 조건이면 실패 시 사용자가 갇힌다.
907
- - **기본값과 같은 `persistent` 를 직접 주지 않는다.** 중복이다.
913
+ - **`SActionModal` 의 `showClose={false}` 는 액션을 끝내야만 나갈 수 있는 모달에만, `closeOnEscape={false}` 와 함께.** 필수 약관 동의, 필수 정보 입력처럼 **중간에 빠져나가면 앱이 정의되지 않은 상태로 남는 경우**로 한정한다. 그 외에는 기본값(X 가 있는 상태)을 그대로 둔다 — "집중시키고 싶다"는 이유로 X 를 없애지 않는다. `persistent` 기본값(true)으로 백드롭이 막혀 있고 ESC 는 `closeOnEscape` 기본값(true)이라 열려 있다 — 필수 액션이면 `closeOnEscape={false}` 도 함께 줘서 ESC 로 빠져나가지 못하게 한다. 그러면 나가는 길이 버튼 하나뿐이 되므로 그 버튼은 **비활성 상태로 시작하더라도 사용자가 스스로 풀 수 있는 조건**이어야 한다(동의 체크 등). 서버 응답을 기다려야 푸는 조건이면 실패 시 사용자가 갇힌다.
914
+ - **기본값과 같은 `persistent`·`closeOnEscape` 를 직접 주지 않는다.** 중복이다.
915
+ - **`closeOnEscape={false}` 는 ESC 한 번으로 빠져나가면 안 되는 흐름에만.** 입력 중 ESC 로 내용을 잃으면 안 되는 긴 폼이나, 반드시 버튼으로 답을 받아야 하는 확인처럼 한정한다. 그 외에는 기본값(ESC 로 닫힘)을 그대로 둔다. `persistent` 와 `closeOnEscape={false}` 를 함께 켜면 백드롭·ESC 가 모두 막히므로, X 나 모달 안의 버튼 같은 나가는 길이 반드시 있어야 한다 — `SActionModal` 에서 `showClose={false}` 까지 겹치면 `button`·`footerLeft` 가 없을 때 개발 중 콘솔 경고가 뜬다.
908
916
  - **`SActionModal`·`SDrawer` 에 `persistent={false}` 는 잃을 입력이 없을 때만.** 단순 알림처럼 임의로 닫혀도 아무것도 사라지지 않는 경우로 한정한다.
909
917
  - **`SConfirmModal` 에 `persistent` 를 켜는 것은 반드시 답을 받아야 할 때만.** 되돌릴 수 없는 파괴적 작업의 확인처럼, 임의로 닫히면 안 되는 경우로 한정한다.
910
- - **`SConfirmModal` 에 `persistent` 와 `mainButtonDisabled` 를 함께 켜면 나가는 길이 X 하나만 남는다.** 갇히지는 않지만(X 는 늘 서 있다) 확인이 왜 잠겼는지가 화면에 없으면 막다른 길로 읽힌다 — 잠근 이유를 `bottomMessage` 나 `contentSlot` 에 함께 둔다(§3-3-4).
918
+ - **`SConfirmModal` 에 `persistent`·`closeOnEscape={false}` 와 `mainButtonDisabled` 를 함께 켜면 나가는 길이 X 하나만 남는다.** 갇히지는 않지만(X 는 늘 서 있다) 확인이 왜 잠겼는지가 화면에 없으면 막다른 길로 읽힌다 — 잠근 이유를 `bottomMessage` 나 `contentSlot` 에 함께 둔다(§3-3-4).
911
919
 
912
- 작성 중인 내용이 있을 때 닫기를 시도하면 이탈 안내를 띄우는 것은 **소비 앱 몫**이다. 디자인 시스템은 dirty 상태를 알지 못하므로 백드롭·ESC 를 일괄 차단할 뿐이다. 안내가 필요하면 앱이 자체 dirty 판정 후 `SModal.confirm` 으로 띄운다.
920
+ 작성 중인 내용이 있을 때 닫기를 시도하면 이탈 안내를 띄우는 것은 **소비 앱 몫**이다. 디자인 시스템은 dirty 상태를 알지 못하므로 백드롭만 일괄 차단하고 ESC 는 열려 있다. ESC 로 닫힐 때도 안내가 필요하면 앱이 `onOpenChange` 에서 자체 dirty 판정 후 닫지 않고 `SModal.confirm` 으로 띄운다.
913
921
 
914
922
  #### 3-3-6. 전역 로딩 오버레이 — `loading`
915
923
 
@@ -2237,6 +2245,29 @@ const [selectedId, setSelectedId] = useState<string>();
2237
2245
  </STooltip>
2238
2246
  ```
2239
2247
 
2248
+ #### 3-7-13. 숫자 — SNumberInput 의 min·max 는 검증 경계다
2249
+
2250
+ `min`·`max` 는 `<input type="number">` 의 그것과 같다 — **값을 고쳐 쓰지 않는 검증 경계**다. 범위를 벗어나면 blur·제출 시 에러 상태가 서고, 사용자가 친 값은 그대로 남아 앱의 범위 가드에 도달한다.
2251
+
2252
+ - **문구를 앱이 대지 않아도 된다.** `rules` 도 `SForm` 도 없는 화면에서 `min`·`max` 만 주면 DS 가 「1~99 사이로 입력해 주세요.」 같은 기본 안내를 자동으로 붙인다. 범위 이탈이 아무 표시 없이 지나가는 경로가 없다.
2253
+ - **화면의 말로 바꾸려면 `rules` 를 준다.** 「우선순위는 1부터」처럼 그 화면에서만 통하는 문구가 있으면 `rules` 가 DS 기본 문구를 이긴다. 앱이 직접 준 `errorMessage` 도 마찬가지다.
2254
+ - **`SForm` 안에서는 범위 이탈이 제출을 막는다.** `rules` 를 따로 걸지 않아도 그렇다 — 네이티브 폼과 같다.
2255
+ - **빈 칸(`null`)은 범위 판정 대상이 아니다.** 필수 입력은 `min` 이 아니라 `rules` 로 막는다.
2256
+ - **경계로 붙여도 사용자가 놀라지 않는 값에서만 `clampOnBlur` 를 켠다.** 켜면 blur 시 값이 경계로 **말없이 바뀌고** `onValueChange` 로 되쏘아진다 — 그래서 앱의 범위 가드에는 범위 밖 값이 도달하지 않는다. 재고 조정 수량처럼 보정이 자연스러운 자리에만 쓰고, 업무 규칙(최소 주문 수량·우선순위 시작값)에는 쓰지 않는다.
2257
+ - 스테퍼(`showButton`)와 위/아래 화살표는 `clampOnBlur` 와 무관하게 언제나 `min`·`max` 를 한계로 삼는다.
2258
+
2259
+ ```tsx
2260
+ ✅ <SNumberInput label="우선순위" min={1} max={99} value={v} onValueChange={setV} />
2261
+ {/* 0 을 넣고 blur 하면 0 이 남고 에러가 선다 — 저장 가드의 v < 1 이 그대로 걸린다 */}
2262
+
2263
+ ✅ <SNumberInput label="우선순위" min={1} max={99} value={v} onValueChange={setV}
2264
+ rules={[v => (typeof v === 'number' && (v < 1 || v > 99) ? '우선순위는 1부터 99까지입니다.' : true)]} />
2265
+ {/* 문구만 화면의 말로 바꾼다 */}
2266
+
2267
+ ❌ <SNumberInput label="우선순위" min={1} clampOnBlur value={v} onValueChange={setV} />
2268
+ {/* 0 이 1 로 바뀐 뒤 도착하므로 저장 직전의 v < 1 가드는 영원히 걸리지 않는다 */}
2269
+ ```
2270
+
2240
2271
  #### 3-7-14. 로고 — SLogo
2241
2272
 
2242
2273
  셀메이트·서비스 로고는 `<svg>` 를 직접 붙이지 않고 `SLogo` 를 쓴다. **`SIcon` 과 같은 손맛(`name`·`size`·`color`)이지만 두 가지가 다르다.**
@@ -2268,29 +2299,6 @@ const [selectedId, setSelectedId] = useState<string>();
2268
2299
  ❌ <SLogo color="grey_45" /> {/* 물러날 이유가 없으면 브랜드 색 그대로 둔다 */}
2269
2300
  ```
2270
2301
 
2271
- #### 3-7-13. 숫자 — SNumberInput 의 min·max 는 검증 경계다
2272
-
2273
- `min`·`max` 는 `<input type="number">` 의 그것과 같다 — **값을 고쳐 쓰지 않는 검증 경계**다. 범위를 벗어나면 blur·제출 시 에러 상태가 서고, 사용자가 친 값은 그대로 남아 앱의 범위 가드에 도달한다.
2274
-
2275
- - **문구를 앱이 대지 않아도 된다.** `rules` 도 `SForm` 도 없는 화면에서 `min`·`max` 만 주면 DS 가 「1~99 사이로 입력해 주세요.」 같은 기본 안내를 자동으로 붙인다. 범위 이탈이 아무 표시 없이 지나가는 경로가 없다.
2276
- - **화면의 말로 바꾸려면 `rules` 를 준다.** 「우선순위는 1부터」처럼 그 화면에서만 통하는 문구가 있으면 `rules` 가 DS 기본 문구를 이긴다. 앱이 직접 준 `errorMessage` 도 마찬가지다.
2277
- - **`SForm` 안에서는 범위 이탈이 제출을 막는다.** `rules` 를 따로 걸지 않아도 그렇다 — 네이티브 폼과 같다.
2278
- - **빈 칸(`null`)은 범위 판정 대상이 아니다.** 필수 입력은 `min` 이 아니라 `rules` 로 막는다.
2279
- - **경계로 붙여도 사용자가 놀라지 않는 값에서만 `clampOnBlur` 를 켠다.** 켜면 blur 시 값이 경계로 **말없이 바뀌고** `onValueChange` 로 되쏘아진다 — 그래서 앱의 범위 가드에는 범위 밖 값이 도달하지 않는다. 재고 조정 수량처럼 보정이 자연스러운 자리에만 쓰고, 업무 규칙(최소 주문 수량·우선순위 시작값)에는 쓰지 않는다.
2280
- - 스테퍼(`showButton`)와 위/아래 화살표는 `clampOnBlur` 와 무관하게 언제나 `min`·`max` 를 한계로 삼는다.
2281
-
2282
- ```tsx
2283
- ✅ <SNumberInput label="우선순위" min={1} max={99} value={v} onValueChange={setV} />
2284
- {/* 0 을 넣고 blur 하면 0 이 남고 에러가 선다 — 저장 가드의 v < 1 이 그대로 걸린다 */}
2285
-
2286
- ✅ <SNumberInput label="우선순위" min={1} max={99} value={v} onValueChange={setV}
2287
- rules={[v => (typeof v === 'number' && (v < 1 || v > 99) ? '우선순위는 1부터 99까지입니다.' : true)]} />
2288
- {/* 문구만 화면의 말로 바꾼다 */}
2289
-
2290
- ❌ <SNumberInput label="우선순위" min={1} clampOnBlur value={v} onValueChange={setV} />
2291
- {/* 0 이 1 로 바뀐 뒤 도착하므로 저장 직전의 v < 1 가드는 영원히 걸리지 않는다 */}
2292
- ```
2293
-
2294
2302
  #### 3-7-15. 로딩 중 자리 — SSkeleton vs SLoadingContainer vs SCircleProgress
2295
2303
 
2296
2304
  | 판별 | 사용 |
@@ -4047,7 +4055,7 @@ function SettingsTab({
4047
4055
  - **푸터는 고정하고 단계 본문만 스크롤한다.** 카드를 `flex h-full flex-col` 로 두고 본문을 `SScrollArea axis="vertical" overlayScrollbar` + `min-h-0 flex-1` 로 잇는다. `SCard` 는 기본이 `h-fit` 이라 이 사슬을 빠뜨리면 2단계처럼 내용이 긴 단계에서 카드가 자라 푸터가 화면 밖으로 밀린다 — 짧은 첫 단계에서는 드러나지 않아 놓치기 쉽다.
4048
4056
  - 검증 결과처럼 일부가 빠지는 단계는 **무엇이 빠지는지 먼저 말한다** — 표 위 `SCallout type="danger"` 에 건수와 처리 방법을 적는다.
4049
4057
  - **완료 단계는 결과 안내다** — 성공 아이콘(`SIcon`, `--sys-color-fg-success`) · 제목(`typo-heading-sm`) · 건수 설명(`typo-body-sm-default` + `text-fg-secondary`)을 본문 가운데에 세로로 쌓는다. 건너뛴 것이 있으면 그 건수도 다시 적는다. 프레임이 이미 카드라 **결과를 카드로 한 번 더 두르지 않는다**(§2-0). 세로 가운데에 놓으려면 본문 쌓기 상자에 `min-h-full`, 결과 블록에 `flex-1 justify-center` 를 준다 — `h-full` 은 기댈 높이가 없어 듣지 않는다.
4050
- - **모달 안에서도 같은 구성이다.** `SActionModal` 본문 맨 위에 스텝퍼를 두고, 이전은 `footerLeft`, 다음은 `button` 으로 넘긴다 — 모달 푸터도 같은 API 다. 그만두기는 모달의 닫기(X)가 맡는다. 단계를 오가며 입력이 쌓이지만 `SActionModal` 은 기본이 바깥 클릭·ESC 로 닫히지 않으므로(§3-3-5) `persistent` 를 따로 주지 않는다.
4058
+ - **모달 안에서도 같은 구성이다.** `SActionModal` 본문 맨 위에 스텝퍼를 두고, 이전은 `footerLeft`, 다음은 `button` 으로 넘긴다 — 모달 푸터도 같은 API 다. 그만두기는 모달의 닫기(X)가 맡는다. 단계를 오가며 입력이 쌓이지만 `SActionModal` 은 기본이 바깥 클릭으로 닫히지 않으므로(§3-3-5) `persistent` 를 따로 주지 않는다.
4051
4059
 
4052
4060
  <!-- recipe:src/recipes/ProductBulkUploadPage.tsx — 아래 코드 블록은 이 파일에서 생성된다(scripts/gen-recipes.mjs). 여기를 고치지 말고 파일을 고친다 -->
4053
4061
  ```tsx
@@ -5064,7 +5072,7 @@ function TemplatePanel({ onPick }: { onPick: (body: string) => void }) {
5064
5072
  - **모달·드로어의 푸터 버튼은 `SForm` 밖에 그려진다.** 본문의 `SForm` 은 푸터의 `type="submit"` 을 받지 못하므로, 저장 버튼에서 **`SForm` 의 ref `validate()`** 로 검증하고 실패하면 `focusFirstInvalid()` 로 첫 필드에 보낸다. 페이지의 카드 푸터(§4-11)는 `SForm` 이 카드를 감싸므로 submit 이 닿는다 — 이 차이를 섞지 않는다.
5065
5073
  - **`button` 은 눌러도 닫지 않는다.** 저장이 끝난 뒤에 모달은 `modalRef.ok()`, 드로어는 닫기 상태로 닫는다. 목록 갱신은 `SModal.create(...).onOk()` 에서 한다.
5066
5074
  - **푸터는 `md` 고정**이고 `footerLeft` 슬롯 버튼에도 `size="md"` 를 명시한다 (§3-5-2). 등록 모달의 취소는 `footerLeft`, 드로어의 삭제(한 건의 파괴 액션)는 `footerLeft` 에 `danger outline` 이다 (§3-5-3).
5067
- - **닫기 경로는 기본값 그대로다.** `SActionModal`·`SDrawer` 는 입력을 담으므로 바깥 클릭·ESC 로 닫히지 않는 것이 기본이다 (§3-3-5) — `persistent` 를 주지 않는다.
5075
+ - **닫기 경로는 기본값 그대로다.** `SActionModal`·`SDrawer` 는 입력을 담으므로 바깥 클릭으로 닫히지 않는 것이 기본이다 (§3-3-5) — `persistent` 를 주지 않는다. ESC 는 기본으로 닫히며 `closeOnEscape` 도 주지 않는다.
5068
5076
  - **드로어는 본문 여백을 넣지 않는다** — 모달은 여백 토큰을 스스로 넣지만, 드로어는 회색 바탕(토큰)만 깔고 여백은 담는 쪽이 준다. 직접 만든 영역 규칙(§2-2)으로 준다. 흰 표·카드가 회색 바탕 위에 놓여 경계가 읽힌다.
5069
5077
  - **모달 높이는 주지 않는다** (§3-3-1) — 내용이 정하고 85% 에서 멈춘다. **그때 스크롤되는 것은 본문이 아니라 표다** — 본문 바로 아래 `flex h-full min-h-0 flex-col` 상자를 두고 표에 `min-h-0 flex-1` 을 준다. 그러면 단계형 모달의 2단계처럼 표가 길어져도 스텝퍼·안내·푸터는 제자리에 있고 표가 남은 높이를 채운다. 건너뛸 행 수처럼 표에 대한 한 줄 상태는 표 바로 위 `SContextAlert` 다 (§3-2-1).
5070
5078
  - **모달 안 단계형은 페이지 단계형(§4-11 가로형)과 같은 구성이다** — 본문 맨 위 가운데 스텝퍼, 이전은 `footerLeft`, 다음·실행은 `button`, 그만두기는 닫기(X). 단계마다 바뀌는 `button` 은 `SActionModalProps['button']` 으로 타입을 붙인다.
@@ -5244,8 +5252,8 @@ export default function ProductOverlayPage() {
5244
5252
 
5245
5253
  /**
5246
5254
  * 상세 드로어 — 목록을 보면서 한 건을 고친다.
5247
- * 레이어라 어디서 띄워도 레이아웃에 영향이 없다 (§2-0). 닫기 경로는 X 와 푸터뿐이다 — 입력을 담으므로
5248
- * 바깥 클릭·ESC 로 닫히지 않는 것이 기본이고 그대로 둔다 (§3-3-5)
5255
+ * 레이어라 어디서 띄워도 레이아웃에 영향이 없다 (§2-0). 닫기 경로는 X·푸터·ESC 다 — 입력을 담으므로
5256
+ * 바깥 클릭으로 닫히지 않는 것이 기본이고 그대로 둔다 (§3-3-5)
5249
5257
  */
5250
5258
  function ProductDrawer({ product, onClose }: { product: Product; onClose: () => void }) {
5251
5259
  const [values, setValues] = useState<Values>(product);
package/README.md CHANGED
@@ -158,14 +158,14 @@ outlet 이 있으면 모달이 앱 렌더 트리의 자식으로 그려져 **Que
158
158
 
159
159
  컴포넌트 조합·간격·타이포 등 **화면을 만들 때 지켜야 할 규칙**은 패키지에 함께 배포됩니다.
160
160
 
161
- | 파일 | 용도 |
162
- | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
163
- | `node_modules/sellmate-design-system-react/AGENTS.md` | 사람이 읽는 사용 규칙서 |
164
- | `node_modules/sellmate-design-system-react/dist/llms.txt` | **AI 가 항상 읽는 문서** — 규칙 + 토큰 어휘 + 컴포넌트 인덱스 (약 32KB) |
165
- | `node_modules/sellmate-design-system-react/dist/components/<이름>/README.md` | 컴포넌트별 Props/Events — 쓸 컴포넌트만 골라서 읽습니다 |
166
- | `node_modules/sellmate-design-system-react/dist/llms-full.txt` | 위 둘을 한 파일에 합친 판본 (약 106KB) — 단일 파일만 물릴 수 있는 도구용 |
167
-
168
- Props 를 `llms.txt` 에서 뺀 이유: 전체 컴포넌트 Props 가 분량의 70% 를 차지하는데,
161
+ | 파일 | 용도 |
162
+ | ---------------------------------------------------------------------------- | ------------------------------------------------------------- |
163
+ | `node_modules/sellmate-design-system-react/AGENTS.md` | 사람이 읽는 사용 규칙서 |
164
+ | `node_modules/sellmate-design-system-react/dist/llms.txt` | **AI 가 항상 읽는 문서** — AGENTS.md 전문 + 토큰 어휘 |
165
+ | `node_modules/sellmate-design-system-react/dist/components/<이름>/README.md` | 컴포넌트별 Props/Events — 쓸 컴포넌트만 골라서 읽습니다 |
166
+ | `node_modules/sellmate-design-system-react/dist/llms-full.txt` | 위 둘을 한 파일에 합친 판본 — 단일 파일만 물릴 수 있는 도구용 |
167
+
168
+ Props 를 `llms.txt` 에서 뺀 이유: 전체 컴포넌트 Props 가 규칙만큼 큰 분량을 차지하는데,
169
169
  prop 오류는 TypeScript 가 잡아주지만 **디자인 규칙은 아무도 잡아주지 않습니다.**
170
170
  한정된 컨텍스트를 규칙에 쓰고, Props 는 필요한 것만 정확히 읽게 하는 편이 낫습니다.
171
171
 
@@ -9,12 +9,13 @@
9
9
  | Prop | Type | Default | Description |
10
10
  |------|------|---------|-------------|
11
11
  | `open?` | `boolean` | — | |
12
- | `persistent?` | `boolean` | — | 백드롭·ESC로 안 닫히고 흔들림 (sd-modal-container persistent). **기본값 true** |
12
+ | `persistent?` | `boolean` | — | 백드롭 클릭으로 안 닫히고 흔들림. **기본값 true** |
13
+ | `closeOnEscape?` | `boolean` | — | ESC 로 닫는다. **기본값 true** — false 면 ESC 로 안 닫히고 흔들림 |
13
14
  | `showClose?` | `boolean` | `true` | 우측 상단 닫기(X) 버튼. **기본값 true** `false` 는 **액션을 끝내야만 나갈 수 있는 모달**에만 준다 — 필수 약관 동의, 필수 정보 입력처럼 중간에 빠져나가면 앱이 정의되지 않은 상태로 남는 경우다. `persistent` 기본값(true)과 겹치면 나가는 길이 모달 안의 버튼 하나뿐이 되므로, `button` 이나 `footerLeft` 로 그 길을 반드시 둔다. |
14
15
  | `modalTitle?` | `string` | `''` | |
15
- | `description?` | `ReactNode` | — | 제목 오른쪽에 붙는 보조 설명 (sd-action-modal 의 header-sub-title 슬롯). 문자열이면 서브텍스트 스타일로 렌더하고, 노드를 넘기면 그대로 배치한다. |
16
+ | `description?` | `ReactNode` | — | 제목 오른쪽에 붙는 보조 설명. 문자열이면 서브텍스트 스타일로 렌더하고, 노드를 넘기면 그대로 배치한다. |
16
17
  | `button?` | `SFooterButton` | — | 하단 액션 버튼 (주 액션 1개). 의도적으로 단수다 — 보조 버튼(취소·삭제 등)은 `footerLeft` 슬롯에 직접 배치한다. |
17
- | `footerLeft?` | `ReactNode` | — | footer 좌측 영역 (sd-action-modal 의 bottom-sub-content 슬롯). 보조 버튼이나 안내 문구를 넣는다. 버튼을 하나 더 쓰고 싶을 때 여기에 SButton 을 넣는다. |
18
+ | `footerLeft?` | `ReactNode` | — | footer 좌측 영역. 보조 버튼이나 안내 문구를 넣는다. 버튼을 하나 더 쓰고 싶을 때 여기에 SButton 을 넣는다. |
18
19
  | `width?` | `number \| string` | — | |
19
20
  | `height?` | `number \| string` | — | |
20
21
  | `children?` | `ReactNode` | — | |
@@ -24,7 +25,7 @@
24
25
  | Event | Type | Description |
25
26
  |-------|------|-------------|
26
27
  | `onOpenChange` | `(open: boolean) => void` | |
27
- | `onClose` | `() => void` | 닫기(X) 버튼 클릭 (sdClose) |
28
+ | `onClose` | `() => void` | 닫기(X) 버튼 클릭 |
28
29
 
29
30
  ## Dependencies
30
31
 
@@ -3,10 +3,12 @@ import { type SFooterButton } from '../SFooter';
3
3
  export interface SActionModalProps {
4
4
  open?: boolean;
5
5
  onOpenChange?: (open: boolean) => void;
6
- /** 닫기(X) 버튼 클릭 (sdClose) */
6
+ /** 닫기(X) 버튼 클릭 */
7
7
  onClose?: () => void;
8
- /** 백드롭·ESC로 안 닫히고 흔들림 (sd-modal-container persistent). **기본값 true** */
8
+ /** 백드롭 클릭으로 안 닫히고 흔들림. **기본값 true** */
9
9
  persistent?: boolean;
10
+ /** ESC 로 닫는다. **기본값 true** — false 면 ESC 로 안 닫히고 흔들림 */
11
+ closeOnEscape?: boolean;
10
12
  /**
11
13
  * 우측 상단 닫기(X) 버튼. **기본값 true**
12
14
  *
@@ -17,7 +19,7 @@ export interface SActionModalProps {
17
19
  showClose?: boolean;
18
20
  modalTitle?: string;
19
21
  /**
20
- * 제목 오른쪽에 붙는 보조 설명 (sd-action-modal 의 header-sub-title 슬롯).
22
+ * 제목 오른쪽에 붙는 보조 설명.
21
23
  * 문자열이면 서브텍스트 스타일로 렌더하고, 노드를 넘기면 그대로 배치한다.
22
24
  */
23
25
  description?: ReactNode;
@@ -27,7 +29,7 @@ export interface SActionModalProps {
27
29
  */
28
30
  button?: SFooterButton;
29
31
  /**
30
- * footer 좌측 영역 (sd-action-modal 의 bottom-sub-content 슬롯).
32
+ * footer 좌측 영역.
31
33
  * 보조 버튼이나 안내 문구를 넣는다. 버튼을 하나 더 쓰고 싶을 때 여기에 SButton 을 넣는다.
32
34
  */
33
35
  footerLeft?: ReactNode;
@@ -36,7 +38,7 @@ export interface SActionModalProps {
36
38
  children?: ReactNode;
37
39
  }
38
40
  /**
39
- * SActionModal — sd-action-modal 포팅. 헤더(제목·닫기) + 본문 + 하단 액션 버튼.
41
+ * SActionModal — 헤더(제목·닫기) + 본문 + 하단 액션 버튼.
40
42
  *
41
43
  * 액션 버튼은 `button.onClick` 만 호출하고 모달을 닫지 않는다. 본문에서 API 를 호출하는 경우가
42
44
  * 많아 성공 여부에 따라 닫힘 시점이 달라지므로, 닫는 책임은 사용하는 쪽에 둔다.
@@ -44,4 +46,4 @@ export interface SActionModalProps {
44
46
  * `button` 은 의도적으로 단수다. 주 액션은 오른쪽에 하나만 두고, 보조 버튼은 `footerLeft` 슬롯에
45
47
  * 소비자가 직접 배치한다. (buttons 배열 API 를 추가하지 말 것)
46
48
  */
47
- export declare function SActionModal({ open, onOpenChange, onClose, persistent, modalTitle, description, button, footerLeft, showClose, width, height, children, }: SActionModalProps): import("react").JSX.Element;
49
+ export declare function SActionModal({ open, onOpenChange, onClose, persistent, closeOnEscape, modalTitle, description, button, footerLeft, showClose, width, height, children, }: SActionModalProps): import("react").JSX.Element;
@@ -6,7 +6,7 @@ export interface SBadgeProps extends HTMLAttributes<HTMLSpanElement> {
6
6
  color?: SBadgeColor;
7
7
  }
8
8
  /**
9
- * SBadge — sd-badge 포팅. 색상 점(dot) 인디케이터.
9
+ * SBadge — 색상 점(dot) 인디케이터.
10
10
  * 색상은 컴포넌트 토큰 `--cmp-badge-<color>` 를 참조한다.
11
11
  */
12
12
  export declare const SBadge: import("react").ForwardRefExoticComponent<SBadgeProps & import("react").RefAttributes<HTMLSpanElement>>;
@@ -45,7 +45,7 @@
45
45
 
46
46
  | Event | Type | Description |
47
47
  |-------|------|-------------|
48
- | `onValueChange` | `(value: string) => void` | 값 변경 (sdUpdate) |
48
+ | `onValueChange` | `(value: string) => void` | 값 변경 |
49
49
  | `onFocus` | `() => void` | |
50
50
  | `onBlur` | `() => void` | |
51
51
 
@@ -8,7 +8,7 @@ import { type SFieldWidth } from '../../lib/field-width';
8
8
  export type SBarcodeInputSize = SFieldSize;
9
9
  export interface SBarcodeInputProps {
10
10
  value?: string | number | null;
11
- /** 값 변경 (sdUpdate) */
11
+ /** 값 변경 */
12
12
  onValueChange?: (value: string) => void;
13
13
  onFocus?: () => void;
14
14
  onBlur?: () => void;
@@ -66,5 +66,5 @@ export interface SBarcodeInputProps {
66
66
  className?: string;
67
67
  style?: CSSProperties;
68
68
  }
69
- /** SBarcodeInput — sd-barcode-input 포팅. 바코드용 노란 배경 입력 + prefix/suffix + clearable. */
69
+ /** SBarcodeInput — 바코드용 노란 배경 입력 + prefix/suffix + clearable. */
70
70
  export declare const SBarcodeInput: import("react").ForwardRefExoticComponent<SBarcodeInputProps & import("react").RefAttributes<HTMLInputElement>>;
@@ -40,8 +40,7 @@ export interface SButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonElemen
40
40
  shape?: SButtonShape;
41
41
  }
42
42
  /**
43
- * SButton — sd-button 포팅.
44
- * color(7) × tone(3) × outline × 5개 사이즈 × shape(3), 아이콘(icon/rightIcon), 컴포넌트 토큰(--cmp-button-*) 기반.
45
- * 텍스트는 label(문자열)로만 받는다. 이벤트: sdClick → 네이티브 onClick.
43
+ * SButton — color(7) × tone(3) × outline × 5개 사이즈 × shape(3), 아이콘(icon/rightIcon), 컴포넌트 토큰(--cmp-button-*) 기반.
44
+ * 텍스트는 label(문자열)로만 받는다.
46
45
  */
47
46
  export declare const SButton: import("react").ForwardRefExoticComponent<SButtonProps & import("react").RefAttributes<HTMLButtonElement>>;
@@ -19,8 +19,8 @@
19
19
 
20
20
  | Event | Type | Description |
21
21
  |-------|------|-------------|
22
- | `onValueChange` | `(date: string) => void` | 선택 변경 (sdUpdate) |
23
- | `onViewChange` | `(v: { year: number; month: number }) => void` | 보이는 연·월 변경 (sdViewChange) |
22
+ | `onValueChange` | `(date: string) => void` | 선택 변경 |
23
+ | `onViewChange` | `(v: { year: number; month: number }) => void` | 보이는 연·월 변경 |
24
24
 
25
25
  ## Types
26
26
 
@@ -9,13 +9,13 @@ export interface SCalendarEventGroup {
9
9
  export interface SCalendarProps {
10
10
  /** 선택 날짜 (YYYY-MM-DD) */
11
11
  value?: string | null;
12
- /** 선택 변경 (sdUpdate) */
12
+ /** 선택 변경 */
13
13
  onValueChange?: (date: string) => void;
14
14
  /** 선택 가능 범위 [시작, 종료] (빈 문자열이면 제한 없음) */
15
15
  selectable?: [string, string];
16
16
  /** 이벤트 그룹 (날짜별 점) */
17
17
  events?: SCalendarEventGroup[];
18
- /** 보이는 연·월 변경 (sdViewChange) */
18
+ /** 보이는 연·월 변경 */
19
19
  onViewChange?: (v: {
20
20
  year: number;
21
21
  month: number;
@@ -25,5 +25,5 @@ export interface SCalendarProps {
25
25
  className?: string;
26
26
  style?: CSSProperties;
27
27
  }
28
- /** SCalendar — sd-calendar 포팅. 월 그리드 + 이벤트 점 + 범례. */
28
+ /** SCalendar — 월 그리드 + 이벤트 점 + 범례. */
29
29
  export declare function SCalendar({ value, onValueChange, selectable, events, onViewChange, elevated, className, style, }: SCalendarProps): import("react").JSX.Element;
@@ -2,6 +2,12 @@ import { type HTMLAttributes, type MouseEvent, type ReactNode } from 'react';
2
2
  import type { CalendarBoardSegment } from './calendar-board-layout';
3
3
  import { type SCalendarBoardDayColor } from './calendarBoard.config';
4
4
  import type { SCalendarBoardEvent } from './types';
5
+ /** 이 칸의 오른쪽·아래 격자선과 오른쪽 아래 교차점 중 경계 오버레이를 얹을 곳 */
6
+ export interface CalendarDayBorderOverlay {
7
+ right: boolean;
8
+ bottom: boolean;
9
+ corner: boolean;
10
+ }
5
11
  /**
6
12
  * 달력의 날짜 한 칸 (SCalendarBoard 내부 조립 요소).
7
13
  * 위에 날짜 숫자가 서고, 그 아래로 일정 막대가 줄 단위로 얹힌다.
@@ -20,6 +26,8 @@ export declare const CalendarDayCard: import("react").ForwardRefExoticComponent<
20
26
  selectable: boolean;
21
27
  /** 칸 배경 */
22
28
  color: SCalendarBoardDayColor;
29
+ /** 이웃 칸과 맞닿은 격자선에 얹을 경계 오버레이 — 판이 이웃 칸 색까지 보고 정한다 */
30
+ borderOverlay?: CalendarDayBorderOverlay;
23
31
  /**
24
32
  * **이 칸에서 시작하는** 일정 막대. 여러 날에 걸친 일정은 시작 칸이 그리고 오른쪽 칸 위로 뻗는다 —
25
33
  * 지나가는 칸은 자기 줄을 비워 두므로(배치 계산이 이미 잡아 둔다) 겹치지 않는다.
@@ -26,9 +26,8 @@ export interface SCalloutProps {
26
26
  style?: CSSProperties;
27
27
  }
28
28
  /**
29
- * SCallout — sd-callout 포팅.
30
- * default/white/danger 타입, 중첩 리스트(불릿은 span으로 렌더), danger는 좌측 타이틀 표시(showTitle 로 끌 수 있다).
29
+ * SCallout — default/white/danger 타입, 중첩 리스트(불릿은 span으로 렌더), danger는 좌측 타이틀 표시(showTitle 로 끌 수 있다).
31
30
  * 색상은 `--cmp-callout-*` 토큰 참조(white 만 예외 — SURFACE 주석 참고).
32
- * message 각 항목은 sanitizeInlineHtml 로 살균한 뒤 innerHTML 로 렌더한다 (원본 sd-callout 과 동일).
31
+ * message 각 항목은 sanitizeInlineHtml 로 살균한 뒤 innerHTML 로 렌더한다.
33
32
  */
34
33
  export declare const SCallout: import("react").ForwardRefExoticComponent<SCalloutProps & import("react").RefAttributes<HTMLDivElement>>;
@@ -23,7 +23,7 @@ export interface SCardProps extends HTMLAttributes<HTMLDivElement> {
23
23
  footerClassName?: string;
24
24
  }
25
25
  /**
26
- * SCard — sd-card 포팅. 라운드 8px, 흰 배경, 옵션 테두리. 콘텐츠는 라운드 경계에서 잘린다.
26
+ * SCard — 라운드 8px, 흰 배경, 옵션 테두리. 콘텐츠는 라운드 경계에서 잘린다.
27
27
  *
28
28
  * 하단 액션은 `children` 에 직접 두지 않고 `footerLeft`·`button` 으로 넘긴다 — 배경·구분선·여백과
29
29
  * 양끝 분리가 컴포넌트 규칙대로 잡히는 자리다.
@@ -21,7 +21,7 @@
21
21
 
22
22
  | Event | Type | Description |
23
23
  |-------|------|-------------|
24
- | `onValueChange` | `(value: boolean \| unknown[]) => void` | 값 변경 (sdUpdate) |
24
+ | `onValueChange` | `(value: boolean \| unknown[]) => void` | 값 변경 |
25
25
 
26
26
  ## Types
27
27
 
@@ -5,7 +5,7 @@ export interface SCheckboxProps {
5
5
  value?: SCheckboxValue;
6
6
  /** 배열(그룹) 모드에서 이 체크박스의 값 */
7
7
  val?: unknown;
8
- /** 값 변경 (sdUpdate) */
8
+ /** 값 변경 */
9
9
  onValueChange?: (value: boolean | unknown[]) => void;
10
10
  /** 라벨 */
11
11
  label?: string;
@@ -25,7 +25,7 @@ export interface SCheckboxProps {
25
25
  style?: CSSProperties;
26
26
  }
27
27
  /**
28
- * SCheckbox — sd-checkbox 포팅 (Radix SCheckbox).
28
+ * SCheckbox — Radix Checkbox 기반.
29
29
  * boolean(단일)·배열(그룹, val 필요)·null(indeterminate) 지원, inverse 스타일.
30
30
  */
31
31
  export declare function SCheckbox({ value, val, onValueChange, label, ariaLabel, disabled, inverse, className, style, }: SCheckboxProps): import("react").JSX.Element;
@@ -24,10 +24,10 @@
24
24
 
25
25
  | Event | Type | Description |
26
26
  |-------|------|-------------|
27
- | `onValueChange` | `(value: string) => void` | 편집 완료 (sdUpdate) |
28
- | `onRemove` | `() => void` | 삭제 (sdRemove) |
29
- | `onFocus` | `() => void` | 편집 진입 (sdFocus) |
30
- | `onBlur` | `() => void` | 편집 이탈 (sdBlur) |
27
+ | `onValueChange` | `(value: string) => void` | 편집 완료 |
28
+ | `onRemove` | `() => void` | 삭제 |
29
+ | `onFocus` | `() => void` | 편집 진입 |
30
+ | `onBlur` | `() => void` | 편집 이탈 |
31
31
 
32
32
  #### Methods (ref)
33
33
 
@@ -25,18 +25,18 @@ export interface SChipProps {
25
25
  placeholder?: string;
26
26
  /** 유효성 규칙 */
27
27
  rules?: Rule[];
28
- /** 편집 완료 (sdUpdate) */
28
+ /** 편집 완료 */
29
29
  onValueChange?: (value: string) => void;
30
- /** 삭제 (sdRemove) */
30
+ /** 삭제 */
31
31
  onRemove?: () => void;
32
- /** 편집 진입 (sdFocus) */
32
+ /** 편집 진입 */
33
33
  onFocus?: () => void;
34
- /** 편집 이탈 (sdBlur) */
34
+ /** 편집 이탈 */
35
35
  onBlur?: () => void;
36
36
  className?: string;
37
37
  style?: CSSProperties;
38
38
  }
39
- /** ref로 노출되는 UX 메서드 (sd-chip @Method sdFocus/sdGetNativeElement 대응) */
39
+ /** ref로 노출되는 UX 메서드 */
40
40
  export interface SChipHandle {
41
41
  /** 편집 input에 포커스합니다. */
42
42
  focus: () => void;
@@ -48,8 +48,8 @@
48
48
 
49
49
  | Event | Type | Description |
50
50
  |-------|------|-------------|
51
- | `onValueChange` | `(values: string[]) => void` | 값 변경 (sdUpdate) — 전체 배열 |
52
- | `onInput` | `(value: string) => void` | 입력 중 문자열 변경 (sdInput) |
51
+ | `onValueChange` | `(values: string[]) => void` | 값 변경 — 전체 배열 |
52
+ | `onInput` | `(value: string) => void` | 입력 중 문자열 변경 |
53
53
  | `onFocus` | `() => void` | |
54
54
  | `onBlur` | `() => void` | |
55
55
 
@@ -7,7 +7,7 @@ import { type STooltipProps } from '../STooltip';
7
7
  import { type SFieldWidth } from '../../lib/field-width';
8
8
  export type SChipInputSize = SFieldSize;
9
9
  export type SChipInputMetaPlacement = 'end' | 'inline';
10
- /** ref로 노출되는 UX 메서드 (sd-chip-input @Method sdFocus 대응) */
10
+ /** ref로 노출되는 UX 메서드 */
11
11
  export interface SChipInputHandle {
12
12
  /** 입력 필드에 포커스를 이동합니다. */
13
13
  focus: () => void;
@@ -15,9 +15,9 @@ export interface SChipInputHandle {
15
15
  export interface SChipInputProps {
16
16
  /** 칩 값 목록 */
17
17
  values?: string[];
18
- /** 값 변경 (sdUpdate) — 전체 배열 */
18
+ /** 값 변경 — 전체 배열 */
19
19
  onValueChange?: (values: string[]) => void;
20
- /** 입력 중 문자열 변경 (sdInput) */
20
+ /** 입력 중 문자열 변경 */
21
21
  onInput?: (value: string) => void;
22
22
  onFocus?: () => void;
23
23
  onBlur?: () => void;
@@ -109,5 +109,5 @@ export interface SChipInputProps {
109
109
  className?: string;
110
110
  style?: CSSProperties;
111
111
  }
112
- /** SChipInput — sd-chip-input 포팅. 칩 목록 + 입력 + 자동완성/추천 드롭다운 + 초기화. */
112
+ /** SChipInput — 칩 목록 + 입력 + 자동완성/추천 드롭다운 + 초기화. */
113
113
  export declare const SChipInput: import("react").ForwardRefExoticComponent<SChipInputProps & import("react").RefAttributes<SChipInputHandle>>;
@@ -28,5 +28,5 @@ export interface SCircleProgressProps {
28
28
  className?: string;
29
29
  style?: CSSProperties;
30
30
  }
31
- /** SCircleProgress — sd-circle-progress 포팅. determinate=진행률 arc, indeterminate=스피너. */
31
+ /** SCircleProgress — determinate=진행률 arc, indeterminate=스피너. */
32
32
  export declare const SCircleProgress: import("react").ForwardRefExoticComponent<SCircleProgressProps & import("react").RefAttributes<HTMLDivElement>>;
@@ -9,14 +9,15 @@
9
9
  | Prop | Type | Default | Description |
10
10
  |------|------|---------|-------------|
11
11
  | `open?` | `boolean` | — | |
12
- | `persistent?` | `boolean` | `false` | 백드롭·ESC로 안 닫히고 흔들림 (sd-modal-container persistent). **기본값 false** — 확인 모달은 잃을 입력이 없어 백드롭·ESC로 닫힌다 |
13
- | `type?` | `SConfirmModalType` | `'positive'` | 타입 (아이콘·메인버튼 색 결정) |
12
+ | `persistent?` | `boolean` | `false` | 백드롭 클릭으로 안 닫히고 흔들림. **기본값 false** — 확인 모달은 잃을 입력이 없어 백드롭으로 닫힌다 |
13
+ | `closeOnEscape?` | `boolean` | `true` | ESC 로 닫는다. **기본값 true** — false 면 ESC 로 안 닫히고 흔들림 |
14
+ | `type?` | `SConfirmModalType` | `'positive'` | 타입 (아이콘·메인버튼 색 결정). positive=알림 · negative=위험 · complete=작업·절차의 끝(완료) · default=중립 |
14
15
  | `modalTitle?` | `string` | `''` | |
15
- | `titleClassName?` | `string` | — | 제목 엘리먼트에 추가할 클래스 (sd-confirm-modal titleClassName) |
16
+ | `titleClassName?` | `string` | — | 제목 엘리먼트에 추가할 클래스 |
16
17
  | `topMessage?` | `string[]` | `[]` | 콘텐츠 박스 상단 메시지 목록 (각 항목이 한 줄, HTML 문자열 허용 — innerHTML 렌더링) |
17
18
  | `bottomMessage?` | `string[]` | `[]` | 콘텐츠 박스 하단 메시지 목록 (각 항목이 한 줄, HTML 문자열 허용 — innerHTML 렌더링) |
18
19
  | `tagLabel?` | `string` | — | tag 슬롯 기본 태그 레이블 (tagSlot 미지정 시) |
19
- | `tagShape?` | `STagShape` | `'square'` | 태그 프리셋 (shape/size/color) — sd-confirm-modal tagPreset 대응 |
20
+ | `tagShape?` | `STagShape` | `'square'` | 태그 프리셋 (shape/size/color) |
20
21
  | `tagSize?` | `STagSize` | `'sm'` | |
21
22
  | `tagColor?` | `STagColor` | `'grey'` | |
22
23
  | `slotLabel?` | `string` | — | 콘텐츠 박스에서 태그 오른쪽에 오는 텍스트 (optionSlot 미지정 시) |
@@ -24,25 +25,25 @@
24
25
  | `optionSlot?` | `ReactNode` | — | 콘텐츠 박스에서 태그 오른쪽 커스텀 콘텐츠 (미지정 시 slotLabel 텍스트) |
25
26
  | `contentSlot?` | `ReactNode` | — | 콘텐츠 박스 바로 아래에 통으로 들어가는 커스텀 콘텐츠 (전체 폭) |
26
27
  | `mainButtonLabel?` | `string` | — | 메인(확인) 버튼에 찍히는 글자. 생략하면 `SLocaleProvider` 로케일의 기본 문구(ko: "확인")를 쓴다 |
27
- | `mainButtonOutline?` | `boolean` | — | 메인 버튼을 아웃라인(테두리)으로 세울지. 지정하지 않으면 `type` 이 정한다 — positive·negative 는 채운 버튼, default 는 아웃라인. **색은 여기서 고르지 않는다.** `type` 이 정한다(positive=primary / negative=danger / default=neutral) — 아이콘·제목이 말하는 것과 버튼 색이 어긋나면 읽는 사람이 헷갈리므로 그 조합은 타입에서 막는다. 글자는 `mainButtonLabel` 이다. `type="default"` 에 `false` 를 주면 테두리 없는 흰 버튼이 된다(neutral 은 solid·outline 이 같은 흰 면이고 테두리 유무만 다르다). 확인 버튼이 눌러야 할 것으로 안 읽히므로 권하지 않는다. |
28
+ | `mainButtonOutline?` | `boolean` | — | 메인 버튼을 아웃라인(테두리)으로 세울지. 지정하지 않으면 `type` 이 정한다 — positive·negative 는 채운 버튼, complete·default 는 아웃라인. **색은 여기서 고르지 않는다.** `type` 이 정한다(positive=primary / negative=danger / complete=neutral / default=neutral) — 아이콘·제목이 말하는 것과 버튼 색이 어긋나면 읽는 사람이 헷갈리므로 그 조합은 타입에서 막는다. 글자는 `mainButtonLabel` 이다. `type="default"`·`"complete"` 에 `false` 를 주면 테두리 없는 흰 버튼이 된다(neutral 은 solid·outline 이 같은 흰 면이고 테두리 유무만 다르다). 확인 버튼이 눌러야 할 것으로 안 읽히므로 권하지 않는다. |
28
29
  | `mainButtonDisabled?` | `boolean` | `false` | 메인 버튼을 눌리지 않게 한다. **기본값 `false`.** 모달 안에서 **아직 확정할 수 없을 때** 쓴다 — 동의 체크를 아직 안 했거나, `contentSlot` 의 입력이 덜 찼거나, 확인 뒤에 나갈 요청이 이미 나가는 중일 때. **왜 못 누르는지는 화면이 말해야 한다.** 흐려진 버튼만 남으면 사용자는 고장으로 읽는다 — 못 채운 것을 `bottomMessage` 나 `contentSlot` 안에서 함께 보인다. 서브(취소) 버튼은 따라 잠기지 않는다. 확정하지 못하는 상태일수록 **나가는 길은 열려 있어야** 하기 때문이다. 그래서 서브 버튼을 막는 prop 은 두지 않는다. |
29
- | `subButtonLabel?` | `string` | — | 서브 버튼에 찍히는 글자. **주지 않는 것이 기본이다** — 비어 있으면 버튼이 아예 서지 않고 확인 하나만 남는다. **`onCancel` 이 닫는 것 말고 따로 할 일이 있을 때만 준다.** 눌러도 창만 닫힌다면 X 버튼이 이미 그 일을 하므로(X 는 늘 서 있고, `persistent` 를 켜지 않았으면 백드롭·ESC 로도 닫힌다) 같은 일을 하는 버튼을 하나 더 세우지 않는다. 되묻는 모달이라고 해서 예외가 아니다 — "삭제할까요?" 에 답하지 않고 나가는 것은 X 로 충분하다. 그래서 서브 버튼이 설 자리는 **확인과 대등한 두 번째 선택지**다. 그때 글자는 `취소`·`닫기` 가 아니라 그 버튼이 하는 일의 이름이 된다. ```tsx ✅ { mainButtonLabel: '자동 취소', subButtonLabel: '수동 처리' } // 두 갈래를 고르게 한다 ❌ { mainButtonLabel: '삭제', subButtonLabel: '취소' } // X 가 이미 하는 일 ``` |
30
+ | `subButtonLabel?` | `string` | — | 서브 버튼에 찍히는 글자. **주지 않는 것이 기본이다** — 비어 있으면 버튼이 아예 서지 않고 확인 하나만 남는다. **`onCancel` 이 닫는 것 말고 따로 할 일이 있을 때만 준다.** 눌러도 창만 닫힌다면 X 버튼이 이미 그 일을 하므로(X 는 늘 서 있고, `persistent` 를 켜지 않았으면 백드롭으로, `closeOnEscape` 를 끄지 않았으면 ESC 로도 닫힌다) 같은 일을 하는 버튼을 하나 더 세우지 않는다. 되묻는 모달이라고 해서 예외가 아니다 — "삭제할까요?" 에 답하지 않고 나가는 것은 X 로 충분하다. 그래서 서브 버튼이 설 자리는 **확인과 대등한 두 번째 선택지**다. 그때 글자는 `취소`·`닫기` 가 아니라 그 버튼이 하는 일의 이름이 된다. ```tsx ✅ { mainButtonLabel: '자동 취소', subButtonLabel: '수동 처리' } // 두 갈래를 고르게 한다 ❌ { mainButtonLabel: '삭제', subButtonLabel: '취소' } // X 가 이미 하는 일 ``` |
30
31
 
31
32
  #### Events
32
33
 
33
34
  | Event | Type | Description |
34
35
  |-------|------|-------------|
35
36
  | `onOpenChange` | `(open: boolean) => void` | |
36
- | `onOk` | `() => void` | 확인 버튼 클릭 (sdOk) |
37
+ | `onOk` | `() => void` | 확인 버튼 클릭 |
37
38
  | `onCancel` | `() => void` | |
38
- | `onClose` | `() => void` | 닫기(X) 버튼 클릭 (sdClose) |
39
+ | `onClose` | `() => void` | 닫기(X) 버튼 클릭 |
39
40
 
40
41
  ## Types
41
42
 
42
43
  ### SConfirmModalType
43
44
 
44
45
  ```ts
45
- export type SConfirmModalType = 'positive' | 'negative' | 'default';
46
+ export type SConfirmModalType = 'positive' | 'negative' | 'complete' | 'default';
46
47
  ```
47
48
 
48
49
  ## Dependencies