sellmate-design-system-react 9.0.0-beta.74 → 9.0.0-beta.76
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 +37 -2
- package/README.md +5 -5
- package/bin/sellmate-ds.mjs +91 -59
- package/dist/components/SCircleProgress/README.md +6 -3
- package/dist/components/SCircleProgress/SCircleProgress.d.ts +11 -3
- package/dist/components/SLauncherListBox/README.md +1 -1
- package/dist/components/SLauncherListBox/launcherListBox.config.d.ts +2 -2
- package/dist/components/SLinearProgress/README.md +2 -2
- package/dist/components/SLinearProgress/SLinearProgress.d.ts +8 -2
- package/dist/components/SLogo/README.md +1 -0
- package/dist/components/SLogo/logo.config.d.ts +1 -1
- package/dist/components/SSelect/README.md +18 -2
- package/dist/components/SSelect/SSelect.d.ts +28 -5
- package/dist/components/SSelect/index.d.ts +1 -1
- package/dist/components/STable/README.md +29 -20
- package/dist/components/STable/STable.d.ts +47 -31
- package/dist/components/STable/index.d.ts +1 -1
- package/dist/index.cjs +167 -50
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +167 -50
- package/dist/index.js.map +1 -1
- package/dist/llms-full.txt +94 -31
- package/dist/llms.txt +39 -3
- package/dist/styles.css +16 -0
- package/dist/theme.css +3 -0
- package/eslint/index.mjs +42 -42
- package/eslint/lib/class-names.mjs +23 -25
- package/eslint/lib/table-column.mjs +11 -11
- package/eslint/rules/component-group-gap.mjs +32 -30
- package/eslint/rules/divider-vertical-height.mjs +22 -22
- package/eslint/rules/field-width-grade.mjs +50 -50
- package/eslint/rules/no-arbitrary-class.mjs +63 -20
- package/eslint/rules/no-off-scale-spacing.mjs +61 -41
- package/eslint/rules/no-raw-html-control.mjs +38 -36
- package/eslint/rules/prefer-typo-preset.mjs +23 -17
- package/eslint/rules/require-locale-number.mjs +14 -19
- package/eslint/rules/table-column-width.mjs +21 -17
- package/eslint/rules/table-numeric-align.mjs +14 -14
- package/eslint/scale.gen.mjs +1 -1
- package/package.json +3 -3
package/dist/llms-full.txt
CHANGED
|
@@ -1061,6 +1061,25 @@ const columns: STableColumn[] = [
|
|
|
1061
1061
|
- **계산값은 픽셀 단위까지 맞추면 어긋난다** — 서브픽셀 반올림 때문이다. 여유 8px 을 얹고 8 단위로 올림한다.
|
|
1062
1062
|
- 좌우 패딩은 `STable` 이 토큰으로 넣으므로 직접 주지 않는다. 그만큼을 뺀 나머지가 요소 몫이라는 점만 계산에 넣는다.
|
|
1063
1063
|
|
|
1064
|
+
#### 행 타입을 알면 넘긴다
|
|
1065
|
+
|
|
1066
|
+
`STable`·`STableColumn` 은 행 타입을 받는다. 넘기면 `render`·`format`·`onRowClick`·`selected` 가 그 타입으로 좁혀져, 없는 키나 오타가 컴파일에서 잡힌다. 넘기지 않으면 행은 `SRow`(키만 아는 느슨한 레코드)다 — 기존 코드는 그대로 동작한다.
|
|
1067
|
+
|
|
1068
|
+
```tsx
|
|
1069
|
+
interface Order { id: number; orderNo: string; qty: number }
|
|
1070
|
+
|
|
1071
|
+
const columns: STableColumn<Order>[] = [
|
|
1072
|
+
{ name: 'orderNo', label: '주문번호', field: 'orderNo', width: 140, align: 'center' },
|
|
1073
|
+
{ name: 'qty', label: '수량', field: 'qty', width: 80, align: 'right',
|
|
1074
|
+
format: v => `${Number(v).toLocaleString()}개` },
|
|
1075
|
+
];
|
|
1076
|
+
|
|
1077
|
+
<STable<Order> columns={columns} rows={orders} onRowClick={order => open(order.id)} />
|
|
1078
|
+
```
|
|
1079
|
+
|
|
1080
|
+
- 행 타입을 넘기면 `format`·`render` 의 **셀 값은 `unknown`** 이다. `field` 가 접근 함수일 수도 있어 행 타입만으로는 값의 타입이 정해지지 않으므로, `Number(v)`·`String(v)` 로 받는 쪽에서 확인한다. 행이 필요하면 두 번째 인자(`format`)·첫 번째 인자(`render`)의 행을 쓴다.
|
|
1081
|
+
- `SSelect` 도 같은 방식으로 `onValueChange` 의 값 타입을 받는다 — `<SSelect<string> valueAsPrimitive … />`. 실제 모양은 `type`·`valueAsPrimitive` 조합이 정하므로 그 조합에 맞는 타입을 넘긴다.
|
|
1082
|
+
|
|
1064
1083
|
#### 컨트롤이 들어가는 컬럼
|
|
1065
1084
|
|
|
1066
1085
|
`<td>` 는 폭을 넘는 내용을 잘라낸다(`overflow: hidden`). 텍스트라면 말줄임으로 끝나지만, 셀에 `STag` · `SButton` · `SGhostButton` · `SSelect` · `SInput` · `SNumberInput` 처럼 **자기 폭을 가진 요소**를 넣으면 요소 자체가 잘려 **누르거나 읽거나 입력할 수 없게 된다.**
|
|
@@ -1588,7 +1607,7 @@ tableRef.current.scrollToRow(row); // 복원
|
|
|
1588
1607
|
```
|
|
1589
1608
|
|
|
1590
1609
|
- **`launcher` 를 주지 않으면 런처 버튼 자체가 렌더되지 않는다.** 옮겨 갈 서비스가 없는 앱은 이 슬롯을 비워 둔다 — 눌러도 아무것도 없는 버튼을 상단바에 남기지 않는다.
|
|
1591
|
-
- **서비스는 키(`service`)로 고른다 — 보이는 것은 아무것도 넘기지 않는다.** 쓸 수 있는 키는 `SLogo` 가 로고를 가진 서비스 전부(`'sellmate' | 'wms' | 'chat' | 'plm' | 'account' | 'people' | 'crm' | 'work' | 'pos'` — `SLauncherService`)이고, 서비스 로고(워드마크와 `WMS`·`Account`·`CRM` … 표기가 한 덩어리인 브랜드 로고)·아래 줄 한글 이름·상태 태그(`NEW`·`출시예정`)·출시 여부를 카탈로그(`LAUNCHER_SERVICE_CATALOG`)가 그 키로 정한다. 앱이 넘기게 하면 같은 서비스가 앱마다 다른 이름·색·문구로 선다. **서비스가 늘거나 이름·상태가 바뀌면 디자인 시스템 릴리스로 따라온다** — 앱은 고칠 것이 없다.
|
|
1610
|
+
- **서비스는 키(`service`)로 고른다 — 보이는 것은 아무것도 넘기지 않는다.** 쓸 수 있는 키는 `SLogo` 가 로고를 가진 서비스 전부(`'sellmate' | 'wms' | 'chat' | 'chatbot' | 'plm' | 'account' | 'people' | 'crm' | 'work' | 'pos'` — `SLauncherService`)이고, 서비스 로고(워드마크와 `WMS`·`Account`·`CRM` … 표기가 한 덩어리인 브랜드 로고)·아래 줄 한글 이름·상태 태그(`NEW`·`출시예정`)·출시 여부를 카탈로그(`LAUNCHER_SERVICE_CATALOG`)가 그 키로 정한다. 앱이 넘기게 하면 같은 서비스가 앱마다 다른 이름·색·문구로 선다. **서비스가 늘거나 이름·상태가 바뀌면 디자인 시스템 릴리스로 따라온다** — 앱은 고칠 것이 없다.
|
|
1592
1611
|
|
|
1593
1612
|
```tsx
|
|
1594
1613
|
✅ { service: 'wms', href: env.WMS_URL }
|
|
@@ -1741,6 +1760,11 @@ tableRef.current.scrollToRow(row); // 복원
|
|
|
1741
1760
|
- 카드 안에 상태 배지를 넣으려면 `tag` 슬롯에 `STag` 를 준다. 라벨 문자열에 "(추천)" 처럼 섞어 쓰지 않는다.
|
|
1742
1761
|
- **카드처럼 생겼다고 `SCard`/`SSectionHeaderCard` 로 감싸지 않는다.** `SRadioCard` 자체가 완결된 요소이고, 나열 간격은 `SRadioCardGroup` 의 `direction` 이 맞춘다 (§2-2).
|
|
1743
1762
|
|
|
1763
|
+
##### 옵션을 서버에서 받는 동안 — `loading`
|
|
1764
|
+
|
|
1765
|
+
- **받는 동안 `loading` 을 켠다.** 트리거의 펼침 아이콘 자리에 스피너가, 드롭다운에 로딩 행이 뜬다. 첫 조회든 다음 페이지든 같은 prop 이다.
|
|
1766
|
+
- **셀렉트 옆에 따로 스피너를 두거나 `disabled` 로 잠가 로딩을 표현하지 않는다.** 로딩 중에도 열 수 있고, 열면 "표시할 항목이 없습니다." 대신 로딩 행이 나온다. `disabled` 와 `loading` 을 함께 주지 않는다 — 비활성이면 `loading` 을 무시해 스피너가 뜨지 않는다. 잠긴 셀렉트의 옵션은 잠금이 풀릴 때 받는다.
|
|
1767
|
+
|
|
1744
1768
|
##### 옵션이 수백~수천 개면 — `onReachEnd`
|
|
1745
1769
|
|
|
1746
1770
|
**렌더는 걱정하지 않아도 된다.** `SSelect` 는 언제나 보이는 범위의 행만 그린다 — 켜고 끄는 prop 이 없고, 옵션이 5개든 5,000개든 여는 비용이 같다. 행 높이가 균일하다고 가정하지도 않으므로 계층 목록이나 큰 글씨가 섞인 라벨도 그대로 넘기면 된다.
|
|
@@ -2001,6 +2025,17 @@ const [selectedId, setSelectedId] = useState<string>();
|
|
|
2001
2025
|
|
|
2002
2026
|
`SCircleProgress` 는 `indeterminate` 로 두면 스피너가 된다. **다만 화면이나 영역을 막아야 하는 상황이면 progress 가 아니라 `SLoadingModal`·`SLoadingContainer` 다** (§3-2) — 진행 표시와 입력 차단은 다른 일이고, 막지 않으면 사용자가 로딩 중에 또 누른다.
|
|
2003
2027
|
|
|
2028
|
+
`type` 은 진행 **상태**로 고른다. 색이 아니라 상태가 기준이다.
|
|
2029
|
+
|
|
2030
|
+
| 상태 | `type` |
|
|
2031
|
+
| --- | --- |
|
|
2032
|
+
| 정상 진행 중 | `primary` |
|
|
2033
|
+
| 진행은 계속되지만 제품이 정한 기준에 미달해 주의가 필요하다 | `warning` |
|
|
2034
|
+
| 실패·중단 등 더 이상 정상적으로 진행되지 않는다 | `error` |
|
|
2035
|
+
| 완료 | `complete` |
|
|
2036
|
+
|
|
2037
|
+
**`warning` 과 `error` 는 "아직 진행 중인가" 로 가른다.** 기준 미달이어도 진행이 계속되면 `warning` 이다 — `error` 로 칠하면 멈춘 작업으로 읽힌다. 미달 기준은 디자인 시스템이 정하지 않으므로 제품 코드에서 판정해 `type` 을 넘긴다.
|
|
2038
|
+
|
|
2004
2039
|
#### 3-7-10. SPortal — 직접 쓸 일이 거의 없다
|
|
2005
2040
|
|
|
2006
2041
|
`STooltip`·`SPopover`·`SSelect`·날짜 피커가 내부에서 쓰는 저수준 레이어다. 앵커에 붙여 띄우는 동작이 필요하면 **먼저 §3-3 에서 대응 컴포넌트를 찾는다.** `SPortal` 을 직접 쓰는 것은 그 넷 중 어느 것도 아닌 새로운 부착형 레이어를 만들 때뿐이고, 그때도 모달 안에서 열릴 수 있다면 소속 컨테이너를 맞춰야 한다.
|
|
@@ -2054,7 +2089,7 @@ const [selectedId, setSelectedId] = useState<string>();
|
|
|
2054
2089
|
|
|
2055
2090
|
셀메이트·서비스 로고는 `<svg>` 를 직접 붙이지 않고 `SLogo` 를 쓴다. **`SIcon` 과 같은 손맛(`name`·`size`·`color`)이지만 두 가지가 다르다.**
|
|
2056
2091
|
|
|
2057
|
-
- **`name` 에 서비스를 넣으면 그 서비스 로고가 나온다**(`sellmate` · `wms` · `chat` · `plm` · `account` · `people` · `crm` · `work` · `pos` · `console` — `SLogoName`). [셀메이트 워드마크 + 서비스 표기]가 한 덩어리다. **워드마크 옆에 표기를 앱이 따로 잇지 않는다** — 표기의 서체·자간·색이 원본과 어긋난다.
|
|
2092
|
+
- **`name` 에 서비스를 넣으면 그 서비스 로고가 나온다**(`sellmate` · `wms` · `chat` · `chatbot` · `plm` · `account` · `people` · `crm` · `work` · `pos` · `console` — `SLogoName`). [셀메이트 워드마크 + 서비스 표기]가 한 덩어리다. **워드마크 옆에 표기를 앱이 따로 잇지 않는다** — 표기의 서체·자간·색이 원본과 어긋난다.
|
|
2058
2093
|
- **배경에 맞춰 `mode` 를 준다.** 밝은 면은 `light`(기본), 어두운 면(dark GNB 등)은 `dark`. 워드마크 색과 서비스 표기 색을 모드가 정하므로 색을 직접 주지 않는다.
|
|
2059
2094
|
- **표기를 워드마크 아래에 두려면 `direction="vertical"` 을 준다**(로그인 카드처럼 폭이 좁고 로고가 가운데 서는 자리). 워드마크 아래에 표기를 앱이 따로 쌓지 않는다. 모든 서비스 로고가 세로를 가진다(`sellmate` 는 표기가 없어 어느 쪽이든 같다). 세로의 표기 색도 원본이 정하므로 색을 직접 맞추지 않는다.
|
|
2060
2095
|
- **`size` 는 높이다.** 로고는 가로로 길어 정사각으로 그리면 찌그러진다 — 폭은 비율에서 저절로 나오므로 주지 않는다. `vertical` 에서는 두 줄 전체의 높이라, 같은 `size` 면 워드마크가 가로보다 작다.
|
|
@@ -3325,7 +3360,7 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
3325
3360
|
- brilliantblue: 10 20 25 40 50 60 70 75 80 85 90 95 99 05
|
|
3326
3361
|
- purple: 15 25 45 55 65 70 75 80 85 90 95 99 strong moderate subtle
|
|
3327
3362
|
- bg: screen frame brand subtle accent accentLight accentBright accentFaint deep danger dangerLight warning caution progress success neutralLight neutralBright tableHeader
|
|
3328
|
-
- fg: primary secondary tertiary quaternary brand subtleWarm subtleCool deep success successLight dangerLight accent accentLight danger warning inverse
|
|
3363
|
+
- fg: primary secondary tertiary quaternary brand subtleWarm subtleCool deep success successLight dangerLight accent accentLight danger warning warningLight inverse
|
|
3329
3364
|
- border: default strong bold accent accentLight danger warning success
|
|
3330
3365
|
- disabled: bg text icon border
|
|
3331
3366
|
- link: accent
|
|
@@ -3464,6 +3499,7 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
3464
3499
|
--sys-color-fg-successLight
|
|
3465
3500
|
--sys-color-fg-tertiary
|
|
3466
3501
|
--sys-color-fg-warning
|
|
3502
|
+
--sys-color-fg-warningLight
|
|
3467
3503
|
--sys-color-field-bg-default
|
|
3468
3504
|
--sys-color-field-bg-hover
|
|
3469
3505
|
--sys-color-field-bg-readOnly
|
|
@@ -5030,9 +5066,9 @@ export type SChipInputMetaPlacement = 'end' | 'inline';
|
|
|
5030
5066
|
| Prop | Type | Default | Description |
|
|
5031
5067
|
|------|------|---------|-------------|
|
|
5032
5068
|
| `value?` | `number` | `0` | 진행률 (0–100) |
|
|
5033
|
-
| `type?` | `SCircleProgressType` | `'primary'` |
|
|
5069
|
+
| `type?` | `SCircleProgressType` | `'primary'` | 진행 상태 - `primary`: 정상 진행 중 - `warning`: 진행 중이지만 설정된 기준에 미달해 주의가 필요하다 (기준은 제품이 정한다) - `error`: 실패·중단 등 더 이상 정상적으로 진행되지 않는다 - `complete`: 완료 - `inverse`: 어두운 배경 위에 놓을 때 - `neutral`: 콘텐츠가 아직 없는 자리를 채울 때 (대기) |
|
|
5034
5070
|
| `indeterminate?` | `boolean` | `false` | 불확정(스피너) 모드 — value 무시 |
|
|
5035
|
-
| `size?` | `number \| string` |
|
|
5071
|
+
| `size?` | `number \| string` | — | 링의 지름. 숫자는 px 로 해석한다. 주지 않으면 `--cmp-progress-circular-size` 를 쓴다. 부모 크기에 비례시키려면 컨테이너 쿼리 단위(`'45cqh'`)를 쓴다 — 퍼센트는 부모가 inline-flex 라 기준 폭이 정해지지 않아 해석되지 않는다. |
|
|
5036
5072
|
| `label?` | `string` | — | 하단 레이블 |
|
|
5037
5073
|
| `innerValue?` | `boolean` | `false` | true면 퍼센트를 원 아래가 아닌 원 가운데에 표시 |
|
|
5038
5074
|
| `className?` | `string` | — | |
|
|
@@ -5043,7 +5079,8 @@ export type SChipInputMetaPlacement = 'end' | 'inline';
|
|
|
5043
5079
|
### SCircleProgressType
|
|
5044
5080
|
|
|
5045
5081
|
```ts
|
|
5046
|
-
export type SCircleProgressType =
|
|
5082
|
+
export type SCircleProgressType =
|
|
5083
|
+
'primary' | 'inverse' | 'warning' | 'error' | 'complete' | 'neutral';
|
|
5047
5084
|
```
|
|
5048
5085
|
|
|
5049
5086
|
## Dependencies
|
|
@@ -5053,6 +5090,7 @@ export type SCircleProgressType = 'primary' | 'inverse' | 'error' | 'complete' |
|
|
|
5053
5090
|
- [SImage](../SImage)
|
|
5054
5091
|
- [SLoadingContainer](../SLoadingContainer)
|
|
5055
5092
|
- [SLoadingModal](../SLoadingModal)
|
|
5093
|
+
- [SSelect](../SSelect)
|
|
5056
5094
|
- [STable](../STable)
|
|
5057
5095
|
|
|
5058
5096
|
### Graph
|
|
@@ -7239,7 +7277,7 @@ export interface SLauncherListBoxItem {
|
|
|
7239
7277
|
* 있는지 보이지 않는다). 대신 아래 `LOGO_COVERAGE` 가 로고 목록과 어긋나지 않는지 타입으로 지킨다.
|
|
7240
7278
|
*/
|
|
7241
7279
|
export type SLauncherService =
|
|
7242
|
-
'sellmate' | 'wms' | 'chat' | 'plm' | 'account' | 'people' | 'crm' | 'work' | 'pos';
|
|
7280
|
+
'sellmate' | 'wms' | 'chat' | 'chatbot' | 'plm' | 'account' | 'people' | 'crm' | 'work' | 'pos';
|
|
7243
7281
|
```
|
|
7244
7282
|
|
|
7245
7283
|
## Dependencies
|
|
@@ -7456,7 +7494,7 @@ export interface SLineChartTooltipCategory {
|
|
|
7456
7494
|
| Prop | Type | Default | Description |
|
|
7457
7495
|
|------|------|---------|-------------|
|
|
7458
7496
|
| `value?` | `number` | `0` | 진행률 (0–100) |
|
|
7459
|
-
| `type?` | `SLinearProgressType` | `'primary'` |
|
|
7497
|
+
| `type?` | `SLinearProgressType` | `'primary'` | 진행 상태 - `primary`: 정상 진행 중 - `warning`: 진행 중이지만 설정된 기준에 미달해 주의가 필요하다 (기준은 제품이 정한다) - `error`: 실패·중단 등 더 이상 정상적으로 진행되지 않는다 - `complete`: 완료 |
|
|
7460
7498
|
| `size?` | `SLinearProgressSize` | `'sm'` | 바 높이. xs 는 바 안에 퍼센트 텍스트를 넣지 않는다 |
|
|
7461
7499
|
| `indeterminate?` | `boolean` | `false` | 진행률 없이 무한 애니메이션 |
|
|
7462
7500
|
| `label?` | `string` | — | 하단 레이블 |
|
|
@@ -7468,7 +7506,7 @@ export interface SLineChartTooltipCategory {
|
|
|
7468
7506
|
### SLinearProgressType
|
|
7469
7507
|
|
|
7470
7508
|
```ts
|
|
7471
|
-
export type SLinearProgressType = 'primary' | 'error' | 'complete';
|
|
7509
|
+
export type SLinearProgressType = 'primary' | 'warning' | 'error' | 'complete';
|
|
7472
7510
|
```
|
|
7473
7511
|
|
|
7474
7512
|
### SLinearProgressSize
|
|
@@ -7765,6 +7803,7 @@ export const LOGO_NAMES = [
|
|
|
7765
7803
|
'sellmate',
|
|
7766
7804
|
'wms',
|
|
7767
7805
|
'chat',
|
|
7806
|
+
'chatbot',
|
|
7768
7807
|
'plm',
|
|
7769
7808
|
'account',
|
|
7770
7809
|
'people',
|
|
@@ -9143,7 +9182,7 @@ export type SSectionHeaderCardChildren = ReactNode;
|
|
|
9143
9182
|
| `virtualBuffer?` | `number` | `DEFAULT_VIRTUAL_BUFFER` | 화면 위·아래로 더 그려둘 여유 행 수 — 빠르게 스크롤할 때 빈 칸이 보이지 않게 한다. 목록은 **언제나 보이는 범위만 렌더한다**(끄는 prop 은 없다). 그래서 여는 비용이 옵션 수와 무관하고, 이 값만이 DOM 에 남는 행 수를 정한다 — 한 화면 분량 + 앞뒤로 이만큼. |
|
|
9144
9183
|
| `reachEndThreshold?` | `number` | `DEFAULT_REACH_END_THRESHOLD` | 목록 끝에서 이만큼 행이 남았을 때 `onReachEnd` 를 부른다 — 스크롤이 바닥에 닿기 전에 미리 받아 둔다. **한 페이지 크기보다 충분히 작게 잡는다.** 한 페이지가 드롭다운을 채우고도 이 문턱만큼 남기지 못하면, 페이지가 도착하는 족족 다음 페이지를 다시 청하게 된다 — 사용자가 스크롤을 하지 않아도 목록 전체를 받아 오므로 페이징을 한 의미가 사라진다. (한 페이지 50개 · 드롭다운에 20행이 보인다면 남는 것은 30행이므로, 문턱은 그보다 작아야 한다) |
|
|
9145
9184
|
| `hasMore?` | `boolean` | `false` | 더 받아올 페이지가 남았는가. `false` 면 `onReachEnd` 를 더 부르지 않는다 |
|
|
9146
|
-
| `loading?` | `boolean` | `false` |
|
|
9185
|
+
| `loading?` | `boolean` | `false` | 옵션을 받는 중 — 트리거의 펼침 아이콘 자리에 스피너를, 드롭다운 하단에 로딩 행을 낸다. 첫 조회(옵션이 아직 없을 때)든 다음 페이지든 같은 prop 이고, 그동안 `onReachEnd` 를 다시 부르지 않는다. 로딩 중에도 열 수 있다 — 서버 검색·페이징은 열린 채로 로딩을 오가므로, 열기를 막으면 닫았다 다시 연 사용자에게 클릭이 먹지 않는 고장으로 보인다. 받는 중에는 "표시할 항목이 없습니다."를 내지 않는다. 비활성이면 트리거 스피너를 내지 않는다 — 열 수 없는데 스피너가 돌면 "기다리면 열린다"로 읽힌다. |
|
|
9147
9186
|
| `serverSearch?` | `boolean` | `false` | 검색을 서버로 넘긴다 — 내부 필터링을 하지 않고 `options` 를 받은 그대로 보여준다. `onSearchChange` 로 온 검색어에 맞는 목록을 소비 앱이 다시 내려줘야 한다. 켜면 검색바가 옵션 수와 무관하게 항상 나온다 — 검색 결과가 줄었다고 검색바가 사라지면 검색어를 지울 수단이 없어지기 때문이다. |
|
|
9148
9187
|
| `searchDebounce?` | `number` | `DEFAULT_SEARCH_DEBOUNCE` | `serverSearch` 에서 검색어를 서버로 넘기기 전 기다리는 시간(ms) |
|
|
9149
9188
|
| `label?` | `string` | — | |
|
|
@@ -9165,7 +9204,7 @@ export type SSectionHeaderCardChildren = ReactNode;
|
|
|
9165
9204
|
|
|
9166
9205
|
| Event | Type | Description |
|
|
9167
9206
|
|-------|------|-------------|
|
|
9168
|
-
| `onValueChange` | `(value:
|
|
9207
|
+
| `onValueChange` | `(value: TValue) => void` | 값 변경. 기본은 SSelectOption(들)이 오고, `valueAsPrimitive` 면 원시값이 온다 |
|
|
9169
9208
|
| `onOpenChange` | `(open: boolean) => void` | 열림/닫힘 변경 (sdDropDownShow) |
|
|
9170
9209
|
| `onReachEnd` | `() => void` | 목록 끝이 가까워지면 부른다 — 다음 페이지를 받아 `options` 뒤에 이어붙이라는 신호다. `hasMore` 가 `false` 이거나 `loading` 중이면 부르지 않고, 같은 목록 길이로 두 번 부르지 않는다. **`options` 는 갈아끼우지 말고 이어붙인다.** depth 타입이면 이미 있는 그룹의 `children` 에 이어야 한다 — 같은 그룹을 새 항목으로 또 밀어 넣으면 목록에 같은 헤더가 두 번 뜬다. 아직 받지 않은 옵션은 라벨을 알 수 없다. 그래서 이 prop 을 쓰는 화면은 `valueAsPrimitive` 를 켜지 않는 편이 안전하다 — 기본값(옵션 객체)이면 선택값이 라벨을 함께 들고 다녀서, 그 옵션이 목록에서 사라져도 트리거에 이름이 그대로 남는다. |
|
|
9171
9210
|
| `onSearchChange` | `(query: string) => void` | 검색어 변경. `serverSearch` 면 `searchDebounce` 만큼 묶어서 온다 |
|
|
@@ -9179,6 +9218,20 @@ export type SSectionHeaderCardChildren = ReactNode;
|
|
|
9179
9218
|
|
|
9180
9219
|
## Types
|
|
9181
9220
|
|
|
9221
|
+
### SSelectChangeValue
|
|
9222
|
+
|
|
9223
|
+
```ts
|
|
9224
|
+
/**
|
|
9225
|
+
* `onValueChange` 로 오는 값의 기본 타입.
|
|
9226
|
+
*
|
|
9227
|
+
* 실제 모양은 `type`(단일/multi)과 `valueAsPrimitive`(옵션 객체/원시값)의 조합으로 정해져 한 타입으로
|
|
9228
|
+
* 적을 수 없다. 화면이 안다면 `<SSelect<string> valueAsPrimitive … />` 처럼 넘겨 좁힌다 — 넘기지 않은
|
|
9229
|
+
* 기존 코드는 이 기본값으로 예전처럼 동작한다.
|
|
9230
|
+
*/
|
|
9231
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- 값 타입을 넘기지 않은 화면의 의도된 탈출구
|
|
9232
|
+
export type SSelectChangeValue = any;
|
|
9233
|
+
```
|
|
9234
|
+
|
|
9182
9235
|
### SSelectOption
|
|
9183
9236
|
|
|
9184
9237
|
```ts
|
|
@@ -9205,6 +9258,7 @@ export type SSelectType = 'default' | 'multi' | 'default_depth' | 'multi_depth';
|
|
|
9205
9258
|
|
|
9206
9259
|
### Depends on
|
|
9207
9260
|
|
|
9261
|
+
- [SCircleProgress](../SCircleProgress)
|
|
9208
9262
|
- [SField](../SField)
|
|
9209
9263
|
- [SIcon](../SIcon)
|
|
9210
9264
|
- [SPortal](../SPortal)
|
|
@@ -9610,15 +9664,15 @@ export interface SSystemActionButtonMenuOption {
|
|
|
9610
9664
|
|
|
9611
9665
|
| Prop | Type | Default | Description |
|
|
9612
9666
|
|------|------|---------|-------------|
|
|
9613
|
-
| `columns?` | `STableColumn[]` | `[]` | |
|
|
9667
|
+
| `columns?` | `STableColumn<TRow>[]` | `[]` | |
|
|
9614
9668
|
| `headerGroups?` | `STableHeaderGroup[]` | — | 상위 그룹 헤더. 지정하면 기존 컬럼 헤더가 두 번째 행에 렌더링된다. |
|
|
9615
|
-
| `rows?` | `
|
|
9669
|
+
| `rows?` | `TRow[]` | `[]` | |
|
|
9616
9670
|
| `rowKey?` | `string` | `'id'` | 행 식별 필드 |
|
|
9617
9671
|
| `selectable?` | `boolean` | `false` | 행 선택 체크박스 |
|
|
9618
9672
|
| `dragSelectable?` | `boolean` | `false` | 행을 드래그해서 고른다. 기본 `false`. 고르는 수단만 다를 뿐 `selectable` 과 같은 선택이다 — `selected` · `onSelectedChange` · `isRowSelectable` 을 그대로 쓴다. 체크박스 열은 생기지 않고, 대신 고른 구간이 배경색과 바깥 테두리로 표시된다. **`selectable` 과 함께 켜면 `selectable` 이 이긴다** — 체크박스가 있는 표에서 드래그까지 걸리면 글자를 긁으려던 손이 선택을 갈아치운다. 둘 다 필요해 보이면 체크박스 쪽만 남긴다. 누른 자리가 구간의 기준점이고, 끌어간 자리까지가 구간이다. **새로 끌면 이전 선택은 풀린다** — 기존 선택에 더하려면 `Ctrl`(macOS 는 `Cmd`)을 짚고 끈다. 잠긴 행(`isRowSelectable`)은 구간 안에 있어도 그냥 지나간다. **켜 두면 셀 안의 글자를 긁어 복사할 수 없다** — 끌기가 곧 선택이라 글자 선택과 같은 손짓을 두고 다툰다. 값을 복사해 가는 표에는 켜지 않는다. 셀 안의 버튼·입력은 그대로 눌린다. |
|
|
9619
9673
|
| `contextMenuItems?` | `STableContextMenuItem[]` | — | 드래그 선택에서 **행을 오른쪽 클릭했을 때** 커서 자리에 뜨는 메뉴의 항목. 주지 않거나 비면 메뉴가 뜨지 않고 브라우저 기본 메뉴가 나온다. **`dragSelectable` 에서만 동작한다** — 체크박스 모드에서는 무시된다. 고르지 않은 행에서 누르면 **그 행만 고른 뒤** 열린다. 메뉴가 다룰 대상과 화면에 칠해진 것이 어긋나지 않게 하기 위함이다. 잠긴 행(`isRowSelectable`) 위에서는 열리지 않는다 — 고를 수 없는 행을 대상으로 삼을 수 없기 때문이다. |
|
|
9620
|
-
| `isRowSelectable?` | `(row:
|
|
9621
|
-
| `selected?` | `
|
|
9674
|
+
| `isRowSelectable?` | `(row: TRow) => boolean` | — | 이 행을 고를 수 있는가. 주지 않으면 모든 행을 고를 수 있다. 고를 수 없는 행은 체크박스가 잠기고, **전체 선택·Shift 구간 선택의 셈에서도 빠진다** — 잠긴 행이 셈에 남으면 "전부 선택됨"에 닿지 못해 헤더 체크박스가 해제 방향으로 가지 못한다. 목록에서 지워 버리는 것과 다르다. 실패 0건인 차수처럼 **자리는 보여야 하지만 대상이 될 수는 없는 행**에 쓴다. 아예 대상이 아니라면 `rows` 에서 거르는 편이 낫다. 잠금은 그리기 시점의 판정일 뿐이라, 이미 `selected` 에 든 행이 나중에 잠겨도 DS 가 빼지 않는다(제어 상태를 말없이 바꾸지 않는다). 다만 헤더의 전체 해제로는 걷어낼 수 있다. |
|
|
9675
|
+
| `selected?` | `TRow[]` | `[]` | |
|
|
9622
9676
|
| `sort?` | `STableSort \| null` | `null` | 정렬 상태 (controlled). `null`·미지정이면 정렬 없음. 컴포넌트는 정렬 상태를 갖지 않는다 — 서버 정렬이면 이 값이 곧 조회 조건이고, 뒤로가기·새로고침·링크 공유로 복원돼야 하므로 진실은 URL·store 쪽에 있어야 한다. 행을 실제로 정렬하는 것도 소비 앱 몫이다 (`STable` 은 받은 순서대로 그린다). |
|
|
9623
9677
|
| `resizable?` | `boolean` | `false` | 컬럼 너비 조절 |
|
|
9624
9678
|
| `width?` | `string` | — | |
|
|
@@ -9647,8 +9701,8 @@ export interface SSystemActionButtonMenuOption {
|
|
|
9647
9701
|
|
|
9648
9702
|
| Event | Type | Description |
|
|
9649
9703
|
|-------|------|-------------|
|
|
9650
|
-
| `onContextMenuItemClick` | `(value: string \| number, rows:
|
|
9651
|
-
| `onSelectedChange` | `(rows:
|
|
9704
|
+
| `onContextMenuItemClick` | `(value: string \| number, rows: TRow[]) => void` | 메뉴 항목을 골랐을 때. 고른 항목의 `value` 와 **그 메뉴가 다루는 행들**이 함께 온다. 행 목록은 메뉴가 열린 시점에 확정되므로 `selected` 를 따로 읽지 않아도 된다 — 오른쪽 클릭이 선택을 바꾸는 경우(고르지 않은 행)에도 바뀐 뒤의 행이 온다. |
|
|
9705
|
+
| `onSelectedChange` | `(rows: TRow[]) => void` | |
|
|
9652
9706
|
| `onSortChange` | `(sort: STableSort \| null) => void` | 정렬 헤더 클릭 (`asc → desc → 해제` 3단). 해제되면 `null` 이 온다. 다중 정렬은 1차 안에서 지원하지 않는다. |
|
|
9653
9707
|
| `onDenseChange` | `(dense: boolean) => void` | 밀도 변경 (하단 바의 밀도 토글을 눌렀을 때). 표시는 테이블이 알아서 바꾸므로 받지 않아도 되고, 사용자가 고른 밀도를 다음 방문까지 기억해 두려는(로컬 저장 등) 페이지만 받으면 된다. |
|
|
9654
9708
|
| `onPageChange` | `(page: number) => void` | |
|
|
@@ -9656,25 +9710,39 @@ export interface SSystemActionButtonMenuOption {
|
|
|
9656
9710
|
| `onScroll` | `(event: ReactUIEvent<HTMLDivElement>) => void` | 본문이 스크롤될 때. **실제 스크롤에만 울린다** — 행 수·컬럼 폭·로딩이 바뀌어 내부에서 위치를 다시 잴 때는 오지 않는다. `onVirtualUpdate` 는 그리는 창이 바뀔 때만 오므로 그 사이 스크롤을 알 수 없다. 스크롤 위치를 읽어 두었다 되돌리는 것처럼 **위치 자체가 필요하면** 이쪽을 쓴다. |
|
|
9657
9711
|
| `onVirtualUpdate` | `(range: { from: number; to: number }) => void` | |
|
|
9658
9712
|
| `onVirtualReachEnd` | `(info: { from: number; to: number; rowCount: number; threshold: number; }) =>…` | |
|
|
9659
|
-
| `onRowClick` | `(row:
|
|
9713
|
+
| `onRowClick` | `(row: TRow) => void` | 행 클릭 |
|
|
9660
9714
|
|
|
9661
9715
|
#### Methods (ref)
|
|
9662
9716
|
|
|
9663
9717
|
| Method | Type | Description |
|
|
9664
9718
|
|--------|------|-------------|
|
|
9665
|
-
| `isRowSelected` | `(row:
|
|
9666
|
-
| `getIsAllChecked` | `(rows:
|
|
9719
|
+
| `isRowSelected` | `(row: TRow) => boolean` | 행 선택 여부 |
|
|
9720
|
+
| `getIsAllChecked` | `(rows: TRow[]) => boolean \| null` | 주어진 행들의 전체 선택 상태 (true/false/null=indeterminate) |
|
|
9667
9721
|
| `getPaginationInfo` | `<union 2종>` | 내부 페이지네이션 정보 (internalPagination 아니면 null) |
|
|
9668
9722
|
| `getStickyStyle` | `(colIdx: number) => CSSProperties` | 컬럼 sticky 스타일 (CSS 변수 + 고정폭) |
|
|
9669
9723
|
| `getScrollElement` | `() => HTMLDivElement \| null` | 본문의 세로 스크롤 컨테이너. 본문이 슬롯으로 대체됐거나 아직 안 그려졌으면 `null`. 스크롤 위치를 읽고 되돌리거나, 표 안에서 스크롤 여지를 재야 할 때 쓴다 — **DOM 을 뒤져 컨테이너를 찾지 않는다.** 내부 구조는 예고 없이 바뀐다. ⚠️ `virtualScroll` 에서 **위치를 픽셀로 저장했다 되돌리는 것은 미덥지 않다.** 다시 그릴 때 아직 안 그려진 행은 추정 높이로 잡히므로, 실측이 쌓이기 전의 같은 `scrollTop` 이 다른 행을 가리킨다. 그 경우엔 위치가 아니라 **보고 있던 행**을 기억해 두는 편이 맞다. |
|
|
9670
9724
|
| `getFirstVisibleRow` | `() => number` | 지금 본문 맨 위에 걸린 행의 인덱스 (`rows` 기준). 행이 없으면 `0`. **스크롤 위치를 저장할 때는 픽셀이 아니라 이 값을 저장한다** — `virtualScroll` 에서 픽셀은 좌표계가 실측으로 움직이는 값이라, 되돌릴 때 다른 행을 가리킨다. |
|
|
9671
9725
|
| `scrollToRow` | `(index: number) => void` | 그 행이 본문 맨 위에 오도록 스크롤한다 (`rows` 기준 인덱스, 범위를 벗어나면 잘린다). `virtualScroll` 에서는 **실측이 정착할 때까지 그 행을 계속 다시 겨냥한다** — 옮기는 시점의 좌표는 아직 추정치라 한 번만 옮기면 몇 행씩 밀린다. 사용자가 직접 스크롤하면 겨냥을 놓는다. 페이지네이션을 쓰는 표에서는 **지금 페이지에 그려진 행만** 닿는다 — 다른 페이지의 행이라면 페이지를 먼저 옮긴다. |
|
|
9672
9726
|
| `setRowCount` | `(count: number) => void` | 가상 스크롤 총 행 수 설정 |
|
|
9673
|
-
| `updateRowSelect` | `(row:
|
|
9674
|
-
| `toggleSelectAll` | `(checked: boolean, rows:
|
|
9727
|
+
| `updateRowSelect` | `(row: TRow) => void` | 단일 행 선택 토글 (sd-table updateRowSelect) — onSelectedChange 발생 |
|
|
9728
|
+
| `toggleSelectAll` | `(checked: boolean, rows: TRow[]) => void` | 주어진 행들 전체 선택/해제 (sd-table toggleSelectAll) — onSelectedChange 발생 |
|
|
9675
9729
|
|
|
9676
9730
|
## Types
|
|
9677
9731
|
|
|
9732
|
+
### SRow
|
|
9733
|
+
|
|
9734
|
+
```ts
|
|
9735
|
+
/**
|
|
9736
|
+
* 행 데이터의 기본 타입 — 키만 아는 느슨한 레코드다.
|
|
9737
|
+
*
|
|
9738
|
+
* 행 타입을 알면 `STableColumn<Order>[]` · `<STable<Order> rows={orders} />` 처럼 넘겨 좁힌다.
|
|
9739
|
+
* 그러면 `render`·`format`·`onRowClick` 이 `Order` 를 받는다. 넘기지 않은 기존 코드는 이 기본값으로
|
|
9740
|
+
* 예전처럼 동작한다 — 기본값을 `unknown` 으로 바꾸면 `row.statusLabel` 같은 코드가 전부 깨진다.
|
|
9741
|
+
*/
|
|
9742
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- 행 타입을 넘기지 않은 화면의 의도된 탈출구
|
|
9743
|
+
export type SRow = Record<string, any>;
|
|
9744
|
+
```
|
|
9745
|
+
|
|
9678
9746
|
### STableColumn
|
|
9679
9747
|
|
|
9680
9748
|
```ts
|
|
@@ -9682,9 +9750,10 @@ export interface SSystemActionButtonMenuOption {
|
|
|
9682
9750
|
* `autoWidth` 스페이서 열은 값을 그리지 않으므로 `field` 를 생략할 수 있다.
|
|
9683
9751
|
* 그 밖의 열은 값 접근자가 있어야 한다.
|
|
9684
9752
|
*/
|
|
9685
|
-
export type STableColumn = STableColumnBase &
|
|
9753
|
+
export type STableColumn<TRow extends SRow = SRow> = STableColumnBase<TRow> &
|
|
9686
9754
|
(
|
|
9687
|
-
{ autoWidth: true; field?: STableColumnField }
|
|
9755
|
+
| { autoWidth: true; field?: STableColumnField<TRow> }
|
|
9756
|
+
| { autoWidth?: false; field: STableColumnField<TRow> }
|
|
9688
9757
|
);
|
|
9689
9758
|
```
|
|
9690
9759
|
|
|
@@ -9703,12 +9772,6 @@ export interface STableHeaderGroup {
|
|
|
9703
9772
|
}
|
|
9704
9773
|
```
|
|
9705
9774
|
|
|
9706
|
-
### SRow
|
|
9707
|
-
|
|
9708
|
-
```ts
|
|
9709
|
-
export type SRow = Record<string, any>;
|
|
9710
|
-
```
|
|
9711
|
-
|
|
9712
9775
|
### STableContextMenuItem
|
|
9713
9776
|
|
|
9714
9777
|
```ts
|
|
@@ -9756,7 +9819,7 @@ export interface STablePagination {
|
|
|
9756
9819
|
|
|
9757
9820
|
```ts
|
|
9758
9821
|
/** 값 접근: 필드명 또는 접근 함수 */
|
|
9759
|
-
export type STableColumnField = string | ((row:
|
|
9822
|
+
export type STableColumnField<TRow extends SRow = SRow> = string | ((row: TRow) => unknown);
|
|
9760
9823
|
```
|
|
9761
9824
|
|
|
9762
9825
|
## Dependencies
|
package/dist/llms.txt
CHANGED
|
@@ -1062,6 +1062,25 @@ const columns: STableColumn[] = [
|
|
|
1062
1062
|
- **계산값은 픽셀 단위까지 맞추면 어긋난다** — 서브픽셀 반올림 때문이다. 여유 8px 을 얹고 8 단위로 올림한다.
|
|
1063
1063
|
- 좌우 패딩은 `STable` 이 토큰으로 넣으므로 직접 주지 않는다. 그만큼을 뺀 나머지가 요소 몫이라는 점만 계산에 넣는다.
|
|
1064
1064
|
|
|
1065
|
+
#### 행 타입을 알면 넘긴다
|
|
1066
|
+
|
|
1067
|
+
`STable`·`STableColumn` 은 행 타입을 받는다. 넘기면 `render`·`format`·`onRowClick`·`selected` 가 그 타입으로 좁혀져, 없는 키나 오타가 컴파일에서 잡힌다. 넘기지 않으면 행은 `SRow`(키만 아는 느슨한 레코드)다 — 기존 코드는 그대로 동작한다.
|
|
1068
|
+
|
|
1069
|
+
```tsx
|
|
1070
|
+
interface Order { id: number; orderNo: string; qty: number }
|
|
1071
|
+
|
|
1072
|
+
const columns: STableColumn<Order>[] = [
|
|
1073
|
+
{ name: 'orderNo', label: '주문번호', field: 'orderNo', width: 140, align: 'center' },
|
|
1074
|
+
{ name: 'qty', label: '수량', field: 'qty', width: 80, align: 'right',
|
|
1075
|
+
format: v => `${Number(v).toLocaleString()}개` },
|
|
1076
|
+
];
|
|
1077
|
+
|
|
1078
|
+
<STable<Order> columns={columns} rows={orders} onRowClick={order => open(order.id)} />
|
|
1079
|
+
```
|
|
1080
|
+
|
|
1081
|
+
- 행 타입을 넘기면 `format`·`render` 의 **셀 값은 `unknown`** 이다. `field` 가 접근 함수일 수도 있어 행 타입만으로는 값의 타입이 정해지지 않으므로, `Number(v)`·`String(v)` 로 받는 쪽에서 확인한다. 행이 필요하면 두 번째 인자(`format`)·첫 번째 인자(`render`)의 행을 쓴다.
|
|
1082
|
+
- `SSelect` 도 같은 방식으로 `onValueChange` 의 값 타입을 받는다 — `<SSelect<string> valueAsPrimitive … />`. 실제 모양은 `type`·`valueAsPrimitive` 조합이 정하므로 그 조합에 맞는 타입을 넘긴다.
|
|
1083
|
+
|
|
1065
1084
|
#### 컨트롤이 들어가는 컬럼
|
|
1066
1085
|
|
|
1067
1086
|
`<td>` 는 폭을 넘는 내용을 잘라낸다(`overflow: hidden`). 텍스트라면 말줄임으로 끝나지만, 셀에 `STag` · `SButton` · `SGhostButton` · `SSelect` · `SInput` · `SNumberInput` 처럼 **자기 폭을 가진 요소**를 넣으면 요소 자체가 잘려 **누르거나 읽거나 입력할 수 없게 된다.**
|
|
@@ -1589,7 +1608,7 @@ tableRef.current.scrollToRow(row); // 복원
|
|
|
1589
1608
|
```
|
|
1590
1609
|
|
|
1591
1610
|
- **`launcher` 를 주지 않으면 런처 버튼 자체가 렌더되지 않는다.** 옮겨 갈 서비스가 없는 앱은 이 슬롯을 비워 둔다 — 눌러도 아무것도 없는 버튼을 상단바에 남기지 않는다.
|
|
1592
|
-
- **서비스는 키(`service`)로 고른다 — 보이는 것은 아무것도 넘기지 않는다.** 쓸 수 있는 키는 `SLogo` 가 로고를 가진 서비스 전부(`'sellmate' | 'wms' | 'chat' | 'plm' | 'account' | 'people' | 'crm' | 'work' | 'pos'` — `SLauncherService`)이고, 서비스 로고(워드마크와 `WMS`·`Account`·`CRM` … 표기가 한 덩어리인 브랜드 로고)·아래 줄 한글 이름·상태 태그(`NEW`·`출시예정`)·출시 여부를 카탈로그(`LAUNCHER_SERVICE_CATALOG`)가 그 키로 정한다. 앱이 넘기게 하면 같은 서비스가 앱마다 다른 이름·색·문구로 선다. **서비스가 늘거나 이름·상태가 바뀌면 디자인 시스템 릴리스로 따라온다** — 앱은 고칠 것이 없다.
|
|
1611
|
+
- **서비스는 키(`service`)로 고른다 — 보이는 것은 아무것도 넘기지 않는다.** 쓸 수 있는 키는 `SLogo` 가 로고를 가진 서비스 전부(`'sellmate' | 'wms' | 'chat' | 'chatbot' | 'plm' | 'account' | 'people' | 'crm' | 'work' | 'pos'` — `SLauncherService`)이고, 서비스 로고(워드마크와 `WMS`·`Account`·`CRM` … 표기가 한 덩어리인 브랜드 로고)·아래 줄 한글 이름·상태 태그(`NEW`·`출시예정`)·출시 여부를 카탈로그(`LAUNCHER_SERVICE_CATALOG`)가 그 키로 정한다. 앱이 넘기게 하면 같은 서비스가 앱마다 다른 이름·색·문구로 선다. **서비스가 늘거나 이름·상태가 바뀌면 디자인 시스템 릴리스로 따라온다** — 앱은 고칠 것이 없다.
|
|
1593
1612
|
|
|
1594
1613
|
```tsx
|
|
1595
1614
|
✅ { service: 'wms', href: env.WMS_URL }
|
|
@@ -1742,6 +1761,11 @@ tableRef.current.scrollToRow(row); // 복원
|
|
|
1742
1761
|
- 카드 안에 상태 배지를 넣으려면 `tag` 슬롯에 `STag` 를 준다. 라벨 문자열에 "(추천)" 처럼 섞어 쓰지 않는다.
|
|
1743
1762
|
- **카드처럼 생겼다고 `SCard`/`SSectionHeaderCard` 로 감싸지 않는다.** `SRadioCard` 자체가 완결된 요소이고, 나열 간격은 `SRadioCardGroup` 의 `direction` 이 맞춘다 (§2-2).
|
|
1744
1763
|
|
|
1764
|
+
##### 옵션을 서버에서 받는 동안 — `loading`
|
|
1765
|
+
|
|
1766
|
+
- **받는 동안 `loading` 을 켠다.** 트리거의 펼침 아이콘 자리에 스피너가, 드롭다운에 로딩 행이 뜬다. 첫 조회든 다음 페이지든 같은 prop 이다.
|
|
1767
|
+
- **셀렉트 옆에 따로 스피너를 두거나 `disabled` 로 잠가 로딩을 표현하지 않는다.** 로딩 중에도 열 수 있고, 열면 "표시할 항목이 없습니다." 대신 로딩 행이 나온다. `disabled` 와 `loading` 을 함께 주지 않는다 — 비활성이면 `loading` 을 무시해 스피너가 뜨지 않는다. 잠긴 셀렉트의 옵션은 잠금이 풀릴 때 받는다.
|
|
1768
|
+
|
|
1745
1769
|
##### 옵션이 수백~수천 개면 — `onReachEnd`
|
|
1746
1770
|
|
|
1747
1771
|
**렌더는 걱정하지 않아도 된다.** `SSelect` 는 언제나 보이는 범위의 행만 그린다 — 켜고 끄는 prop 이 없고, 옵션이 5개든 5,000개든 여는 비용이 같다. 행 높이가 균일하다고 가정하지도 않으므로 계층 목록이나 큰 글씨가 섞인 라벨도 그대로 넘기면 된다.
|
|
@@ -2002,6 +2026,17 @@ const [selectedId, setSelectedId] = useState<string>();
|
|
|
2002
2026
|
|
|
2003
2027
|
`SCircleProgress` 는 `indeterminate` 로 두면 스피너가 된다. **다만 화면이나 영역을 막아야 하는 상황이면 progress 가 아니라 `SLoadingModal`·`SLoadingContainer` 다** (§3-2) — 진행 표시와 입력 차단은 다른 일이고, 막지 않으면 사용자가 로딩 중에 또 누른다.
|
|
2004
2028
|
|
|
2029
|
+
`type` 은 진행 **상태**로 고른다. 색이 아니라 상태가 기준이다.
|
|
2030
|
+
|
|
2031
|
+
| 상태 | `type` |
|
|
2032
|
+
| --- | --- |
|
|
2033
|
+
| 정상 진행 중 | `primary` |
|
|
2034
|
+
| 진행은 계속되지만 제품이 정한 기준에 미달해 주의가 필요하다 | `warning` |
|
|
2035
|
+
| 실패·중단 등 더 이상 정상적으로 진행되지 않는다 | `error` |
|
|
2036
|
+
| 완료 | `complete` |
|
|
2037
|
+
|
|
2038
|
+
**`warning` 과 `error` 는 "아직 진행 중인가" 로 가른다.** 기준 미달이어도 진행이 계속되면 `warning` 이다 — `error` 로 칠하면 멈춘 작업으로 읽힌다. 미달 기준은 디자인 시스템이 정하지 않으므로 제품 코드에서 판정해 `type` 을 넘긴다.
|
|
2039
|
+
|
|
2005
2040
|
#### 3-7-10. SPortal — 직접 쓸 일이 거의 없다
|
|
2006
2041
|
|
|
2007
2042
|
`STooltip`·`SPopover`·`SSelect`·날짜 피커가 내부에서 쓰는 저수준 레이어다. 앵커에 붙여 띄우는 동작이 필요하면 **먼저 §3-3 에서 대응 컴포넌트를 찾는다.** `SPortal` 을 직접 쓰는 것은 그 넷 중 어느 것도 아닌 새로운 부착형 레이어를 만들 때뿐이고, 그때도 모달 안에서 열릴 수 있다면 소속 컨테이너를 맞춰야 한다.
|
|
@@ -2055,7 +2090,7 @@ const [selectedId, setSelectedId] = useState<string>();
|
|
|
2055
2090
|
|
|
2056
2091
|
셀메이트·서비스 로고는 `<svg>` 를 직접 붙이지 않고 `SLogo` 를 쓴다. **`SIcon` 과 같은 손맛(`name`·`size`·`color`)이지만 두 가지가 다르다.**
|
|
2057
2092
|
|
|
2058
|
-
- **`name` 에 서비스를 넣으면 그 서비스 로고가 나온다**(`sellmate` · `wms` · `chat` · `plm` · `account` · `people` · `crm` · `work` · `pos` · `console` — `SLogoName`). [셀메이트 워드마크 + 서비스 표기]가 한 덩어리다. **워드마크 옆에 표기를 앱이 따로 잇지 않는다** — 표기의 서체·자간·색이 원본과 어긋난다.
|
|
2093
|
+
- **`name` 에 서비스를 넣으면 그 서비스 로고가 나온다**(`sellmate` · `wms` · `chat` · `chatbot` · `plm` · `account` · `people` · `crm` · `work` · `pos` · `console` — `SLogoName`). [셀메이트 워드마크 + 서비스 표기]가 한 덩어리다. **워드마크 옆에 표기를 앱이 따로 잇지 않는다** — 표기의 서체·자간·색이 원본과 어긋난다.
|
|
2059
2094
|
- **배경에 맞춰 `mode` 를 준다.** 밝은 면은 `light`(기본), 어두운 면(dark GNB 등)은 `dark`. 워드마크 색과 서비스 표기 색을 모드가 정하므로 색을 직접 주지 않는다.
|
|
2060
2095
|
- **표기를 워드마크 아래에 두려면 `direction="vertical"` 을 준다**(로그인 카드처럼 폭이 좁고 로고가 가운데 서는 자리). 워드마크 아래에 표기를 앱이 따로 쌓지 않는다. 모든 서비스 로고가 세로를 가진다(`sellmate` 는 표기가 없어 어느 쪽이든 같다). 세로의 표기 색도 원본이 정하므로 색을 직접 맞추지 않는다.
|
|
2061
2096
|
- **`size` 는 높이다.** 로고는 가로로 길어 정사각으로 그리면 찌그러진다 — 폭은 비율에서 저절로 나오므로 주지 않는다. `vertical` 에서는 두 줄 전체의 높이라, 같은 `size` 면 워드마크가 가로보다 작다.
|
|
@@ -3326,7 +3361,7 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
3326
3361
|
- brilliantblue: 10 20 25 40 50 60 70 75 80 85 90 95 99 05
|
|
3327
3362
|
- purple: 15 25 45 55 65 70 75 80 85 90 95 99 strong moderate subtle
|
|
3328
3363
|
- bg: screen frame brand subtle accent accentLight accentBright accentFaint deep danger dangerLight warning caution progress success neutralLight neutralBright tableHeader
|
|
3329
|
-
- fg: primary secondary tertiary quaternary brand subtleWarm subtleCool deep success successLight dangerLight accent accentLight danger warning inverse
|
|
3364
|
+
- fg: primary secondary tertiary quaternary brand subtleWarm subtleCool deep success successLight dangerLight accent accentLight danger warning warningLight inverse
|
|
3330
3365
|
- border: default strong bold accent accentLight danger warning success
|
|
3331
3366
|
- disabled: bg text icon border
|
|
3332
3367
|
- link: accent
|
|
@@ -3465,6 +3500,7 @@ Tailwind 유틸리티는 아래 스케일에 있는 값만 사용한다. 리터
|
|
|
3465
3500
|
--sys-color-fg-successLight
|
|
3466
3501
|
--sys-color-fg-tertiary
|
|
3467
3502
|
--sys-color-fg-warning
|
|
3503
|
+
--sys-color-fg-warningLight
|
|
3468
3504
|
--sys-color-field-bg-default
|
|
3469
3505
|
--sys-color-field-bg-hover
|
|
3470
3506
|
--sys-color-field-bg-readOnly
|
package/dist/styles.css
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
--color-yellow-50: #EBB110;
|
|
12
12
|
--color-green-50: oklch(98.2% 0.018 155.826);
|
|
13
13
|
--color-green-700: oklch(52.7% 0.154 150.069);
|
|
14
|
+
--color-gray-50: oklch(98.5% 0.002 247.839);
|
|
14
15
|
--color-gray-100: oklch(96.7% 0.003 264.542);
|
|
15
16
|
--color-gray-200: oklch(92.8% 0.006 264.531);
|
|
16
17
|
--color-gray-300: oklch(87.2% 0.01 258.338);
|
|
@@ -211,6 +212,7 @@
|
|
|
211
212
|
--color-fg-accentLight: #64ABFF;
|
|
212
213
|
--color-fg-danger: #E30000;
|
|
213
214
|
--color-fg-warning: #FF6B00;
|
|
215
|
+
--color-fg-warningLight: #FF7F22;
|
|
214
216
|
--color-fg-inverse: #FFFFFF;
|
|
215
217
|
--color-border-default: #E1E1E1;
|
|
216
218
|
--color-border-strong: #AAAAAA;
|
|
@@ -2117,6 +2119,9 @@
|
|
|
2117
2119
|
.gap-\[var\(--cmp-progress-circular-gap\)\] {
|
|
2118
2120
|
gap: var(--cmp-progress-circular-gap);
|
|
2119
2121
|
}
|
|
2122
|
+
.gap-\[var\(--cmp-progress-linear-gap\)\] {
|
|
2123
|
+
gap: var(--cmp-progress-linear-gap);
|
|
2124
|
+
}
|
|
2120
2125
|
.gap-\[var\(--cmp-radio-group-gap-horizontal\)\] {
|
|
2121
2126
|
gap: var(--cmp-radio-group-gap-horizontal);
|
|
2122
2127
|
}
|
|
@@ -2379,6 +2384,9 @@
|
|
|
2379
2384
|
.rounded-\[var\(--cmp-popover-radius\)\] {
|
|
2380
2385
|
border-radius: var(--cmp-popover-radius);
|
|
2381
2386
|
}
|
|
2387
|
+
.rounded-\[var\(--cmp-progress-linear-radius\)\] {
|
|
2388
|
+
border-radius: var(--cmp-progress-linear-radius);
|
|
2389
|
+
}
|
|
2382
2390
|
.rounded-\[var\(--cmp-table-radius\)\] {
|
|
2383
2391
|
border-radius: var(--cmp-table-radius);
|
|
2384
2392
|
}
|
|
@@ -3111,6 +3119,9 @@
|
|
|
3111
3119
|
.bg-disabled-bg {
|
|
3112
3120
|
background-color: var(--color-disabled-bg);
|
|
3113
3121
|
}
|
|
3122
|
+
.bg-gray-50 {
|
|
3123
|
+
background-color: var(--color-gray-50);
|
|
3124
|
+
}
|
|
3114
3125
|
.bg-gray-100 {
|
|
3115
3126
|
background-color: var(--color-gray-100);
|
|
3116
3127
|
}
|
|
@@ -3513,6 +3524,9 @@
|
|
|
3513
3524
|
.py-1 {
|
|
3514
3525
|
padding-block: var(--spacing);
|
|
3515
3526
|
}
|
|
3527
|
+
.py-2 {
|
|
3528
|
+
padding-block: calc(var(--spacing) * 2);
|
|
3529
|
+
}
|
|
3516
3530
|
.py-\[4px\] {
|
|
3517
3531
|
padding-block: 4px;
|
|
3518
3532
|
}
|
|
@@ -7311,6 +7325,7 @@
|
|
|
7311
7325
|
--sys-color-fg-accentLight: #64ABFF;
|
|
7312
7326
|
--sys-color-fg-danger: #E30000;
|
|
7313
7327
|
--sys-color-fg-warning: #FF6B00;
|
|
7328
|
+
--sys-color-fg-warningLight: #FF7F22;
|
|
7314
7329
|
--sys-color-fg-inverse: #FFFFFF;
|
|
7315
7330
|
--sys-color-border-default: #E1E1E1;
|
|
7316
7331
|
--sys-color-border-strong: #AAAAAA;
|
|
@@ -8171,6 +8186,7 @@
|
|
|
8171
8186
|
--cmp-progress-color-active: #2D8DFF;
|
|
8172
8187
|
--cmp-progress-color-success: #12B553;
|
|
8173
8188
|
--cmp-progress-color-error: #FB4444;
|
|
8189
|
+
--cmp-progress-color-warning: #FF7F22;
|
|
8174
8190
|
--cmp-progress-color-neutral: #D8D8D8;
|
|
8175
8191
|
--cmp-progress-bar-text: #FFFFFF;
|
|
8176
8192
|
--cmp-progress-inverse-color: #FFFFFF;
|
package/dist/theme.css
CHANGED
|
@@ -177,6 +177,7 @@
|
|
|
177
177
|
--color-fg-accentLight: #64ABFF;
|
|
178
178
|
--color-fg-danger: #E30000;
|
|
179
179
|
--color-fg-warning: #FF6B00;
|
|
180
|
+
--color-fg-warningLight: #FF7F22;
|
|
180
181
|
--color-fg-inverse: #FFFFFF;
|
|
181
182
|
--color-border-default: #E1E1E1;
|
|
182
183
|
--color-border-strong: #AAAAAA;
|
|
@@ -514,6 +515,7 @@
|
|
|
514
515
|
--sys-color-fg-accentLight: #64ABFF;
|
|
515
516
|
--sys-color-fg-danger: #E30000;
|
|
516
517
|
--sys-color-fg-warning: #FF6B00;
|
|
518
|
+
--sys-color-fg-warningLight: #FF7F22;
|
|
517
519
|
--sys-color-fg-inverse: #FFFFFF;
|
|
518
520
|
--sys-color-border-default: #E1E1E1;
|
|
519
521
|
--sys-color-border-strong: #AAAAAA;
|
|
@@ -1376,6 +1378,7 @@
|
|
|
1376
1378
|
--cmp-progress-color-active: #2D8DFF;
|
|
1377
1379
|
--cmp-progress-color-success: #12B553;
|
|
1378
1380
|
--cmp-progress-color-error: #FB4444;
|
|
1381
|
+
--cmp-progress-color-warning: #FF7F22;
|
|
1379
1382
|
--cmp-progress-color-neutral: #D8D8D8;
|
|
1380
1383
|
--cmp-progress-bar-text: #FFFFFF;
|
|
1381
1384
|
--cmp-progress-inverse-color: #FFFFFF;
|