@makitt.io/mds-mcp-server 0.1.2 → 0.2.0
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/README.md +142 -41
- package/dist/catalog.d.ts +16 -0
- package/dist/catalog.d.ts.map +1 -0
- package/dist/catalog.js +383 -0
- package/dist/catalog.js.map +1 -0
- package/dist/data/catalog.json +42535 -5019
- package/dist/data/playbook/ai-fill.md +62 -49
- package/dist/data/playbook/anti-patterns.md +112 -110
- package/dist/data/playbook/array-input.md +95 -50
- package/dist/data/playbook/async-states.md +71 -61
- package/dist/data/playbook/data-grid.md +177 -80
- package/dist/data/playbook/feedback.md +107 -88
- package/dist/data/playbook/form.md +164 -134
- package/dist/data/playbook/overlay.md +98 -89
- package/dist/data/playbook/page-layout.md +96 -77
- package/dist/data/playbook/responsive-tokens.md +78 -59
- package/dist/data/recipes/admin-list-page.md +86 -0
- package/dist/data/recipes/async-state-section.md +60 -0
- package/dist/data/recipes/dashboard-overview.md +65 -0
- package/dist/data/recipes/detail-drawer.md +69 -0
- package/dist/data/recipes/modal-form.md +67 -0
- package/dist/data/recipes/settings-form-page.md +79 -0
- package/dist/index.d.ts +4 -23
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -339
- package/dist/index.js.map +1 -1
- package/dist/loaders.d.ts +8 -0
- package/dist/loaders.d.ts.map +1 -0
- package/dist/loaders.js +120 -0
- package/dist/loaders.js.map +1 -0
- package/dist/recipes.d.ts +13 -0
- package/dist/recipes.d.ts.map +1 -0
- package/dist/recipes.js +82 -0
- package/dist/recipes.js.map +1 -0
- package/dist/responses.d.ts +8 -0
- package/dist/responses.d.ts.map +1 -0
- package/dist/responses.js +25 -0
- package/dist/responses.js.map +1 -0
- package/dist/text.d.ts +4 -0
- package/dist/text.d.ts.map +1 -0
- package/dist/text.js +20 -0
- package/dist/text.js.map +1 -0
- package/dist/tool-definitions.d.ts +3 -0
- package/dist/tool-definitions.d.ts.map +1 -0
- package/dist/tool-definitions.js +199 -0
- package/dist/tool-definitions.js.map +1 -0
- package/dist/tools.d.ts +4 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +233 -0
- package/dist/tools.js.map +1 -0
- package/dist/types.d.ts +107 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +10 -0
- package/dist/types.js.map +1 -0
- package/package.json +20 -16
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
# MDS Playbook — DataGrid + Table
|
|
2
2
|
|
|
3
|
-
list / table 데이터
|
|
3
|
+
list / table 데이터 표시에 대한 약속.
|
|
4
4
|
|
|
5
|
-
> mds 의 list 컴포넌트 = **DataGrid** (full-feature) + **Table** (custom
|
|
6
|
-
> 99% list 페이지 = DataGrid. Table 은 특수 case.
|
|
5
|
+
> mds 의 list 컴포넌트 = **DataGrid** (full-feature) + **Table** (custom
|
|
6
|
+
> column). 99% list 페이지 = DataGrid. Table 은 특수 case.
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
## 1. DataGrid vs Table 선택
|
|
11
11
|
|
|
12
|
-
| 케이스
|
|
13
|
-
|
|
14
|
-
| 셀러 admin 의 list 페이지 (customers / orders / products) | **DataGrid**
|
|
15
|
-
| URL state 동기화 필요 (deep link)
|
|
16
|
-
| Server-side filter/sort
|
|
17
|
-
| 단순 정보 표시 (정적 list, 보통 5-10 row)
|
|
18
|
-
| 페이지의 한 섹션 안 작은 list (예: 고객의 최근 주문 3개)
|
|
19
|
-
| 매우 custom layout (column 별 다른 component)
|
|
20
|
-
| Tree / nested rows
|
|
12
|
+
| 케이스 | 답 | 이유 |
|
|
13
|
+
| --------------------------------------------------------- | --------------------------------- | -------------------------------------------------------------------------------------------- |
|
|
14
|
+
| 셀러 admin 의 list 페이지 (customers / orders / products) | **DataGrid** | filter / search / sort / pagination / selection / bulk action / loading / empty / error 자동 |
|
|
15
|
+
| URL state 동기화 필요 (deep link) | **DataGrid** | controlled props (page / sort / filter / search) 모두 노출 |
|
|
16
|
+
| Server-side filter/sort | **DataGrid** `mode="server"` | totalItems / onPageChange / onSortChange caller 처리 |
|
|
17
|
+
| 단순 정보 표시 (정적 list, 보통 5-10 row) | **Table** | DataGrid 의 toolbar 과잉 |
|
|
18
|
+
| 페이지의 한 섹션 안 작은 list (예: 고객의 최근 주문 3개) | **Table** | 페이지의 다른 컨텍스트와 통합 |
|
|
19
|
+
| 매우 custom layout (column 별 다른 component) | **Table** + caller column render |
|
|
20
|
+
| Tree / nested rows | **Tree** (compounds) — Table 아님 |
|
|
21
21
|
|
|
22
22
|
> **Default = DataGrid**. Table 은 의식적 결정.
|
|
23
23
|
|
|
@@ -34,7 +34,12 @@ list / table 데이터 표시 의 약속.
|
|
|
34
34
|
title="주문"
|
|
35
35
|
filters={[
|
|
36
36
|
{ value: 'all', label: '전체', count: 248, predicate: () => true },
|
|
37
|
-
{
|
|
37
|
+
{
|
|
38
|
+
value: 'pending',
|
|
39
|
+
label: '결제 대기',
|
|
40
|
+
count: 12,
|
|
41
|
+
predicate: (o) => o.status === 'pending',
|
|
42
|
+
},
|
|
38
43
|
]}
|
|
39
44
|
defaultFilter="all"
|
|
40
45
|
searchableKeys={['id', 'customer']}
|
|
@@ -52,7 +57,7 @@ list / table 데이터 표시 의 약속.
|
|
|
52
57
|
onRowClick={(o) => navigate(`/orders/${o.id}`)}
|
|
53
58
|
density="default"
|
|
54
59
|
stickyHeader
|
|
55
|
-
storageKey="orders-grid"
|
|
60
|
+
storageKey="orders-grid" // localStorage column widths / hidden
|
|
56
61
|
/>
|
|
57
62
|
```
|
|
58
63
|
|
|
@@ -69,104 +74,195 @@ const columns: ColumnDef<Order>[] = [
|
|
|
69
74
|
|
|
70
75
|
### Column 패턴 (셀러 admin 표준)
|
|
71
76
|
|
|
72
|
-
| Column 종류
|
|
73
|
-
|
|
74
|
-
| ID / 번호
|
|
75
|
-
| 사람 (고객 / 사용자) | `PersonCell`
|
|
76
|
-
| 상태
|
|
77
|
-
| 금액
|
|
78
|
-
| 날짜
|
|
79
|
-
| 액션
|
|
80
|
-
| 진행률
|
|
81
|
-
| 추세
|
|
77
|
+
| Column 종류 | 컴포넌트 | 예 |
|
|
78
|
+
| -------------------- | -------------------------------- | --------------------------------- |
|
|
79
|
+
| ID / 번호 | `Code size="xs"` | `#HE-2719` |
|
|
80
|
+
| 사람 (고객 / 사용자) | `PersonCell` | avatar + name + sub |
|
|
81
|
+
| 상태 | `Pill tone={...}` | success / warning / danger / info |
|
|
82
|
+
| 금액 | tabular-nums + right-align | `₩123,456` |
|
|
83
|
+
| 날짜 | `relativeDate` 또는 `formatDate` | `3일 전` / `2026-05-01` |
|
|
84
|
+
| 액션 | `IconButton` / `Menu` | 행 끝 |
|
|
85
|
+
| 진행률 | `ProgressRow` | bar + pct |
|
|
86
|
+
| 추세 | `Sparkline` | mini chart |
|
|
87
|
+
|
|
88
|
+
### Column key 명명 표준 (필수)
|
|
89
|
+
|
|
90
|
+
| 표준 key | 의도 | 비고 |
|
|
91
|
+
| ------------------------- | ------------------------------- | ----------------------------------------------------------------------- |
|
|
92
|
+
| `id` | entity primary id | snake_case 금지 (`created_at` ❌ → `createdAt`) |
|
|
93
|
+
| `name` / `title` | entity 의 식별용 이름 / 제목 | 도메인 별 alias (`orderNumber` / `productName`) 는 의도가 명확하면 허용 |
|
|
94
|
+
| `customer` | actor (PersonCell) | — |
|
|
95
|
+
| `status` | Pill tone enum | — |
|
|
96
|
+
| `amount` | 금액 (right-align mono) | `total` 같은 alias 허용 단 의도 일관성 우선 |
|
|
97
|
+
| `createdAt` / `updatedAt` | 날짜 (camelCase 필수) | `created_at` 같은 snake_case 금지 |
|
|
98
|
+
| `actions` | row actions (IconButton / Menu) | 모든 list 의 마지막 column 으로 고정 |
|
|
99
|
+
|
|
100
|
+
→ **camelCase 필수**. snake_case (`created_at`) / mixed naming 금지.
|
|
101
|
+
|
|
102
|
+
### Column 순서 표준
|
|
103
|
+
|
|
104
|
+
`[entity-id → entity-info → actor → status → amount → metadata → actions]`
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
// 표준 순서 예 (order list)
|
|
108
|
+
const columns = [
|
|
109
|
+
{ key: 'id', header: '주문 번호' }, // 1. entity-id
|
|
110
|
+
{ key: 'product', header: '상품' }, // 2. entity-info
|
|
111
|
+
{ key: 'customer', header: '고객' }, // 3. actor
|
|
112
|
+
{ key: 'status', header: '상태' }, // 4. status
|
|
113
|
+
{ key: 'amount', header: '금액', align: 'right' }, // 5. amount
|
|
114
|
+
{ key: 'createdAt', header: '주문일' }, // 6. metadata
|
|
115
|
+
{ key: 'actions', header: '', align: 'right' }, // 7. actions (마지막)
|
|
116
|
+
];
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
> 일부 도메인 (예: blog) 은 amount 가 없을 수 있음 → metadata 직전 으로 의도된
|
|
120
|
+
> 생략 OK. 단 — `id → ... → actions` 의 **앞 / 뒤 anchor** 는 모든 list 에서
|
|
121
|
+
> 동일.
|
|
122
|
+
|
|
123
|
+
### Actions column 활용
|
|
124
|
+
|
|
125
|
+
```tsx
|
|
126
|
+
{
|
|
127
|
+
key: 'actions',
|
|
128
|
+
header: '',
|
|
129
|
+
align: 'right',
|
|
130
|
+
width: '60px',
|
|
131
|
+
render: (row) => (
|
|
132
|
+
<Menu.Root>
|
|
133
|
+
<Menu.Trigger><IconButton aria-label="더보기" variant="ghost"><MoreHorizontal size={14} /></IconButton></Menu.Trigger>
|
|
134
|
+
<Menu.Content>
|
|
135
|
+
<Menu.Item onClick={() => edit(row.id)}>편집</Menu.Item>
|
|
136
|
+
<Menu.Item onClick={() => duplicate(row.id)}>복제</Menu.Item>
|
|
137
|
+
<Menu.Separator />
|
|
138
|
+
<Menu.Item onClick={() => remove(row.id)} variant="danger">삭제</Menu.Item>
|
|
139
|
+
</Menu.Content>
|
|
140
|
+
</Menu.Root>
|
|
141
|
+
),
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
- **모든 list 의 마지막 column 은 actions** (강제)
|
|
146
|
+
- Menu 활용 — 2 개 이상 action 이면 Menu 안에 단일 IconButton trigger
|
|
147
|
+
- Single action — 직접 IconButton (예: preview / open)
|
|
148
|
+
- destructive action — `Menu.Item variant="danger"` + Menu.Separator 로 분리
|
|
82
149
|
|
|
83
150
|
---
|
|
84
151
|
|
|
85
|
-
## 3. Mobile 변형
|
|
152
|
+
## 3. Mobile 변형 (Responsive 표준)
|
|
153
|
+
|
|
154
|
+
| Desktop (≥ md) | Mobile (< md) |
|
|
155
|
+
| ----------------------------------------- | ---------------------------------------------------------- |
|
|
156
|
+
| DataGrid table 형 | **Card list** (각 row → card, primary column 1-2개만 표시) |
|
|
157
|
+
| Sticky header | `position: sticky` 그대로 |
|
|
158
|
+
| Toolbar (filter pills + search + actions) | toolbar collapse — filter drawer / search expand |
|
|
159
|
+
| Pagination (page numbers) | infinite scroll 또는 numeric only |
|
|
160
|
+
| selection (checkbox column) | hidden — long-press 또는 명시 toggle |
|
|
161
|
+
| actions column (Menu) | swipe-actions 또는 row-bottom IconButton |
|
|
162
|
+
|
|
163
|
+
### Mobile 변환 mechanism
|
|
164
|
+
|
|
165
|
+
```tsx
|
|
166
|
+
<DataGrid
|
|
167
|
+
data={rows}
|
|
168
|
+
columns={columns}
|
|
169
|
+
// Mobile = card 형식 으로 자동 변환
|
|
170
|
+
mobileView={{
|
|
171
|
+
title: (row) => row.name, // card primary
|
|
172
|
+
subtitle: (row) => row.email, // card secondary
|
|
173
|
+
statusKey: 'status', // status badge 표시 column
|
|
174
|
+
actionsKey: 'actions', // 우측 swipe actions
|
|
175
|
+
}}
|
|
176
|
+
/>
|
|
177
|
+
```
|
|
86
178
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
| Pagination (page numbers) | infinite scroll 또는 numeric only |
|
|
93
|
-
| selection (checkbox column) | 일반적으로 숨김 — long-press 로 multi-select 도 가능 (TBD) |
|
|
179
|
+
- **Default**: Tailwind preset 의 token responsive (text-3xl / space-\* 등
|
|
180
|
+
mobile fallback 자동 — responsive-tokens.md)
|
|
181
|
+
- **DataGrid 의 mobile = card list 자동 변환** — `mobileView` prop 만 지정하면
|
|
182
|
+
caller 가 별도 layout 작성 없이 mobile 의 card 표시 가능
|
|
183
|
+
- **Breakpoint = md (768px)** — `useIsBreakpointUp('md')` 내부 활용
|
|
94
184
|
|
|
95
|
-
>
|
|
96
|
-
>
|
|
185
|
+
> 현재 상태 — DataGrid 의 `mobileView` prop 은 **TBD: mds 의 DataGrid 에 추가
|
|
186
|
+
> 필요** (지금은 desktop only). 임시로는 caller 가 `useBreakpoint()` + 별도
|
|
187
|
+
> CardList 직접 구현.
|
|
97
188
|
|
|
98
189
|
---
|
|
99
190
|
|
|
100
191
|
## 4. Loading / Error / Empty state — async-states.md §1 참조
|
|
101
192
|
|
|
102
|
-
| state
|
|
103
|
-
|
|
104
|
-
| loading (초기 fetch)
|
|
105
|
-
| loading 더 보기 (page 2) | 부분 skeleton + 기존 row 유지
|
|
106
|
-
| error
|
|
107
|
-
| empty (필터 결과 0)
|
|
108
|
-
| empty (한 번도 없음)
|
|
193
|
+
| state | 컴포넌트 |
|
|
194
|
+
| ------------------------ | ------------------------------------------------------------------------- |
|
|
195
|
+
| loading (초기 fetch) | `<SkeletonRows>` (DataGrid 자동) |
|
|
196
|
+
| loading 더 보기 (page 2) | 부분 skeleton + 기존 row 유지 |
|
|
197
|
+
| error | `<ErrorState error={err} onRetry={refetch}>` (DataGrid `errorState` prop) |
|
|
198
|
+
| empty (필터 결과 0) | `<EmptyState variant="search" action={resetFilters}>` |
|
|
199
|
+
| empty (한 번도 없음) | `<EmptyState variant="onboarding" action={createX}>` |
|
|
109
200
|
|
|
110
201
|
---
|
|
111
202
|
|
|
112
203
|
## 5. 결정 표 (lookup)
|
|
113
204
|
|
|
114
|
-
| 케이스
|
|
115
|
-
|
|
116
|
-
| "page list 표시"
|
|
117
|
-
| "5개 정도 단순 table"
|
|
118
|
-
| "tree / nested"
|
|
119
|
-
| "row click → 상세"
|
|
120
|
-
| "row click → 옆 편집 (목록 유지)" | DataGrid `onRowClick={() => openDrawer(...)}`
|
|
121
|
-
| "URL filter / page 동기화"
|
|
122
|
-
| "Server-side"
|
|
123
|
-
| "bulk action"
|
|
124
|
-
| "column resize / hide / reorder"
|
|
125
|
-
| "column custom component"
|
|
126
|
-
| "특정 column 만 sortable"
|
|
127
|
-
| "1 필드 inline 편집"
|
|
205
|
+
| 케이스 | 답 |
|
|
206
|
+
| --------------------------------- | -------------------------------------------------------------------------------------- |
|
|
207
|
+
| "page list 표시" | DataGrid |
|
|
208
|
+
| "5개 정도 단순 table" | Table |
|
|
209
|
+
| "tree / nested" | Tree (compounds) |
|
|
210
|
+
| "row click → 상세" | DataGrid `onRowClick={navigate(...)}` |
|
|
211
|
+
| "row click → 옆 편집 (목록 유지)" | DataGrid `onRowClick={() => openDrawer(...)}` |
|
|
212
|
+
| "URL filter / page 동기화" | DataGrid controlled props (`page`, `onPageChange` etc.) |
|
|
213
|
+
| "Server-side" | DataGrid `mode="server" totalItems={N}` |
|
|
214
|
+
| "bulk action" | DataGrid `selectable bulkActions={(selected) => ...}` |
|
|
215
|
+
| "column resize / hide / reorder" | DataGrid `storageKey` + 자동 |
|
|
216
|
+
| "column custom component" | `column.render` prop |
|
|
217
|
+
| "특정 column 만 sortable" | `column.sortable: true` |
|
|
218
|
+
| "1 필드 inline 편집" | DataGrid column.render = controlled input + form.md §5 (Inline 편집 — Enter 명시 저장) |
|
|
128
219
|
|
|
129
220
|
---
|
|
130
221
|
|
|
131
222
|
## 6. 안티 패턴
|
|
132
223
|
|
|
133
|
-
- ❌ **DataGrid 안에 form (모든 row 의 input)** — 큰 form 은 별도 페이지
|
|
134
|
-
-
|
|
224
|
+
- ❌ **DataGrid 안에 form (모든 row 의 input)** — 큰 form 은 별도 페이지
|
|
225
|
+
(page-form). DataGrid 는 list / inline 편집 1 필드만
|
|
226
|
+
- ❌ **Table 으로 DataGrid 흉내** — toolbar / filter / pagination 다 caller 구현
|
|
227
|
+
= 중복. DataGrid 사용
|
|
135
228
|
- ❌ **DataGrid 의 column 50+** — 사용자 스캔 불가. 일부 hidden + caller 가 선택
|
|
136
|
-
- ❌ **Loading 시 empty state 표시** — async-states.md §2 — 로딩 중
|
|
137
|
-
|
|
229
|
+
- ❌ **Loading 시 empty state 표시** — async-states.md §2 — 로딩 중
|
|
230
|
+
ErrorState/EmptyState 금지
|
|
231
|
+
- ❌ **row 클릭 + checkbox 동시 작동** — 한쪽만. selection 모드 시 row click
|
|
232
|
+
막힘 또는 다른 의미
|
|
138
233
|
- ❌ **pagination size 가 50+** — render 부담 + a11y. 최대 20-30 권장
|
|
139
234
|
- ❌ **column header 가 너무 길거나 wrap** — fixed height, ellipsis
|
|
140
|
-
- ❌ **selection state 가 page 이동 후 사라짐** — DataGrid 의 selectedIds Set
|
|
235
|
+
- ❌ **selection state 가 page 이동 후 사라짐** — DataGrid 의 selectedIds Set
|
|
236
|
+
유지 (cross-page)
|
|
141
237
|
|
|
142
238
|
---
|
|
143
239
|
|
|
144
240
|
## 7. Cross-cutting
|
|
145
241
|
|
|
146
|
-
| Axis
|
|
147
|
-
|
|
148
|
-
| **Responsive** | DataGrid → card list mobile (TBD 자동 변환)
|
|
149
|
-
| **A11y**
|
|
150
|
-
| **i18n**
|
|
151
|
-
| **AI fill**
|
|
152
|
-
| **Catalog**
|
|
153
|
-
| **Telemetry**
|
|
242
|
+
| Axis | 적용 |
|
|
243
|
+
| -------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
|
244
|
+
| **Responsive** | DataGrid → card list mobile (TBD 자동 변환) |
|
|
245
|
+
| **A11y** | `<table role="table">` 자동 / column header `<th scope="col">` / sortable button aria-sort / selection checkbox aria-label |
|
|
246
|
+
| **i18n** | column label / filter label / empty message 모두 `t()` |
|
|
247
|
+
| **AI fill** | DataGrid 의 filter 가 AI 자동 제안 가능 (Step 4.8) |
|
|
248
|
+
| **Catalog** | DataGrid props (columns / filters / mode / state props) 자동 catalog (Step 5) |
|
|
249
|
+
| **Telemetry** | row click / sort / filter / search / bulk action 자동 log (Step 7) |
|
|
154
250
|
|
|
155
251
|
---
|
|
156
252
|
|
|
157
253
|
## 8. apps/web 의 실 사용 (Step 7 Web Playbook 의 baseline)
|
|
158
254
|
|
|
159
|
-
| Page
|
|
160
|
-
|
|
161
|
-
| `/merchant/customers`
|
|
162
|
-
| `/merchant/orders`
|
|
163
|
-
| `/merchant/payment/transactions`
|
|
164
|
-
| `/merchant/payment/refunds`
|
|
165
|
-
| `/merchant/content/blogs` (list)
|
|
166
|
-
| `/merchant/content/announcements` | DataGrid
|
|
167
|
-
| `/merchant/content/banners`
|
|
168
|
-
| `/admin/dead-letters`
|
|
169
|
-
| `/admin/exchange-rates`
|
|
255
|
+
| Page | DataGrid 사용 |
|
|
256
|
+
| --------------------------------- | ------------------------------------------------------------------------- |
|
|
257
|
+
| `/merchant/customers` | DataGrid (filter / search / row click → drawer / bulk action) |
|
|
258
|
+
| `/merchant/orders` | DataGrid (filter / search / row click → detail page / status update bulk) |
|
|
259
|
+
| `/merchant/payment/transactions` | DataGrid (server mode + URL state) |
|
|
260
|
+
| `/merchant/payment/refunds` | 동일 |
|
|
261
|
+
| `/merchant/content/blogs` (list) | DataGrid |
|
|
262
|
+
| `/merchant/content/announcements` | DataGrid |
|
|
263
|
+
| `/merchant/content/banners` | DataGrid |
|
|
264
|
+
| `/admin/dead-letters` | DataGrid (admin) |
|
|
265
|
+
| `/admin/exchange-rates` | DataGrid (admin) |
|
|
170
266
|
|
|
171
267
|
→ 20+ 페이지가 DataGrid 사용. 균일성 보장 = Web Playbook (Step 7) 의 표준.
|
|
172
268
|
|
|
@@ -176,7 +272,8 @@ const columns: ColumnDef<Order>[] = [
|
|
|
176
272
|
|
|
177
273
|
1. **Mobile 자동 변환** — DataGrid → card list 의 자동 변환 컴포넌트 (CardList?)
|
|
178
274
|
2. **Long-press multi-select** — 모바일 의 selection 패턴
|
|
179
|
-
3. **Inline 편집 컴포넌트** — DataGrid cell 의 EditCell wrapper 추가 (Enter /
|
|
275
|
+
3. **Inline 편집 컴포넌트** — DataGrid cell 의 EditCell wrapper 추가 (Enter /
|
|
276
|
+
체크 명시 저장)
|
|
180
277
|
4. **Sticky column** — first / last column 의 sticky (가로 scroll 시)
|
|
181
278
|
5. **Virtual scroll** — row 1000+ 시 virtualization (현재 mds 미지원)
|
|
182
279
|
6. **Group by** — row grouping (예: status 별 group)
|