sellmate-design-system-react 9.0.0-beta.56 → 9.0.0-beta.57

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
@@ -1092,11 +1092,57 @@ const columns: STableColumn[] = [
1092
1092
  <STable selectable isRowSelectable={row => row.failedCount > 0} … />
1093
1093
  ```
1094
1094
 
1095
+ - **잠긴 행은 회색으로 가라앉는다.** 배경과 글자색이 `--cmp-table-body-disabled-*` 로 바뀌고 hover 도 꺼진다. `selectable` · `dragSelectable` 어느 쪽이든 같다. **그 회색을 직접 그리지 않는다** — `tdClass` 로 따로 칠하면 토큰이 바뀔 때 그 열만 어긋난 채 남는다.
1096
+ - **셀이 자기 색을 정한 내용까지는 닿지 않는다.** `column.render` 안의 `STag` · `SIcon` 처럼 색을 직접 받는 것은 그대로 선명하게 남는다. 잠긴 행에서 그것도 가라앉혀야 하면 `render` 에서 행 상태를 보고 정한다.
1095
1097
  - **체크박스를 직접 잠그려 하지 않는다.** 색·커서·hover 가 한 벌로 움직이므로 밖에서 속성만 바꾸면 "잠기지 않았는데 멀쩡해 보이는" 상태가 된다. 이 prop 하나로 셋이 함께 잡힌다.
1096
1098
  - **전체 선택과 Shift 구간 선택의 셈에서도 빠진다.** 잠긴 행이 셈에 남으면 "전부 선택됨"에 닿지 못해 헤더 체크박스가 해제 방향으로 못 가고 한 방향이 된다. DS 가 이걸 처리하므로 직접 보정하지 않는다.
1097
1099
  - **아예 대상이 아닌 행이라면 `rows` 에서 거르는 편이 낫다.** 잠긴 행이 잔뜩 섞이면 무엇을 고를 수 있는지가 오히려 안 읽힌다. 고를 수 있는 행이 한 줄도 없으면 헤더까지 잠기는데, 그 상태라면 `selectable` 을 켤 자리가 아니다.
1098
1100
  - 잠금은 그리는 시점의 판정이라, **이미 `selected` 에 든 행이 나중에 잠겨도 DS 가 빼지 않는다** — 제어 상태를 말없이 바꾸지 않기 때문이다. 헤더의 전체 해제로는 걷힌다.
1099
1101
 
1102
+ #### 체크박스 대신 드래그로 고르게 하려면 `dragSelectable`
1103
+
1104
+ 고르는 **수단만** 다른 같은 선택이다. `selected` · `onSelectedChange` · `isRowSelectable` 을 그대로 쓰고, 체크박스 열 대신 고른 구간이 배경색과 바깥 테두리로 표시된다.
1105
+
1106
+ ```tsx
1107
+ <STable dragSelectable selected={selected} onSelectedChange={setSelected} … />
1108
+ ```
1109
+
1110
+ | | `selectable` | `dragSelectable` |
1111
+ | --- | --- | --- |
1112
+ | 고르는 법 | 체크박스 클릭 · Shift 구간 | 행을 눌러 끌기 |
1113
+ | 흩어진 행 모으기 | 하나씩 체크 | `Ctrl`(macOS `Cmd`)을 짚고 끌기 |
1114
+ | 선택 열 | 생긴다 (48px, 왼쪽 고정) | 없다 |
1115
+ | 헤더 전체 선택 | 있다 | 없다 |
1116
+ | 셀 글자 복사 | 된다 | **안 된다** |
1117
+
1118
+ - **둘 다 켜면 `selectable` 이 이긴다.** 체크박스가 있는 표에서 드래그까지 걸리면 글자를 긁으려던 손이 선택을 통째로 갈아치운다. 어느 쪽이 맞는지 정해서 하나만 켠다.
1119
+ - **값을 복사해 가는 표에는 켜지 않는다.** 끌기가 곧 선택이라 글자 선택과 같은 손짓을 두고 다투고, 그래서 셀 안의 텍스트를 긁을 수 없다. 주문번호·송장번호처럼 복사해 쓰는 열이 있으면 `selectable` 이다.
1120
+ - **새로 끌면 이전 선택은 풀린다.** 여러 구간을 모으려면 `Ctrl`/`Cmd` 를 짚고 끈다. 이 규칙을 화면에 안내할 자리가 없으면, 흩어진 행을 자주 고르는 표에는 맞지 않는다.
1121
+ - **한 번에 이어진 구간을 고르는 표에 쓴다.** 목록에서 연속한 기간·회차를 통째로 집어 처리하는 화면이 제자리다.
1122
+ - 셀 안의 버튼·입력·링크는 그대로 눌린다 — 거기서 시작한 손짓은 드래그로 세지 않는다. 그래도 컨트롤이 빽빽한 표라면 끌 여백이 없어 잘 맞지 않는다.
1123
+ - 잠긴 행(`isRowSelectable`)은 구간 안에 있어도 그냥 지나간다 — 체크박스 쪽과 같은 규칙이다.
1124
+
1125
+ ##### 고른 행에 바로 할 일을 붙이려면 `contextMenuItems`
1126
+
1127
+ 행을 **오른쪽 클릭**하면 커서 자리에 메뉴가 뜬다. 고른 항목의 `value` 와 **그 메뉴가 다루는 행들**이 함께 온다.
1128
+
1129
+ ```tsx
1130
+ <STable
1131
+ dragSelectable
1132
+ contextMenuItems={[
1133
+ { value: 'export', label: '내보내기', icon: 'download' },
1134
+ { value: 'delete', label: '삭제', icon: 'remove' },
1135
+ ]}
1136
+ onContextMenuItemClick={(value, rows) => run(value, rows)}
1137
+ … />
1138
+ ```
1139
+
1140
+ - **`dragSelectable` 전용이다.** 체크박스 모드에 주면 무시된다. 체크박스 표에서 일괄 작업을 붙이는 자리는 표 위의 `STableBar` 다 (§4 페이지 레시피).
1141
+ - **행 목록을 인자로 받는다. `selected` 를 따로 읽지 않는다.** 오른쪽 클릭이 선택을 바꾸는 경우가 있어서, 그때 앱이 든 `selected` 는 아직 이전 값일 수 있다.
1142
+ - **고르지 않은 행에서 누르면 그 행만 골라진 뒤 열린다.** 메뉴가 다룰 대상과 화면에 칠해진 것이 어긋나지 않게 하기 위함이다. 잠긴 행 위에서는 열리지 않고 브라우저 기본 메뉴가 그대로 나온다.
1143
+ - **오른쪽 클릭에만 있는 기능을 두지 않는다.** 뜨는 것을 모르면 닿을 수 없고, 키보드로도 열 수 없다. 여기 넣는 것은 표 위 버튼이나 행 안 메뉴에도 있는 **지름길**이어야 한다.
1144
+ - 항목을 주지 않으면 오른쪽 클릭을 가로채지 않는다 — 브라우저 기본 메뉴가 그대로 뜬다.
1145
+
1100
1146
  #### 헤더 전체 선택이 집는 범위
1101
1147
 
1102
1148
  **모드가 정한다. 앱이 보정하지 않는다.**
@@ -13,6 +13,8 @@
13
13
  | `rows?` | `SRow[]` | `[]` | |
14
14
  | `rowKey?` | `string` | `'id'` | 행 식별 필드 |
15
15
  | `selectable?` | `boolean` | `false` | 행 선택 체크박스 |
16
+ | `dragSelectable?` | `boolean` | `false` | 행을 드래그해서 고른다. 기본 `false`. 고르는 수단만 다를 뿐 `selectable` 과 같은 선택이다 — `selected` · `onSelectedChange` · `isRowSelectable` 을 그대로 쓴다. 체크박스 열은 생기지 않고, 대신 고른 구간이 배경색과 바깥 테두리로 표시된다. **`selectable` 과 함께 켜면 `selectable` 이 이긴다** — 체크박스가 있는 표에서 드래그까지 걸리면 글자를 긁으려던 손이 선택을 갈아치운다. 둘 다 필요해 보이면 체크박스 쪽만 남긴다. 누른 자리가 구간의 기준점이고, 끌어간 자리까지가 구간이다. **새로 끌면 이전 선택은 풀린다** — 기존 선택에 더하려면 `Ctrl`(macOS 는 `Cmd`)을 짚고 끈다. 잠긴 행(`isRowSelectable`)은 구간 안에 있어도 그냥 지나간다. **켜 두면 셀 안의 글자를 긁어 복사할 수 없다** — 끌기가 곧 선택이라 글자 선택과 같은 손짓을 두고 다툰다. 값을 복사해 가는 표에는 켜지 않는다. 셀 안의 버튼·입력은 그대로 눌린다. |
17
+ | `contextMenuItems?` | `STableContextMenuItem[]` | — | 드래그 선택에서 **행을 오른쪽 클릭했을 때** 커서 자리에 뜨는 메뉴의 항목. 주지 않거나 비면 메뉴가 뜨지 않고 브라우저 기본 메뉴가 나온다. **`dragSelectable` 에서만 동작한다** — 체크박스 모드에서는 무시된다. 고르지 않은 행에서 누르면 **그 행만 고른 뒤** 열린다. 메뉴가 다룰 대상과 화면에 칠해진 것이 어긋나지 않게 하기 위함이다. 잠긴 행(`isRowSelectable`) 위에서는 열리지 않는다 — 고를 수 없는 행을 대상으로 삼을 수 없기 때문이다. |
16
18
  | `isRowSelectable?` | `(row: SRow) => boolean` | — | 이 행을 고를 수 있는가. 주지 않으면 모든 행을 고를 수 있다. 고를 수 없는 행은 체크박스가 잠기고, **전체 선택·Shift 구간 선택의 셈에서도 빠진다** — 잠긴 행이 셈에 남으면 "전부 선택됨"에 닿지 못해 헤더 체크박스가 해제 방향으로 가지 못한다. 목록에서 지워 버리는 것과 다르다. 실패 0건인 차수처럼 **자리는 보여야 하지만 대상이 될 수는 없는 행**에 쓴다. 아예 대상이 아니라면 `rows` 에서 거르는 편이 낫다. 잠금은 그리기 시점의 판정일 뿐이라, 이미 `selected` 에 든 행이 나중에 잠겨도 DS 가 빼지 않는다(제어 상태를 말없이 바꾸지 않는다). 다만 헤더의 전체 해제로는 걷어낼 수 있다. |
17
19
  | `selected?` | `SRow[]` | `[]` | |
18
20
  | `sort?` | `STableSort \| null` | `null` | 정렬 상태 (controlled). `null`·미지정이면 정렬 없음. 컴포넌트는 정렬 상태를 갖지 않는다 — 서버 정렬이면 이 값이 곧 조회 조건이고, 뒤로가기·새로고침·링크 공유로 복원돼야 하므로 진실은 URL·store 쪽에 있어야 한다. 행을 실제로 정렬하는 것도 소비 앱 몫이다 (`STable` 은 받은 순서대로 그린다). |
@@ -43,6 +45,7 @@
43
45
 
44
46
  | Event | Type | Description |
45
47
  |-------|------|-------------|
48
+ | `onContextMenuItemClick` | `(value: string \| number, rows: SRow[]) => void` | 메뉴 항목을 골랐을 때. 고른 항목의 `value` 와 **그 메뉴가 다루는 행들**이 함께 온다. 행 목록은 메뉴가 열린 시점에 확정되므로 `selected` 를 따로 읽지 않아도 된다 — 오른쪽 클릭이 선택을 바꾸는 경우(고르지 않은 행)에도 바뀐 뒤의 행이 온다. |
46
49
  | `onSelectedChange` | `(rows: SRow[]) => void` | |
47
50
  | `onSortChange` | `(sort: STableSort \| null) => void` | 정렬 헤더 클릭 (`asc → desc → 해제` 3단). 해제되면 `null` 이 온다. 다중 정렬은 1차 안에서 지원하지 않는다. |
48
51
  | `onDenseChange` | `(dense: boolean) => void` | 밀도 변경 (하단 바의 밀도 토글을 눌렀을 때). 표시는 테이블이 알아서 바꾸므로 받지 않아도 되고, 사용자가 고른 밀도를 다음 방문까지 기억해 두려는(로컬 저장 등) 페이지만 받으면 된다. |
@@ -104,6 +107,19 @@ export interface STableHeaderGroup {
104
107
  export type SRow = Record<string, any>;
105
108
  ```
106
109
 
110
+ ### STableContextMenuItem
111
+
112
+ ```ts
113
+ /** 오른쪽 클릭 메뉴의 항목 한 줄 */
114
+ export interface STableContextMenuItem {
115
+ /** 클릭 시 `onContextMenuItemClick` 으로 돌아오는 값 */
116
+ value: string | number;
117
+ label: string;
118
+ icon?: SIconName;
119
+ disabled?: boolean;
120
+ }
121
+ ```
122
+
107
123
  ### STableSort
108
124
 
109
125
  ```ts
@@ -1,6 +1,15 @@
1
1
  import { type CSSProperties, type UIEvent as ReactUIEvent, type ReactNode } from 'react';
2
+ import { type SIconName } from '../SIcon';
2
3
  import { type SSelectOption } from '../SSelect';
3
4
  export type SRow = Record<string, any>;
5
+ /** 오른쪽 클릭 메뉴의 항목 한 줄 */
6
+ export interface STableContextMenuItem {
7
+ /** 클릭 시 `onContextMenuItemClick` 으로 돌아오는 값 */
8
+ value: string | number;
9
+ label: string;
10
+ icon?: SIconName;
11
+ disabled?: boolean;
12
+ }
4
13
  /** `STableColumn.renderCell`에 전달되는 컨텍스트 */
5
14
  export interface STableCellContext {
6
15
  row: SRow;
@@ -217,6 +226,42 @@ export interface STableProps {
217
226
  rowKey?: string;
218
227
  /** 행 선택 체크박스 */
219
228
  selectable?: boolean;
229
+ /**
230
+ * 행을 드래그해서 고른다. 기본 `false`.
231
+ *
232
+ * 고르는 수단만 다를 뿐 `selectable` 과 같은 선택이다 — `selected` · `onSelectedChange` ·
233
+ * `isRowSelectable` 을 그대로 쓴다. 체크박스 열은 생기지 않고, 대신 고른 구간이 배경색과
234
+ * 바깥 테두리로 표시된다.
235
+ *
236
+ * **`selectable` 과 함께 켜면 `selectable` 이 이긴다** — 체크박스가 있는 표에서 드래그까지
237
+ * 걸리면 글자를 긁으려던 손이 선택을 갈아치운다. 둘 다 필요해 보이면 체크박스 쪽만 남긴다.
238
+ *
239
+ * 누른 자리가 구간의 기준점이고, 끌어간 자리까지가 구간이다. **새로 끌면 이전 선택은 풀린다** —
240
+ * 기존 선택에 더하려면 `Ctrl`(macOS 는 `Cmd`)을 짚고 끈다. 잠긴 행(`isRowSelectable`)은
241
+ * 구간 안에 있어도 그냥 지나간다.
242
+ *
243
+ * **켜 두면 셀 안의 글자를 긁어 복사할 수 없다** — 끌기가 곧 선택이라 글자 선택과 같은 손짓을
244
+ * 두고 다툰다. 값을 복사해 가는 표에는 켜지 않는다. 셀 안의 버튼·입력은 그대로 눌린다.
245
+ */
246
+ dragSelectable?: boolean;
247
+ /**
248
+ * 드래그 선택에서 **행을 오른쪽 클릭했을 때** 커서 자리에 뜨는 메뉴의 항목.
249
+ * 주지 않거나 비면 메뉴가 뜨지 않고 브라우저 기본 메뉴가 나온다.
250
+ *
251
+ * **`dragSelectable` 에서만 동작한다** — 체크박스 모드에서는 무시된다.
252
+ *
253
+ * 고르지 않은 행에서 누르면 **그 행만 고른 뒤** 열린다. 메뉴가 다룰 대상과 화면에 칠해진
254
+ * 것이 어긋나지 않게 하기 위함이다. 잠긴 행(`isRowSelectable`) 위에서는 열리지 않는다 —
255
+ * 고를 수 없는 행을 대상으로 삼을 수 없기 때문이다.
256
+ */
257
+ contextMenuItems?: STableContextMenuItem[];
258
+ /**
259
+ * 메뉴 항목을 골랐을 때. 고른 항목의 `value` 와 **그 메뉴가 다루는 행들**이 함께 온다.
260
+ *
261
+ * 행 목록은 메뉴가 열린 시점에 확정되므로 `selected` 를 따로 읽지 않아도 된다 — 오른쪽
262
+ * 클릭이 선택을 바꾸는 경우(고르지 않은 행)에도 바뀐 뒤의 행이 온다.
263
+ */
264
+ onContextMenuItemClick?: (value: string | number, rows: SRow[]) => void;
220
265
  /**
221
266
  * 이 행을 고를 수 있는가. 주지 않으면 모든 행을 고를 수 있다.
222
267
  *
@@ -61,6 +61,7 @@ export const TAG_COLORS = [
61
61
  'blue',
62
62
  'darkblue',
63
63
  'indigo',
64
+ 'purple',
64
65
  ] as const;
65
66
  ```
66
67
 
@@ -1,6 +1,6 @@
1
1
  export declare const TAG_SHAPES: readonly ["square", "pill"];
2
2
  export declare const TAG_SIZES: readonly ["xs", "sm", "md"];
3
- export declare const TAG_COLORS: readonly ["grey", "red", "orange", "yellow", "green", "blue", "darkblue", "indigo"];
3
+ export declare const TAG_COLORS: readonly ["grey", "red", "orange", "yellow", "green", "blue", "darkblue", "indigo", "purple"];
4
4
  export type STagShape = (typeof TAG_SHAPES)[number];
5
5
  export type STagSize = (typeof TAG_SIZES)[number];
6
6
  export type STagColor = (typeof TAG_COLORS)[number];