velox-grid 0.11.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.
Files changed (84) hide show
  1. package/CHANGELOG.md +483 -0
  2. package/README.md +755 -0
  3. package/dist/react/index.esm.js +3757 -0
  4. package/dist/react/index.esm.js.map +1 -0
  5. package/dist/react/index.js +7 -0
  6. package/dist/react/index.js.map +1 -0
  7. package/dist/react/react/VeloxGridReact.d.ts +33 -0
  8. package/dist/react/react/VeloxGridReact.d.ts.map +1 -0
  9. package/dist/react/react/index.d.ts +9 -0
  10. package/dist/react/react/index.d.ts.map +1 -0
  11. package/dist/react/react/types.d.ts +52 -0
  12. package/dist/react/react/types.d.ts.map +1 -0
  13. package/dist/react/react/useVeloxGrid.d.ts +23 -0
  14. package/dist/react/react/useVeloxGrid.d.ts.map +1 -0
  15. package/dist/react/types/index.d.ts +878 -0
  16. package/dist/react/types/index.d.ts.map +1 -0
  17. package/dist/types/core/GridColumnMenu.d.ts +35 -0
  18. package/dist/types/core/GridColumnMenu.d.ts.map +1 -0
  19. package/dist/types/core/GridDragManager.d.ts +83 -0
  20. package/dist/types/core/GridDragManager.d.ts.map +1 -0
  21. package/dist/types/core/GridEditorFactory.d.ts +33 -0
  22. package/dist/types/core/GridEditorFactory.d.ts.map +1 -0
  23. package/dist/types/core/GridFilterPopup.d.ts +43 -0
  24. package/dist/types/core/GridFilterPopup.d.ts.map +1 -0
  25. package/dist/types/core/GridHistory.d.ts +84 -0
  26. package/dist/types/core/GridHistory.d.ts.map +1 -0
  27. package/dist/types/core/GridRenderer.d.ts +73 -0
  28. package/dist/types/core/GridRenderer.d.ts.map +1 -0
  29. package/dist/types/core/GridSummary.d.ts +98 -0
  30. package/dist/types/core/GridSummary.d.ts.map +1 -0
  31. package/dist/types/core/GridTooltip.d.ts +43 -0
  32. package/dist/types/core/GridTooltip.d.ts.map +1 -0
  33. package/dist/types/core/GridValidator.d.ts +63 -0
  34. package/dist/types/core/GridValidator.d.ts.map +1 -0
  35. package/dist/types/core/VeloxGrid.d.ts +437 -0
  36. package/dist/types/core/VeloxGrid.d.ts.map +1 -0
  37. package/dist/types/core/index.d.ts +15 -0
  38. package/dist/types/core/index.d.ts.map +1 -0
  39. package/dist/types/index.d.ts +16 -0
  40. package/dist/types/index.d.ts.map +1 -0
  41. package/dist/types/react/VeloxGridReact.d.ts +40 -0
  42. package/dist/types/react/VeloxGridReact.d.ts.map +1 -0
  43. package/dist/types/react/index.d.ts +9 -0
  44. package/dist/types/react/index.d.ts.map +1 -0
  45. package/dist/types/react/types.d.ts +55 -0
  46. package/dist/types/react/types.d.ts.map +1 -0
  47. package/dist/types/react/useVeloxGrid.d.ts +54 -0
  48. package/dist/types/react/useVeloxGrid.d.ts.map +1 -0
  49. package/dist/types/types/index.d.ts +878 -0
  50. package/dist/types/types/index.d.ts.map +1 -0
  51. package/dist/types/utils/data.d.ts +41 -0
  52. package/dist/types/utils/data.d.ts.map +1 -0
  53. package/dist/types/utils/dom.d.ts +13 -0
  54. package/dist/types/utils/dom.d.ts.map +1 -0
  55. package/dist/types/utils/export.d.ts +60 -0
  56. package/dist/types/utils/export.d.ts.map +1 -0
  57. package/dist/types/utils/index.d.ts +4 -0
  58. package/dist/types/utils/index.d.ts.map +1 -0
  59. package/dist/types/vue/index.d.ts +9 -0
  60. package/dist/types/vue/index.d.ts.map +1 -0
  61. package/dist/types/vue/types.d.ts +68 -0
  62. package/dist/types/vue/types.d.ts.map +1 -0
  63. package/dist/types/vue/useVeloxGrid.d.ts +49 -0
  64. package/dist/types/vue/useVeloxGrid.d.ts.map +1 -0
  65. package/dist/velox-grid.css +1 -0
  66. package/dist/velox-grid.esm.js +3387 -0
  67. package/dist/velox-grid.esm.js.map +1 -0
  68. package/dist/velox-grid.iife.js +14 -0
  69. package/dist/velox-grid.iife.js.map +1 -0
  70. package/dist/velox-grid.js +14 -0
  71. package/dist/velox-grid.js.map +1 -0
  72. package/dist/vue/index.esm.js +3754 -0
  73. package/dist/vue/index.esm.js.map +1 -0
  74. package/dist/vue/index.js +7 -0
  75. package/dist/vue/index.js.map +1 -0
  76. package/dist/vue/types/index.d.ts +878 -0
  77. package/dist/vue/types/index.d.ts.map +1 -0
  78. package/dist/vue/vue/index.d.ts +9 -0
  79. package/dist/vue/vue/index.d.ts.map +1 -0
  80. package/dist/vue/vue/types.d.ts +65 -0
  81. package/dist/vue/vue/types.d.ts.map +1 -0
  82. package/dist/vue/vue/useVeloxGrid.d.ts +21 -0
  83. package/dist/vue/vue/useVeloxGrid.d.ts.map +1 -0
  84. package/package.json +120 -0
package/README.md ADDED
@@ -0,0 +1,755 @@
1
+ # VeloxGrid πŸš€
2
+
3
+ λΉ λ₯΄κ³  κ°€λ²Όμš΄ ν”„λ ˆμž„μ›Œν¬ 독립적 데이터 κ·Έλ¦¬λ“œ 라이브러리
4
+
5
+ > **Velox** (라틴어) = "λΉ λ₯Έ" - λΉ λ₯΄κ³  κ°€λ²Όμš΄ 데이터 κ·Έλ¦¬λ“œλ₯Ό μ§€ν–₯ν•©λ‹ˆλ‹€.
6
+
7
+ [![npm version](https://img.shields.io/npm/v/velox-grid.svg)](https://www.npmjs.com/package/velox-grid)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
+ [![Bundle Size](https://img.shields.io/bundlephobia/minzip/velox-grid)](https://bundlephobia.com/package/velox-grid)
10
+
11
+ ## 🌐 Live Demo
12
+
13
+ **[πŸ‘‰ https://bart-idea.github.io/velox-grid/](https://bart-idea.github.io/velox-grid/)**
14
+
15
+ λ‹€μ–‘ν•œ κΈ°λŠ₯을 직접 μ²΄ν—˜ν•΄λ³΄μ„Έμš”:
16
+ - Selection & Navigation
17
+ - Excel Export/Import
18
+ - Keyboard & Undo/Redo
19
+ - Column Management
20
+ - Row Drag & Drop
21
+ - Validation & Custom Editor
22
+
23
+ ## ✨ μ£Όμš” κΈ°λŠ₯
24
+
25
+ - 🎯 **ν”„λ ˆμž„μ›Œν¬ 독립적** - Vanilla JS, React, Vue, Angular λ“± λͺ¨λ“  ν™˜κ²½μ—μ„œ μ‚¬μš© κ°€λŠ₯
26
+ - πŸš€ **가상 슀크둀** - λŒ€μš©λŸ‰ 데이터(100,000+ ν–‰) 처리
27
+ - πŸ“¦ **Zero Dependencies** - μ™ΈλΆ€ μ˜μ‘΄μ„± μ—†μŒ (Excel κΈ°λŠ₯의 경우 SheetJS 선택적 μ‚¬μš©)
28
+ - 🎨 **μ»€μŠ€ν„°λ§ˆμ΄μ§• κ°€λŠ₯** - CSS Variablesλ₯Ό ν†΅ν•œ μ‰¬μš΄ ν…Œλ§ˆ μ»€μŠ€ν„°λ§ˆμ΄μ§•
29
+ - πŸ“ **TypeScript** - μ™„λ²½ν•œ νƒ€μž… 지원
30
+ - βš›οΈ **React/Vue 래퍼** - 곡식 React μ»΄ν¬λ„ŒνŠΈ & Vue 3 μ»΄ν¬λ„ŒνŠΈ 제곡 (v0.11.0)
31
+ - ⚑ **κ²½λŸ‰ν™”** - ~100KB minified (~25KB gzipped)
32
+
33
+ ### 핡심 κΈ°λŠ₯
34
+
35
+ - βœ… **μ…€ 선택** - κ°œλ³„ μ…€ 및 블둝 선택 (v0.3.0)
36
+ - ⌨️ **ν‚€λ³΄λ“œ λ‚΄λΉ„κ²Œμ΄μ…˜** - ν™”μ‚΄ν‘œ ν‚€, Tab, 단좕킀 지원 (v0.3.0, v0.9.1)
37
+ - πŸ“Š **Excel 내보내기/κ°€μ Έμ˜€κΈ°** - Excel, CSV, JSON 지원 (v0.4.0)
38
+ - ↩️ **μ‹€ν–‰ μ·¨μ†Œ/λ‹€μ‹œ μ‹€ν–‰** - Ctrl+Z / Ctrl+Y 지원 (v0.5.0)
39
+ - πŸ”„ **컬럼 μž¬μ •λ ¬** - λ“œλž˜κ·Έ μ•€ λ“œλ‘­μœΌλ‘œ 컬럼 μˆœμ„œ λ³€κ²½ (v0.6.0)
40
+ - πŸ“‹ **ν–‰ λ“œλž˜κ·Έ μ•€ λ“œλ‘­** - λ“œλž˜κ·Έλ‘œ ν–‰ μˆœμ„œ λ³€κ²½ (v0.6.0)
41
+ - βœ”οΈ **μ…€ 검증** - λ‹€μ–‘ν•œ κ·œμΉ™μœΌλ‘œ μž…λ ₯κ°’ 검증 (v0.7.0)
42
+ - πŸŽ›οΈ **μ»€μŠ€ν…€ 에디터** - λ“œλ‘­λ‹€μš΄, λ‚ μ§œ 선택기, μ²΄ν¬λ°•μŠ€ 에디터 (v0.7.0)
43
+ - πŸ’¬ **μ…€ 툴팁** - μ…€ ν˜Έλ²„ μ‹œ 툴팁 ν‘œμ‹œ (v0.7.0)
44
+ - πŸ”§ **μ•ˆμ •μ μΈ Edit λͺ¨λ“œ** - νŽΈμ§‘ 쀑 μƒν˜Έμž‘μš© κ°œμ„  (v0.7.1)
45
+ - πŸ“Š **Summary/Aggregation** - Footer μš”μ•½ ν–‰μœΌλ‘œ 데이터 집계 (sum, avg, count, min, max) (v0.7.1)
46
+ - πŸ“Œ **Fixed Columns** - μ™Όμͺ½/였λ₯Έμͺ½ 컬럼 κ³ μ • (v0.8.0)
47
+ - πŸ”’ **Row State Management** - ν–‰ μƒνƒœ 좔적 (생성/μˆ˜μ •/μ‚­μ œ) (v0.9.0)
48
+ - ⚑ **Quick Edit** - μ…€ 선택 ν›„ λ°”λ‘œ νƒ€μ΄ν•‘μœΌλ‘œ νŽΈμ§‘ (v0.9.1)
49
+ - ⌨️ **Enhanced Keyboard** - Enter/Tab/Shift μ‘°ν•© μ™„λ²½ 지원 (v0.9.1)
50
+ - πŸ“„ **Pagination** - Local/Remote νŽ˜μ΄μ§€λ„€μ΄μ…˜, νŽ˜μ΄μ§€ 크기 λ³€κ²½ (v0.10.0)
51
+ - 🌐 **Server-Side Data** - μ„œλ²„ μΈ‘ μ •λ ¬/ν•„ν„°/νŽ˜μ΄μ§• 연동 (v0.10.0)
52
+ - ♾️ **Infinite Scroll** - 슀크둀 끝 도달 μ‹œ λ‹€μŒ 데이터 μžλ™ λ‘œλ“œ (v0.10.0)
53
+ - βš›οΈ **React 래퍼** - VeloxGridReact μ»΄ν¬λ„ŒνŠΈ + useVeloxGrid Hook (v0.11.0)
54
+ - πŸ’š **Vue 3 래퍼** - VeloxGridVue μ»΄ν¬λ„ŒνŠΈ + useVeloxGrid Composable (v0.11.0)
55
+
56
+ ### μ½”λ“œ ꡬ쑰 μ΅œμ ν™” (v0.7.0+)
57
+
58
+ - πŸ—οΈ **λͺ¨λ“ˆν™” μ•„ν‚€ν…μ²˜** - VeloxGrid.ts 2,826쀄 β†’ 2,044쀄 (27.7% κ°μ†Œ)
59
+ - πŸ“ **CSS λͺ¨λ“ˆν™”** - 11개 파일둜 λΆ„λ¦¬ν•˜μ—¬ μœ μ§€λ³΄μˆ˜μ„± ν–₯상
60
+ - πŸ”§ **핡심 λͺ¨λ“ˆ** - GridRenderer, GridFilterPopup, GridColumnMenu, GridDragManager 뢄리
61
+
62
+ ## πŸ“¦ μ„€μΉ˜
63
+
64
+ ### Vanilla JS / 곡톡
65
+ ```bash
66
+ npm install velox-grid
67
+ ```
68
+
69
+ ### React ν”„λ‘œμ νŠΈ
70
+ ```bash
71
+ npm install velox-grid react react-dom
72
+ ```
73
+
74
+ ```json
75
+ // package.json
76
+ {
77
+ "dependencies": {
78
+ "velox-grid": "^0.11.0",
79
+ "react": "^18.2.0",
80
+ "react-dom": "^18.2.0"
81
+ }
82
+ }
83
+ ```
84
+
85
+ ### Vue 3 ν”„λ‘œμ νŠΈ
86
+ ```bash
87
+ npm install velox-grid vue
88
+ ```
89
+
90
+ ```json
91
+ // package.json
92
+ {
93
+ "dependencies": {
94
+ "velox-grid": "^0.11.0",
95
+ "vue": "^3.4.0"
96
+ }
97
+ }
98
+ ```
99
+
100
+ > **μ°Έκ³ **: `react`, `vue`λŠ” peerDependencies(optional)둜 μ„€μ •λ˜μ–΄ μžˆμ–΄, ν•΄λ‹Ή ν”„λ ˆμž„μ›Œν¬λ₯Ό μ‚¬μš©ν•˜μ§€ μ•ŠμœΌλ©΄ μ„€μΉ˜ν•  ν•„μš” μ—†μŠ΅λ‹ˆλ‹€.
101
+
102
+ ## πŸš€ λΉ λ₯Έ μ‹œμž‘
103
+
104
+ ### CDN (λΈŒλΌμš°μ €)
105
+
106
+ ```html
107
+ <link rel="stylesheet" href="https://unpkg.com/velox-grid@0.11.0/dist/velox-grid.css">
108
+ <script src="https://unpkg.com/velox-grid@0.11.0/dist/velox-grid.iife.js"></script>
109
+
110
+ <div id="grid"></div>
111
+
112
+ <script>
113
+ const grid = new VeloxGrid.VeloxGrid('#grid', {
114
+ columns: [
115
+ { field: 'id', header: 'ID', type: 'number', width: 60 },
116
+ { field: 'name', header: '이름', type: 'text', width: 120 },
117
+ { field: 'email', header: '이메일', type: 'text', width: 200,
118
+ editable: true,
119
+ validation: [
120
+ { type: 'required', message: '이메일은 ν•„μˆ˜μž…λ‹ˆλ‹€' },
121
+ { type: 'pattern', value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, message: 'μ˜¬λ°”λ₯Έ 이메일 ν˜•μ‹μ΄ μ•„λ‹™λ‹ˆλ‹€' }
122
+ ]
123
+ },
124
+ { field: 'age', header: 'λ‚˜μ΄', type: 'number', width: 80 },
125
+ ],
126
+ data: [
127
+ { id: 1, name: 'κΉ€μ² μˆ˜', email: 'john@example.com', age: 28 },
128
+ { id: 2, name: '이영희', email: 'jane@example.com', age: 32 },
129
+ ],
130
+ height: 400,
131
+ editable: true,
132
+ sortable: true,
133
+ selectionStyle: 'cell',
134
+ });
135
+ </script>
136
+ ```
137
+
138
+ ### ES Module
139
+
140
+ ```javascript
141
+ import { VeloxGrid } from 'velox-grid';
142
+ import 'velox-grid/dist/velox-grid.css';
143
+
144
+ const grid = new VeloxGrid('#grid', {
145
+ columns: [
146
+ {
147
+ field: 'status',
148
+ header: 'μƒνƒœ',
149
+ editable: true,
150
+ editor: {
151
+ type: 'select',
152
+ options: [
153
+ { value: 'active', label: 'ν™œμ„±' },
154
+ { value: 'inactive', label: 'λΉ„ν™œμ„±' },
155
+ { value: 'pending', label: 'λŒ€κΈ°μ€‘' }
156
+ ]
157
+ }
158
+ },
159
+ {
160
+ field: 'date',
161
+ header: 'λ‚ μ§œ',
162
+ editable: true,
163
+ editor: { type: 'date' }
164
+ },
165
+ ],
166
+ data: [...],
167
+ });
168
+ ```
169
+
170
+ ### React (v0.11.0)
171
+
172
+ ```tsx
173
+ import { useRef } from 'react';
174
+ import { VeloxGridReact } from 'velox-grid/react';
175
+ import type { VeloxGridReactRef } from 'velox-grid/react';
176
+ import 'velox-grid/css';
177
+
178
+ function App() {
179
+ const gridRef = useRef<VeloxGridReactRef>(null);
180
+
181
+ return (
182
+ <div>
183
+ <button onClick={() => gridRef.current?.addRow({ name: 'New', age: 0 })}>
184
+ ν–‰ μΆ”κ°€
185
+ </button>
186
+ <VeloxGridReact
187
+ ref={gridRef}
188
+ columns={[
189
+ { field: 'name', header: '이름', width: 150 },
190
+ { field: 'age', header: 'λ‚˜μ΄', type: 'number', width: 80 },
191
+ ]}
192
+ data={data}
193
+ height={400}
194
+ editable={true}
195
+ onCellEditEnd={(e) => console.log('Edit:', e)}
196
+ />
197
+ </div>
198
+ );
199
+ }
200
+ ```
201
+
202
+ **useVeloxGrid Hook:**
203
+
204
+ ```tsx
205
+ import { useVeloxGrid } from 'velox-grid/react';
206
+
207
+ function App() {
208
+ const { containerRef, grid, isReady } = useVeloxGrid({
209
+ columns, data, height: 400, editable: true,
210
+ onCellEditEnd: (e) => console.log(e),
211
+ });
212
+
213
+ return (
214
+ <div>
215
+ <button onClick={() => grid?.addRow({ name: 'New' })}>ν–‰ μΆ”κ°€</button>
216
+ <div ref={containerRef} />
217
+ </div>
218
+ );
219
+ }
220
+ ```
221
+
222
+ ### Vue 3 (v0.11.0)
223
+
224
+ ```vue
225
+ <template>
226
+ <button @click="gridRef?.addRow({ name: 'New' })">ν–‰ μΆ”κ°€</button>
227
+ <VeloxGridVue
228
+ ref="gridRef"
229
+ :columns="columns"
230
+ :data="data"
231
+ :height="400"
232
+ :editable="true"
233
+ @cell-edit-end="onCellEditEnd"
234
+ />
235
+ </template>
236
+
237
+ <script setup lang="ts">
238
+ import { ref } from 'vue';
239
+ import { VeloxGridVue } from 'velox-grid/vue';
240
+ import 'velox-grid/css';
241
+
242
+ const gridRef = ref<InstanceType<typeof VeloxGridVue>>();
243
+
244
+ function onCellEditEnd(e) {
245
+ console.log('Edit:', e);
246
+ }
247
+ </script>
248
+ ```
249
+
250
+ **useVeloxGrid Composable:**
251
+
252
+ ```vue
253
+ <template>
254
+ <button @click="grid?.addRow({ name: 'New' })">ν–‰ μΆ”κ°€</button>
255
+ <div ref="containerRef" />
256
+ </template>
257
+
258
+ <script setup lang="ts">
259
+ import { useVeloxGrid } from 'velox-grid/vue';
260
+
261
+ const { containerRef, grid, isReady } = useVeloxGrid({
262
+ columns, data, height: 400, editable: true,
263
+ onCellEditEnd: (e) => console.log(e),
264
+ });
265
+ </script>
266
+ ```
267
+
268
+ ## πŸ“š 핡심 API
269
+
270
+ ### Grid Options
271
+
272
+ ```typescript
273
+ interface GridOptions {
274
+ columns: ColumnDefinition[];
275
+ data?: RowData[];
276
+ width?: number | string;
277
+ height?: number | string;
278
+ rowHeight?: number;
279
+ headerHeight?: number;
280
+
281
+ // 선택
282
+ selectable?: boolean;
283
+ selectionMode?: 'none' | 'single' | 'multiple' | 'extended';
284
+ selectionStyle?: 'row' | 'cell' | 'block' | 'none';
285
+
286
+ // μ²΄ν¬λ°•μŠ€ (v0.3.0)
287
+ checkBar?: {
288
+ visible: boolean;
289
+ exclusive?: boolean; // λΌλ””μ˜€ λ²„νŠΌ μŠ€νƒ€μΌ
290
+ showAll?: boolean; // 전체 선택 μ²΄ν¬λ°•μŠ€ ν‘œμ‹œ
291
+ checkableCallback?: (row: RowData, index: number) => boolean;
292
+ };
293
+
294
+ // κΈ°λŠ₯
295
+ sortable?: boolean;
296
+ filterable?: boolean;
297
+ editable?: boolean;
298
+ resizable?: boolean;
299
+ virtualScroll?: boolean;
300
+ undoable?: boolean; // v0.5.0
301
+
302
+ // UI
303
+ showRowNumbers?: boolean;
304
+ loading?: boolean;
305
+ loadingMessage?: string;
306
+ emptyMessage?: string;
307
+ theme?: 'default';
308
+ }
309
+ ```
310
+
311
+ ### Column Definition
312
+
313
+ ```typescript
314
+ interface ColumnDefinition {
315
+ field: string;
316
+ header: string;
317
+ type?: 'text' | 'number' | 'date' | 'boolean';
318
+ width?: number;
319
+ minWidth?: number;
320
+ align?: 'left' | 'center' | 'right';
321
+
322
+ // κΈ°λŠ₯
323
+ sortable?: boolean;
324
+ filterable?: boolean;
325
+ editable?: boolean;
326
+ resizable?: boolean;
327
+ fixed?: 'left' | 'right' | false;
328
+ visible?: boolean;
329
+
330
+ // λ Œλ”λ§
331
+ formatter?: (value: CellValue, row: RowData, column: ColumnDefinition) => string;
332
+ renderer?: (value: CellValue, row: RowData, column: ColumnDefinition) => string;
333
+ cellClass?: string | ((value: CellValue, row: RowData) => string);
334
+
335
+ // 검증 (v0.7.0)
336
+ validation?: ValidationRule[];
337
+
338
+ // μ»€μŠ€ν…€ 에디터 (v0.7.0)
339
+ editor?: EditorOptions;
340
+
341
+ // 툴팁 (v0.7.0)
342
+ tooltip?: boolean | ((value: CellValue, row: RowData) => string);
343
+
344
+ // Summary (v0.7.1)
345
+ summary?: SummaryConfig;
346
+ }
347
+ ```
348
+
349
+ ### 검증 κ·œμΉ™ (v0.7.0)
350
+
351
+ ```typescript
352
+ interface ValidationRule {
353
+ type: 'required' | 'min' | 'max' | 'minLength' | 'maxLength' | 'pattern' | 'custom';
354
+ value?: number | string | RegExp;
355
+ message: string;
356
+ validator?: (value: CellValue, row: RowData) => boolean | string;
357
+ }
358
+
359
+ // μ‚¬μš© 예제
360
+ {
361
+ field: 'email',
362
+ validation: [
363
+ { type: 'required', message: '이메일은 ν•„μˆ˜μž…λ‹ˆλ‹€' },
364
+ { type: 'pattern', value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, message: 'μ˜¬λ°”λ₯Έ 이메일 ν˜•μ‹μ΄ μ•„λ‹™λ‹ˆλ‹€' }
365
+ ]
366
+ }
367
+ ```
368
+
369
+ ### μ»€μŠ€ν…€ 에디터 (v0.7.0)
370
+
371
+ ```typescript
372
+ interface EditorOptions {
373
+ type: 'text' | 'number' | 'select' | 'date' | 'checkbox' | 'custom';
374
+ options?: Array<{ value: CellValue; label: string }>; // 'select'용
375
+ min?: number;
376
+ max?: number;
377
+ step?: number;
378
+ placeholder?: string;
379
+ renderer?: (cell: HTMLElement, value: CellValue, save: (v: CellValue) => void, cancel: () => void) => void;
380
+ }
381
+
382
+ // μ‚¬μš© 예제
383
+ {
384
+ field: 'category',
385
+ editor: {
386
+ type: 'select',
387
+ options: [
388
+ { value: 'A', label: 'μΉ΄ν…Œκ³ λ¦¬ A' },
389
+ { value: 'B', label: 'μΉ΄ν…Œκ³ λ¦¬ B' }
390
+ ]
391
+ }
392
+ }
393
+ ```
394
+
395
+ ### Summary/Aggregation (v0.7.1)
396
+
397
+ ```typescript
398
+ interface SummaryConfig {
399
+ function: 'sum' | 'avg' | 'count' | 'min' | 'max' | 'custom';
400
+ customFunction?: (values: CellValue[], data: RowData[]) => CellValue;
401
+ label?: string;
402
+ format?: string;
403
+ formatter?: (value: CellValue) => string;
404
+ className?: string;
405
+ }
406
+
407
+ // μ‚¬μš© 예제
408
+ const grid = new VeloxGrid('#grid', {
409
+ columns: [
410
+ {
411
+ field: 'quantity',
412
+ header: 'Quantity',
413
+ type: 'number',
414
+ summary: {
415
+ function: 'sum',
416
+ label: 'Total:',
417
+ formatter: (value) => `${value} units`
418
+ }
419
+ },
420
+ {
421
+ field: 'revenue',
422
+ header: 'Revenue',
423
+ type: 'number',
424
+ summary: {
425
+ function: 'sum',
426
+ label: 'Total Revenue:',
427
+ formatter: (value) => `${value.toLocaleString()}`,
428
+ className: 'velox-footer-cell--total'
429
+ }
430
+ }
431
+ ],
432
+ footerSummary: {
433
+ visible: true,
434
+ height: 44
435
+ }
436
+ });
437
+
438
+ // API λ©”μ„œλ“œ
439
+ const totalRevenue = grid.getSummaryValue('revenue');
440
+ const allSummaries = grid.getSummaryValues();
441
+ grid.refreshSummary(); // μˆ˜λ™ μƒˆλ‘œκ³ μΉ¨
442
+ ```
443
+
444
+ ### Pagination (v0.10.0)
445
+
446
+ ```typescript
447
+ const grid = new VeloxGrid('#grid', {
448
+ columns: [...],
449
+ data: localData, // Local mode
450
+ pagination: {
451
+ enabled: true,
452
+ pageSize: 20,
453
+ showInfo: true, // "1-20 / 500" ν‘œμ‹œ
454
+ showSizeChanger: true, // νŽ˜μ΄μ§€ 크기 μ…€λ ‰ν„°
455
+ pageSizeOptions: [10, 20, 50, 100],
456
+ maxPageButtons: 5,
457
+ },
458
+ });
459
+
460
+ // Remote mode (μ„œλ²„ μΈ‘ 데이터)
461
+ const grid = new VeloxGrid('#grid', {
462
+ columns: [...],
463
+ dataSource: {
464
+ type: 'remote',
465
+ fetch: async (params) => {
466
+ // params: { page, pageSize, sort?, filter? }
467
+ const res = await fetch(`/api/data?page=${params.page}&size=${params.pageSize}`);
468
+ return await res.json(); // { data: RowData[], totalCount: number }
469
+ },
470
+ },
471
+ pagination: { enabled: true, pageSize: 20 },
472
+ });
473
+
474
+ // API λ©”μ„œλ“œ
475
+ grid.goToPage(3); // νŽ˜μ΄μ§€ 이동
476
+ grid.setPageSize(50); // νŽ˜μ΄μ§€ 크기 λ³€κ²½
477
+ grid.getPaginationState(); // { currentPage, pageSize, totalCount, totalPages }
478
+ await grid.fetchData(); // μ„œλ²„ 데이터 μˆ˜λ™ μƒˆλ‘œκ³ μΉ¨
479
+
480
+ // Infinite Scroll mode
481
+ const grid = new VeloxGrid('#grid', {
482
+ columns: [...],
483
+ data: localData,
484
+ pagination: {
485
+ enabled: true,
486
+ mode: 'infinite', // 슀크둀 끝 도달 μ‹œ μžλ™ λ‘œλ“œ
487
+ pageSize: 50,
488
+ infiniteScrollThreshold: 100, // λ°”λ‹₯ μ—¬μœ  px
489
+ },
490
+ });
491
+ ```
492
+
493
+ ### λ©”μ„œλ“œ
494
+
495
+ ```typescript
496
+ // 데이터
497
+ getData(): RowData[]
498
+ setData(data: RowData[]): void
499
+ addRow(row: RowData, index?: number): void
500
+ updateRow(index: number, data: Partial<RowData>): void
501
+ removeRow(index: number): void
502
+ clearData(): void
503
+
504
+ // 선택
505
+ getSelectedRows(): number[]
506
+ selectRow(index: number, selected?: boolean): void
507
+ selectAll(selected?: boolean): void
508
+ clearSelection(): void
509
+
510
+ // μ…€ 선택 (v0.3.0)
511
+ getSelectedCells(): CellIndex[]
512
+ selectCell(rowIndex: number, field: string, selected?: boolean): void
513
+ setFocusedCell(rowIndex: number, field: string): void
514
+ getSelectionData(): CellValue[][]
515
+
516
+ // μ²΄ν¬λ°•μŠ€ (v0.3.0)
517
+ checkItem(index: number, checked?: boolean): void
518
+ checkAll(checked?: boolean): void
519
+ getCheckedItems(): number[]
520
+ getCheckedData(): RowData[]
521
+
522
+ // 컬럼
523
+ getColumn(field: string): ColumnDefinition | null
524
+ setColumnWidth(field: string, width: number): void
525
+ showColumn(field: string): void
526
+ hideColumn(field: string): void
527
+ autoFitColumn(field: string): void
528
+ fixColumn(field: string, position: 'left' | 'right' | false): void // v0.6.0
529
+ reorderColumn(sourceField: string, targetField: string): void // v0.6.0
530
+
531
+ // ν–‰
532
+ moveRow(fromIndex: number, toIndex: number): void // v0.6.0
533
+
534
+ // ν΄λ¦½λ³΄λ“œ (v0.3.0)
535
+ copy(): void
536
+ paste(): void
537
+ cut(): void
538
+
539
+ // μ‹€ν–‰ μ·¨μ†Œ/λ‹€μ‹œ μ‹€ν–‰ (v0.5.0)
540
+ undo(): boolean
541
+ redo(): boolean
542
+ canUndo(): boolean
543
+ canRedo(): boolean
544
+ clearHistory(): void
545
+
546
+ // 내보내기/κ°€μ Έμ˜€κΈ° (v0.4.0)
547
+ exportToExcel(options?: ExportOptions): void
548
+ importFromExcel(file: File, sheetIndex?: number): Promise<ImportResult>
549
+ exportToCSV(options?: ExportOptions): string
550
+ downloadCSV(options?: ExportOptions): void
551
+
552
+ // Summary/Aggregation (v0.7.1)
553
+ getSummaryValue(field: string): CellValue
554
+ getSummaryValues(): Record<string, CellValue>
555
+ refreshSummary(): void
556
+
557
+ // Pagination (v0.10.0)
558
+ goToPage(page: number): void
559
+ setPageSize(pageSize: number): void
560
+ getPaginationState(): PaginationState
561
+ fetchData(): Promise<void>
562
+
563
+ // μœ ν‹Έλ¦¬ν‹°
564
+ refresh(): void
565
+ setLoading(loading: boolean): void
566
+ destroy(): void
567
+ ```
568
+
569
+ ### 이벀트
570
+
571
+ ```typescript
572
+ interface GridEvents {
573
+ onReady?: (grid: VeloxGridInstance) => void;
574
+ onDataChange?: (data: RowData[]) => void;
575
+
576
+ // ν–‰
577
+ onRowClick?: (index: number, row: RowData) => void;
578
+ onRowDoubleClick?: (index: number, row: RowData) => void;
579
+ onRowSelect?: (index: number, selected: boolean) => void;
580
+ onSelectionChange?: (selectedRows: number[]) => void;
581
+
582
+ // μ…€
583
+ onCellClick?: (rowIndex: number, field: string, value: CellValue) => void;
584
+ onCellSelect?: (cell: CellIndex, selected: boolean) => void;
585
+ onCellSelectionChange?: (selectedCells: CellIndex[]) => void;
586
+
587
+ // νŽΈμ§‘
588
+ onCellEditStart?: (rowIndex: number, field: string, value: CellValue) => void;
589
+ onCellEditEnd?: (event: CellEditEvent) => void;
590
+ onCellEditCancel?: (rowIndex: number, field: string) => void;
591
+ onValidationError?: (event: ValidationErrorEvent) => void; // v0.7.0
592
+
593
+ // μ²΄ν¬λ°•μŠ€
594
+ onCheckChange?: (index: number, checked: boolean) => void;
595
+ onCheckAllChange?: (checked: boolean) => void;
596
+
597
+ // 데이터 μž‘μ—…
598
+ onRowAdd?: (row: RowData, index: number) => void;
599
+ onRowUpdate?: (row: RowData, index: number, changes: Partial<RowData>) => void;
600
+ onRowRemove?: (row: RowData, index: number) => void;
601
+
602
+ // 컬럼
603
+ onColumnResize?: (field: string, width: number) => void;
604
+ onColumnReorder?: (sourceField: string, sourceIndex: number, targetIndex: number) => void; // v0.6.0
605
+
606
+ // ν΄λ¦½λ³΄λ“œ
607
+ onCopy?: (data: string[][]) => void;
608
+ onPaste?: (data: string[][], focusedCell: CellIndex) => void;
609
+ onCut?: (data: string[][]) => void;
610
+
611
+ // μ‹€ν–‰ μ·¨μ†Œ/λ‹€μ‹œ μ‹€ν–‰
612
+ onUndo?: (action: UndoAction) => void;
613
+ onRedo?: (action: UndoAction) => void;
614
+
615
+ // 기타
616
+ onSort?: (sortState: SortState[]) => void;
617
+ onFilter?: (filterState: FilterState) => void;
618
+ onScroll?: (scrollTop: number, scrollLeft: number) => void;
619
+ onDestroy?: () => void;
620
+
621
+ // Pagination (v0.10.0)
622
+ onPageChange?: (page: number, pageSize: number) => void;
623
+ onPageSizeChange?: (pageSize: number) => void;
624
+ }
625
+ ```
626
+
627
+ ## 🎹 ν‚€λ³΄λ“œ 단좕킀
628
+
629
+ ### Read λͺ¨λ“œ (읽기/선택)
630
+ | 단좕킀 | λ™μž‘ |
631
+ |----------|--------|
632
+ | `λ°©ν–₯ν‚€` | μ…€ 이동 |
633
+ | `Tab` | 였λ₯Έμͺ½ μ…€λ‘œ 이동 (ν–‰ λμ—μ„œ λ‹€μŒ ν–‰μœΌλ‘œ λž˜ν•‘) |
634
+ | `Shift + Tab` | μ™Όμͺ½ μ…€λ‘œ 이동 (ν–‰ μ‹œμž‘μ—μ„œ 이전 ν–‰μœΌλ‘œ λž˜ν•‘) |
635
+ | `Shift + λ°©ν–₯ν‚€` | 선택 μ˜μ—­ ν™•μž₯ |
636
+ | `Ctrl + A` | 전체 선택 |
637
+ | `Ctrl + C` | 볡사 |
638
+ | `Ctrl + V` | λΆ™μ—¬λ„£κΈ° |
639
+ | `Ctrl + X` | μž˜λΌλ‚΄κΈ° |
640
+ | `Ctrl + Z` | μ‹€ν–‰ μ·¨μ†Œ |
641
+ | `Ctrl + Y` | λ‹€μ‹œ μ‹€ν–‰ |
642
+ | `Delete / Backspace` | μ„ νƒλœ μ…€ λ‚΄μš© μ‚­μ œ |
643
+ | `Enter / F2` | νŽΈμ§‘ μ‹œμž‘ |
644
+ | `Space` | μ²΄ν¬λ°•μŠ€ ν† κΈ€ |
645
+ | `Home / End` | 첫 / λ§ˆμ§€λ§‰ 컬럼 |
646
+ | `Ctrl + Home/End` | 첫 / λ§ˆμ§€λ§‰ ν–‰μ˜ 첫 / λ§ˆμ§€λ§‰ 컬럼 |
647
+ | `Page Up/Down` | νŽ˜μ΄μ§€ 이동 |
648
+
649
+ ### Edit λͺ¨λ“œ (νŽΈμ§‘ 쀑)
650
+ | 단좕킀 | λ™μž‘ |
651
+ |----------|--------|
652
+ | `Enter` | μ €μž₯ ν›„ μ•„λž˜ μ…€λ‘œ 이동 |
653
+ | `Shift + Enter` | μ €μž₯ ν›„ μœ„ μ…€λ‘œ 이동 |
654
+ | `Tab` | μ €μž₯ ν›„ 였λ₯Έμͺ½ μ…€λ‘œ 이동 (ν–‰ λμ—μ„œ λ‹€μŒ ν–‰μœΌλ‘œ) |
655
+ | `Shift + Tab` | μ €μž₯ ν›„ μ™Όμͺ½ μ…€λ‘œ 이동 (ν–‰ μ‹œμž‘μ—μ„œ 이전 ν–‰μœΌλ‘œ) |
656
+ | `Escape` | νŽΈμ§‘ μ·¨μ†Œ |
657
+ | `타이핑` | Quick Edit - μ¦‰μ‹œ νŽΈμ§‘ μ‹œμž‘ (κΈ°μ‘΄ λ‚΄μš© λŒ€μ²΄) |
658
+
659
+ > **Quick Edit**: 셀을 μ„ νƒν•œ μƒνƒœμ—μ„œ λ°”λ‘œ νƒ€μ΄ν•‘ν•˜λ©΄ νŽΈμ§‘ λͺ¨λ“œλ‘œ μ§„μž…ν•˜μ—¬ κΈ°μ‘΄ 값을 λŒ€μ²΄ν•©λ‹ˆλ‹€ (Excel μŠ€νƒ€μΌ)
660
+
661
+ ## πŸ“ ν”„λ‘œμ νŠΈ ꡬ쑰
662
+
663
+ ```
664
+ velox-grid/
665
+ β”œβ”€β”€ src/
666
+ β”‚ β”œβ”€β”€ core/
667
+ β”‚ β”‚ β”œβ”€β”€ VeloxGrid.ts # Facade 클래슀 (2,044쀄)
668
+ β”‚ β”‚ β”œβ”€β”€ GridRenderer.ts # λ Œλ”λ§ λ‹΄λ‹Ή (482쀄)
669
+ β”‚ β”‚ β”œβ”€β”€ GridFilterPopup.ts # ν•„ν„° νŒμ—… UI (191쀄)
670
+ β”‚ β”‚ β”œβ”€β”€ GridColumnMenu.ts # 컬럼 메뉴 UI (188쀄)
671
+ β”‚ β”‚ β”œβ”€β”€ GridDragManager.ts # λ“œλž˜κ·Έ & λ¦¬μ‚¬μ΄μ¦ˆ (364쀄)
672
+ β”‚ β”‚ β”œβ”€β”€ GridHistory.ts # Undo/Redo 관리
673
+ β”‚ β”‚ β”œβ”€β”€ GridSelection.ts # 선택 관리
674
+ β”‚ β”‚ β”œβ”€β”€ GridVirtualScroll.ts # 가상 슀크둀
675
+ β”‚ β”‚ β”œβ”€β”€ GridEditor.ts # νŽΈμ§‘ 관리
676
+ β”‚ β”‚ β”œβ”€β”€ GridEditorFactory.ts # 에디터 생성
677
+ β”‚ β”‚ β”œβ”€β”€ GridKeyboard.ts # ν‚€λ³΄λ“œ 핸듀링
678
+ β”‚ β”‚ β”œβ”€β”€ GridColumnManager.ts # 컬럼 관리
679
+ β”‚ β”‚ β”œβ”€β”€ GridDataManager.ts # 데이터 관리
680
+ β”‚ β”‚ β”œβ”€β”€ GridValidator.ts # μ…€ 검증
681
+ β”‚ β”‚ β”œβ”€β”€ GridTooltip.ts # 툴팁
682
+ β”‚ β”‚ └── index.ts
683
+ β”‚ β”œβ”€β”€ styles/
684
+ β”‚ β”‚ β”œβ”€β”€ velox-grid.css # 메인 (@import)
685
+ β”‚ β”‚ β”œβ”€β”€ _variables.css # CSS λ³€μˆ˜
686
+ β”‚ β”‚ β”œβ”€β”€ _base.css # κΈ°λ³Έ λ ˆμ΄μ•„μ›ƒ
687
+ β”‚ β”‚ β”œβ”€β”€ _header.css # 헀더 μŠ€νƒ€μΌ
688
+ β”‚ β”‚ β”œβ”€β”€ _body.css # λ°”λ””/μ…€ μŠ€νƒ€μΌ
689
+ β”‚ β”‚ β”œβ”€β”€ _selection.css # 선택 μŠ€νƒ€μΌ
690
+ β”‚ β”‚ β”œβ”€β”€ _filter.css # ν•„ν„° νŒμ—…
691
+ β”‚ β”‚ β”œβ”€β”€ _column-menu.css # 컬럼 메뉴
692
+ β”‚ β”‚ β”œβ”€β”€ _drag.css # λ“œλž˜κ·Έ μ•€ λ“œλ‘­
693
+ β”‚ β”‚ β”œβ”€β”€ _editor.css # 에디터
694
+ β”‚ β”‚ β”œβ”€β”€ _tooltip.css # 툴팁
695
+ β”‚ β”‚ └── _loading.css # λ‘œλ”©
696
+ β”‚ β”œβ”€β”€ types/
697
+ β”‚ β”‚ └── index.ts
698
+ β”‚ └── utils/
699
+ β”‚ β”œβ”€β”€ data.ts
700
+ β”‚ β”œβ”€β”€ dom.ts
701
+ β”‚ β”œβ”€β”€ export.ts
702
+ β”‚ └── index.ts
703
+ β”œβ”€β”€ dist/ # λΉŒλ“œ 좜λ ₯
704
+ β”œβ”€β”€ examples/ # 예제 νŽ˜μ΄μ§€
705
+ β”œβ”€β”€ README.md
706
+ β”œβ”€β”€ ROADMAP.md
707
+ β”œβ”€β”€ CHANGELOG.md
708
+ └── package.json
709
+ ```
710
+
711
+ ## πŸ“Š λ²ˆλ“€ 크기
712
+
713
+ | 버전 | UMD | Gzipped |
714
+ |---------|-----|---------|
715
+ | v0.10.0 | 100.20 KB | 24.69 KB |
716
+ | v0.7.0 | 71.35 KB | 18.23 KB |
717
+ | v0.6.0 | 58.94 KB | 14.92 KB |
718
+
719
+ ## πŸ—ΊοΈ λ‘œλ“œλ§΅
720
+
721
+ μžμ„Έν•œ κΈ°λŠ₯ κ³„νšμ€ [ROADMAP.md](./ROADMAP.md)λ₯Ό μ°Έκ³ ν•˜μ„Έμš”.
722
+
723
+ ### μ˜ˆμ •λœ κΈ°λŠ₯
724
+
725
+ - **v0.8.0**: ν‘Έν„° μš”μ•½, κ·Έλ£Ή μš”μ•½
726
+ - **v0.9.0**: React 래퍼 μ»΄ν¬λ„ŒνŠΈ
727
+ - **v1.0.0**: μ•ˆμ •μ μΈ API, μ ‘κ·Όμ„±, ν…Œλ§ˆ μ‹œμŠ€ν…œ
728
+
729
+ ## πŸ“„ λ³€κ²½ 이λ ₯
730
+
731
+ 버전 νžˆμŠ€ν† λ¦¬λŠ” [CHANGELOG.md](./CHANGELOG.md)λ₯Ό μ°Έκ³ ν•˜μ„Έμš”.
732
+
733
+ ## 🀝 κΈ°μ—¬ν•˜κΈ°
734
+
735
+ κΈ°μ—¬λŠ” μ–Έμ œλ‚˜ ν™˜μ˜ν•©λ‹ˆλ‹€! Pull Requestλ₯Ό 자유둭게 μ œμΆœν•΄μ£Όμ„Έμš”.
736
+
737
+ ## πŸ“ λΌμ΄μ„ μŠ€
738
+
739
+ MIT Β© [bart](https://github.com/bart-idea)
740
+
741
+ ## πŸ”— 링크
742
+
743
+ - [GitHub μ €μž₯μ†Œ](https://github.com/bart-idea/velox-grid)
744
+ - [NPM νŒ¨ν‚€μ§€](https://www.npmjs.com/package/velox-grid)
745
+ - [데λͺ¨](https://bart-idea.github.io/velox-grid/)
746
+ - [λ¬Έμ„œ](https://github.com/bart-idea/velox-grid/wiki)
747
+
748
+ ## πŸ’¬ 지원
749
+
750
+ - [이슈](https://github.com/bart-idea/velox-grid/issues)
751
+ - [ν† λ‘ ](https://github.com/bart-idea/velox-grid/discussions)
752
+
753
+ ---
754
+
755
+ **bartκ°€ ❀️ λ₯Ό λ‹΄μ•„ λ§Œλ“€μ—ˆμŠ΅λ‹ˆλ‹€**