sellmate-design-system-react 9.0.0-beta.46 → 9.0.0-beta.48

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 (124) hide show
  1. package/AGENTS.md +17 -6
  2. package/README.md +13 -0
  3. package/dist/components/SChatAttachedFile/README.md +33 -0
  4. package/dist/components/SChatAttachedFile/SChatAttachedFile.d.ts +13 -0
  5. package/dist/components/SChatAttachedFile/chatAttachedFile.config.d.ts +23 -0
  6. package/dist/components/SChatAttachedFile/index.d.ts +2 -0
  7. package/dist/components/SChatFile/README.md +33 -0
  8. package/dist/components/SChatFile/SChatFile.d.ts +37 -0
  9. package/dist/components/SChatFile/chatFile.config.d.ts +56 -0
  10. package/dist/components/SChatFile/index.d.ts +2 -0
  11. package/dist/components/SChatMessage/README.md +4 -2
  12. package/dist/components/SChatMessage/SChatMessage.d.ts +19 -2
  13. package/dist/components/SChatSystemMessage/README.md +12 -0
  14. package/dist/components/SChatSystemMessage/SChatSystemMessage.d.ts +18 -0
  15. package/dist/components/SChatSystemMessage/index.d.ts +1 -0
  16. package/dist/components/SGhostButton/README.md +2 -0
  17. package/dist/components/SIcon/README.md +4 -0
  18. package/dist/components/SIcon/icons.gen.d.ts +3 -1
  19. package/dist/components/SSystemActionButton/README.md +20 -0
  20. package/dist/fonts/LICENSE.txt +94 -0
  21. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.0.woff2 +0 -0
  22. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.1.woff2 +0 -0
  23. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.10.woff2 +0 -0
  24. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.11.woff2 +0 -0
  25. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.12.woff2 +0 -0
  26. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.13.woff2 +0 -0
  27. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.14.woff2 +0 -0
  28. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.15.woff2 +0 -0
  29. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.16.woff2 +0 -0
  30. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.17.woff2 +0 -0
  31. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.18.woff2 +0 -0
  32. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.19.woff2 +0 -0
  33. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.2.woff2 +0 -0
  34. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.20.woff2 +0 -0
  35. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.21.woff2 +0 -0
  36. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.22.woff2 +0 -0
  37. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.23.woff2 +0 -0
  38. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.24.woff2 +0 -0
  39. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.25.woff2 +0 -0
  40. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.26.woff2 +0 -0
  41. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.27.woff2 +0 -0
  42. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.28.woff2 +0 -0
  43. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.29.woff2 +0 -0
  44. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.3.woff2 +0 -0
  45. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.30.woff2 +0 -0
  46. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.31.woff2 +0 -0
  47. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.32.woff2 +0 -0
  48. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.33.woff2 +0 -0
  49. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.34.woff2 +0 -0
  50. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.35.woff2 +0 -0
  51. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.36.woff2 +0 -0
  52. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.37.woff2 +0 -0
  53. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.38.woff2 +0 -0
  54. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.39.woff2 +0 -0
  55. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.4.woff2 +0 -0
  56. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.40.woff2 +0 -0
  57. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.41.woff2 +0 -0
  58. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.42.woff2 +0 -0
  59. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.43.woff2 +0 -0
  60. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.44.woff2 +0 -0
  61. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.45.woff2 +0 -0
  62. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.46.woff2 +0 -0
  63. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.47.woff2 +0 -0
  64. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.48.woff2 +0 -0
  65. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.49.woff2 +0 -0
  66. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.5.woff2 +0 -0
  67. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.50.woff2 +0 -0
  68. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.51.woff2 +0 -0
  69. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.52.woff2 +0 -0
  70. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.53.woff2 +0 -0
  71. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.54.woff2 +0 -0
  72. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.55.woff2 +0 -0
  73. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.56.woff2 +0 -0
  74. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.57.woff2 +0 -0
  75. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.58.woff2 +0 -0
  76. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.59.woff2 +0 -0
  77. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.6.woff2 +0 -0
  78. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.60.woff2 +0 -0
  79. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.61.woff2 +0 -0
  80. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.62.woff2 +0 -0
  81. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.63.woff2 +0 -0
  82. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.64.woff2 +0 -0
  83. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.65.woff2 +0 -0
  84. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.66.woff2 +0 -0
  85. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.67.woff2 +0 -0
  86. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.68.woff2 +0 -0
  87. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.69.woff2 +0 -0
  88. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.7.woff2 +0 -0
  89. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.70.woff2 +0 -0
  90. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.71.woff2 +0 -0
  91. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.72.woff2 +0 -0
  92. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.73.woff2 +0 -0
  93. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.74.woff2 +0 -0
  94. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.75.woff2 +0 -0
  95. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.76.woff2 +0 -0
  96. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.77.woff2 +0 -0
  97. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.78.woff2 +0 -0
  98. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.79.woff2 +0 -0
  99. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.8.woff2 +0 -0
  100. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.80.woff2 +0 -0
  101. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.81.woff2 +0 -0
  102. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.82.woff2 +0 -0
  103. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.83.woff2 +0 -0
  104. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.84.woff2 +0 -0
  105. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.85.woff2 +0 -0
  106. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.86.woff2 +0 -0
  107. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.87.woff2 +0 -0
  108. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.88.woff2 +0 -0
  109. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.89.woff2 +0 -0
  110. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.9.woff2 +0 -0
  111. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.90.woff2 +0 -0
  112. package/dist/fonts/woff2-dynamic-subset/PretendardVariable.subset.91.woff2 +0 -0
  113. package/dist/index.cjs +391 -104
  114. package/dist/index.cjs.map +1 -1
  115. package/dist/index.d.ts +3 -0
  116. package/dist/index.js +384 -105
  117. package/dist/index.js.map +1 -1
  118. package/dist/llms-full.txt +118 -9
  119. package/dist/llms.txt +20 -9
  120. package/dist/pretendard.css +937 -0
  121. package/dist/styles.css +1001 -25
  122. package/dist/theme.css +90 -5
  123. package/eslint/scale.gen.mjs +1 -1
  124. package/package.json +2 -2
package/AGENTS.md CHANGED
@@ -29,7 +29,7 @@
29
29
  | **버튼·링크** | `SButton` `SGhostButton` `SDropdownButton` `STextLink` `SSwitch` `SToggle` |
30
30
  | **입력 (폼)** | `SForm` `SField` `SInput` `SChatInput`(대화 입력창) `SSearchInput` `SNumberInput` `STextarea` `SEditor` `SSelect` `SCheckbox` `SRadio` `SRadioGroup` `SRadioButton` `SRadioCard` `SRadioCardGroup` `SChip` `SChipInput` `SBarcodeInput` `SFilePicker` |
31
31
  | **날짜·시간** | `SCalendar` `SCalendarBoard`(한 달치 일정 판) `SDatePicker` `SDatePickerYearListbox` `SDatePickerMonthListbox` `SDateRangePicker` `STimePicker` `STimeRangePicker` |
32
- | **표·목록** | `SChatMessage`(대화의 메시지 한 건) `STable` `STableBar` `SChipFilter` `SKeyValueTable` `SList` `SListItem` `SExpansionList` `SDraggableList` `SDraggableItem` `STree` |
32
+ | **표·목록** | `SChatMessage`(대화의 메시지 한 건) `SChatAttachedFile`(입력창 위, 아직 보내지 않은 첨부 한 칸) `SChatFile`(대화 흐름에 선, 보낸 파일 한 칸) `SChatSystemMessage`(대화 흐름 가운데 서는 시스템 안내) `STable` `STableBar` `SChipFilter` `SKeyValueTable` `SList` `SListItem` `SExpansionList` `SDraggableList` `SDraggableItem` `STree` |
33
33
  | **레이아웃** | `SLayout` `SGnb` `SGnbSystem` `SSystemActionButton`(GNB system 패널에 한 줄씩 쌓는 액션 행 — 버튼 고르기는 §3-5) `SAccountListBox`(계정 행을 눌러 뜨는 계정 패널) `SPage` `SPageHeader`(페이지 제목 영역 — `SLayout` 안에서 `SPage` 앞에 둔다) `SSectionHeaderCard` `SCard` `SLoginCard`(통합 계정 로그인 화면의 카드) `SDivider` `SSplitter` `SScrollArea` `SExpansionItem` |
34
34
  | **내비게이션** | `STabs` `SPagination` `SStepper` |
35
35
  | **표시·상태** | `STag` `SBadge` `SIcon` `SImage` `SCallout` `SGuide` |
@@ -153,8 +153,8 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
153
153
  | 층 | 무엇인가 | 컴포넌트 |
154
154
  | --- | --- | --- |
155
155
  | **셸** | 앱 전체 뼈대. 페이지가 바뀌어도 남는다 | `SLayout` `SGnb` `SGnbSystem`(GNB 맨 아래 판 · 전폭 상단바 오른쪽 끝) `SPage` `SPageHeader`(`SLayout` 안에서 `SPage` 앞에 둔다) |
156
- | **블록** | `SPage` 의 직계 자식. 페이지를 세로로 쌓는 단위 | `SSectionHeaderCard` `SCard` `SChatMessage` `SChatInput` `SLoginCard`(유일하게 `SPage` 밖에 선다 — 로그인 화면 자체가 자기 자리다, §4-6) `SForm` `SSplitter` `SScrollArea` `SCalendarBoard` `STable` `STableBar` `SChipFilter` `SKeyValueTable` `SList` `SExpansionList` `SDraggableList` `STree` `SCallout` `STabs` `SStepper` `SPagination` `SDivider` |
157
- | **요소** | 블록 **안에** 놓이는 컨트롤. 혼자 페이지에 서지 않는다 | `SButton` `SGhostButton` `SDropdownButton` `SSystemActionButton` `SField` `SInput` `SSearchInput` `SNumberInput` `STextarea` `SEditor` `SSelect` `SCheckbox` `SRadio` `SRadioGroup` `SRadioButton` `SRadioCard` `SRadioCardGroup` `SSwitch` `SToggle` `SChipInput` `SBarcodeInput` `SFilePicker` `SDatePicker` `SDatePickerYearListbox` `SDatePickerMonthListbox` `SDateRangePicker` `STimePicker` `STimeRangePicker` `SCalendar` `SListItem` `SExpansionItem` `SDraggableItem` `SImage` `SLinearProgress` `SCircleProgress` |
156
+ | **블록** | `SPage` 의 직계 자식. 페이지를 세로로 쌓는 단위 | `SSectionHeaderCard` `SCard` `SChatMessage` `SChatSystemMessage` `SChatInput` `SLoginCard`(유일하게 `SPage` 밖에 선다 — 로그인 화면 자체가 자기 자리다, §4-6) `SForm` `SSplitter` `SScrollArea` `SCalendarBoard` `STable` `STableBar` `SChipFilter` `SKeyValueTable` `SList` `SExpansionList` `SDraggableList` `STree` `SCallout` `STabs` `SStepper` `SPagination` `SDivider` |
157
+ | **요소** | 블록 **안에** 놓이는 컨트롤. 혼자 페이지에 서지 않는다 | `SButton` `SGhostButton` `SDropdownButton` `SSystemActionButton` `SField` `SInput` `SSearchInput` `SNumberInput` `STextarea` `SEditor` `SSelect` `SCheckbox` `SRadio` `SRadioGroup` `SRadioButton` `SRadioCard` `SRadioCardGroup` `SSwitch` `SToggle` `SChipInput` `SBarcodeInput` `SFilePicker` `SDatePicker` `SDatePickerYearListbox` `SDatePickerMonthListbox` `SDateRangePicker` `STimePicker` `STimeRangePicker` `SCalendar` `SListItem` `SExpansionItem` `SDraggableItem` `SChatAttachedFile` `SChatFile` `SImage` `SLinearProgress` `SCircleProgress` |
158
158
  | **인라인** | 텍스트 흐름·셀·라벨 안에 섞인다. 혼자 블록이 되지 않는다 | `STag` `SBadge` `SIcon` `STextLink` `SChip` |
159
159
  | **레이어** | 문서 흐름 **밖**에 떠서 그려진다. 어느 층에서 띄우든 레이아웃에 영향이 없다 | `SModal` `SActionModal` `SConfirmModal` `SPopup` `SDrawer` `SPopover` `STooltip` `SPortal` `SAccountListBox`(계정 행에 붙어 뜬다 — 직접 띄우지 않는다) `SToast` `SLoadingModal` `SLoadingContainer` `SGuide` |
160
160
 
@@ -244,6 +244,13 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
244
244
 
245
245
  **보조 설명의 색** — 기본은 `text-fg-tertiary`(`grey_65`) 다. 보조 설명 안에서 위계가 한 단계 더 필요할 때만 `text-fg-secondary`(`grey_80`) → `text-fg-tertiary`(`grey_65`) 순으로 내려 쓴다 (§2-3).
246
246
 
247
+ **글꼴은 지정하지 않는다.** `theme.css` 가 문서 전역 글꼴을 `--font-base` 로 깔고 글꼴 파일(Pretendard)까지 함께 싣는다. 앱이 할 일은 없다 — 글꼴 유틸리티(기본 sans 유틸리티, 임의 값 글꼴 클래스)나 `fontFamily` 인라인 스타일을 붙일 이유가 없다. 기본 sans 유틸리티도 같은 스택을 가리킨다.
248
+
249
+ ```tsx
250
+ ✅ <p className="typo-body-sm-default">주문이 접수되었습니다.</p>
251
+ ❌ <p style={{ fontFamily: 'Pretendard' }}>주문이 접수되었습니다.</p> // 폴백 스택이 사라진다
252
+ ```
253
+
247
254
  ### 2-2. 간격 (spacing)
248
255
 
249
256
  - **간격 유틸리티는 `sd-` 접두를 붙인다** — `gap-sd-8`, `p-sd-16`, `mt-sd-12`. 숫자 = px 다.
@@ -492,7 +499,9 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
492
499
  | 목록을 좁히는 필터를 켜고 끄게 한다 | `SToggle` | §3-7-3 |
493
500
  | 자유 입력값을 여러 개 쌓게 한다 | `SChipInput` | §3-1 |
494
501
  | 입력된 값 하나를 지우거나 고치게 한다 | `SChip` | §3-1 |
495
- | 파일을 받는다 | `SFilePicker` | |
502
+ | 파일을 받는다 (고르게 한다) | `SFilePicker` | 고른·보낸 파일을 **보여주는** 것은 `SChatAttachedFile`·`SChatFile` |
503
+ | 보내기 전 첨부를 입력창 위에 보여준다 (눌러 뺀다) | `SChatAttachedFile` | 보낸 뒤는 `SChatFile` · 고르게 하는 것은 `SFilePicker` |
504
+ | 보낸 파일을 대화 흐름에 보여준다 (눌러 연다) | `SChatFile` | `SChatMessage` 의 `attachments` 로 넘긴다 — 직접 나열하지 않는다 |
496
505
  | 날짜 하나를 받는다 | `SDatePicker` | §3-7-4 |
497
506
  | 연도 선택 리스트만 커스텀 조합에 넣는다 | `SDatePickerYearListbox` | §3-7-4 |
498
507
  | 연도+월 선택 리스트만 커스텀 조합에 넣는다 | `SDatePickerMonthListbox` | §3-7-4 |
@@ -513,6 +522,7 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
513
522
  | 항목 하나의 속성들을 `라벨: 값` 으로 보여준다 | `SKeyValueTable` | §4-4 |
514
523
  | 한 줄로 읽히는 항목을 세로로 나열한다 | `SList` + `SListItem` | §3-7-6 |
515
524
  | 대화의 메시지 한 건을 보여준다 | `SChatMessage` | |
525
+ | 대화 중간에 시스템이 알리는 사실을 끼운다 (상담사 배정·대화 종료 등) | `SChatSystemMessage` | 사람이 보낸 말이면 `SChatMessage` |
516
526
  | 나열한 항목을 펼쳐 하위 내용을 보여준다 | `SExpansionList` + `SExpansionItem` | §3-7-7 |
517
527
  | 부모-자식 계층을 들여쓰기로 보여준다 | `STree` | §3-7-7 |
518
528
  | 사용자가 순서를 드래그로 바꾸게 한다 | `SDraggableList` + `SDraggableItem` | §3-7-6 |
@@ -925,7 +935,7 @@ const columns: STableColumn[] = [
925
935
  render: (row: SRow) => (
926
936
  <div className="flex items-center justify-center gap-sd-4">
927
937
  <SGhostButton size="xs" intent="action" icon="edit" ariaLabel="수정" onClick={() => editRow(row)} />
928
- <SGhostButton size="xs" icon="delete" ariaLabel="삭제" onClick={() => confirmRemove(row)} />
938
+ <SGhostButton size="xs" icon="remove" ariaLabel="삭제" onClick={() => confirmRemove(row)} />
929
939
  </div>
930
940
  ) },
931
941
 
@@ -1136,7 +1146,7 @@ const columns: STableColumn[] = [
1136
1146
  <SGhostButton size="xs" intent="action" icon="edit"
1137
1147
  ariaLabel="수정" onClick={() => editRow(row)} />
1138
1148
  {/* 되돌릴 수 없는 삭제일 때만 intent="danger" */}
1139
- <SGhostButton size="xs" icon="delete"
1149
+ <SGhostButton size="xs" icon="remove"
1140
1150
  ariaLabel="삭제" onClick={() => confirmRemove(row)} />
1141
1151
  </div>
1142
1152
  ) },
@@ -2279,6 +2289,7 @@ export default function ProductDetailPage() {
2279
2289
  - [ ] 생 HTML 컨트롤(`<button>` `<input>` `<select>` `<table>` …)이 없는가
2280
2290
  - [ ] `text-[14px]`, `bg-[#...]` 같은 리터럴 임의 값이 없는가 (`var(--sys-*)` 참조는 허용)
2281
2291
  - [ ] 텍스트에 `typo-*` 프리셋을 썼는가
2292
+ - [ ] 글꼴을 직접 지정하지 않았는가 (글꼴 유틸리티·`fontFamily` 인라인 ❌ — 전역 기본이 이미 적용된다, §2-1)
2282
2293
  - [ ] 간격이 전부 `sd-` 접두 스케일 값인가 (`gap-13`·`gap-sd-13` ❌ → `gap-sd-12` ✅)
2283
2294
  - [ ] 본문이 12px(`typo-body-sm-default`)인가 (14px 본문 ❌)
2284
2295
  - [ ] 텍스트 회색 위계를 순차 적용했는가 (기본 → `text-fg-secondary` → `text-fg-tertiary`, 단계 건너뛰기 ❌)
package/README.md CHANGED
@@ -50,6 +50,19 @@ npx sellmate-ds init
50
50
  `sd-` 접두로 씁니다: `p-sd-16`(16px) · `gap-sd-8`(8px). 자세한 규칙은 `AGENTS.md` §2-2.
51
51
  - `@source` — 라이브러리 `dist`를 Tailwind 스캔 대상에 포함시켜 컴포넌트 클래스가 생성되게 합니다. **경로는 여러분의 CSS 파일 위치 기준 상대경로**이며, `node_modules` 전체가 아니라 반드시 이 패키지 `dist`만 지정하세요.
52
52
 
53
+ ## 글꼴
54
+
55
+ `theme.css` 가 문서 전역 글꼴을 디자인 토큰 `--font-base`(Pretendard 스택)로 깝니다. 앱은 **글꼴 클래스를 붙이지 않습니다** — `font-sans` 도 같은 스택을 가리키므로 붙일 이유가 없습니다.
56
+
57
+ **글꼴 파일도 패키지에 들어 있습니다.** Pretendard Variable 다이내믹 서브셋을 `theme.css` 가 함께 싣기 때문에, 앱은 CDN 을 걸거나 폰트를 따로 설치할 필요가 없습니다. 위 **방식 1**(또는 방식 2) 설정만 하면 끝입니다.
58
+
59
+ - **CDN 을 쓰지 않습니다.** 사내망·오프라인·폐쇄망에서도 그대로 동작하고, 외부로 나가는 요청이 없습니다.
60
+ - **버전이 이 패키지에 고정됩니다.** CDN 최신 추적으로 글꼴이 말없이 바뀌는 일이 없습니다.
61
+ - 다이내믹 서브셋이라 브라우저는 화면에 실제 쓰인 글자 범위의 조각만 내려받습니다. 한글 전체 폰트(약 1MB)를 통째로 받지 않습니다. 패키지 용량은 약 3MB 늘지만 **앱 JS 번들에는 전혀 들어가지 않습니다.**
62
+ - 폰트 자산은 `dist/fonts/` 에 있고 라이선스(SIL OFL 1.1)는 `dist/fonts/LICENSE.txt` 입니다.
63
+
64
+ 앱이 Pretendard 를 이미 자체 호스팅하고 있어도 문제되지 않습니다 — `@font-face` 선언이 겹칠 뿐, 브라우저가 실제로 내려받는 파일은 한 벌입니다.
65
+
53
66
  ## 설정 (방식 2 — 폴백, Tailwind 미사용)
54
67
 
55
68
  Tailwind를 쓰지 않는 앱은 컴파일된 CSS를 직접 import 합니다.
@@ -0,0 +1,33 @@
1
+ # SChatAttachedFile
2
+
3
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
4
+
5
+ ### SChatAttachedFile
6
+
7
+ #### Props
8
+
9
+ | Prop | Type | Default | Description |
10
+ |------|------|---------|-------------|
11
+ | `name` | `string` | — | 파일명. 칸을 넘치면 말줄임된다 — 칸의 폭은 부모가 정한다. |
12
+
13
+ #### Events
14
+
15
+ | Event | Type | Description |
16
+ |-------|------|-------------|
17
+ | `onRemove` | `() => void` | 제거 버튼을 눌렀을 때. 주지 않으면 버튼이 서지 않는다. 입력창 위의 첨부는 보내기 전까지 뺄 수 있어야 하므로 대개 준다 — 버튼을 세우지 않는 것은 보내는 중처럼 무를 수 없는 짧은 순간뿐이다. |
18
+
19
+ ## Dependencies
20
+
21
+ ### Depends on
22
+
23
+ - [SGhostButton](../SGhostButton)
24
+ - [SIcon](../SIcon)
25
+
26
+ ### Graph
27
+
28
+ ```mermaid
29
+ graph TD;
30
+ SChatAttachedFile --> SGhostButton
31
+ SChatAttachedFile --> SIcon
32
+ style SChatAttachedFile fill:#f9f,stroke:#333,stroke-width:4px
33
+ ```
@@ -0,0 +1,13 @@
1
+ import { type HTMLAttributes } from 'react';
2
+ export interface SChatAttachedFileProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
3
+ /** 파일명. 칸을 넘치면 말줄임된다 — 칸의 폭은 부모가 정한다. */
4
+ name: string;
5
+ /**
6
+ * 제거 버튼을 눌렀을 때. 주지 않으면 버튼이 서지 않는다.
7
+ *
8
+ * 입력창 위의 첨부는 보내기 전까지 뺄 수 있어야 하므로 대개 준다 —
9
+ * 버튼을 세우지 않는 것은 보내는 중처럼 무를 수 없는 짧은 순간뿐이다.
10
+ */
11
+ onRemove?: () => void;
12
+ }
13
+ export declare const SChatAttachedFile: import("react").ForwardRefExoticComponent<SChatAttachedFileProps & import("react").RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * 보내기 전 첨부 칸이 쓰는 값. `component.chatAttachedFile` 토큰 한 세트가 여백·면·선·칩을
3
+ * 모두 쥔다.
4
+ *
5
+ * 높이는 고정하지 않고 세로 패딩(`paddingY`)과 내용이 정한다 — 예전에 스케일에 안 떨어져
6
+ * 값으로 두던 높이(30)·타일(20)은 `paddingY` + `icon.wrapper.padding` + 아이콘 크기로 따라온다.
7
+ */
8
+ export declare const CHAT_ATTACHED_FILE_CONFIG: {
9
+ /** 바깥 상자 — 좌우·상하 여백·면·선·라운드 */
10
+ readonly box: "px-(--cmp-chatAttachedFile-paddingX) py-(--cmp-chatAttachedFile-paddingY) rounded-(--cmp-chatAttachedFile-radius) border-(length:--cmp-chatAttachedFile-border-width) border-(--cmp-chatAttachedFile-border-color) bg-(--cmp-chatAttachedFile-bg)";
11
+ /** 콘텐츠 묶음 ↔ 제거 버튼 간격 */
12
+ readonly outerGap: "gap-(--cmp-chatAttachedFile-gap)";
13
+ /** 아이콘 칩 ↔ 파일명 간격 */
14
+ readonly contentGap: "gap-(--cmp-chatAttachedFile-content-gap)";
15
+ /** 클립 아이콘이 앉는 칩 — 내부 패딩·면·라운드 (높이는 아이콘+패딩이 정한다) */
16
+ readonly tile: "p-(--cmp-chatAttachedFile-icon-wrapper-padding) rounded-(--cmp-chatAttachedFile-icon-wrapper-radius) bg-(--cmp-chatAttachedFile-icon-wrapper-bg)";
17
+ /** 칩 안 클립 아이콘 크기(px). SVG width/height 는 속성값이라 CSS 변수를 못 받아 값으로 둔다 (토큰 값과 동일) */
18
+ readonly iconSize: 16;
19
+ /** 파일명 타이포 프리셋 */
20
+ readonly nameTypo: "typo-body-sm-default";
21
+ /** 파일명 텍스트색 */
22
+ readonly textColor: "text-(--cmp-chatAttachedFile-filename-color)";
23
+ };
@@ -0,0 +1,2 @@
1
+ export { SChatAttachedFile, type SChatAttachedFileProps } from './SChatAttachedFile';
2
+ export { CHAT_ATTACHED_FILE_CONFIG } from './chatAttachedFile.config';
@@ -0,0 +1,33 @@
1
+ # SChatFile
2
+
3
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
4
+
5
+ ### SChatFile
6
+
7
+ #### Props
8
+
9
+ | Prop | Type | Default | Description |
10
+ |------|------|---------|-------------|
11
+ | `name` | `string` | — | 파일명. 칸을 넘치면 말줄임된다 — 칸의 폭은 부모가 정한다. |
12
+ | `extension?` | `string` | — | 보조 줄에 쓸 형식. 주지 않으면 **파일명의 마지막 점 뒤**를 쓴다. 파일명에서 뽑는 것이 틀리는 자리에서만 준다 — 확장자가 없거나(`README`), 확장자처럼 안 생겼거나(`backup.2026` → `2026`), 두 마디인 경우(`archive.tar.gz` → `gz`). 그 밖에는 주지 않는 편이 낫다. 파일명과 어긋난 형식이 적히는 일을 막는다. 앞의 점과 대소문자는 알아서 정리한다 — `'.CSV'` 와 `'csv'` 가 같게 나온다. |
13
+ | `size` | `number` | — | 파일 크기(바이트). 형식(`extension`)과 묶어 `csv / 9.4KB` 로 그린다. **바이트 수를 그대로 넘긴다** — `'9.4KB'` 같은 완성된 문자열이 아니다. 표기를 앱이 만들면 같은 파일이 화면마다 다른 크기로 읽히므로 서식은 디자인 시스템이 쥔다. 서버가 준 바이트 수를 그대로 넘기면 된다. **필수다** — 보낸 파일은 확장자와 크기를 반드시 밝힌다. 선택으로 두면 빠뜨려도 타입이 통과하고 화면에서 보조 줄만 조용히 사라진다. |
14
+
15
+ #### Events
16
+
17
+ | Event | Type | Description |
18
+ |-------|------|-------------|
19
+ | `onFileClick` | `() => void` | 칸을 눌렀을 때 — 파일을 여는 자리다. **필수다** — 보낸 파일은 언제나 열 수 있다. 선택으로 두면 빠뜨려도 타입이 통과하고, 커서·hover 면까지 갖춘 칸이 눌러도 아무 일이 없는 화면이 조용히 나간다. 루트가 `div` 라 `role="button"`·`tabIndex`·Enter/Space 를 DS 가 함께 붙인다 — 앱이 `onClick` 만 직접 달면 키보드로 닿지 않는 칸이 되므로 그 대신 이걸 쓴다. |
20
+
21
+ ## Dependencies
22
+
23
+ ### Depends on
24
+
25
+ - [SIcon](../SIcon)
26
+
27
+ ### Graph
28
+
29
+ ```mermaid
30
+ graph TD;
31
+ SChatFile --> SIcon
32
+ style SChatFile fill:#f9f,stroke:#333,stroke-width:4px
33
+ ```
@@ -0,0 +1,37 @@
1
+ import { type HTMLAttributes } from 'react';
2
+ export interface SChatFileProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
3
+ /** 파일명. 칸을 넘치면 말줄임된다 — 칸의 폭은 부모가 정한다. */
4
+ name: string;
5
+ /**
6
+ * 보조 줄에 쓸 형식. 주지 않으면 **파일명의 마지막 점 뒤**를 쓴다.
7
+ *
8
+ * 파일명에서 뽑는 것이 틀리는 자리에서만 준다 — 확장자가 없거나(`README`),
9
+ * 확장자처럼 안 생겼거나(`backup.2026` → `2026`), 두 마디인 경우(`archive.tar.gz` → `gz`).
10
+ * 그 밖에는 주지 않는 편이 낫다. 파일명과 어긋난 형식이 적히는 일을 막는다.
11
+ *
12
+ * 앞의 점과 대소문자는 알아서 정리한다 — `'.CSV'` 와 `'csv'` 가 같게 나온다.
13
+ */
14
+ extension?: string;
15
+ /**
16
+ * 파일 크기(바이트). 형식(`extension`)과 묶어 `csv / 9.4KB` 로 그린다.
17
+ *
18
+ * **바이트 수를 그대로 넘긴다** — `'9.4KB'` 같은 완성된 문자열이 아니다. 표기를 앱이 만들면
19
+ * 같은 파일이 화면마다 다른 크기로 읽히므로 서식은 디자인 시스템이 쥔다. 서버가 준 바이트 수를
20
+ * 그대로 넘기면 된다.
21
+ *
22
+ * **필수다** — 보낸 파일은 확장자와 크기를 반드시 밝힌다. 선택으로 두면 빠뜨려도 타입이
23
+ * 통과하고 화면에서 보조 줄만 조용히 사라진다.
24
+ */
25
+ size: number;
26
+ /**
27
+ * 칸을 눌렀을 때 — 파일을 여는 자리다.
28
+ *
29
+ * **필수다** — 보낸 파일은 언제나 열 수 있다. 선택으로 두면 빠뜨려도 타입이 통과하고,
30
+ * 커서·hover 면까지 갖춘 칸이 눌러도 아무 일이 없는 화면이 조용히 나간다.
31
+ *
32
+ * 루트가 `div` 라 `role="button"`·`tabIndex`·Enter/Space 를 DS 가 함께 붙인다 — 앱이
33
+ * `onClick` 만 직접 달면 키보드로 닿지 않는 칸이 되므로 그 대신 이걸 쓴다.
34
+ */
35
+ onFileClick: () => void;
36
+ }
37
+ export declare const SChatFile: import("react").ForwardRefExoticComponent<SChatFileProps & import("react").RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,56 @@
1
+ /**
2
+ * 보낸 파일 칸이 쓰는 값. `component.chatFile` 토큰 한 세트가 여백·면·선·칩을 모두 쥔다.
3
+ *
4
+ * 높이는 고정하지 않고 세로 패딩(`paddingY`)과 내용이 정한다 — 예전에 스케일에 안 떨어져
5
+ * 값으로 두던 높이(56)·타일(28)은 `paddingY` + `icon.wrapper.padding` + 아이콘 크기로 따라온다.
6
+ */
7
+ export declare const CHAT_FILE_CONFIG: {
8
+ /** 바깥 상자 — 좌우 비대칭 여백·상하 여백·면·선·라운드 */
9
+ readonly box: "pl-(--cmp-chatFile-paddingLeft) pr-(--cmp-chatFile-paddingRight) py-(--cmp-chatFile-paddingY) rounded-(--cmp-chatFile-radius) border-(length:--cmp-chatFile-border-width) border-(--cmp-chatFile-border-color) bg-(--cmp-chatFile-bg-default)";
10
+ /** 아이콘 칩 ↔ 파일명 간격 */
11
+ readonly contentGap: "gap-(--cmp-chatFile-gap)";
12
+ /**
13
+ * 클립 아이콘이 앉는 칩 — 내부 패딩·면·라운드 (높이는 아이콘+패딩이 정한다).
14
+ *
15
+ * hover 면은 상자가 아니라 여기에도 걸린다 — 카드가 어두워질 때 칩은 반대로 밝아져
16
+ * 칩이 배경에 묻히지 않는다. 상자의 hover 를 따라가야 하므로 group-hover 로 받는다.
17
+ *
18
+ * **`--sys-color-bg-frame` 은 임시다.** 시안의 hover 칩 색(`#FFFFFF`)에 대응하는
19
+ * 컴포넌트 토큰이 없다 — `component.chatMessage.chatFile.icon.wrapper.bg` 는 값이 하나뿐이라
20
+ * hover 를 담지 못한다. 상자의 `bg.default` 가 참조하는 것과 같은 시스템 토큰으로 대신 걸어 둔다.
21
+ * 디자인팀에 `icon.wrapper.bg.hover` 를 요청해 나오는 대로 갈아끼운다.
22
+ */
23
+ readonly tile: "p-(--cmp-chatFile-icon-wrapper-padding) rounded-(--cmp-chatFile-icon-wrapper-radius) bg-(--cmp-chatFile-icon-wrapper-bg) group-hover/chat-file:bg-(--sys-color-bg-frame)";
24
+ /** 칩 안 클립 아이콘 크기(px). SVG width/height 는 속성값이라 CSS 변수를 못 받아 값으로 둔다 (토큰 값과 동일) */
25
+ readonly iconSize: 20;
26
+ /** 파일명 타이포 프리셋 */
27
+ readonly nameTypo: "typo-body-md-bold";
28
+ /** 파일명·부가정보 텍스트색 */
29
+ readonly textColor: "text-(--cmp-chatFile-text-color)";
30
+ /**
31
+ * 눌리는 모양 — 커서·hover 면·포커스 링. 보낸 파일은 언제나 열 수 있으므로 늘 붙는다
32
+ * (`onFileClick` 이 필수다).
33
+ *
34
+ * 상자의 여백·면·선과 따로 두는 이유: 이건 상호작용을 드러내는 값이라 성격이 다르다.
35
+ * 링은 `SExpansionItem`·`SStepper` 와 같은 accent outline.
36
+ */
37
+ readonly interactive: "cursor-pointer hover:bg-(--cmp-chatFile-bg-hover) focus-visible:outline focus-visible:outline-2 focus-visible:outline-(--sys-color-border-accent)";
38
+ };
39
+ /**
40
+ * 바이트 수 → `9.4KB` 표기.
41
+ *
42
+ * **표기를 앱이 아니라 여기서 정하는 이유**: 문자열로 받으면 제품마다 `9.4KB`·`9.4 KB`·
43
+ * `9,626B` 로 갈린다. 같은 파일이 화면마다 다른 크기로 읽히는 것을 막으려면 한 곳에서 만든다.
44
+ *
45
+ * 1024 배수로 올리고, 바이트는 소수점이 뜻이 없어 정수로, 그 위는 한 자리까지 쓴다(시안 표기).
46
+ * 금액·수량의 세 자리 콤마 규칙(AGENTS.md §1-4)과는 별개다 — 용량은 단위가 자리를 대신한다.
47
+ */
48
+ export declare function formatFileSize(bytes: number): string;
49
+ /** 앱이 준 확장자를 표기 형태로 맞춘다 — 앞의 점을 떼고 소문자로. `'.CSV'` → `'csv'` */
50
+ export declare function normalizeExtension(extension: string): string;
51
+ /**
52
+ * 파일명 → 소문자 확장자. 확장자가 없으면 빈 문자열.
53
+ *
54
+ * 맨 앞의 점은 확장자가 아니다(`.gitignore` 는 이름 전체가 이름이다).
55
+ */
56
+ export declare function fileExtension(name: string): string;
@@ -0,0 +1,2 @@
1
+ export { SChatFile, type SChatFileProps } from './SChatFile';
2
+ export { CHAT_FILE_CONFIG, formatFileSize, fileExtension, normalizeExtension, } from './chatFile.config';
@@ -15,9 +15,10 @@
15
15
  | `avatarSrc?` | `string` | — | 아바타 이미지 경로. 없으면 사람 아이콘 자리표시가 선다. **받은 메시지에만 쓰인다** — 보낸 메시지에는 아바타가 서지 않는다(`direction` 참고). |
16
16
  | `avatarAlt?` | `string` | — | 아바타 대체 텍스트 |
17
17
  | `continued?` | `boolean` | `false` | 같은 사람이 이어서 말한 묶음인지. 켜면 **아바타와 `sender` 를 그리지 않는다** — 말풍선이 위 묶음과 같은 선에서 시작해 한 사람의 말로 이어 읽힌다. 받은 메시지에서는 아바타 자리를 비워 둔 채 남긴다 — 안 남기면 말풍선이 44px 왼쪽으로 밀려 위 묶음과 어긋난다. 보낸 메시지는 아바타가 없으므로 이름만 숨긴다. `time`·`readStatus` 는 그대로 나온다. 시간이 벌어져 묶음을 나눴어도 얼굴은 한 번만 보이면 되고 시각은 묶음마다 필요하기 때문이다. **얼마나 벌어져야 묶음을 나눌지는 앱이 정한다** — 30분이든 하루든 대화의 성격에 달렸으므로 컴포넌트는 기준을 갖지 않고 스위치만 연다. |
18
- | `variant?` | `SChatMessageVariant` | `'default'` | 말풍선 종류. `template` 이면 말풍선이 옅은 파랑 면으로 서서 미리 등록된 서식으로 보내진 메시지임을 드러낸다. **`direction` 별개 축이라** 받은 메시지에도 보낸 메시지에도 붙는다. |
18
+ | `variant?` | `SChatMessageVariant` | `'default'` | 말풍선 종류. `template` 이면 말풍선이 옅은 파랑 면으로 서서 미리 등록된 서식으로 보내진 메시지임을 드러낸다. **보낸 메시지(`direction="outgoing"`)에만 쓰인다** 받은 메시지에는 넘겨도 그리지 않는다. 템플릿은 사업자가 보내는 것이라 받은 쪽에는 뜻이 없다. 앱이 메시지를 한 번만 매핑해 방향과 무관하게 넘길 수 있도록 막지 않고 무시한다. |
19
19
  | `actions?` | `ReactNode` | — | 시각 아래에 붙는 동작 슬롯. 없으면 그 줄이 통째로 빠진다. 「상담원 연결」처럼 **이 메시지에 이어서 할 수 있는 동작**이 오는 자리다. 무엇을 놓을지는 앱이 정하고, 메시지는 줄로 세우고 방향에 맞춰 정렬하는 것까지만 한다. 시각과 같은 선에서 시작한다 — 말풍선 끝에서 한 칸 안이다. |
20
20
  | `messages?` | `ReactNode[]` | — | 말풍선 목록. **한 항목이 말풍선 하나**가 되어 위에서 아래로 쌓인다. 한 사람이 연달아 말하면 아바타·이름·시각은 한 번만 서고 말풍선만 늘어난다 — 그 묶음이 이 컴포넌트 한 개다. 말풍선 안에는 글뿐 아니라 버튼 같은 요소도 올 수 있다. |
21
+ | `attachments?` | `ReactNode[]` | — | 말풍선 아래에 붙는 파일 목록. **한 항목이 칸 하나**가 되어 위에서 아래로 쌓인다. `SChatFile` 을 넣는 자리다 — 말풍선과 달리 면으로 감싸지 않고 넘긴 것을 그대로 세운다. 파일 칸은 제 면·선·라운드를 이미 갖고 있어 한 번 더 감싸면 칸이 두 겹으로 보인다. **`messages` 대신 여기로 넘긴다.** `messages` 로 넘기면 말풍선 면에 감싸여 그렇게 된다. 아바타·이름·시각·읽음 표시는 글 메시지와 똑같이 이 컴포넌트가 그린다 — 파일을 보낸 것도 대화의 한 줄이므로 행 규칙을 따로 갖지 않는다. |
21
22
 
22
23
  ## Types
23
24
 
@@ -59,7 +60,8 @@ export const CHAT_MESSAGE_READ_STATUSES = ['read', 'unread'] as const;
59
60
  /**
60
61
  * 말풍선의 종류. `template` 은 미리 등록된 서식으로 보내진 메시지다.
61
62
  *
62
- * **`direction` 별개 축이다** 받은 메시지도 보낸 메시지도 템플릿일 수 있다.
63
+ * **보낸 메시지에서만 면이 갈린다.** 템플릿은 사업자가 보내는 것이라 받은 메시지에는
64
+ * 뜻이 없고, 받은 메시지는 템플릿이든 아니든 같은 면으로 선다.
63
65
  */
64
66
  export const CHAT_MESSAGE_VARIANTS = ['default', 'template'] as const;
65
67
  ```
@@ -5,7 +5,8 @@ export type SChatMessageDirection = (typeof CHAT_MESSAGE_DIRECTIONS)[number];
5
5
  /**
6
6
  * 말풍선의 종류. `template` 은 미리 등록된 서식으로 보내진 메시지다.
7
7
  *
8
- * **`direction` 별개 축이다** 받은 메시지도 보낸 메시지도 템플릿일 수 있다.
8
+ * **보낸 메시지에서만 면이 갈린다.** 템플릿은 사업자가 보내는 것이라 받은 메시지에는
9
+ * 뜻이 없고, 받은 메시지는 템플릿이든 아니든 같은 면으로 선다.
9
10
  */
10
11
  export declare const CHAT_MESSAGE_VARIANTS: readonly ["default", "template"];
11
12
  export type SChatMessageVariant = (typeof CHAT_MESSAGE_VARIANTS)[number];
@@ -62,7 +63,11 @@ export interface SChatMessageProps extends Omit<HTMLAttributes<HTMLDivElement>,
62
63
  continued?: boolean;
63
64
  /**
64
65
  * 말풍선 종류. `template` 이면 말풍선이 옅은 파랑 면으로 서서 미리 등록된 서식으로 보내진
65
- * 메시지임을 드러낸다. **`direction` 과 별개 축이라** 받은 메시지에도 보낸 메시지에도 붙는다.
66
+ * 메시지임을 드러낸다.
67
+ *
68
+ * **보낸 메시지(`direction="outgoing"`)에만 쓰인다** — 받은 메시지에는 넘겨도 그리지 않는다.
69
+ * 템플릿은 사업자가 보내는 것이라 받은 쪽에는 뜻이 없다. 앱이 메시지를 한 번만 매핑해
70
+ * 방향과 무관하게 넘길 수 있도록 막지 않고 무시한다.
66
71
  */
67
72
  variant?: SChatMessageVariant;
68
73
  /**
@@ -81,6 +86,18 @@ export interface SChatMessageProps extends Omit<HTMLAttributes<HTMLDivElement>,
81
86
  * 그 묶음이 이 컴포넌트 한 개다. 말풍선 안에는 글뿐 아니라 버튼 같은 요소도 올 수 있다.
82
87
  */
83
88
  messages?: ReactNode[];
89
+ /**
90
+ * 말풍선 아래에 붙는 파일 목록. **한 항목이 칸 하나**가 되어 위에서 아래로 쌓인다.
91
+ *
92
+ * `SChatFile` 을 넣는 자리다 — 말풍선과 달리 면으로 감싸지 않고 넘긴 것을 그대로 세운다.
93
+ * 파일 칸은 제 면·선·라운드를 이미 갖고 있어 한 번 더 감싸면 칸이 두 겹으로 보인다.
94
+ *
95
+ * **`messages` 대신 여기로 넘긴다.** `messages` 로 넘기면 말풍선 면에 감싸여 그렇게 된다.
96
+ *
97
+ * 아바타·이름·시각·읽음 표시는 글 메시지와 똑같이 이 컴포넌트가 그린다 — 파일을 보낸 것도
98
+ * 대화의 한 줄이므로 행 규칙을 따로 갖지 않는다.
99
+ */
100
+ attachments?: ReactNode[];
84
101
  }
85
102
  /**
86
103
  * SChatMessage — 대화 화면에서 한 사람이 한 번에 말한 묶음.
@@ -0,0 +1,12 @@
1
+ # SChatSystemMessage
2
+
3
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
4
+
5
+ ### SChatSystemMessage
6
+
7
+ #### Props
8
+
9
+ | Prop | Type | Default | Description |
10
+ |------|------|---------|-------------|
11
+ | `message?` | `string[]` | — | 안내 문구. **항목 수만큼 알약이 쌓인다** — 서로 다른 시점에 일어난 사실은 한 알약에 몰아넣지 않고 항목을 나눈다 (`SChatMessage` 의 `messages` 와 같은 규칙). 각 항목은 **인라인 HTML 문자열을 받는다.** `<b>` `<strong>` `<i>` `<em>` `<br>` `<span>` `<a>` 만 살아남고 나머지 태그·속성과 `javascript:` 같은 위험한 스킴은 `sanitizeInlineHtml` 이 제거한다. 그래도 **서버·사용자가 만든 문자열을 그대로 넘기지 않는다** — 살균은 마지막 방어선이지 입력 검증의 대체가 아니다. ```tsx <SChatSystemMessage message={['<b>김상담</b> 상담사가 배정되었습니다.']} /> ``` |
12
+
@@ -0,0 +1,18 @@
1
+ import { type HTMLAttributes } from 'react';
2
+ export interface SChatSystemMessageProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
3
+ /**
4
+ * 안내 문구. **항목 수만큼 알약이 쌓인다** — 서로 다른 시점에 일어난 사실은
5
+ * 한 알약에 몰아넣지 않고 항목을 나눈다 (`SChatMessage` 의 `messages` 와 같은 규칙).
6
+ *
7
+ * 각 항목은 **인라인 HTML 문자열을 받는다.** `<b>` `<strong>` `<i>` `<em>` `<br>`
8
+ * `<span>` `<a>` 만 살아남고 나머지 태그·속성과 `javascript:` 같은 위험한 스킴은
9
+ * `sanitizeInlineHtml` 이 제거한다. 그래도 **서버·사용자가 만든 문자열을 그대로 넘기지 않는다** —
10
+ * 살균은 마지막 방어선이지 입력 검증의 대체가 아니다.
11
+ *
12
+ * ```tsx
13
+ * <SChatSystemMessage message={['<b>김상담</b> 상담사가 배정되었습니다.']} />
14
+ * ```
15
+ */
16
+ message?: string[];
17
+ }
18
+ export declare const SChatSystemMessage: import("react").ForwardRefExoticComponent<SChatSystemMessageProps & import("react").RefAttributes<HTMLDivElement>>;
@@ -0,0 +1 @@
1
+ export { SChatSystemMessage, type SChatSystemMessageProps } from './SChatSystemMessage';
@@ -48,6 +48,7 @@ export type SGhostButtonIntent = 'default' | 'danger' | 'action' | 'subAction' |
48
48
  - [SBarcodeInput](../SBarcodeInput)
49
49
  - [SCalendar](../SCalendar)
50
50
  - [SCalendarBoard](../SCalendarBoard)
51
+ - [SChatAttachedFile](../SChatAttachedFile)
51
52
  - [SChatInput](../SChatInput)
52
53
  - [SChip](../SChip)
53
54
  - [SDatePicker](../SDatePicker)
@@ -82,6 +83,7 @@ graph TD;
82
83
  SBarcodeInput --> SGhostButton
83
84
  SCalendar --> SGhostButton
84
85
  SCalendarBoard --> SGhostButton
86
+ SChatAttachedFile --> SGhostButton
85
87
  SChatInput --> SGhostButton
86
88
  SChip --> SGhostButton
87
89
  SDatePicker --> SGhostButton
@@ -23,6 +23,8 @@
23
23
  - [SAccountListBox](../SAccountListBox)
24
24
  - [SButton](../SButton)
25
25
  - [SCallout](../SCallout)
26
+ - [SChatAttachedFile](../SChatAttachedFile)
27
+ - [SChatFile](../SChatFile)
26
28
  - [SChatMessage](../SChatMessage)
27
29
  - [SCheckbox](../SCheckbox)
28
30
  - [SChipFilter](../SChipFilter)
@@ -67,6 +69,8 @@ graph TD;
67
69
  SAccountListBox --> SIcon
68
70
  SButton --> SIcon
69
71
  SCallout --> SIcon
72
+ SChatAttachedFile --> SIcon
73
+ SChatFile --> SIcon
70
74
  SChatMessage --> SIcon
71
75
  SCheckbox --> SIcon
72
76
  SChipFilter --> SIcon
@@ -14,6 +14,7 @@ export declare const ICONS: {
14
14
  readonly arrowUp: (p: IconRenderProps) => import("react").JSX.Element;
15
15
  readonly asterisk: (p: IconRenderProps) => import("react").JSX.Element;
16
16
  readonly attachFile: (p: IconRenderProps) => import("react").JSX.Element;
17
+ readonly back: (p: IconRenderProps) => import("react").JSX.Element;
17
18
  readonly barcode: (p: IconRenderProps) => import("react").JSX.Element;
18
19
  readonly bell: (p: IconRenderProps) => import("react").JSX.Element;
19
20
  readonly bellRinging: (p: IconRenderProps) => import("react").JSX.Element;
@@ -43,10 +44,10 @@ export declare const ICONS: {
43
44
  readonly close: (p: IconRenderProps) => import("react").JSX.Element;
44
45
  readonly company: (p: IconRenderProps) => import("react").JSX.Element;
45
46
  readonly copy: (p: IconRenderProps) => import("react").JSX.Element;
47
+ readonly counsel: (p: IconRenderProps) => import("react").JSX.Element;
46
48
  readonly coupon: (p: IconRenderProps) => import("react").JSX.Element;
47
49
  readonly dashboard: (p: IconRenderProps) => import("react").JSX.Element;
48
50
  readonly date: (p: IconRenderProps) => import("react").JSX.Element;
49
- readonly delete: (p: IconRenderProps) => import("react").JSX.Element;
50
51
  readonly dot: (p: IconRenderProps) => import("react").JSX.Element;
51
52
  readonly download: (p: IconRenderProps) => import("react").JSX.Element;
52
53
  readonly drag: (p: IconRenderProps) => import("react").JSX.Element;
@@ -57,6 +58,7 @@ export declare const ICONS: {
57
58
  readonly helpFill: (p: IconRenderProps) => import("react").JSX.Element;
58
59
  readonly helpOutline: (p: IconRenderProps) => import("react").JSX.Element;
59
60
  readonly history: (p: IconRenderProps) => import("react").JSX.Element;
61
+ readonly home: (p: IconRenderProps) => import("react").JSX.Element;
60
62
  readonly image: (p: IconRenderProps) => import("react").JSX.Element;
61
63
  readonly inline: (p: IconRenderProps) => import("react").JSX.Element;
62
64
  readonly launcher: (p: IconRenderProps) => import("react").JSX.Element;
@@ -21,6 +21,16 @@
21
21
  | `color?` | `SSystemActionButtonColor` | — | 색상(놓이는 표면). light = 흰 면, dark = 오션블루 면, darker = 네이비 면. 기본 `'light'` |
22
22
  | `disabled?` | `boolean` | — | 비활성 여부. 기본 `false` |
23
23
 
24
+ ### SSystemActionButton
25
+
26
+ #### Props
27
+
28
+ | Prop | Type | Default | Description |
29
+ |------|------|---------|-------------|
30
+ | `type` | `'account' \| 'select' \| 'switch' \| 'subSelect' \| 'status'` | — | 계정 행 |
31
+ | `label?` | `string` | — | 라벨 텍스트 |
32
+ | `option` | `<union 5종>` | — | 누르면 뜨는 계정 리스트박스. **필수다** — 계정 행은 늘 이 패널의 트리거이고, 눌러도 아무것도 뜨지 않는 계정 행은 없다. 아이콘은 `user` 로 고정이라 여기서 받지 않는다. |
33
+
24
34
  ### SSystemActionButtonSelect
25
35
 
26
36
  #### Props
@@ -61,6 +71,16 @@
61
71
  | `label?` | `string` | — | 라벨 텍스트 |
62
72
  | `option` | `SSystemActionButtonTextOption` | — | 선행 아이콘 + 우측 액션 텍스트 |
63
73
 
74
+ ### SSystemActionButtonText
75
+
76
+ #### Props
77
+
78
+ | Prop | Type | Default | Description |
79
+ |------|------|---------|-------------|
80
+ | `type` | `'switch' \| 'subSelect'` | — | 우측에 보조 조작 문구가 붙는 행 |
81
+ | `label?` | `string` | — | 라벨 텍스트 |
82
+ | `option` | `SSystemActionButtonTextOption \| SSystemActionButtonSubSelectOption` | — | 선행 아이콘 + 우측 액션 텍스트 |
83
+
64
84
  ## Types
65
85
 
66
86
  ### SSystemActionButtonAccountOption
@@ -0,0 +1,94 @@
1
+ Copyright (c) 2021, Kil Hyung-jin (https://github.com/orioncactus/pretendard),
2
+ with Reserved Font Name Pretendard.
3
+
4
+ This Font Software is licensed under the SIL Open Font License, Version 1.1.
5
+ This license is copied below, and is also available with a FAQ at:
6
+ https://scripts.sil.org/OFL
7
+
8
+
9
+ -----------------------------------------------------------
10
+ SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
11
+ -----------------------------------------------------------
12
+
13
+ PREAMBLE
14
+ The goals of the Open Font License (OFL) are to stimulate worldwide
15
+ development of collaborative font projects, to support the font creation
16
+ efforts of academic and linguistic communities, and to provide a free and
17
+ open framework in which fonts may be shared and improved in partnership
18
+ with others.
19
+
20
+ The OFL allows the licensed fonts to be used, studied, modified and
21
+ redistributed freely as long as they are not sold by themselves. The
22
+ fonts, including any derivative works, can be bundled, embedded,
23
+ redistributed and/or sold with any software provided that any reserved
24
+ names are not used by derivative works. The fonts and derivatives,
25
+ however, cannot be released under any other type of license. The
26
+ requirement for fonts to remain under this license does not apply
27
+ to any document created using the fonts or their derivatives.
28
+
29
+ DEFINITIONS
30
+ "Font Software" refers to the set of files released by the Copyright
31
+ Holder(s) under this license and clearly marked as such. This may
32
+ include source files, build scripts and documentation.
33
+
34
+ "Reserved Font Name" refers to any names specified as such after the
35
+ copyright statement(s).
36
+
37
+ "Original Version" refers to the collection of Font Software components as
38
+ distributed by the Copyright Holder(s).
39
+
40
+ "Modified Version" refers to any derivative made by adding to, deleting,
41
+ or substituting -- in part or in whole -- any of the components of the
42
+ Original Version, by changing formats or by porting the Font Software to a
43
+ new environment.
44
+
45
+ "Author" refers to any designer, engineer, programmer, technical
46
+ writer or other person who contributed to the Font Software.
47
+
48
+ PERMISSION & CONDITIONS
49
+ Permission is hereby granted, free of charge, to any person obtaining
50
+ a copy of the Font Software, to use, study, copy, merge, embed, modify,
51
+ redistribute, and sell modified and unmodified copies of the Font
52
+ Software, subject to the following conditions:
53
+
54
+ 1) Neither the Font Software nor any of its individual components,
55
+ in Original or Modified Versions, may be sold by itself.
56
+
57
+ 2) Original or Modified Versions of the Font Software may be bundled,
58
+ redistributed and/or sold with any software, provided that each copy
59
+ contains the above copyright notice and this license. These can be
60
+ included either as stand-alone text files, human-readable headers or
61
+ in the appropriate machine-readable metadata fields within text or
62
+ binary files as long as those fields can be easily viewed by the user.
63
+
64
+ 3) No Modified Version of the Font Software may use the Reserved Font
65
+ Name(s) unless explicit written permission is granted by the corresponding
66
+ Copyright Holder. This restriction only applies to the primary font name as
67
+ presented to the users.
68
+
69
+ 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
70
+ Software shall not be used to promote, endorse or advertise any
71
+ Modified Version, except to acknowledge the contribution(s) of the
72
+ Copyright Holder(s) and the Author(s) or with their explicit written
73
+ permission.
74
+
75
+ 5) The Font Software, modified or unmodified, in part or in whole,
76
+ must be distributed entirely under this license, and must not be
77
+ distributed under any other license. The requirement for fonts to
78
+ remain under this license does not apply to any document created
79
+ using the Font Software.
80
+
81
+ TERMINATION
82
+ This license becomes null and void if any of the above conditions are
83
+ not met.
84
+
85
+ DISCLAIMER
86
+ THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
87
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
88
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
89
+ OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
90
+ COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
91
+ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
92
+ DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
93
+ FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
94
+ OTHER DEALINGS IN THE FONT SOFTWARE.