sellmate-design-system-react 9.0.0-beta.47 → 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.
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`(대화의 메시지 한 건) `SChatAttachedFile`(대화에 딸린 파일 한 칸) `SChatSystemMessage`(대화 흐름 가운데 서는 시스템 안내) `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` |
@@ -154,7 +154,7 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
154
154
  | --- | --- | --- |
155
155
  | **셸** | 앱 전체 뼈대. 페이지가 바뀌어도 남는다 | `SLayout` `SGnb` `SGnbSystem`(GNB 맨 아래 판 · 전폭 상단바 오른쪽 끝) `SPage` `SPageHeader`(`SLayout` 안에서 `SPage` 앞에 둔다) |
156
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` `SImage` `SLinearProgress` `SCircleProgress` |
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
 
@@ -499,8 +499,9 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
499
499
  | 목록을 좁히는 필터를 켜고 끄게 한다 | `SToggle` | §3-7-3 |
500
500
  | 자유 입력값을 여러 개 쌓게 한다 | `SChipInput` | §3-1 |
501
501
  | 입력된 값 하나를 지우거나 고치게 한다 | `SChip` | §3-1 |
502
- | 파일을 받는다 (고르게 한다) | `SFilePicker` | 고른·보낸 파일을 **보여주는** 것은 `SChatAttachedFile` |
503
- | 대화에 딸린 파일을 보여준다 (보내기 전 첨부·보낸 파일) | `SChatAttachedFile` | 고르게 하는 것은 `SFilePicker` |
502
+ | 파일을 받는다 (고르게 한다) | `SFilePicker` | 고른·보낸 파일을 **보여주는** 것은 `SChatAttachedFile`·`SChatFile` |
503
+ | 보내기 첨부를 입력창 위에 보여준다 (눌러 뺀다) | `SChatAttachedFile` | 보낸 뒤는 `SChatFile` · 고르게 하는 것은 `SFilePicker` |
504
+ | 보낸 파일을 대화 흐름에 보여준다 (눌러 연다) | `SChatFile` | `SChatMessage` 의 `attachments` 로 넘긴다 — 직접 나열하지 않는다 |
504
505
  | 날짜 하나를 받는다 | `SDatePicker` | §3-7-4 |
505
506
  | 연도 선택 리스트만 커스텀 조합에 넣는다 | `SDatePickerYearListbox` | §3-7-4 |
506
507
  | 연도+월 선택 리스트만 커스텀 조합에 넣는다 | `SDatePickerMonthListbox` | §3-7-4 |
@@ -9,26 +9,25 @@
9
9
  | Prop | Type | Default | Description |
10
10
  |------|------|---------|-------------|
11
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'` 같은 완성된 문자열이 아니다. 표기를 앱이 만들면 같은 파일이 화면마다 다른 크기로 읽히므로 서식은 디자인 시스템이 쥔다. `attached` 는 `File.size`, `sent` 은 서버가 준 바이트 수를 그대로 넘기면 된다. **`sent` 에서는 필수다** — 보낸 파일은 확장자와 크기를 반드시 밝힌다. `attached` 에는 넘겨도 그리지 않는다(한 줄짜리 칸이다). 앱이 데이터를 한 번만 매핑해 자리와 무관하게 넘길 수 있도록 `attached` 에서 막지는 않고 무시한다. |
14
- | `variant` | `'attached' \| 'sent'` | — | 파일 칸이 서는 자리. **크기·면·선·글자·아이콘이 전부 여기서 갈린다.** `attached` 는 아직 보내지 않은 첨부다 — 입력창 위에 서는 한 줄짜리 칸이고 눌러 뺄 수 있다. `sent` 은 이미 보낸 파일이다 — 대화창에 서는 두 줄짜리 칸이고 `size` 가 **필수**다. |
15
12
 
16
13
  #### Events
17
14
 
18
15
  | Event | Type | Description |
19
16
  |-------|------|-------------|
20
- | `onRemove` | `() => void` | 제거 버튼을 눌렀을 때. 주지 않으면 버튼이 서지 않는다. **`attached` 에만 쓰인다**이미 보낸 파일은 무를 수 없으므로 `sent` 에는 그리지 않는다. |
17
+ | `onRemove` | `() => void` | 제거 버튼을 눌렀을 때. 주지 않으면 버튼이 서지 않는다. 입력창 위의 첨부는 보내기 전까지 뺄 수 있어야 하므로 대개 준다 버튼을 세우지 않는 것은 보내는 중처럼 무를 수 없는 짧은 순간뿐이다. |
21
18
 
22
19
  ## Dependencies
23
20
 
24
21
  ### Depends on
25
22
 
23
+ - [SGhostButton](../SGhostButton)
26
24
  - [SIcon](../SIcon)
27
25
 
28
26
  ### Graph
29
27
 
30
28
  ```mermaid
31
29
  graph TD;
30
+ SChatAttachedFile --> SGhostButton
32
31
  SChatAttachedFile --> SIcon
33
32
  style SChatAttachedFile fill:#f9f,stroke:#333,stroke-width:4px
34
33
  ```
@@ -1,54 +1,13 @@
1
1
  import { type HTMLAttributes } from 'react';
2
- interface SChatAttachedFileBaseProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
2
+ export interface SChatAttachedFileProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
3
3
  /** 파일명. 칸을 넘치면 말줄임된다 — 칸의 폭은 부모가 정한다. */
4
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
- * `attached` 는 `File.size`, `sent` 은 서버가 준 바이트 수를 그대로 넘기면 된다.
21
- *
22
- * **`sent` 에서는 필수다** — 보낸 파일은 확장자와 크기를 반드시 밝힌다.
23
- * `attached` 에는 넘겨도 그리지 않는다(한 줄짜리 칸이다). 앱이 데이터를 한 번만 매핑해
24
- * 자리와 무관하게 넘길 수 있도록 `attached` 에서 막지는 않고 무시한다.
25
- */
26
- size?: number;
27
5
  /**
28
6
  * 제거 버튼을 눌렀을 때. 주지 않으면 버튼이 서지 않는다.
29
7
  *
30
- * **`attached` 에만 쓰인다** 이미 보낸 파일은 무를 없으므로 `sent` 에는 그리지 않는다.
8
+ * 입력창 위의 첨부는 보내기 전까지 있어야 하므로 대개 준다
9
+ * 버튼을 세우지 않는 것은 보내는 중처럼 무를 수 없는 짧은 순간뿐이다.
31
10
  */
32
11
  onRemove?: () => void;
33
12
  }
34
- /**
35
- * 자리에 따라 필수 prop 이 갈린다.
36
- *
37
- * `sent` 은 `size` 가 **필수**다 — 보낸 파일은 확장자와 크기를 반드시 밝혀야 하는데,
38
- * 선택으로 두면 빠뜨려도 타입이 통과하고 화면에서 보조 줄만 조용히 사라진다.
39
- * `attached` 는 한 줄짜리 칸이라 필요 없다.
40
- */
41
- export type SChatAttachedFileProps = (SChatAttachedFileBaseProps & {
42
- /**
43
- * 파일 칸이 서는 자리. **크기·면·선·글자·아이콘이 전부 여기서 갈린다.**
44
- *
45
- * `attached` 는 아직 보내지 않은 첨부다 — 입력창 위에 서는 한 줄짜리 칸이고 눌러 뺄 수 있다.
46
- * `sent` 은 이미 보낸 파일이다 — 대화창에 서는 두 줄짜리 칸이고 `size` 가 **필수**다.
47
- */
48
- variant: 'attached';
49
- }) | (SChatAttachedFileBaseProps & {
50
- variant: 'sent';
51
- size: number;
52
- });
53
13
  export declare const SChatAttachedFile: import("react").ForwardRefExoticComponent<SChatAttachedFileProps & import("react").RefAttributes<HTMLDivElement>>;
54
- export {};
@@ -1,43 +1,23 @@
1
1
  /**
2
- * 파일 칸이 서는 자리. **이 하나로 크기·면·선·글자·아이콘이 전부 갈린다.**
2
+ * 보내기 전 첨부 칸이 쓰는 값. `component.chatAttachedFile` 토큰 세트가 여백·면·선·칩을
3
+ * 모두 쥔다.
3
4
  *
4
- * - `attached` 아직 보내지 않은 첨부. 입력창 위에 가로로 늘어서고 눌러 뺄 수 있다.
5
- * - `sent` 이미 보낸 파일. 대화창에 세로로 쌓이고 뺄 수 없다.
5
+ * 높이는 고정하지 않고 세로 패딩(`paddingY`)과 내용이 정한다 예전에 스케일에 떨어져
6
+ * 값으로 두던 높이(30)·타일(20)은 `paddingY` + `icon.wrapper.padding` + 아이콘 크기로 따라온다.
6
7
  */
7
- export declare const CHAT_ATTACHED_FILE_VARIANTS: readonly ["attached", "sent"];
8
- export type SChatAttachedFileVariant = (typeof CHAT_ATTACHED_FILE_VARIANTS)[number];
9
- interface ChatAttachedFilePreset {
10
- /** 바깥 상자 높이·좌우 여백·항목 간격·면·선 */
11
- box: string;
12
- /** 클립 아이콘이 앉는 정사각 타일 */
13
- tile: string;
14
- /** 타일 클립 아이콘 크기(px) */
15
- iconSize: number;
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;
16
19
  /** 파일명 타이포 프리셋 */
17
- nameTypo: string;
18
- }
19
- /**
20
- * 높이(30/56)와 타일 크기(20/28)는 대응 토큰이 없어 값으로 둔다
21
- * (디자인팀 토큰 추가 요청 대상). 세로 여백이 스페이싱 스케일에 떨어지지 않아
22
- * 여백을 주는 대신 높이를 고정하고 세로 가운데 정렬로 세운다.
23
- */
24
- export declare const CHAT_ATTACHED_FILE_CONFIG: Record<SChatAttachedFileVariant, ChatAttachedFilePreset>;
25
- /**
26
- * 바이트 수 → `9.4KB` 표기.
27
- *
28
- * **표기를 앱이 아니라 여기서 정하는 이유**: 문자열로 받으면 제품마다 `9.4KB`·`9.4 KB`·
29
- * `9,626B` 로 갈린다. 같은 파일이 화면마다 다른 크기로 읽히는 것을 막으려면 한 곳에서 만든다.
30
- *
31
- * 1024 배수로 올리고, 바이트는 소수점이 뜻이 없어 정수로, 그 위는 한 자리까지 쓴다(시안 표기).
32
- * 금액·수량의 세 자리 콤마 규칙(AGENTS.md §1-4)과는 별개다 — 용량은 단위가 자리를 대신한다.
33
- */
34
- export declare function formatFileSize(bytes: number): string;
35
- /** 앱이 준 확장자를 표기 형태로 맞춘다 — 앞의 점을 떼고 소문자로. `'.CSV'` → `'csv'` */
36
- export declare function normalizeExtension(extension: string): string;
37
- /**
38
- * 파일명 → 소문자 확장자. 확장자가 없으면 빈 문자열.
39
- *
40
- * 맨 앞의 점은 확장자가 아니다(`.gitignore` 는 이름 전체가 이름이다).
41
- */
42
- export declare function fileExtension(name: string): string;
43
- export {};
20
+ readonly nameTypo: "typo-body-sm-default";
21
+ /** 파일명 텍스트색 */
22
+ readonly textColor: "text-(--cmp-chatAttachedFile-filename-color)";
23
+ };
@@ -1,2 +1,2 @@
1
1
  export { SChatAttachedFile, type SChatAttachedFileProps } from './SChatAttachedFile';
2
- export { CHAT_ATTACHED_FILE_VARIANTS, CHAT_ATTACHED_FILE_CONFIG, formatFileSize, fileExtension, normalizeExtension, type SChatAttachedFileVariant, } from './chatAttachedFile.config';
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';
@@ -18,6 +18,7 @@
18
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
 
@@ -86,6 +86,18 @@ export interface SChatMessageProps extends Omit<HTMLAttributes<HTMLDivElement>,
86
86
  * 그 묶음이 이 컴포넌트 한 개다. 말풍선 안에는 글뿐 아니라 버튼 같은 요소도 올 수 있다.
87
87
  */
88
88
  messages?: ReactNode[];
89
+ /**
90
+ * 말풍선 아래에 붙는 파일 목록. **한 항목이 칸 하나**가 되어 위에서 아래로 쌓인다.
91
+ *
92
+ * `SChatFile` 을 넣는 자리다 — 말풍선과 달리 면으로 감싸지 않고 넘긴 것을 그대로 세운다.
93
+ * 파일 칸은 제 면·선·라운드를 이미 갖고 있어 한 번 더 감싸면 칸이 두 겹으로 보인다.
94
+ *
95
+ * **`messages` 대신 여기로 넘긴다.** `messages` 로 넘기면 말풍선 면에 감싸여 그렇게 된다.
96
+ *
97
+ * 아바타·이름·시각·읽음 표시는 글 메시지와 똑같이 이 컴포넌트가 그린다 — 파일을 보낸 것도
98
+ * 대화의 한 줄이므로 행 규칙을 따로 갖지 않는다.
99
+ */
100
+ attachments?: ReactNode[];
89
101
  }
90
102
  /**
91
103
  * SChatMessage — 대화 화면에서 한 사람이 한 번에 말한 묶음.
@@ -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
@@ -24,6 +24,7 @@
24
24
  - [SButton](../SButton)
25
25
  - [SCallout](../SCallout)
26
26
  - [SChatAttachedFile](../SChatAttachedFile)
27
+ - [SChatFile](../SChatFile)
27
28
  - [SChatMessage](../SChatMessage)
28
29
  - [SCheckbox](../SCheckbox)
29
30
  - [SChipFilter](../SChipFilter)
@@ -69,6 +70,7 @@ graph TD;
69
70
  SButton --> SIcon
70
71
  SCallout --> SIcon
71
72
  SChatAttachedFile --> SIcon
73
+ SChatFile --> SIcon
72
74
  SChatMessage --> SIcon
73
75
  SCheckbox --> SIcon
74
76
  SChipFilter --> SIcon