sellmate-design-system-react 9.0.0-beta.0 → 9.0.0-beta.3

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
@@ -32,7 +32,7 @@
32
32
  | **표·목록** | `STable` `STableBar` `SChipFilter` `SKeyValueTable` `SList` `SListItem` `SExpansionList` `SDraggableList` `SDraggableItem` `STree` |
33
33
  | **레이아웃** | `SLayout` `SGnb` `SPage`(제목 영역은 `header` prop) `SSectionHeaderCard` `SCard` `SDivider` `SSplitter` `SScrollArea` `SExpansionItem` |
34
34
  | **내비게이션** | `STabs` `SPagination` `SStepper` |
35
- | **표시·상태** | `STag` `SBadge` `SIcon` `SCallout` `SGuide` |
35
+ | **표시·상태** | `STag` `SBadge` `SIcon` `SImage` `SCallout` `SGuide` |
36
36
  | **진행·로딩** | `SLinearProgress` `SCircleProgress` `SLoadingContainer` `SLoadingModal` |
37
37
  | **오버레이** | `STooltip` `SPopover` `SPopup` `SDrawer` `SPortal` |
38
38
  | **모달** | `SModal.confirm()` `SModal.create()` + `SActionModal` `SConfirmModal` `SModalOutlet`(앱 루트 1회) |
@@ -138,7 +138,7 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
138
138
  | --- | --- | --- |
139
139
  | **셸** | 앱 전체 뼈대. 페이지가 바뀌어도 남는다 | `SLayout` `SGnb` `SPage`(제목 영역은 `header` prop) |
140
140
  | **블록** | `SPage` 의 직계 자식. 페이지를 세로로 쌓는 단위 | `SSectionHeaderCard` `SCard` `SForm` `SSplitter` `SScrollArea` `STable` `STableBar` `SChipFilter` `SKeyValueTable` `SList` `SExpansionList` `SDraggableList` `STree` `SCallout` `STabs` `SStepper` `SPagination` `SDivider` |
141
- | **요소** | 블록 **안에** 놓이는 컨트롤. 혼자 페이지에 서지 않는다 | `SButton` `SGhostButton` `SDropdownButton` `SField` `SInput` `SNumberInput` `STextarea` `SSelect` `SCheckbox` `SRadio` `SRadioGroup` `SRadioButton` `SSwitch` `SToggle` `SChipInput` `SBarcodeInput` `SFilePicker` `SDatePicker` `SDateRangePicker` `STimePicker` `STimeRangePicker` `SCalendar` `SListItem` `SExpansionItem` `SDraggableItem` `SLinearProgress` `SCircleProgress` |
141
+ | **요소** | 블록 **안에** 놓이는 컨트롤. 혼자 페이지에 서지 않는다 | `SButton` `SGhostButton` `SDropdownButton` `SField` `SInput` `SNumberInput` `STextarea` `SSelect` `SCheckbox` `SRadio` `SRadioGroup` `SRadioButton` `SSwitch` `SToggle` `SChipInput` `SBarcodeInput` `SFilePicker` `SDatePicker` `SDateRangePicker` `STimePicker` `STimeRangePicker` `SCalendar` `SListItem` `SExpansionItem` `SDraggableItem` `SImage` `SLinearProgress` `SCircleProgress` |
142
142
  | **인라인** | 텍스트 흐름·셀·라벨 안에 섞인다. 혼자 블록이 되지 않는다 | `STag` `SBadge` `SIcon` `STextLink` `SChip` |
143
143
  | **레이어** | 문서 흐름 **밖**에 떠서 그려진다. 어느 층에서 띄우든 레이아웃에 영향이 없다 | `SModal` `SActionModal` `SConfirmModal` `SPopup` `SDrawer` `SPopover` `STooltip` `SPortal` `SToast` `SLoadingModal` `SLoadingContainer` `SGuide` |
144
144
 
@@ -442,6 +442,7 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
442
442
  | 상태·분류를 라벨로 찍는다 | `STag` | §3-1 |
443
443
  | 색 점만으로 상태를 찍는다 | `SBadge` | §3-1 |
444
444
  | 아이콘을 넣는다 | `SIcon` | |
445
+ | 사진·썸네일을 보여준다 (로딩·실패 상태 포함) | `SImage` | §3-7-12 |
445
446
  | 문장 안에서 다른 화면으로 보낸다 | `STextLink` | §3-5-6 |
446
447
 
447
448
  #### C. 동작을 실행시킨다
@@ -1085,6 +1086,23 @@ const columns: STableColumn[] = [
1085
1086
  - 검색 실행 시점이 다르다 — `SKeyValueTable` 필터는 앱이 검색 버튼을 직접 놓지만, `SChipFilter` 는 편집 팝오버가 닫히거나 "검색" 을 누를 때 `onSearch` 가 값 맵과 함께 호출된다. 값이 바뀌지 않았으면 호출되지 않는다.
1086
1087
  - 함께 걸어야 하는 조건(예: 기간 중 하나는 필수)이 있으면 `fields` 를 그룹으로 넘긴다. 규칙을 못 채운 동안 경고 툴팁이 떠 있고 `onSearch` 가 막힌다.
1087
1088
 
1089
+ #### 3-7-12. 이미지 — SImage
1090
+
1091
+ 사진·썸네일은 `<img>` 를 직접 쓰지 않고 `SImage` 를 쓴다. **로딩·실패 상태를 컴포넌트가 이미 처리하기 때문이다.**
1092
+
1093
+ - **로딩 스피너와 실패 표시를 앱이 만들지 않는다.** `src` 가 없거나 로드에 실패하면 빈 이미지 아이콘이, 로딩 중에는 스피너가 자동으로 놓인다. 크기는 상자 높이에 비례하므로 썸네일이든 큰 미리보기든 따로 맞출 것이 없다.
1094
+ - **크기는 `ratio` 와 너비로 준다.** `ratio` 기본값이 `1`(정사각형)이라 너비만 주면 정사각형이 된다. `className`/`style` 로 높이를 직접 주면 그쪽이 이기고 `ratio` 는 무시된다.
1095
+ - 상자를 채우는 방식은 `fit`(기본 `cover`), 붙는 위치는 `position` 이다. 안쪽 `<img>` 에 네이티브 속성이 필요하면 `imgProps` 로 넘긴다 — 목록에서 고스트 드래그를 막는 `imgProps={{ draggable: false }}` 가 대표적이다.
1096
+ - 썸네일이 많은 목록에서는 `loadingShowDelay` 를 준다. 캐시된 이미지가 즉시 로드될 때 스피너가 한 프레임 번쩍이는 것을 막는다.
1097
+
1098
+ ```tsx
1099
+ ✅ <SImage src={item.thumbnailUrl} alt={item.name} style={{ width: 64 }} /> {/* 정사각 썸네일 */}
1100
+ ✅ <SImage src={banner} ratio={16 / 9} className="w-full" /> {/* 가로형 배너 */}
1101
+
1102
+ ❌ <img src={item.thumbnailUrl} /> {/* 로딩·실패 상태가 없다 */}
1103
+ ❌ {loading ? <SCircleProgress indeterminate /> : <SImage src={url} />} {/* SImage 가 이미 한다 */}
1104
+ ```
1105
+
1088
1106
  ---
1089
1107
 
1090
1108
  ## 4. 페이지 레시피 — 표준 골격
@@ -11,6 +11,7 @@
11
11
  | `value?` | `number` | `0` | 진행률 (0–100) |
12
12
  | `type?` | `SCircleProgressType` | `'primary'` | 색상 테마 |
13
13
  | `indeterminate?` | `boolean` | `false` | 불확정(스피너) 모드 — value 무시 |
14
+ | `size?` | `number \| string` | `DEFAULT_SIZE` | 링의 지름. 숫자는 px 로 해석한다. 부모 크기에 비례시키려면 컨테이너 쿼리 단위(`'45cqh'`)를 쓴다 — 퍼센트는 부모가 inline-flex 라 기준 폭이 정해지지 않아 해석되지 않는다. |
14
15
  | `label?` | `string` | — | 하단 레이블 |
15
16
  | `innerValue?` | `boolean` | `false` | true면 퍼센트를 원 아래가 아닌 원 가운데에 표시 |
16
17
  | `className?` | `string` | — | |
@@ -20,6 +21,7 @@
20
21
 
21
22
  ### Used by
22
23
 
24
+ - [SImage](../SImage)
23
25
  - [SLoadingContainer](../SLoadingContainer)
24
26
  - [SLoadingModal](../SLoadingModal)
25
27
  - [STable](../STable)
@@ -28,6 +30,7 @@
28
30
 
29
31
  ```mermaid
30
32
  graph TD;
33
+ SImage --> SCircleProgress
31
34
  SLoadingContainer --> SCircleProgress
32
35
  SLoadingModal --> SCircleProgress
33
36
  STable --> SCircleProgress
@@ -1,5 +1,5 @@
1
1
  import { type CSSProperties } from 'react';
2
- export type SCircleProgressType = 'primary' | 'inverse' | 'error' | 'complete';
2
+ export type SCircleProgressType = 'primary' | 'inverse' | 'error' | 'complete' | 'neutral';
3
3
  export interface SCircleProgressProps {
4
4
  /** 진행률 (0–100) */
5
5
  value?: number;
@@ -7,6 +7,12 @@ export interface SCircleProgressProps {
7
7
  type?: SCircleProgressType;
8
8
  /** 불확정(스피너) 모드 — value 무시 */
9
9
  indeterminate?: boolean;
10
+ /**
11
+ * 링의 지름. 숫자는 px 로 해석한다.
12
+ * 부모 크기에 비례시키려면 컨테이너 쿼리 단위(`'45cqh'`)를 쓴다 — 퍼센트는 부모가
13
+ * inline-flex 라 기준 폭이 정해지지 않아 해석되지 않는다.
14
+ */
15
+ size?: number | string;
10
16
  /** 하단 레이블 */
11
17
  label?: string;
12
18
  /** true면 퍼센트를 원 아래가 아닌 원 가운데에 표시 */
@@ -8,9 +8,10 @@
8
8
 
9
9
  | Prop | Type | Default | Description |
10
10
  |------|------|---------|-------------|
11
- | `value?` | `string \| null` | — | 선택 날짜 (YYYY-MM-DD) |
11
+ | `value?` | `string \| null` | — | 선택 (date: YYYY-MM-DD, month: YYYY-MM, year: YYYY) |
12
+ | `mode?` | `SDatePickerMode` | `'date'` | 선택 모드 |
12
13
  | `size?` | `SDatePickerSize` | `'sm'` | |
13
- | `placeholder?` | `string` | `'YYYY-MM-DD'` | |
14
+ | `placeholder?` | `string` | | |
14
15
  | `selectable?` | `[string, string]` | — | 선택 가능 범위 [시작, 종료] |
15
16
  | `disabled?` | `boolean` | `false` | |
16
17
  | `width?` | `number \| string` | — | |
@@ -35,7 +36,7 @@
35
36
 
36
37
  | Event | Type | Description |
37
38
  |-------|------|-------------|
38
- | `onValueChange` | `(date: string) => void` | 선택 변경 (sdUpdate) |
39
+ | `onValueChange` | `(value: string) => void` | 선택 변경 (sdUpdate) |
39
40
  | `onViewChange` | `(view: { year: number; month: number }) => void` | 보이는 연·월 변경 (sdViewChange) |
40
41
 
41
42
  ## Dependencies
@@ -5,16 +5,19 @@ import { type SColor } from '../../lib/color';
5
5
  import { type Rule } from '../../lib/form';
6
6
  import { type STooltipProps } from '../STooltip';
7
7
  export type SDatePickerSize = SFieldSize;
8
+ export type SDatePickerMode = 'date' | 'month' | 'year';
8
9
  export interface SDatePickerProps {
9
- /** 선택 날짜 (YYYY-MM-DD) */
10
+ /** 선택 (date: YYYY-MM-DD, month: YYYY-MM, year: YYYY) */
10
11
  value?: string | null;
11
12
  /** 선택 변경 (sdUpdate) */
12
- onValueChange?: (date: string) => void;
13
+ onValueChange?: (value: string) => void;
13
14
  /** 보이는 연·월 변경 (sdViewChange) */
14
15
  onViewChange?: (view: {
15
16
  year: number;
16
17
  month: number;
17
18
  }) => void;
19
+ /** 선택 모드 */
20
+ mode?: SDatePickerMode;
18
21
  size?: SDatePickerSize;
19
22
  placeholder?: string;
20
23
  /** 선택 가능 범위 [시작, 종료] */
@@ -39,4 +42,4 @@ export interface SDatePickerProps {
39
42
  style?: CSSProperties;
40
43
  }
41
44
  /** SDatePicker — sd-date-picker 포팅. SField + SPopover(SCalendar). */
42
- export declare function SDatePicker({ value, onValueChange, onViewChange, size, placeholder, selectable, disabled, width, name, rules, status, label, labelWidth, icon, iconColor, labelTooltip, labelTooltipProps, addonLabel, addonAlign, hint, error, errorMessage, className, style, }: SDatePickerProps): import("react").JSX.Element;
45
+ export declare function SDatePicker({ value, onValueChange, onViewChange, mode, size, placeholder, selectable, disabled, width, name, rules, status, label, labelWidth, icon, iconColor, labelTooltip, labelTooltipProps, addonLabel, addonAlign, hint, error, errorMessage, className, style, }: SDatePickerProps): import("react").JSX.Element;
@@ -1 +1 @@
1
- export { SDatePicker, type SDatePickerProps, type SDatePickerSize } from './SDatePicker';
1
+ export { SDatePicker, type SDatePickerMode, type SDatePickerProps, type SDatePickerSize, } from './SDatePicker';
@@ -36,6 +36,7 @@
36
36
  - [SGhostButton](../SGhostButton)
37
37
  - [SGnb](../SGnb)
38
38
  - [SGuide](../SGuide)
39
+ - [SImage](../SImage)
39
40
  - [SKeyValueTable](../SKeyValueTable)
40
41
  - [SListItem](../SListItem)
41
42
  - [SLoadingModal](../SLoadingModal)
@@ -73,6 +74,7 @@ graph TD;
73
74
  SGhostButton --> SIcon
74
75
  SGnb --> SIcon
75
76
  SGuide --> SIcon
77
+ SImage --> SIcon
76
78
  SKeyValueTable --> SIcon
77
79
  SListItem --> SIcon
78
80
  SLoadingModal --> SIcon
@@ -0,0 +1,44 @@
1
+ # SImage
2
+
3
+ > 자동 생성 문서 — `npm run docs:gen`. 소스: 각 컴포넌트의 Props/Handle 인터페이스 + import 의존성.
4
+
5
+ ### SImage
6
+
7
+ #### Props
8
+
9
+ | Prop | Type | Default | Description |
10
+ |------|------|---------|-------------|
11
+ | `src?` | `string` | — | 이미지 경로. 없으면 빈 이미지 아이콘이 나온다 |
12
+ | `alt?` | `string` | `''` | 대체 텍스트 |
13
+ | `ratio?` | `number` | `1` | 가로세로 비율. 소비자가 높이를 직접 주면 그쪽이 이긴다 |
14
+ | `fit?` | `SImageFit` | `'cover'` | 이미지가 상자를 채우는 방식 (object-fit) |
15
+ | `position?` | `string` | `'50% 50%'` | 이미지 정렬 위치 (object-position) |
16
+ | `loading?` | `'lazy' \| 'eager'` | `'lazy'` | 브라우저 로딩 방식 |
17
+ | `loadingShowDelay?` | `number` | `0` | 스피너를 띄우기까지 기다리는 시간(ms). 이 시간 안에 로드되면 스피너를 아예 그리지 않는다 — 캐시된 이미지에서 스피너가 한 프레임 번쩍이는 것을 막는다. |
18
+ | `noSpinner?` | `boolean` | `false` | 로딩 중에도 스피너를 그리지 않는다 |
19
+ | `loadingSlot?` | `ReactNode` | — | 로딩 중 표시를 직접 지정한다 (기본: 스피너) |
20
+ | `errorSlot?` | `ReactNode` | — | 실패·빈 상태 표시를 직접 지정한다 (기본: 빈 이미지 아이콘) |
21
+ | `imgProps?` | `ImgHTMLAttributes<HTMLImageElement>` | — | 안쪽 `<img>` 로 넘길 네이티브 속성 (srcSet · sizes · crossOrigin · draggable 등) |
22
+
23
+ #### Events
24
+
25
+ | Event | Type | Description |
26
+ |-------|------|-------------|
27
+ | `onLoad` | `(src: string) => void` | 로드 완료. 로드된 src 를 넘긴다 |
28
+ | `onError` | `(event: SyntheticEvent<HTMLImageElement>) => void` | 로드 실패 |
29
+
30
+ ## Dependencies
31
+
32
+ ### Depends on
33
+
34
+ - [SCircleProgress](../SCircleProgress)
35
+ - [SIcon](../SIcon)
36
+
37
+ ### Graph
38
+
39
+ ```mermaid
40
+ graph TD;
41
+ SImage --> SCircleProgress
42
+ SImage --> SIcon
43
+ style SImage fill:#f9f,stroke:#333,stroke-width:4px
44
+ ```
@@ -0,0 +1,41 @@
1
+ import { type HTMLAttributes, type ImgHTMLAttributes, type ReactNode, type SyntheticEvent } from 'react';
2
+ export declare const IMAGE_FITS: readonly ["cover", "contain", "fill", "none", "scale-down"];
3
+ export type SImageFit = (typeof IMAGE_FITS)[number];
4
+ export interface SImageProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onLoad' | 'onError'> {
5
+ /** 이미지 경로. 없으면 빈 이미지 아이콘이 나온다 */
6
+ src?: string;
7
+ /** 대체 텍스트 */
8
+ alt?: string;
9
+ /** 가로세로 비율. 소비자가 높이를 직접 주면 그쪽이 이긴다 */
10
+ ratio?: number;
11
+ /** 이미지가 상자를 채우는 방식 (object-fit) */
12
+ fit?: SImageFit;
13
+ /** 이미지 정렬 위치 (object-position) */
14
+ position?: string;
15
+ /** 브라우저 로딩 방식 */
16
+ loading?: 'lazy' | 'eager';
17
+ /**
18
+ * 스피너를 띄우기까지 기다리는 시간(ms). 이 시간 안에 로드되면 스피너를 아예 그리지 않는다 —
19
+ * 캐시된 이미지에서 스피너가 한 프레임 번쩍이는 것을 막는다.
20
+ */
21
+ loadingShowDelay?: number;
22
+ /** 로딩 중에도 스피너를 그리지 않는다 */
23
+ noSpinner?: boolean;
24
+ /** 로딩 중 표시를 직접 지정한다 (기본: 스피너) */
25
+ loadingSlot?: ReactNode;
26
+ /** 실패·빈 상태 표시를 직접 지정한다 (기본: 빈 이미지 아이콘) */
27
+ errorSlot?: ReactNode;
28
+ /** 안쪽 `<img>` 로 넘길 네이티브 속성 (srcSet · sizes · crossOrigin · draggable 등) */
29
+ imgProps?: ImgHTMLAttributes<HTMLImageElement>;
30
+ /** 로드 완료. 로드된 src 를 넘긴다 */
31
+ onLoad?: (src: string) => void;
32
+ /** 로드 실패 */
33
+ onError?: (event: SyntheticEvent<HTMLImageElement>) => void;
34
+ }
35
+ /**
36
+ * SImage — 이미지 로딩·실패 상태를 스스로 표현하는 이미지 상자.
37
+ *
38
+ * 로딩 중에는 스피너, `src` 가 없거나 로드에 실패하면 빈 이미지 아이콘을 컨테이너 높이에
39
+ * 비례한 크기로 가운데에 그린다.
40
+ */
41
+ export declare const SImage: import("react").ForwardRefExoticComponent<SImageProps & import("react").RefAttributes<HTMLDivElement>>;
@@ -0,0 +1 @@
1
+ export { SImage, IMAGE_FITS, type SImageProps, type SImageFit } from './SImage';
@@ -11,6 +11,7 @@
11
11
  | `value?` | `boolean` | `false` | 선택(on/off) 상태 |
12
12
  | `label?` | `string` | `''` | 라벨 |
13
13
  | `disabled?` | `boolean` | `false` | 비활성 |
14
+ | `size?` | `SToggleSize` | `'sm'` | 크기 |
14
15
  | `className?` | `string` | — | |
15
16
  | `style?` | `CSSProperties` | — | |
16
17
 
@@ -1,4 +1,5 @@
1
1
  import { type CSSProperties } from 'react';
2
+ export type SToggleSize = 'xs' | 'sm';
2
3
  export interface SToggleProps {
3
4
  /** 선택(on/off) 상태 */
4
5
  value?: boolean;
@@ -6,6 +7,8 @@ export interface SToggleProps {
6
7
  label?: string;
7
8
  /** 비활성 */
8
9
  disabled?: boolean;
10
+ /** 크기 */
11
+ size?: SToggleSize;
9
12
  /** 변경 (sdUpdate) */
10
13
  onValueChange?: (value: boolean) => void;
11
14
  className?: string;
@@ -15,4 +18,4 @@ export interface SToggleProps {
15
18
  * SToggle — sd-toggle 포팅. 알약형 토글 버튼.
16
19
  * default(흰 배경/회색), hover(파란 채움), select(흰 배경/파란 테두리·글자), disabled.
17
20
  */
18
- export declare function SToggle({ value, label, disabled, onValueChange, className, style, }: SToggleProps): import("react").JSX.Element;
21
+ export declare function SToggle({ value, label, disabled, size, onValueChange, className, style, }: SToggleProps): import("react").JSX.Element;