@vmscloud/moz-ui-grid-vue 0.2.16 → 0.2.18
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/dist/MozGrid.vue.d.ts +0 -1
- package/dist/bridge/VueRenderer.d.ts +2 -1
- package/dist/compat/MozGridVue.vue.d.ts +0 -1
- package/dist/compat/MozPivotVue.vue.d.ts +1 -1
- package/dist/composables/useGridPreset.d.ts +6 -0
- package/dist/index.cjs +2 -2
- package/dist/index.d.ts +3 -1
- package/dist/index.js +62 -16
- package/dist/stories/DataLoader.stories.d.ts +53 -0
- package/dist/stories/ObjectValue.stories.d.ts +34 -0
- package/dist/stories/_helpers/localData.worker.d.ts +1 -0
- package/dist/stories/_helpers/localDataLoader.d.ts +3 -3
- package/dist/stories/_helpers/serverRowsLoader.d.ts +55 -0
- package/dist/stories/_helpers/serverTreeLoader.d.ts +18 -0
- package/dist/style.css +1 -1
- package/dist/types/pivot-props.d.ts +4 -1
- package/dist/types/props.d.ts +6 -7
- package/dist/types/toolbar.d.ts +31 -11
- package/docs/manual/examples.md +131 -2
- package/docs/manual/reference.md +1042 -147
- package/docs/manual/reference_vue.md +259 -48
- package/docs/manual/runtime-contracts.md +23 -17
- package/package.json +3 -2
|
@@ -83,11 +83,14 @@ export interface PivotGridProps {
|
|
|
83
83
|
loading?: boolean;
|
|
84
84
|
/** 빈 상태 설정 — 객체를 넘겨야 EmptyState가 활성화됨 (레거시 ExtendPivotGrid와 동일한 opt-in) */
|
|
85
85
|
emptyState?: {
|
|
86
|
+
/** 빈 상태 안내 사용 (기본 true). false 면 0행·필터 0건이어도 안내를 그리지 않음(헤더만 있는 빈 표) */
|
|
87
|
+
enabled?: boolean;
|
|
86
88
|
/** 커스텀 빈 상태 (customState 슬롯 사용) */
|
|
87
89
|
isCustomState?: boolean;
|
|
88
90
|
headerMsg?: string;
|
|
89
91
|
contentMsg?: string;
|
|
90
|
-
|
|
92
|
+
/** 안내 그림 (기본 true). 'auto' 면 안내 영역 높이가 모자랄 때 그림만 숨기고 문구는 남김(리사이즈 시 재판정) */
|
|
93
|
+
useImg?: boolean | 'auto';
|
|
91
94
|
/** 빈 상태 이미지 강제 지정 (생략 시 totalRowCount/filteredRowCount로 자동 결정) */
|
|
92
95
|
selectImg?: 'Custom' | 'Result' | 'Data' | 'Search';
|
|
93
96
|
};
|
package/dist/types/props.d.ts
CHANGED
|
@@ -72,9 +72,7 @@ export interface MozGridVueCoreProps {
|
|
|
72
72
|
locale?: string;
|
|
73
73
|
/** 데이터 처리 엔진 (기본: 'native') */
|
|
74
74
|
engine?: 'aq' | 'native';
|
|
75
|
-
/**
|
|
76
|
-
useWorker?: boolean;
|
|
77
|
-
/** Worker 데이터 로더 설정 (useWorker: true 시 사용) */
|
|
75
|
+
/** 데이터 로더 설정 — `{ use: DataLoader 클래스 }`(메인 스레드) 또는 `{ worker: () => new Worker(...) }`(Worker) */
|
|
78
76
|
loader?: LoaderConfig;
|
|
79
77
|
/** 키 컬럼 편집 방지 (기본: false) */
|
|
80
78
|
preventKeyEdit?: boolean;
|
|
@@ -114,8 +112,6 @@ export interface MozGridVueProps extends MozGridVueCoreProps {
|
|
|
114
112
|
useToolBoxSetting?: boolean;
|
|
115
113
|
/** 푸터 표시 (기본: true) */
|
|
116
114
|
useExtendFooter?: boolean;
|
|
117
|
-
/** 집계 푸터 표시 (기본: false) */
|
|
118
|
-
useSummaryFooter?: boolean;
|
|
119
115
|
/** 정렬 버튼 표시 (기본: true) */
|
|
120
116
|
useSort?: boolean;
|
|
121
117
|
/** 조건부 스타일 버튼 표시 (기본: true) */
|
|
@@ -128,11 +124,14 @@ export interface MozGridVueProps extends MozGridVueCoreProps {
|
|
|
128
124
|
loading?: boolean;
|
|
129
125
|
/** 빈 상태 설정 — 객체를 넘겨야 EmptyState가 활성화됨 (레거시 ExtendFlexGrid와 동일한 opt-in) */
|
|
130
126
|
emptyState?: {
|
|
127
|
+
/** 빈 상태 안내 사용 (기본 true). false 면 0행·필터 0건이어도 안내를 그리지 않음(헤더만 있는 빈 표) */
|
|
128
|
+
enabled?: boolean;
|
|
131
129
|
/** 커스텀 빈 상태 (customState 슬롯 사용) */
|
|
132
130
|
isCustomState?: boolean;
|
|
133
131
|
headerMsg?: string;
|
|
134
132
|
contentMsg?: string;
|
|
135
|
-
|
|
133
|
+
/** 안내 그림 (기본 true). 'auto' 면 안내 영역 높이가 모자랄 때 그림만 숨기고 문구는 남김(리사이즈 시 재판정) */
|
|
134
|
+
useImg?: boolean | 'auto';
|
|
136
135
|
/** 빈 상태 이미지 강제 지정 (생략 시 totalRowCount/filteredRowCount로 자동 결정) */
|
|
137
136
|
selectImg?: 'Custom' | 'Result' | 'Data' | 'Search';
|
|
138
137
|
};
|
|
@@ -169,7 +168,7 @@ export interface MozGridVueEmits {
|
|
|
169
168
|
(e: 'contextmenu', payload: unknown): void;
|
|
170
169
|
}
|
|
171
170
|
/** Props → PureSheetConfig 변환에 사용하는 키 목록 */
|
|
172
|
-
export declare const CONFIG_PROP_KEYS: readonly ["keyFields", "theme", "rowHeight", "headerHeight", "showHeader", "resizableColumns", "reorderableColumns", "showRowHeader", "rowHeader", "rowSelection", "cellSelection", "cellSelectionImpliesRows", "editable", "tooltipMode", "alternatingRows", "autoFitColumns", "dynamicRowHeights", "reorderableRows", "locale", "engine", "
|
|
171
|
+
export declare const CONFIG_PROP_KEYS: readonly ["keyFields", "theme", "rowHeight", "headerHeight", "showHeader", "resizableColumns", "reorderableColumns", "showRowHeader", "rowHeader", "rowSelection", "cellSelection", "cellSelectionImpliesRows", "editable", "tooltipMode", "alternatingRows", "autoFitColumns", "dynamicRowHeights", "reorderableRows", "locale", "engine", "loader", "preventKeyEdit", "showStatusBar", "showDragHandleColumn", "undoStack", "formatRow", "translations", "rowStyle", "headerStyle", "rowHeightChangeThreshold"];
|
|
173
172
|
/** flat 모드 전용 Props 키 목록 (pivot 모드에서는 제외) */
|
|
174
173
|
export declare const FLAT_ONLY_PROP_KEYS: readonly ["columnGroups", "rowTemplate"];
|
|
175
174
|
/**
|
package/dist/types/toolbar.d.ts
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Grid UI 타입 정의
|
|
3
|
-
*
|
|
4
|
-
* moz-wijmo-grid의 types.d.ts에서 포팅.
|
|
5
|
-
* ComparisonOperator, LogicalOperator, IFilterDef, ISortDef, ICustomCellRuleSet 등
|
|
6
|
-
* Wijmo 비의존 타입은 그대로 재사용합니다.
|
|
7
|
-
*/
|
|
1
|
+
import { FieldDef } from '@vmscloud/moz-ui-grid-core';
|
|
8
2
|
/** 비교 연산자 */
|
|
9
3
|
export type ComparisonOperator = '=' | '≠' | '%' | '!%' | '>' | '≥' | '<' | '≤' | '∅' | '●';
|
|
10
4
|
/** 논리 연산자 */
|
|
@@ -125,6 +119,24 @@ export interface IColumnFormatConfig {
|
|
|
125
119
|
/** columnId → 사용자 헤더 서식 */
|
|
126
120
|
header: Record<string, IColumnHeaderFormat>;
|
|
127
121
|
}
|
|
122
|
+
/**
|
|
123
|
+
* 우클릭한 위치 정보 — 컨텍스트 메뉴 항목의 `hidden`/`disabled` 조건 함수와 `handler` 가 받는다.
|
|
124
|
+
* (wrapper `ContextMenuContext` 와 같은 모양)
|
|
125
|
+
*
|
|
126
|
+
* - `area`: 데이터 셀·피벗 행 머리글·빈 영역 = 'cell', 열 머리글 = 'header'.
|
|
127
|
+
* - 머리글이면 `rowIndex` 는 -1, `cellValue`/`rowData` 는 null.
|
|
128
|
+
* - 피벗 값 셀: `columnId` = '<열 경로>␞<값 필드>', `field` = 값 필드, `cellValue` = 집계값(빈 칸 null).
|
|
129
|
+
*/
|
|
130
|
+
export interface ContextMenuContext {
|
|
131
|
+
area?: 'cell' | 'header';
|
|
132
|
+
rowIndex: number;
|
|
133
|
+
rowId?: string | number;
|
|
134
|
+
columnId: string;
|
|
135
|
+
groupId?: string;
|
|
136
|
+
field: FieldDef | null;
|
|
137
|
+
cellValue: unknown;
|
|
138
|
+
rowData: Record<string, unknown> | null;
|
|
139
|
+
}
|
|
128
140
|
/** 컨텍스트 메뉴 항목 */
|
|
129
141
|
export interface IContextMenuItem {
|
|
130
142
|
/** 메뉴 ID */
|
|
@@ -133,14 +145,20 @@ export interface IContextMenuItem {
|
|
|
133
145
|
label: string;
|
|
134
146
|
/** 아이콘 */
|
|
135
147
|
icon?: string;
|
|
136
|
-
/**
|
|
137
|
-
disabled?: boolean;
|
|
148
|
+
/** 비활성 조건 — boolean 또는 우클릭마다 계산하는 함수. 예외 시 활성(false). */
|
|
149
|
+
disabled?: boolean | ((ctx: ContextMenuContext) => boolean);
|
|
150
|
+
/**
|
|
151
|
+
* 숨김 조건 — boolean 또는 우클릭마다 계산하는 함수. 미지정 = 항상 보임. 예외 시 보임.
|
|
152
|
+
* children 에도 적용되고, 자식이 모두 숨으면 부모도 빠진다.
|
|
153
|
+
* @example hidden: ({ area, cellValue }) => area !== 'cell' || cellValue === 0
|
|
154
|
+
*/
|
|
155
|
+
hidden?: boolean | ((ctx: ContextMenuContext) => boolean);
|
|
138
156
|
/** 구분선 */
|
|
139
157
|
separator?: boolean;
|
|
140
158
|
/** 하위 메뉴 */
|
|
141
159
|
children?: IContextMenuItem[];
|
|
142
|
-
/** 클릭 핸들러 */
|
|
143
|
-
handler?: () => void;
|
|
160
|
+
/** 클릭 핸들러 — 우클릭한 위치(ctx)를 받는다(인자 없는 기존 핸들러도 동작). */
|
|
161
|
+
handler?: (ctx: ContextMenuContext) => void;
|
|
144
162
|
/**
|
|
145
163
|
* 메뉴 표시 순서 (낮을수록 위에 표시)
|
|
146
164
|
*
|
|
@@ -153,6 +171,8 @@ export interface IContextMenuItem {
|
|
|
153
171
|
export interface IContextMenuConfig {
|
|
154
172
|
/** 컬럼 선택 UI 표시 여부 (설정 모달 내) */
|
|
155
173
|
useViewSelectColumn?: boolean;
|
|
174
|
+
/** '첫 번째 열로 보내기' 메뉴 (기본 false, 고정·합성 열·다단 헤더에서는 비활성) */
|
|
175
|
+
useMoveColumnFirst?: boolean;
|
|
156
176
|
/** 고정 컬럼 메뉴 */
|
|
157
177
|
useFixedColumn?: boolean;
|
|
158
178
|
/** 그룹 메뉴 */
|
package/docs/manual/examples.md
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
- [기본 플랫 그리드](#기본-플랫-그리드)
|
|
10
10
|
- [행 식별 정책과 자동 ID](#행-식별-정책과-자동-id)
|
|
11
11
|
- [숫자 제약과 날짜시간 편집기](#숫자-제약과-날짜시간-편집기)
|
|
12
|
+
- [객체 값 열·점 표기 필드 사용 예제](#객체-값-열점-표기-필드-사용-예제)
|
|
12
13
|
- [grid.rows 사용 예제](#gridrows-사용-예제)
|
|
13
14
|
- [grid.columns 사용 예제](#gridcolumns-사용-예제)
|
|
14
15
|
- [grid.changes 사용 예제](#gridchanges-사용-예제)
|
|
@@ -179,6 +180,133 @@ await grid.waitForReady();
|
|
|
179
180
|
|
|
180
181
|
---
|
|
181
182
|
|
|
183
|
+
### 객체 값 열·점 표기 필드 사용 예제
|
|
184
|
+
|
|
185
|
+
서버가 담당자를 객체(`owner: { id, name, team }`)로 주는 경우입니다. 담당자 열은 객체 그대로 두고, 팀은 점 표기 열로, 호칭은 수식 열로 보입니다. 규칙 전체는 [reference.md — 점 표기 필드 경로](reference.md#점-표기-필드-경로-field-ownerteam) · [객체 값 열](reference.md#객체-값-열-datatype-object)을 보세요.
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
import { PureSheet, type FieldDef } from '@vmscloud/moz-ui-grid-core';
|
|
189
|
+
|
|
190
|
+
const users = [
|
|
191
|
+
{ id: 'u1', name: '홍길동', team: 'A' },
|
|
192
|
+
{ id: 'u2', name: '김철수', team: 'B' },
|
|
193
|
+
{ id: 'u3', name: '김철수', team: 'C' }, // 동명이인
|
|
194
|
+
{ id: 'u4', name: '이영희', team: 'D' },
|
|
195
|
+
];
|
|
196
|
+
|
|
197
|
+
/** 아이디 또는 이름으로 찾는다. 동명이인이면 throw, 없으면 undefined. */
|
|
198
|
+
function findUser(text: string) {
|
|
199
|
+
const byId = users.find(u => u.id === text);
|
|
200
|
+
if (byId) return byId;
|
|
201
|
+
const byName = users.filter(u => u.name === text);
|
|
202
|
+
if (byName.length > 1) throw new Error(`동명이인: ${text} — 아이디로 입력하세요`);
|
|
203
|
+
return byName[0];
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const fields: FieldDef[] = [
|
|
207
|
+
{ id: 'no', header: 'No', dataType: 'number' },
|
|
208
|
+
{
|
|
209
|
+
id: 'owner', header: '담당자', dataType: 'object',
|
|
210
|
+
objectKey: 'id', // 묶기·필터·병합·선택 비교 키
|
|
211
|
+
sortValue: 'name', // 이름순 정렬 (생략하면 id 순)
|
|
212
|
+
mask: { type: 'function', formatter: v => (v as { name?: string } | null)?.name ?? '', parser: findUser },
|
|
213
|
+
},
|
|
214
|
+
{ id: 'ownerTeam', header: '팀', field: 'owner.team' }, // 점 표기 = row.owner.team
|
|
215
|
+
{ id: 'label', header: '호칭', dataType: 'string', formula: '[owner.name] & "님"' },
|
|
216
|
+
{ id: 'qty', header: '수량', dataType: 'number' },
|
|
217
|
+
];
|
|
218
|
+
|
|
219
|
+
const shared = { id: 'u1', name: '홍길동', team: 'A' };
|
|
220
|
+
const grid = new PureSheet(container, {
|
|
221
|
+
fields,
|
|
222
|
+
keyFields: ['no'],
|
|
223
|
+
enableReferCache: true,
|
|
224
|
+
editable: true,
|
|
225
|
+
data: [
|
|
226
|
+
{ no: 1, owner: { id: 'u2', name: '김철수', team: 'B' }, qty: 10 },
|
|
227
|
+
{ no: 2, owner: { id: 'u2', name: '김철수', team: 'B' }, qty: 20 }, // 같은 id 의 다른 객체
|
|
228
|
+
{ no: 3, owner: { id: 'u3', name: '김철수', team: 'C' }, qty: 30 }, // 동명이인
|
|
229
|
+
{ no: 4, owner: null, qty: 40 },
|
|
230
|
+
{ no: 5, owner: shared, qty: 50 }, // 5·6행이 같은 객체를 공유
|
|
231
|
+
{ no: 6, owner: shared, qty: 60 },
|
|
232
|
+
],
|
|
233
|
+
});
|
|
234
|
+
await grid.waitForReady();
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
화면에 보이는 값:
|
|
238
|
+
|
|
239
|
+
| no | 담당자 | 팀 | 호칭 |
|
|
240
|
+
|---|---|---|---|
|
|
241
|
+
| 1, 2 | 김철수 | B | 김철수님 |
|
|
242
|
+
| 3 | 김철수 | C | 김철수님 |
|
|
243
|
+
| 4 | (빈 칸) | (빈 칸) | 님 |
|
|
244
|
+
| 5, 6 | 홍길동 | A | 홍길동님 |
|
|
245
|
+
|
|
246
|
+
- 묶기: 같은 설정에 `rowGroupConfig: { columns: ['owner'] }` 를 더하면 1·2행은 객체 참조가 달라도 id 가 같아 그룹 하나(`owner:u2`), 3행(동명이인)은 따로(`owner:u3`)입니다. 배너 라벨은 둘 다 `김철수` 입니다.
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
await grid.rowGroup.collapse('owner:u2'); // 1·2행만 접힌다 — 3행은 그대로 보인다
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
- 값 목록 필터: `김철수` 항목이 둘(u2, u3) 나옵니다. 필터는 키나 객체로 겁니다.
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
await grid.filter([{ id: 'owner', operator: 'in', filterValue: ['u2'], sequence: 0 }]); // 1·2행
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
**텍스트 편집 (`mask.parser`)** — 5행 담당자 칸을 더블클릭하면 `홍길동` 이 입력된 채로 열립니다.
|
|
259
|
+
|
|
260
|
+
| 입력 → Enter | 결과 |
|
|
261
|
+
|---|---|
|
|
262
|
+
| `이영희` | `owner` = `{ id: 'u4', name: '이영희', team: 'D' }` |
|
|
263
|
+
| `u3` | `owner` = u3 객체 |
|
|
264
|
+
| `김철수` | 편집 유지, 오류 문구 `동명이인: 김철수 — 아이디로 입력하세요` (parser 가 throw) |
|
|
265
|
+
| `없는사람` | 편집 유지, 오류 문구 `값을 찾을 수 없습니다` (parser 가 `undefined`) |
|
|
266
|
+
| (빈 글자) | `owner` = `null` |
|
|
267
|
+
| `홍길동` 그대로 | 변경 없음 |
|
|
268
|
+
|
|
269
|
+
**선택 편집** — 목록에서 고르게 하려면 `editor` 에 사용자 객체 배열을 그대로 넘깁니다. 라벨은 위 `formatter`(이름), 현재 값 표시와 "같은 값" 판단은 `objectKey`(id)입니다.
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
const ownerSelect: FieldDef = {
|
|
273
|
+
...fields[1]!,
|
|
274
|
+
editor: { type: 'dropdown', options: users }, // 객체 배열 그대로 (options: EditorOption[] | CellObject[])
|
|
275
|
+
};
|
|
276
|
+
// 목록: 홍길동 / 김철수 / 김철수 / 이영희 — 1행을 열면 2번째(u2)가 선택 표시
|
|
277
|
+
// 같은 u2 를 다시 고르면 변경 없음, 3번째(u3)를 고르면 owner = u3 객체
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
**점 표기 열 쓰기** — 최상위 객체를 복사해 바꾸므로 공유 객체를 쓰는 다른 행은 그대로입니다.
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
await grid.changes.updateCellById(5, 'owner.team', 'Z');
|
|
284
|
+
const { modified } = grid.changes.getAll();
|
|
285
|
+
// 5행: fields ['owner'], data.owner = { id: 'u1', name: '홍길동', team: 'Z' }
|
|
286
|
+
// 같은 객체를 공유하던 6행의 owner.team 은 'A' 그대로
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
**붙여넣기** — 1행 담당자 칸에서 담당자·수량 두 칸에 `이영희\t11\n없는사람\t22` 를 붙이면 1행은 담당자·수량이 모두 바뀌고, 2행은 담당자를 건너뛰고(parser 실패) 수량만 바뀝니다.
|
|
290
|
+
|
|
291
|
+
> Worker 모드(`loader: { worker }`)에서는 `objectKey`·`sortValue` 를 경로 문자열(`'id'`, `'name'`)이나 등록 함수 참조로 쓰세요. 인라인 함수는 오류입니다.
|
|
292
|
+
|
|
293
|
+
**JSON 편집기 (래퍼 `jsonEditor`)** — 정해진 모양이 없는 객체(설정·속성 등)는 JSON 으로 고칩니다. Vue/React `MozGrid` 는 `editor`·`mask.parser`·`displayKey` 가 없는 편집 가능한 객체 열에 자동으로 붙이고, 코어를 직접 쓸 때는 wrapper 에서 가져와 지정합니다.
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
import { jsonEditor } from '@vmscloud/moz-ui-grid-wrapper'; // Vue/React 는 각 패키지에서 import
|
|
297
|
+
|
|
298
|
+
const fields: FieldDef[] = [
|
|
299
|
+
{ id: 'no', header: 'No', dataType: 'number' },
|
|
300
|
+
{ id: 'meta', header: '속성', dataType: 'object', editor: jsonEditor() },
|
|
301
|
+
];
|
|
302
|
+
// meta = { color: 'red', tags: ['a'] } 인 칸을 더블클릭 → 줄을 나눈 JSON 이 팝업으로 열린다
|
|
303
|
+
// '{"color": "blue"}' 로 고치고 Ctrl+Enter → meta = { color: 'blue' }
|
|
304
|
+
// 내용을 모두 지우고 Ctrl+Enter → meta = null
|
|
305
|
+
// '[1, 2]' 처럼 객체가 아니면 [적용]이 꺼지고, 창 밖을 누르면 원래 값 그대로
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
182
310
|
### grid.rows 사용 예제
|
|
183
311
|
|
|
184
312
|
```typescript
|
|
@@ -386,8 +514,9 @@ grid.on('column:reset', ({ columnId }) => {
|
|
|
386
514
|
|
|
387
515
|
```typescript
|
|
388
516
|
// ── CRUD (Dirty State) ──
|
|
389
|
-
const newId = await grid.changes.addRow({ name: '신규', age: 0 });
|
|
390
|
-
await grid.changes.
|
|
517
|
+
const newId = await grid.changes.addRow({ name: '신규', age: 0 }); // 맨 끝
|
|
518
|
+
await grid.changes.addRowByIndex({ name: '삽입', age: 25 }, 3); // 원본 2번 행 바로 뒤
|
|
519
|
+
await grid.changes.addRowAfterSelection({ name: '' }, { startEdit: true }); // 선택 행 아래 + 편집 시작
|
|
391
520
|
|
|
392
521
|
// ById 패턴 (rowId 기반)
|
|
393
522
|
await grid.changes.updateCellById('row-1', 'name', '수정됨');
|