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.
- package/AGENTS.md +49 -41
- package/README.md +8 -8
- package/dist/components/SActionModal/README.md +5 -4
- package/dist/components/SActionModal/SActionModal.d.ts +8 -6
- package/dist/components/SBadge/SBadge.d.ts +1 -1
- package/dist/components/SBarcodeInput/README.md +1 -1
- package/dist/components/SBarcodeInput/SBarcodeInput.d.ts +2 -2
- package/dist/components/SButton/SButton.d.ts +2 -3
- package/dist/components/SCalendar/README.md +2 -2
- package/dist/components/SCalendar/SCalendar.d.ts +3 -3
- package/dist/components/SCalendarBoard/CalendarDayCard.d.ts +8 -0
- package/dist/components/SCallout/SCallout.d.ts +2 -3
- package/dist/components/SCard/SCard.d.ts +1 -1
- package/dist/components/SCheckbox/README.md +1 -1
- package/dist/components/SCheckbox/SCheckbox.d.ts +2 -2
- package/dist/components/SChip/README.md +4 -4
- package/dist/components/SChip/SChip.d.ts +5 -5
- package/dist/components/SChipInput/README.md +2 -2
- package/dist/components/SChipInput/SChipInput.d.ts +4 -4
- package/dist/components/SCircleProgress/SCircleProgress.d.ts +1 -1
- package/dist/components/SConfirmModal/README.md +10 -9
- package/dist/components/SConfirmModal/SConfirmModal.d.ts +15 -13
- package/dist/components/SDatePicker/README.md +2 -2
- package/dist/components/SDatePicker/SDatePicker.d.ts +3 -3
- package/dist/components/SDateRangePicker/README.md +2 -2
- package/dist/components/SDateRangePicker/SDateRangePicker.d.ts +3 -3
- package/dist/components/SDivider/SDivider.d.ts +1 -1
- package/dist/components/SDrawer/README.md +2 -1
- package/dist/components/SDrawer/SDrawer.d.ts +10 -5
- package/dist/components/SDropdownButton/README.md +5 -5
- package/dist/components/SDropdownButton/SDropdownButton.d.ts +5 -5
- package/dist/components/SEditor/README.md +1 -1
- package/dist/components/SEditor/SEditor.d.ts +1 -1
- package/dist/components/SField/SField.d.ts +3 -3
- package/dist/components/SFilePicker/README.md +2 -2
- package/dist/components/SFilePicker/SFilePicker.d.ts +3 -3
- package/dist/components/SForm/README.md +4 -4
- package/dist/components/SForm/SForm.d.ts +6 -6
- package/dist/components/SGhostButton/README.md +1 -1
- package/dist/components/SGhostButton/SGhostButton.d.ts +2 -2
- package/dist/components/SGnb/README.md +2 -2
- package/dist/components/SGnb/SGnb.d.ts +2 -2
- package/dist/components/SGuide/README.md +1 -1
- package/dist/components/SGuide/SGuide.d.ts +2 -2
- package/dist/components/SIcon/SIcon.d.ts +1 -2
- package/dist/components/SInput/README.md +3 -3
- package/dist/components/SInput/SInput.d.ts +4 -4
- package/dist/components/SKeyValueTable/README.md +2 -2
- package/dist/components/SKeyValueTable/SKeyValueTable.d.ts +3 -3
- package/dist/components/SLinearProgress/SLinearProgress.d.ts +1 -1
- package/dist/components/SLoadingContainer/README.md +1 -1
- package/dist/components/SLoadingContainer/SLoadingContainer.d.ts +3 -3
- package/dist/components/SLoadingModal/README.md +4 -3
- package/dist/components/SLoadingModal/SLoadingModal.d.ts +7 -5
- package/dist/components/SModal/README.md +7 -7
- package/dist/components/SModal/SModal.d.ts +4 -4
- package/dist/components/SModalContainer/README.md +3 -2
- package/dist/components/SModalContainer/SModalContainer.d.ts +12 -7
- package/dist/components/SNumberInput/README.md +3 -3
- package/dist/components/SNumberInput/SNumberInput.d.ts +4 -4
- package/dist/components/SPage/SPageHeader.d.ts +1 -1
- package/dist/components/SPagination/README.md +11 -1
- package/dist/components/SPagination/SPagination.d.ts +9 -3
- package/dist/components/SPagination/index.d.ts +1 -1
- package/dist/components/SPopover/README.md +1 -1
- package/dist/components/SPopover/SPopover.d.ts +2 -2
- package/dist/components/SPopup/README.md +1 -1
- package/dist/components/SPopup/SPopup.d.ts +2 -2
- package/dist/components/SPortal/README.md +6 -5
- package/dist/components/SPortal/SPortal.d.ts +11 -9
- package/dist/components/SRadio/README.md +4 -4
- package/dist/components/SRadio/SRadio.d.ts +3 -3
- package/dist/components/SRadio/SRadioGroup.d.ts +3 -3
- package/dist/components/SRadioButton/README.md +1 -1
- package/dist/components/SRadioButton/SRadioButton.d.ts +2 -2
- package/dist/components/SRadioCard/README.md +2 -2
- package/dist/components/SRadioCard/SRadioCard.d.ts +1 -1
- package/dist/components/SRadioCard/SRadioCardGroup.d.ts +1 -1
- package/dist/components/SScrollArea/SScrollArea.d.ts +1 -2
- package/dist/components/SSelect/README.md +4 -4
- package/dist/components/SSelect/SSelect.d.ts +4 -4
- package/dist/components/SSelect/select.config.d.ts +2 -2
- package/dist/components/SSlider/README.md +1 -1
- package/dist/components/SSlider/SSlider.d.ts +1 -1
- package/dist/components/SStepper/SStepper.d.ts +1 -1
- package/dist/components/SSwitch/README.md +1 -1
- package/dist/components/SSwitch/SSwitch.d.ts +2 -2
- package/dist/components/STable/README.md +2 -2
- package/dist/components/STable/STable.d.ts +4 -4
- package/dist/components/STabs/README.md +1 -1
- package/dist/components/STabs/STabs.d.ts +3 -4
- package/dist/components/STag/STag.d.ts +1 -2
- package/dist/components/STextLink/STextLink.d.ts +1 -1
- package/dist/components/STextarea/README.md +1 -1
- package/dist/components/STextarea/STextarea.d.ts +2 -2
- package/dist/components/STimePicker/README.md +2 -2
- package/dist/components/STimePicker/STimePicker.d.ts +3 -3
- package/dist/components/STimeRangePicker/README.md +2 -2
- package/dist/components/STimeRangePicker/STimeRangePicker.d.ts +3 -3
- package/dist/components/SToast/README.md +4 -4
- package/dist/components/SToast/SToast.d.ts +3 -3
- package/dist/components/SToast/SToastContainer.d.ts +3 -3
- package/dist/components/SToggle/README.md +1 -1
- package/dist/components/SToggle/SToggle.d.ts +2 -2
- package/dist/components/STooltip/STooltip.d.ts +1 -1
- package/dist/index.cjs +224 -134
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +224 -134
- package/dist/index.js.map +1 -1
- package/dist/lib/color.d.ts +1 -1
- package/dist/lib/floating-anim.d.ts +2 -2
- package/dist/lib/floating-width.d.ts +1 -2
- package/dist/lib/form.d.ts +1 -1
- package/dist/llms-full.txt +167 -145
- package/dist/llms.txt +54 -56
- package/dist/styles.css +19 -0
- package/dist/theme.css +8 -5
- 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
|
-
|
|
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
|
-
**입력을 담는 모달은 백드롭
|
|
897
|
+
**`persistent` 는 백드롭(바깥) 클릭만, `closeOnEscape` 는 ESC 만 제어한다.** 두 prop 은 서로 영향을 주지 않는다. **ESC 는 로딩 모달을 뺀 모든 모달·드로어에서 기본으로 닫힌다**(`closeOnEscape` 기본 `true`, `SLoadingModal` 만 `false`). **입력을 담는 모달은 백드롭 클릭으로 닫히지 않는 것이 기본**이다 — 닫기 시도는 흔들림(shake)으로만 반응한다. 작성 중인 내용을 실수로 잃지 않게 하기 위한 것이다. 반면 `SConfirmModal` 은 잃을 입력이 없으므로 **백드롭으로도 닫히는 것이 기본**이다.
|
|
894
898
|
|
|
895
|
-
| | `persistent` 기본값 | 기본 닫기 경로 |
|
|
896
|
-
| --- | --- | --- | --- |
|
|
897
|
-
| `SActionModal`
|
|
898
|
-
|
|
|
899
|
-
| `
|
|
900
|
-
| `
|
|
901
|
-
| `
|
|
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}` 는 액션을 끝내야만 나갈 수 있는
|
|
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 상태를 알지 못하므로
|
|
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` 은 기본이 바깥
|
|
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` 는 입력을 담으므로 바깥
|
|
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
|
-
* 바깥
|
|
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 가 항상 읽는 문서** —
|
|
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 가
|
|
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` | — |
|
|
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` | — | 제목 오른쪽에 붙는 보조
|
|
16
|
+
| `description?` | `ReactNode` | — | 제목 오른쪽에 붙는 보조 설명. 문자열이면 서브텍스트 스타일로 렌더하고, 노드를 넘기면 그대로 배치한다. |
|
|
16
17
|
| `button?` | `SFooterButton` | — | 하단 액션 버튼 (주 액션 1개). 의도적으로 단수다 — 보조 버튼(취소·삭제 등)은 `footerLeft` 슬롯에 직접 배치한다. |
|
|
17
|
-
| `footerLeft?` | `ReactNode` | — | footer 좌측
|
|
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) 버튼 클릭
|
|
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) 버튼 클릭
|
|
6
|
+
/** 닫기(X) 버튼 클릭 */
|
|
7
7
|
onClose?: () => void;
|
|
8
|
-
/**
|
|
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
|
-
* 제목 오른쪽에 붙는 보조
|
|
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 좌측
|
|
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 —
|
|
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 —
|
|
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` | 값 변경
|
|
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
|
-
/** 값 변경
|
|
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 —
|
|
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 —
|
|
44
|
-
*
|
|
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` | 선택 변경
|
|
23
|
-
| `onViewChange` | `(v: { year: number; month: number }) => void` | 보이는 연·월 변경
|
|
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
|
-
/** 선택 변경
|
|
12
|
+
/** 선택 변경 */
|
|
13
13
|
onValueChange?: (date: string) => void;
|
|
14
14
|
/** 선택 가능 범위 [시작, 종료] (빈 문자열이면 제한 없음) */
|
|
15
15
|
selectable?: [string, string];
|
|
16
16
|
/** 이벤트 그룹 (날짜별 점) */
|
|
17
17
|
events?: SCalendarEventGroup[];
|
|
18
|
-
/** 보이는 연·월 변경
|
|
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 —
|
|
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 —
|
|
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 로
|
|
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 —
|
|
26
|
+
* SCard — 라운드 8px, 흰 배경, 옵션 테두리. 콘텐츠는 라운드 경계에서 잘린다.
|
|
27
27
|
*
|
|
28
28
|
* 하단 액션은 `children` 에 직접 두지 않고 `footerLeft`·`button` 으로 넘긴다 — 배경·구분선·여백과
|
|
29
29
|
* 양끝 분리가 컴포넌트 규칙대로 잡히는 자리다.
|
|
@@ -5,7 +5,7 @@ export interface SCheckboxProps {
|
|
|
5
5
|
value?: SCheckboxValue;
|
|
6
6
|
/** 배열(그룹) 모드에서 이 체크박스의 값 */
|
|
7
7
|
val?: unknown;
|
|
8
|
-
/** 값 변경
|
|
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 —
|
|
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` | 편집 완료
|
|
28
|
-
| `onRemove` | `() => void` | 삭제
|
|
29
|
-
| `onFocus` | `() => void` | 편집 진입
|
|
30
|
-
| `onBlur` | `() => void` | 편집 이탈
|
|
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
|
-
/** 편집 완료
|
|
28
|
+
/** 편집 완료 */
|
|
29
29
|
onValueChange?: (value: string) => void;
|
|
30
|
-
/** 삭제
|
|
30
|
+
/** 삭제 */
|
|
31
31
|
onRemove?: () => void;
|
|
32
|
-
/** 편집 진입
|
|
32
|
+
/** 편집 진입 */
|
|
33
33
|
onFocus?: () => void;
|
|
34
|
-
/** 편집 이탈
|
|
34
|
+
/** 편집 이탈 */
|
|
35
35
|
onBlur?: () => void;
|
|
36
36
|
className?: string;
|
|
37
37
|
style?: CSSProperties;
|
|
38
38
|
}
|
|
39
|
-
/** ref로 노출되는 UX 메서드
|
|
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` | 값 변경
|
|
52
|
-
| `onInput` | `(value: string) => void` | 입력 중 문자열 변경
|
|
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 메서드
|
|
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
|
-
/** 값 변경
|
|
18
|
+
/** 값 변경 — 전체 배열 */
|
|
19
19
|
onValueChange?: (values: string[]) => void;
|
|
20
|
-
/** 입력 중 문자열 변경
|
|
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 —
|
|
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 —
|
|
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` |
|
|
13
|
-
| `
|
|
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` | — | 제목 엘리먼트에 추가할 클래스
|
|
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)
|
|
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` 를 켜지 않았으면
|
|
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` | 확인 버튼 클릭
|
|
37
|
+
| `onOk` | `() => void` | 확인 버튼 클릭 |
|
|
37
38
|
| `onCancel` | `() => void` | |
|
|
38
|
-
| `onClose` | `() => void` | 닫기(X) 버튼 클릭
|
|
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
|