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
@@ -0,0 +1,3387 @@
1
+ function f(d, t, e) {
2
+ const i = document.createElement(d);
3
+ return t && (i.className = t), e && Object.entries(e).forEach(([s, n]) => {
4
+ i.setAttribute(s, n);
5
+ }), i;
6
+ }
7
+ function y(d, ...t) {
8
+ d.classList.add(...t);
9
+ }
10
+ function b(d, ...t) {
11
+ d.classList.remove(...t);
12
+ }
13
+ function ct(d, t, e) {
14
+ d.classList.toggle(t, e);
15
+ }
16
+ function ht(d, t) {
17
+ return d.classList.contains(t);
18
+ }
19
+ function ut(d, t) {
20
+ Object.assign(d.style, t);
21
+ }
22
+ function V(d, t) {
23
+ let e = !1;
24
+ return function(...i) {
25
+ e || (d.apply(this, i), e = !0, setTimeout(() => e = !1, t));
26
+ };
27
+ }
28
+ function ft(d, t) {
29
+ let e = null;
30
+ return function(...i) {
31
+ e && clearTimeout(e), e = setTimeout(() => d.apply(this, i), t);
32
+ };
33
+ }
34
+ function P(d) {
35
+ if (d === null || typeof d != "object")
36
+ return d;
37
+ if (d instanceof Date)
38
+ return new Date(d.getTime());
39
+ if (Array.isArray(d))
40
+ return d.map((e) => P(e));
41
+ const t = {};
42
+ for (const e in d)
43
+ Object.prototype.hasOwnProperty.call(d, e) && (t[e] = P(d[e]));
44
+ return t;
45
+ }
46
+ function _(d = "velox") {
47
+ return `${d}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
48
+ }
49
+ function k(d, t = "text") {
50
+ if (d == null)
51
+ return "";
52
+ switch (t) {
53
+ case "number":
54
+ return typeof d == "number" ? d.toLocaleString() : String(d);
55
+ case "boolean":
56
+ return d ? "Yes" : "No";
57
+ case "date":
58
+ return d instanceof Date ? d.toLocaleDateString() : String(d);
59
+ case "datetime":
60
+ return d instanceof Date ? d.toLocaleString() : String(d);
61
+ case "text":
62
+ default:
63
+ return String(d);
64
+ }
65
+ }
66
+ function pt(d, t) {
67
+ if (d === "" || d === null || d === void 0)
68
+ return null;
69
+ switch (t) {
70
+ case "number":
71
+ const e = parseFloat(d.replace(/,/g, ""));
72
+ return isNaN(e) ? null : e;
73
+ case "boolean":
74
+ return d.toLowerCase() === "true" || d === "1" || d.toLowerCase() === "yes";
75
+ case "date":
76
+ case "datetime":
77
+ const i = new Date(d);
78
+ return isNaN(i.getTime()) ? null : i;
79
+ case "text":
80
+ default:
81
+ return d;
82
+ }
83
+ }
84
+ function z(d, t, e = "text") {
85
+ if (d == null) return t == null ? 0 : -1;
86
+ if (t == null) return 1;
87
+ switch (e) {
88
+ case "number":
89
+ return d - t;
90
+ case "boolean":
91
+ return d === t ? 0 : d ? 1 : -1;
92
+ case "date":
93
+ case "datetime":
94
+ const i = d instanceof Date ? d : new Date(d), s = t instanceof Date ? t : new Date(t);
95
+ return i.getTime() - s.getTime();
96
+ case "text":
97
+ default:
98
+ return String(d).localeCompare(String(t));
99
+ }
100
+ }
101
+ function L(d, t, e) {
102
+ return t.length ? [...d].sort((i, s) => {
103
+ for (const { field: n, direction: o } of t) {
104
+ if (!o) continue;
105
+ const l = e[n] || "text", a = z(i[n], s[n], l);
106
+ if (a !== 0)
107
+ return o === "asc" ? a : -a;
108
+ }
109
+ return 0;
110
+ }) : d;
111
+ }
112
+ function W(d, t) {
113
+ const { operator: e, value: i, value2: s } = t;
114
+ if (e === "isEmpty")
115
+ return d == null || d === "";
116
+ if (e === "isNotEmpty")
117
+ return d != null && d !== "";
118
+ const n = String(d ?? "").toLowerCase(), o = String(i ?? "").toLowerCase();
119
+ switch (e) {
120
+ case "equals":
121
+ return n === o;
122
+ case "notEquals":
123
+ return n !== o;
124
+ case "contains":
125
+ return n.includes(o);
126
+ case "notContains":
127
+ return !n.includes(o);
128
+ case "startsWith":
129
+ return n.startsWith(o);
130
+ case "endsWith":
131
+ return n.endsWith(o);
132
+ case "greaterThan":
133
+ return Number(d) > Number(i);
134
+ case "lessThan":
135
+ return Number(d) < Number(i);
136
+ case "greaterThanOrEqual":
137
+ return Number(d) >= Number(i);
138
+ case "lessThanOrEqual":
139
+ return Number(d) <= Number(i);
140
+ case "between":
141
+ const l = Number(d);
142
+ return l >= Number(i) && l <= Number(s);
143
+ default:
144
+ return !0;
145
+ }
146
+ }
147
+ function T(d, t) {
148
+ if (!t || !t.conditions.length)
149
+ return d;
150
+ const { conditions: e, logic: i } = t;
151
+ return d.filter((s) => {
152
+ const n = e.map(
153
+ (o) => W(s[o.field], o)
154
+ );
155
+ return i === "and" ? n.every(Boolean) : n.some(Boolean);
156
+ });
157
+ }
158
+ function gt(d) {
159
+ const t = document.createElement("div");
160
+ return t.textContent = d, t.innerHTML;
161
+ }
162
+ function O(d) {
163
+ const { data: t, displayData: e, columns: i, selectedRows: s, options: n } = d, o = n.columns ? i.filter((r) => n.columns.includes(r.field) && r.visible !== !1) : i.filter((r) => r.visible !== !1);
164
+ let l;
165
+ if (n.selectedOnly && s.length > 0) {
166
+ const r = n.filteredOnly ? e : t;
167
+ l = s.map((h) => r[h]).filter(Boolean);
168
+ } else n.filteredOnly ? l = e : l = t;
169
+ const a = [];
170
+ if (n.includeHeader !== !1) {
171
+ const r = o.map((h) => F(h.header));
172
+ a.push(r.join(","));
173
+ }
174
+ return l.forEach((r) => {
175
+ const h = o.map((p) => {
176
+ const c = r[p.field];
177
+ return F(X(c, p.type));
178
+ });
179
+ a.push(h.join(","));
180
+ }), a.join(`\r
181
+ `);
182
+ }
183
+ function F(d) {
184
+ if (d == null) return "";
185
+ const t = String(d);
186
+ return t.includes(",") || t.includes('"') || t.includes(`
187
+ `) || t.includes("\r") ? `"${t.replace(/"/g, '""')}"` : t;
188
+ }
189
+ function A(d) {
190
+ const { data: t, displayData: e, columns: i, selectedRows: s, options: n } = d, o = n.columns ? i.filter((r) => n.columns.includes(r.field) && r.visible !== !1) : i.filter((r) => r.visible !== !1);
191
+ let l;
192
+ if (n.selectedOnly && s.length > 0) {
193
+ const r = n.filteredOnly ? e : t;
194
+ l = s.map((h) => r[h]).filter(Boolean);
195
+ } else n.filteredOnly ? l = e : l = t;
196
+ const a = l.map((r) => {
197
+ const h = {};
198
+ return o.forEach((p) => {
199
+ h[p.field] = r[p.field];
200
+ }), h;
201
+ });
202
+ return JSON.stringify(a, null, 2);
203
+ }
204
+ function B() {
205
+ return typeof window.XLSX < "u";
206
+ }
207
+ function I() {
208
+ const d = window.XLSX;
209
+ if (!d)
210
+ throw new Error(
211
+ 'SheetJS (xlsx) library is not loaded. Please include it via CDN: <script src="https://cdn.sheetjs.com/xlsx-0.20.1/package/dist/xlsx.full.min.js"><\/script>'
212
+ );
213
+ return d;
214
+ }
215
+ function K(d) {
216
+ const t = I(), { data: e, displayData: i, columns: s, selectedRows: n, options: o } = d, l = o.columns ? s.filter((m) => o.columns.includes(m.field) && m.visible !== !1) : s.filter((m) => m.visible !== !1);
217
+ let a;
218
+ if (o.selectedOnly && n.length > 0) {
219
+ const m = o.filteredOnly ? i : e;
220
+ a = n.map((C) => m[C]).filter(Boolean);
221
+ } else o.filteredOnly ? a = i : a = e;
222
+ const r = [];
223
+ o.includeHeader !== !1 && r.push(l.map((m) => m.header)), a.forEach((m) => {
224
+ const C = l.map((x) => {
225
+ const v = m[x.field];
226
+ return q(v, x.type);
227
+ });
228
+ r.push(C);
229
+ });
230
+ const h = t.utils.aoa_to_sheet(r), p = l.map((m) => ({
231
+ wch: Math.max(
232
+ m.header.length,
233
+ ...a.slice(0, 100).map((C) => {
234
+ const x = C[m.field];
235
+ return String(x ?? "").length;
236
+ })
237
+ ) + 2
238
+ }));
239
+ h["!cols"] = p;
240
+ const c = t.utils.book_new(), u = o.sheetName || "Sheet1";
241
+ t.utils.book_append_sheet(c, h, u);
242
+ const g = (o.filename || "export") + ".xlsx";
243
+ t.writeFile(c, g);
244
+ }
245
+ function q(d, t) {
246
+ if (d == null) return "";
247
+ switch (t) {
248
+ case "number":
249
+ return typeof d == "number" ? d : parseFloat(String(d)) || 0;
250
+ case "boolean":
251
+ return !!d;
252
+ case "date":
253
+ case "datetime":
254
+ if (d instanceof Date) return d;
255
+ if (typeof d == "string" || typeof d == "number") {
256
+ const e = new Date(d);
257
+ return isNaN(e.getTime()) ? String(d) : e;
258
+ }
259
+ return String(d);
260
+ default:
261
+ return String(d);
262
+ }
263
+ }
264
+ function X(d, t) {
265
+ if (d == null) return "";
266
+ switch (t) {
267
+ case "date":
268
+ return d instanceof Date ? d.toISOString().split("T")[0] : String(d);
269
+ case "datetime":
270
+ return d instanceof Date ? d.toISOString() : String(d);
271
+ default:
272
+ return String(d);
273
+ }
274
+ }
275
+ function j(d, t = !0) {
276
+ var i;
277
+ const e = {
278
+ data: [],
279
+ headers: [],
280
+ errors: []
281
+ };
282
+ try {
283
+ const s = U(d);
284
+ if (s.length === 0)
285
+ return e.errors.push("CSV file is empty"), e;
286
+ let n = 0;
287
+ if (t)
288
+ e.headers = s[0], n = 1;
289
+ else {
290
+ const o = ((i = s[0]) == null ? void 0 : i.length) || 0;
291
+ e.headers = Array.from({ length: o }, (l, a) => `Column${a + 1}`);
292
+ }
293
+ for (let o = n; o < s.length; o++) {
294
+ const l = s[o], a = {};
295
+ e.headers.forEach((r, h) => {
296
+ a[r] = l[h] ?? "";
297
+ }), e.data.push(a);
298
+ }
299
+ } catch (s) {
300
+ e.errors.push(`CSV parsing error: ${s instanceof Error ? s.message : "Unknown error"}`);
301
+ }
302
+ return e;
303
+ }
304
+ function U(d) {
305
+ const t = [];
306
+ let e = [], i = "", s = !1;
307
+ for (let n = 0; n < d.length; n++) {
308
+ const o = d[n], l = d[n + 1];
309
+ if (s)
310
+ o === '"' ? l === '"' ? (i += '"', n++) : s = !1 : i += o;
311
+ else if (o === '"')
312
+ s = !0;
313
+ else if (o === ",")
314
+ e.push(i), i = "";
315
+ else {
316
+ if (o === "\r")
317
+ continue;
318
+ o === `
319
+ ` ? (e.push(i), e.some((a) => a.trim() !== "") && t.push(e), e = [], i = "") : i += o;
320
+ }
321
+ }
322
+ return (i !== "" || e.length > 0) && (e.push(i), e.some((n) => n.trim() !== "") && t.push(e)), t;
323
+ }
324
+ function G(d, t = 0) {
325
+ return new Promise((e) => {
326
+ const i = {
327
+ data: [],
328
+ headers: [],
329
+ errors: []
330
+ };
331
+ if (!B()) {
332
+ i.errors.push(
333
+ 'SheetJS (xlsx) library is not loaded. Please include it via CDN: <script src="https://cdn.sheetjs.com/xlsx-0.20.1/package/dist/xlsx.full.min.js"><\/script>'
334
+ ), e(i);
335
+ return;
336
+ }
337
+ const s = I(), n = new FileReader();
338
+ n.onload = (o) => {
339
+ var l;
340
+ try {
341
+ const a = (l = o.target) == null ? void 0 : l.result, r = s.read(a, { type: "array" }), h = r.SheetNames;
342
+ if (t >= h.length) {
343
+ i.errors.push(`Sheet index ${t} not found. Available sheets: ${h.join(", ")}`), e(i);
344
+ return;
345
+ }
346
+ const p = h[t], c = r.Sheets[p], u = s.utils.sheet_to_json(c, { header: 1 });
347
+ if (u.length === 0) {
348
+ i.errors.push("Excel sheet is empty"), e(i);
349
+ return;
350
+ }
351
+ i.headers = u[0].map((g) => String(g ?? ""));
352
+ for (let g = 1; g < u.length; g++) {
353
+ const m = {}, C = u[g];
354
+ i.headers.forEach((x, v) => {
355
+ const w = C[v];
356
+ m[x] = w ?? "";
357
+ }), Object.values(m).some((x) => x !== "") && i.data.push(m);
358
+ }
359
+ e(i);
360
+ } catch (a) {
361
+ i.errors.push(`Excel parsing error: ${a instanceof Error ? a.message : "Unknown error"}`), e(i);
362
+ }
363
+ }, n.onerror = () => {
364
+ i.errors.push("Failed to read file"), e(i);
365
+ }, n.readAsArrayBuffer(d);
366
+ });
367
+ }
368
+ function mt(d, t) {
369
+ return new Promise((e) => {
370
+ const i = {
371
+ data: [],
372
+ headers: [],
373
+ errors: []
374
+ };
375
+ if (!B()) {
376
+ i.errors.push("SheetJS (xlsx) library is not loaded."), e(i);
377
+ return;
378
+ }
379
+ const s = I(), n = new FileReader();
380
+ n.onload = (o) => {
381
+ var l;
382
+ try {
383
+ const a = (l = o.target) == null ? void 0 : l.result, r = s.read(a, { type: "array" });
384
+ if (!r.SheetNames.includes(t)) {
385
+ i.errors.push(`Sheet "${t}" not found. Available sheets: ${r.SheetNames.join(", ")}`), e(i);
386
+ return;
387
+ }
388
+ const h = r.Sheets[t], p = s.utils.sheet_to_json(h, { header: 1 });
389
+ if (p.length === 0) {
390
+ i.errors.push("Excel sheet is empty"), e(i);
391
+ return;
392
+ }
393
+ i.headers = p[0].map((c) => String(c ?? ""));
394
+ for (let c = 1; c < p.length; c++) {
395
+ const u = {}, g = p[c];
396
+ i.headers.forEach((m, C) => {
397
+ u[m] = g[C] ?? "";
398
+ }), Object.values(u).some((m) => m !== "") && i.data.push(u);
399
+ }
400
+ e(i);
401
+ } catch (a) {
402
+ i.errors.push(`Excel parsing error: ${a instanceof Error ? a.message : "Unknown error"}`), e(i);
403
+ }
404
+ }, n.onerror = () => {
405
+ i.errors.push("Failed to read file"), e(i);
406
+ }, n.readAsArrayBuffer(d);
407
+ });
408
+ }
409
+ function N(d, t, e = "text/plain") {
410
+ const i = d instanceof Blob ? d : new Blob([d], { type: e }), s = URL.createObjectURL(i), n = document.createElement("a");
411
+ n.href = s, n.download = t, n.style.display = "none", document.body.appendChild(n), n.click(), document.body.removeChild(n), URL.revokeObjectURL(s);
412
+ }
413
+ function Y(d) {
414
+ const t = O(d), e = (d.options.filename || "export") + ".csv";
415
+ N(t, e, "text/csv;charset=utf-8");
416
+ }
417
+ function J(d) {
418
+ const t = A(d), e = (d.options.filename || "export") + ".json";
419
+ N(t, e, "application/json");
420
+ }
421
+ const Q = {
422
+ enabled: !0,
423
+ maxSize: 50
424
+ };
425
+ class Z {
426
+ constructor(t = {}) {
427
+ this.undoStack = [], this.redoStack = [], this.options = { ...Q, ...t };
428
+ }
429
+ /**
430
+ * Check if history tracking is enabled
431
+ */
432
+ isEnabled() {
433
+ return this.options.enabled;
434
+ }
435
+ /**
436
+ * Set enabled state
437
+ */
438
+ setEnabled(t) {
439
+ this.options.enabled = t;
440
+ }
441
+ /**
442
+ * Set maximum stack size
443
+ */
444
+ setMaxSize(t) {
445
+ this.options.maxSize = t, this.trimStack();
446
+ }
447
+ /**
448
+ * Push an action to the undo stack
449
+ */
450
+ push(t) {
451
+ this.options.enabled && (this.undoStack.push(t), this.trimStack(), this.redoStack = []);
452
+ }
453
+ /**
454
+ * Create and push a cell edit action
455
+ */
456
+ pushCellEdit(t, e, i, s) {
457
+ this.push({
458
+ type: "cell_edit",
459
+ timestamp: Date.now(),
460
+ data: { rowIndex: t, field: e, oldValue: i, newValue: s }
461
+ });
462
+ }
463
+ /**
464
+ * Create and push a bulk edit action (paste, cut, delete)
465
+ */
466
+ pushBulkEdit(t, e) {
467
+ e.length !== 0 && this.push({
468
+ type: t,
469
+ timestamp: Date.now(),
470
+ data: { changes: e }
471
+ });
472
+ }
473
+ /**
474
+ * Create and push a row add action
475
+ */
476
+ pushRowAdd(t, e) {
477
+ this.push({
478
+ type: "row_add",
479
+ timestamp: Date.now(),
480
+ data: { row: { ...t }, index: e }
481
+ });
482
+ }
483
+ /**
484
+ * Create and push a row remove action
485
+ */
486
+ pushRowRemove(t, e) {
487
+ this.push({
488
+ type: "row_remove",
489
+ timestamp: Date.now(),
490
+ data: { row: { ...t }, index: e }
491
+ });
492
+ }
493
+ /**
494
+ * Pop from undo stack and push to redo stack
495
+ * Returns the action to be undone
496
+ */
497
+ popUndo() {
498
+ if (!this.options.enabled || this.undoStack.length === 0)
499
+ return null;
500
+ const t = this.undoStack.pop();
501
+ return this.redoStack.push(t), t;
502
+ }
503
+ /**
504
+ * Pop from redo stack and push to undo stack
505
+ * Returns the action to be redone
506
+ */
507
+ popRedo() {
508
+ if (!this.options.enabled || this.redoStack.length === 0)
509
+ return null;
510
+ const t = this.redoStack.pop();
511
+ return this.undoStack.push(t), t;
512
+ }
513
+ /**
514
+ * Check if undo is available
515
+ */
516
+ canUndo() {
517
+ return this.options.enabled && this.undoStack.length > 0;
518
+ }
519
+ /**
520
+ * Check if redo is available
521
+ */
522
+ canRedo() {
523
+ return this.options.enabled && this.redoStack.length > 0;
524
+ }
525
+ /**
526
+ * Get undo stack size
527
+ */
528
+ getUndoCount() {
529
+ return this.undoStack.length;
530
+ }
531
+ /**
532
+ * Get redo stack size
533
+ */
534
+ getRedoCount() {
535
+ return this.redoStack.length;
536
+ }
537
+ /**
538
+ * Clear all history
539
+ */
540
+ clear() {
541
+ this.undoStack = [], this.redoStack = [];
542
+ }
543
+ /**
544
+ * Trim undo stack to max size
545
+ */
546
+ trimStack() {
547
+ for (; this.undoStack.length > this.options.maxSize; )
548
+ this.undoStack.shift();
549
+ }
550
+ }
551
+ class tt {
552
+ /**
553
+ * 단일 값에 대한 유효성 검사
554
+ * @param value - 검사할 값
555
+ * @param rules - 유효성 규칙 배열
556
+ * @param row - 전체 행 데이터 (custom validator용)
557
+ * @returns 검사 결과
558
+ */
559
+ static validate(t, e, i) {
560
+ const s = [];
561
+ for (const n of e) {
562
+ const o = this.validateRule(t, n, i);
563
+ o && s.push({ field: "", message: o });
564
+ }
565
+ return {
566
+ valid: s.length === 0,
567
+ errors: s
568
+ };
569
+ }
570
+ /**
571
+ * 단일 규칙 검사
572
+ * @param value - 검사할 값
573
+ * @param rule - 유효성 규칙
574
+ * @param row - 전체 행 데이터
575
+ * @returns 에러 메시지 또는 null
576
+ */
577
+ static validateRule(t, e, i) {
578
+ switch (e.type) {
579
+ case "required":
580
+ return this.validateRequired(t) ? null : e.message;
581
+ case "min":
582
+ return this.validateMin(t, e.value) ? null : e.message;
583
+ case "max":
584
+ return this.validateMax(t, e.value) ? null : e.message;
585
+ case "minLength":
586
+ return this.validateMinLength(t, e.value) ? null : e.message;
587
+ case "maxLength":
588
+ return this.validateMaxLength(t, e.value) ? null : e.message;
589
+ case "pattern":
590
+ return this.validatePattern(t, e.value) ? null : e.message;
591
+ case "custom":
592
+ if (e.validator) {
593
+ const s = e.validator(t, i || {});
594
+ return typeof s == "boolean" ? s ? null : e.message : s || null;
595
+ }
596
+ return null;
597
+ default:
598
+ return null;
599
+ }
600
+ }
601
+ /**
602
+ * Required 검사 - null, undefined, 빈 문자열 체크
603
+ */
604
+ static validateRequired(t) {
605
+ return !(t == null || typeof t == "string" && t.trim() === "");
606
+ }
607
+ /**
608
+ * Min 검사 - 숫자 최소값
609
+ */
610
+ static validateMin(t, e) {
611
+ if (t == null)
612
+ return !0;
613
+ const i = typeof t == "number" ? t : parseFloat(String(t));
614
+ return !isNaN(i) && i >= e;
615
+ }
616
+ /**
617
+ * Max 검사 - 숫자 최대값
618
+ */
619
+ static validateMax(t, e) {
620
+ if (t == null)
621
+ return !0;
622
+ const i = typeof t == "number" ? t : parseFloat(String(t));
623
+ return !isNaN(i) && i <= e;
624
+ }
625
+ /**
626
+ * MinLength 검사 - 문자열 최소 길이
627
+ */
628
+ static validateMinLength(t, e) {
629
+ return t == null ? !0 : String(t).length >= e;
630
+ }
631
+ /**
632
+ * MaxLength 검사 - 문자열 최대 길이
633
+ */
634
+ static validateMaxLength(t, e) {
635
+ return t == null ? !0 : String(t).length <= e;
636
+ }
637
+ /**
638
+ * Pattern 검사 - 정규식 매칭
639
+ */
640
+ static validatePattern(t, e) {
641
+ return t == null ? !0 : (typeof e == "string" ? new RegExp(e) : e).test(String(t));
642
+ }
643
+ /**
644
+ * 전체 행 유효성 검사
645
+ * @param row - 검사할 행 데이터
646
+ * @param columns - 컬럼 정의 (validation 규칙 포함)
647
+ * @returns 검사 결과
648
+ */
649
+ static validateRow(t, e) {
650
+ const i = [];
651
+ for (const s of e)
652
+ if (s.validation && s.validation.length > 0) {
653
+ const n = t[s.field], o = this.validate(n, s.validation, t);
654
+ if (!o.valid)
655
+ for (const l of o.errors)
656
+ i.push({
657
+ field: s.field,
658
+ message: l.message
659
+ });
660
+ }
661
+ return {
662
+ valid: i.length === 0,
663
+ errors: i
664
+ };
665
+ }
666
+ /**
667
+ * 전체 데이터 유효성 검사
668
+ * @param data - 검사할 데이터 배열
669
+ * @param columns - 컬럼 정의
670
+ * @returns 행별 검사 결과 배열
671
+ */
672
+ static validateAll(t, e) {
673
+ return t.map((i) => this.validateRow(i, e));
674
+ }
675
+ }
676
+ class et {
677
+ /**
678
+ * Create an editor element based on type
679
+ */
680
+ static createEditor(t, e, i, s, n) {
681
+ const { type: o } = e;
682
+ switch (o) {
683
+ case "text":
684
+ case "number":
685
+ return this.createTextEditor(t, e, i, s, n);
686
+ case "select":
687
+ return this.createSelectEditor(t, e, i, s, n);
688
+ case "date":
689
+ return this.createDateEditor(t, e, i, s, n);
690
+ case "checkbox":
691
+ return this.createCheckboxEditor(t, e, i, s, n);
692
+ case "custom":
693
+ return this.createCustomEditor(t, e, i, s, n);
694
+ default:
695
+ return this.createTextEditor(t, e, i, s, n);
696
+ }
697
+ }
698
+ /**
699
+ * Create text/number input editor
700
+ */
701
+ static createTextEditor(t, e, i, s, n) {
702
+ const o = document.createElement("input");
703
+ return o.className = "velox-edit-input", o.type = e.type === "number" ? "number" : "text", o.value = t != null ? String(t) : "", e.placeholder && (o.placeholder = e.placeholder), e.type === "number" && (e.min !== void 0 && (o.min = String(e.min)), e.max !== void 0 && (o.max = String(e.max)), e.step !== void 0 && (o.step = String(e.step))), o.addEventListener("blur", () => {
704
+ const l = e.type === "number" ? parseFloat(o.value) : o.value;
705
+ i(l);
706
+ }), o.addEventListener("keydown", (l) => {
707
+ if (l.key === "Enter") {
708
+ l.preventDefault(), l.stopPropagation();
709
+ const a = e.type === "number" ? parseFloat(o.value) : o.value;
710
+ i(a), n && n(l.shiftKey ? "up" : "down");
711
+ } else if (l.key === "Tab") {
712
+ l.preventDefault(), l.stopPropagation();
713
+ const a = e.type === "number" ? parseFloat(o.value) : o.value;
714
+ i(a), n && n(l.shiftKey ? "left" : "right");
715
+ } else l.key === "Escape" && (l.preventDefault(), l.stopPropagation(), s());
716
+ }), o;
717
+ }
718
+ /**
719
+ * Create select dropdown editor
720
+ */
721
+ static createSelectEditor(t, e, i, s, n) {
722
+ const o = document.createElement("select");
723
+ return o.className = "velox-edit-select", e.options && e.options.length > 0 && e.options.forEach((l) => {
724
+ const a = document.createElement("option");
725
+ a.value = String(l.value ?? ""), a.textContent = l.label, l.value === t && (a.selected = !0), o.appendChild(a);
726
+ }), o.addEventListener("change", () => {
727
+ console.log("📦 Select change event", o.value), i(o.value);
728
+ }), o.addEventListener("keydown", (l) => {
729
+ l.key === "Enter" ? (l.preventDefault(), l.stopPropagation(), i(o.value), n && n(l.shiftKey ? "up" : "down")) : l.key === "Tab" ? (l.preventDefault(), l.stopPropagation(), i(o.value), n && n(l.shiftKey ? "left" : "right")) : l.key === "Escape" && (l.preventDefault(), l.stopPropagation(), s());
730
+ }), o;
731
+ }
732
+ /**
733
+ * Create date input editor
734
+ */
735
+ static createDateEditor(t, e, i, s, n) {
736
+ const o = document.createElement("input");
737
+ if (o.className = "velox-edit-input velox-edit-date", o.type = "date", t instanceof Date)
738
+ o.value = t.toISOString().split("T")[0];
739
+ else if (typeof t == "string" && t) {
740
+ const l = new Date(t);
741
+ isNaN(l.getTime()) || (o.value = l.toISOString().split("T")[0]);
742
+ }
743
+ return o.addEventListener("blur", () => {
744
+ i(o.value ? new Date(o.value) : null);
745
+ }), o.addEventListener("keydown", (l) => {
746
+ l.key === "Enter" ? (l.preventDefault(), l.stopPropagation(), i(o.value ? new Date(o.value) : null), n && n(l.shiftKey ? "up" : "down")) : l.key === "Tab" ? (l.preventDefault(), l.stopPropagation(), i(o.value ? new Date(o.value) : null), n && n(l.shiftKey ? "left" : "right")) : l.key === "Escape" && (l.preventDefault(), l.stopPropagation(), s());
747
+ }), o;
748
+ }
749
+ /**
750
+ * Create checkbox editor
751
+ */
752
+ static createCheckboxEditor(t, e, i, s, n) {
753
+ const o = document.createElement("div");
754
+ o.className = "velox-edit-checkbox-container";
755
+ const l = document.createElement("input");
756
+ return l.className = "velox-edit-checkbox", l.type = "checkbox", l.checked = !!t, o.appendChild(l), l.addEventListener("change", () => {
757
+ console.log("📦 Checkbox change event", l.checked), i(l.checked);
758
+ }), l.addEventListener("keydown", (a) => {
759
+ a.key === "Enter" || a.key === " " ? (a.preventDefault(), a.stopPropagation(), l.checked = !l.checked, i(l.checked), a.key === "Enter" && n && n(a.shiftKey ? "up" : "down")) : a.key === "Tab" ? (a.preventDefault(), a.stopPropagation(), i(l.checked), n && n(a.shiftKey ? "left" : "right")) : a.key === "Escape" && (a.preventDefault(), a.stopPropagation(), s());
760
+ }), o;
761
+ }
762
+ /**
763
+ * Create custom editor using renderer function
764
+ */
765
+ static createCustomEditor(t, e, i, s, n) {
766
+ const o = document.createElement("div");
767
+ if (o.className = "velox-edit-custom", e.renderer)
768
+ e.renderer(o, t, i, s);
769
+ else
770
+ return this.createTextEditor(t, { type: "text" }, i, s, n);
771
+ return o;
772
+ }
773
+ }
774
+ class it {
775
+ // ms
776
+ constructor(t) {
777
+ this.container = t, this.tooltip = null, this.currentCell = null, this.hideTimeout = null, this.HIDE_DELAY = 100, this.createTooltipElement();
778
+ }
779
+ /**
780
+ * Create tooltip element
781
+ */
782
+ createTooltipElement() {
783
+ this.tooltip = document.createElement("div"), this.tooltip.className = "velox-tooltip", this.tooltip.style.display = "none", this.tooltip.style.position = "absolute", this.tooltip.style.pointerEvents = "none", this.container.appendChild(this.tooltip);
784
+ }
785
+ /**
786
+ * Show tooltip for a cell
787
+ */
788
+ show(t, e, i, s) {
789
+ this.hideTimeout && (clearTimeout(this.hideTimeout), this.hideTimeout = null), this.currentCell = t;
790
+ let n = null;
791
+ if (s.tooltip === !0 ? n = this.getAutoTooltipContent(t, e) : typeof s.tooltip == "function" && (n = s.tooltip(e, i)), !n || !this.tooltip) {
792
+ this.hide();
793
+ return;
794
+ }
795
+ this.tooltip.textContent = n, this.tooltip.style.display = "block", this.positionTooltip(t);
796
+ }
797
+ /**
798
+ * Get auto tooltip content for truncated text
799
+ */
800
+ getAutoTooltipContent(t, e) {
801
+ const i = t.querySelector(".velox-cell-content");
802
+ return i && (i.scrollWidth > i.clientWidth || i.scrollHeight > i.clientHeight) ? String(e ?? "") : null;
803
+ }
804
+ /**
805
+ * Position tooltip relative to cell
806
+ */
807
+ positionTooltip(t) {
808
+ if (!this.tooltip) return;
809
+ const e = t.getBoundingClientRect(), i = this.container.getBoundingClientRect(), s = this.tooltip.getBoundingClientRect();
810
+ let n = e.bottom - i.top + 5, o = e.left - i.left;
811
+ const l = window.innerWidth, a = window.innerHeight;
812
+ e.left + s.width > l && (o = e.right - i.left - s.width), e.bottom + s.height + 5 > a && (n = e.top - i.top - s.height - 5), this.tooltip.style.top = `${n}px`, this.tooltip.style.left = `${o}px`;
813
+ }
814
+ /**
815
+ * Hide tooltip
816
+ */
817
+ hide() {
818
+ this.hideTimeout && clearTimeout(this.hideTimeout), this.hideTimeout = window.setTimeout(() => {
819
+ this.tooltip && (this.tooltip.style.display = "none", this.tooltip.textContent = ""), this.currentCell = null, this.hideTimeout = null;
820
+ }, this.HIDE_DELAY);
821
+ }
822
+ /**
823
+ * Update tooltip position (for scroll events)
824
+ */
825
+ update() {
826
+ this.currentCell && this.tooltip && this.tooltip.style.display !== "none" && this.positionTooltip(this.currentCell);
827
+ }
828
+ /**
829
+ * Destroy tooltip
830
+ */
831
+ destroy() {
832
+ this.hideTimeout && clearTimeout(this.hideTimeout), this.tooltip && (this.tooltip.remove(), this.tooltip = null), this.currentCell = null;
833
+ }
834
+ }
835
+ class st {
836
+ constructor(t) {
837
+ this.ctx = t;
838
+ }
839
+ /**
840
+ * 전체 그리드 렌더링
841
+ */
842
+ render() {
843
+ const t = this.ctx.getState();
844
+ console.log("🎨 GridRenderer.render() called", { editing: t.edit.editing }), this.renderHeader(), this.renderBody(), this.renderFooter(), this.updateLoadingState(), console.log("🎨 GridRenderer.render() completed", { editing: t.edit.editing });
845
+ }
846
+ /**
847
+ * 헤더 렌더링 (Phase 14: Fixed Right 지원)
848
+ */
849
+ renderHeader() {
850
+ const t = this.ctx;
851
+ if (t.fixedLeftHeader) {
852
+ t.fixedLeftHeader.innerHTML = "";
853
+ const i = f("div", "velox-header-row");
854
+ t.getFixedLeftColumns().forEach((s) => {
855
+ if (s.field === "__drag") {
856
+ const n = f("div", "velox-row-drag-handle");
857
+ n.style.visibility = "hidden", i.appendChild(n);
858
+ } else if (s.field === "__checkbox")
859
+ i.appendChild(this.createHeaderCheckbarCell());
860
+ else if (s.field === "__rownum") {
861
+ const n = f("div", "velox-header-cell velox-rownumber-cell");
862
+ n.textContent = "#", i.appendChild(n);
863
+ } else
864
+ i.appendChild(this.createHeaderCell(s));
865
+ }), t.fixedLeftHeader.appendChild(i);
866
+ }
867
+ const e = f("div", "velox-header-row");
868
+ if (t.getScrollableColumns().forEach((i) => {
869
+ if (i.field === "__drag") {
870
+ const s = f("div", "velox-row-drag-handle");
871
+ s.style.visibility = "hidden", e.appendChild(s);
872
+ } else if (i.field === "__checkbox")
873
+ e.appendChild(this.createHeaderCheckbarCell());
874
+ else if (i.field === "__rownum") {
875
+ const s = f("div", "velox-header-cell velox-rownumber-cell");
876
+ s.textContent = "#", e.appendChild(s);
877
+ } else
878
+ e.appendChild(this.createHeaderCell(i));
879
+ }), t.headerElement.innerHTML = "", t.headerElement.appendChild(e), t.fixedRightHeader) {
880
+ t.fixedRightHeader.innerHTML = "";
881
+ const i = f("div", "velox-header-row");
882
+ t.getFixedRightColumns().forEach(
883
+ (s) => i.appendChild(this.createHeaderCell(s))
884
+ ), t.fixedRightHeader.appendChild(i), this.updateFixedRightWidth();
885
+ }
886
+ }
887
+ /**
888
+ * CheckBar 헤더 셀 생성
889
+ */
890
+ createHeaderCheckbarCell() {
891
+ const t = this.ctx, e = t.getOptions(), i = t.getState(), s = f("div", "velox-header-cell velox-checkbox-cell"), n = e.checkBar;
892
+ if (n.showAll && !n.exclusive) {
893
+ const o = f("input", "velox-checkbox");
894
+ o.type = "checkbox";
895
+ const l = i.checkBar.checkableRows.size, a = i.checkBar.checkedRows.size, r = l > 0 && a === l, h = a > 0 && !r;
896
+ o.checked = r, o.indeterminate = h, o.addEventListener("change", () => t.checkAll(o.checked)), s.appendChild(o);
897
+ } else if (n.exclusive) {
898
+ const o = f("span", "velox-checkbox-label");
899
+ o.textContent = "선택", s.appendChild(o);
900
+ }
901
+ return s;
902
+ }
903
+ /**
904
+ * 헤더 셀 생성
905
+ */
906
+ createHeaderCell(t) {
907
+ var p;
908
+ const e = this.ctx, i = e.getOptions(), s = e.getState(), n = f("div", "velox-header-cell");
909
+ n.dataset.field = t.field;
910
+ const o = t.headerAlign || t.align || "left";
911
+ if (y(n, `velox-header-cell--align-${o}`), t.width ? (n.style.width = `${t.width}px`, n.style.minWidth = `${t.minWidth || t.width}px`, n.style.maxWidth = `${t.width}px`, n.style.flexShrink = "0") : (n.style.flex = "1", n.style.minWidth = `${t.minWidth || 100}px`), t.headerClass && y(n, t.headerClass), i.sortable && t.sortable !== !1) {
912
+ y(n, "velox-header-cell--sortable");
913
+ const c = s.sort.find((u) => u.field === t.field);
914
+ c != null && c.direction && y(n, "velox-header-cell--sorted");
915
+ }
916
+ const l = f("div", "velox-header-content"), a = f("span", "velox-column-drag-handle");
917
+ a.innerHTML = "⋮⋮", a.title = "드래그하여 컬럼 순서 변경", a.addEventListener("mousedown", (c) => e.startColumnDrag(c, t)), l.appendChild(a);
918
+ const r = f("span", "velox-header-text");
919
+ if (r.textContent = t.header, l.appendChild(r), n.appendChild(l), i.sortable && t.sortable !== !1) {
920
+ const c = f("button", "velox-sort-btn"), u = s.sort.find((g) => g.field === t.field);
921
+ (u == null ? void 0 : u.direction) === "asc" ? (y(c, "velox-sort-btn--asc"), c.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M3 4.5h14.25M3 9h9.75M3 13.5h5.25m5.25-.75L17.25 9m0 0L21 12.75M17.25 9v12" /></svg>') : ((u == null ? void 0 : u.direction) === "desc" && y(c, "velox-sort-btn--desc"), c.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M3 4.5h14.25M3 9h9.75M3 13.5h9.75m4.5-4.5v12m0 0-3.75-3.75M17.25 21 21 17.25" /></svg>'), u != null && u.direction && y(c, "velox-sort-btn--active"), c.title = "정렬", c.addEventListener("click", (g) => {
922
+ g.stopPropagation(), e.handleSort(t.field);
923
+ }), n.appendChild(c);
924
+ }
925
+ if (i.filterable && t.filterable !== !1) {
926
+ const c = f("button", "velox-filter-btn");
927
+ c.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M12 3c2.755 0 5.455.232 8.083.678.533.09.917.556.917 1.096v1.044a2.25 2.25 0 0 1-.659 1.591l-5.432 5.432a2.25 2.25 0 0 0-.659 1.591v2.927a2.25 2.25 0 0 1-1.244 2.013L9.75 21v-6.568a2.25 2.25 0 0 0-.659-1.591L3.659 7.409A2.25 2.25 0 0 1 3 5.818V4.774c0-.54.384-1.006.917-1.096A48.32 48.32 0 0 1 12 3Z" /></svg>', ((p = s.filter) == null ? void 0 : p.conditions.some((g) => g.field === t.field)) && y(c, "velox-filter-btn--active"), c.addEventListener("click", (g) => {
928
+ g.stopPropagation(), e.showFilterPopup(t, c);
929
+ }), n.appendChild(c);
930
+ }
931
+ const h = f("button", "velox-column-menu-btn");
932
+ if (h.innerHTML = "⋯", h.title = "컬럼 메뉴", h.addEventListener("click", (c) => {
933
+ c.stopPropagation(), e.showColumnMenu(t, h);
934
+ }), n.appendChild(h), i.resizable && t.resizable !== !1) {
935
+ const c = f("div", "velox-resize-handle");
936
+ c.addEventListener("mousedown", (u) => e.startResize(u, t)), n.appendChild(c);
937
+ }
938
+ return n;
939
+ }
940
+ /**
941
+ * 바디 렌더링 (Phase 14: Fixed Right 지원)
942
+ */
943
+ renderBody() {
944
+ const t = this.ctx, e = t.getOptions(), i = t.getVisibleRows(), s = t.getVirtualState(), n = e.rowHeight || 40;
945
+ if (t.fixedLeftBodyInner && (t.fixedLeftBodyInner.innerHTML = "", e.virtualScroll ? (t.fixedLeftBodyInner.style.height = `${s.totalHeight}px`, t.fixedLeftBodyInner.style.position = "relative") : (t.fixedLeftBodyInner.style.height = "", t.fixedLeftBodyInner.style.position = ""), i.forEach(({ data: o, index: l }) => {
946
+ const a = this.createRowBase(o, l, "fixedLeft");
947
+ e.virtualScroll && (a.style.position = "absolute", a.style.top = `${l * n}px`, a.style.left = "0", a.style.right = "0"), t.fixedLeftBodyInner.appendChild(a);
948
+ })), t.bodyInner.innerHTML = "", e.virtualScroll ? (t.bodyInner.style.height = `${s.totalHeight}px`, t.bodyInner.style.position = "relative") : (t.bodyInner.style.height = "", t.bodyInner.style.position = ""), i.length === 0) {
949
+ const o = f("div", "velox-empty");
950
+ o.textContent = e.emptyMessage || "데이터가 없습니다.", t.bodyInner.appendChild(o);
951
+ return;
952
+ }
953
+ i.forEach(({ data: o, index: l }) => {
954
+ const a = this.createRowBase(o, l, "scrollable");
955
+ e.virtualScroll && (a.style.position = "absolute", a.style.top = `${l * n}px`, a.style.left = "0", a.style.right = "0"), t.bodyInner.appendChild(a);
956
+ }), t.fixedRightBodyInner && (t.fixedRightBodyInner.innerHTML = "", e.virtualScroll ? (t.fixedRightBodyInner.style.height = `${s.totalHeight}px`, t.fixedRightBodyInner.style.position = "relative") : (t.fixedRightBodyInner.style.height = "", t.fixedRightBodyInner.style.position = ""), i.forEach(({ data: o, index: l }) => {
957
+ const a = this.createRowBase(o, l, "fixedRight");
958
+ e.virtualScroll && (a.style.position = "absolute", a.style.top = `${l * n}px`, a.style.left = "0", a.style.right = "0"), t.fixedRightBodyInner.appendChild(a);
959
+ }), this.updateFixedRightWidth());
960
+ }
961
+ /**
962
+ * Update Fixed Right container width based on columns
963
+ * Phase 14: Ensure header and body alignment
964
+ */
965
+ updateFixedRightWidth() {
966
+ const t = this.ctx;
967
+ if (!t.fixedRightContainer) return;
968
+ const e = t.getFixedRightColumns();
969
+ if (e.length === 0) return;
970
+ let i = 0;
971
+ e.forEach((n) => {
972
+ i += n.width || n.minWidth || 100;
973
+ });
974
+ const s = this.getScrollbarWidth(t.fixedRightBody);
975
+ t.fixedRightContainer.style.width = `${i + s}px`, t.fixedRightContainer.style.minWidth = `${i + s}px`, t.fixedRightContainer.style.maxWidth = `${i + s}px`, t.fixedRightHeader && s > 0 && (t.fixedRightHeader.style.paddingRight = `${s}px`), t.fixedRightFooter && s > 0 && (t.fixedRightFooter.style.paddingRight = `${s}px`);
976
+ }
977
+ /**
978
+ * Get scrollbar width of an element
979
+ * Phase 14: For Fixed Right alignment
980
+ */
981
+ getScrollbarWidth(t) {
982
+ return t ? t.offsetWidth - t.clientWidth : 0;
983
+ }
984
+ /**
985
+ * Row 생성 (통합 메서드)
986
+ * @param area - 'fixedLeft' | 'scrollable' | 'fixedRight' (Phase 14)
987
+ */
988
+ createRowBase(t, e, i) {
989
+ const s = this.ctx, n = s.getOptions(), o = s.getState(), l = f("div", "velox-row");
990
+ return l.dataset.rowIndex = String(e), e % 2 === 1 && y(l, "velox-row--alt"), n.selectionStyle === "row" && o.selection.selectedRows.has(e) && y(l, "velox-row--selected"), i === "fixedLeft" && o.checkBar.checkedRows.has(e) && y(l, "velox-row--checked"), l.addEventListener("click", (a) => {
991
+ const r = a.target;
992
+ r.classList.contains("velox-checkbox") || r.classList.contains("velox-row-drag-handle") || s.handleRowClick(e, a);
993
+ }), i !== "fixedLeft" && l.addEventListener("dblclick", (a) => s.handleRowDoubleClick(e, a)), i === "fixedLeft" ? s.getFixedLeftColumns().forEach((a) => {
994
+ if (a.field === "__drag") {
995
+ const r = f("div", "velox-row-drag-handle");
996
+ r.innerHTML = "☰", r.title = "드래그하여 행 순서 변경", r.addEventListener("mousedown", (h) => s.startRowDrag(h, e, l)), l.appendChild(r);
997
+ } else if (a.field === "__checkbox")
998
+ l.appendChild(this.createCheckbarCell(e));
999
+ else if (a.field === "__rownum") {
1000
+ const r = f("div", "velox-cell velox-rownumber-cell");
1001
+ r.textContent = String(e + 1), l.appendChild(r);
1002
+ } else
1003
+ l.appendChild(this.createCell(t, e, a));
1004
+ }) : i === "scrollable" ? s.getScrollableColumns().forEach((a) => {
1005
+ if (a.field === "__drag") {
1006
+ const r = f("div", "velox-row-drag-handle");
1007
+ r.innerHTML = "☰", r.title = "드래그하여 행 순서 변경", r.addEventListener("mousedown", (h) => s.startRowDrag(h, e, l)), l.appendChild(r);
1008
+ } else if (a.field === "__checkbox")
1009
+ l.appendChild(this.createCheckbarCell(e));
1010
+ else if (a.field === "__rownum") {
1011
+ const r = f("div", "velox-cell velox-rownumber-cell");
1012
+ r.textContent = String(e + 1), l.appendChild(r);
1013
+ } else
1014
+ l.appendChild(this.createCell(t, e, a));
1015
+ }) : i === "fixedRight" && s.getFixedRightColumns().forEach(
1016
+ (a) => l.appendChild(this.createCell(t, e, a))
1017
+ ), l;
1018
+ }
1019
+ /**
1020
+ * CheckBar 셀 생성
1021
+ */
1022
+ createCheckbarCell(t) {
1023
+ const e = this.ctx, i = e.getOptions(), s = e.getState(), n = f("div", "velox-cell velox-checkbox-cell"), o = i.checkBar, l = s.checkBar.checkableRows.has(t), a = s.checkBar.checkedRows.has(t), r = f("input", "velox-checkbox");
1024
+ return r.type = o.exclusive ? "radio" : "checkbox", r.name = o.exclusive ? `${e.getGridId()}-check` : "", r.checked = a, r.disabled = !l, l || y(n, "velox-checkbox-cell--disabled"), r.addEventListener("click", (h) => {
1025
+ console.log("✅ Checkbox clicked", { rowIndex: t, checked: r.checked }), h.stopPropagation();
1026
+ }), r.addEventListener("change", () => {
1027
+ if (console.log("🔄 Checkbox changed", { rowIndex: t, checked: r.checked }), o.exclusive) {
1028
+ const h = { ...e.getState().edit };
1029
+ s.checkBar.checkedRows.clear(), r.checked && s.checkBar.checkedRows.add(t), this.render(), h.editing && h.rowIndex !== null && h.field !== null && (e.getState().edit = h, e.renderEditCell(h.rowIndex, h.field, h.originalValue)), e.emitEvent("onCheckChange", t, r.checked);
1030
+ } else
1031
+ e.checkItem(t, r.checked);
1032
+ }), n.appendChild(r), n;
1033
+ }
1034
+ /**
1035
+ * Cell 생성
1036
+ */
1037
+ createCell(t, e, i) {
1038
+ const s = this.ctx, n = s.getOptions(), o = s.getState(), l = f("div", "velox-cell");
1039
+ l.dataset.field = i.field, l.dataset.rowIndex = String(e);
1040
+ const a = i.align || "left";
1041
+ if (y(l, `velox-cell--align-${a}`), i.width ? (l.style.width = `${i.width}px`, l.style.minWidth = `${i.minWidth || i.width}px`, l.style.maxWidth = `${i.width}px`, l.style.flexShrink = "0") : (l.style.flex = "1", l.style.minWidth = `${i.minWidth || 100}px`), i.cellClass) {
1042
+ const u = typeof i.cellClass == "function" ? i.cellClass(t[i.field], t) : i.cellClass;
1043
+ u && y(l, u);
1044
+ }
1045
+ const r = `${e}:${i.field}`;
1046
+ o.selection.selectedCells.has(r) && y(l, "velox-cell--selected");
1047
+ const h = o.selection.focusedCell;
1048
+ h && h.rowIndex === e && h.field === i.field && y(l, "velox-cell--focused"), n.editable && i.editable !== !1 && (y(l, "velox-cell--editable"), l.addEventListener("dblclick", (u) => {
1049
+ if (l.classList.contains("velox-cell--editing")) {
1050
+ console.log("🚫 Double click ignored - already editing"), u.stopPropagation(), u.preventDefault();
1051
+ return;
1052
+ }
1053
+ console.log("🖱️🖱️ Double click detected", { rowIndex: e, field: i.field }), u.stopPropagation(), s.startEdit(e, i.field);
1054
+ }));
1055
+ const p = t[i.field], c = f("span", "velox-cell-content");
1056
+ return i.renderer ? c.innerHTML = i.renderer(p, t, i) : i.formatter ? c.textContent = i.formatter(p, t, i) : c.textContent = k(p, i.type), l.appendChild(c), l.addEventListener("click", (u) => {
1057
+ if (l.classList.contains("velox-cell--editing")) {
1058
+ const g = u.target;
1059
+ if (g.tagName === "INPUT" || g.tagName === "SELECT" || g.tagName === "BUTTON" || g.tagName === "TEXTAREA") {
1060
+ console.log("✅ Interactive element click allowed during edit"), u.stopPropagation();
1061
+ return;
1062
+ }
1063
+ console.log("🚫 Cell click ignored - editing mode (cell background)"), u.stopPropagation(), u.preventDefault();
1064
+ return;
1065
+ }
1066
+ s.handleCellClick(e, i.field, p, u);
1067
+ }), l.addEventListener("mousedown", (u) => {
1068
+ if (l.classList.contains("velox-cell--editing")) {
1069
+ console.log("🔒 Cell mousedown - editing mode (ignored in renderer)");
1070
+ return;
1071
+ }
1072
+ n.selectionStyle === "block" && u.button === 0 && s.startBlockSelection(e, i.field);
1073
+ }), l.addEventListener("mouseenter", () => {
1074
+ s.isBlockSelecting() && s.updateBlockSelection(e, i.field);
1075
+ }), i.tooltip && (y(l, "velox-cell--has-tooltip"), l.addEventListener("mouseenter", () => s.showTooltip(l, p, t, i)), l.addEventListener("mouseleave", () => s.hideTooltip())), l;
1076
+ }
1077
+ /**
1078
+ * Footer Summary 렌더링 (Phase 13, Phase 14: Fixed Right 지원)
1079
+ */
1080
+ renderFooter() {
1081
+ var i, s;
1082
+ const t = this.ctx, e = t.getOptions();
1083
+ if ((i = e.footerSummary) != null && i.visible) {
1084
+ if (t.fixedLeftFooter) {
1085
+ t.fixedLeftFooter.innerHTML = "";
1086
+ const n = f("div", "velox-footer-row");
1087
+ if (e.rowDraggable) {
1088
+ const o = f("div", "velox-row-drag-handle");
1089
+ o.style.visibility = "hidden", n.appendChild(o);
1090
+ }
1091
+ if ((s = e.checkBar) != null && s.visible) {
1092
+ const o = f("div", "velox-footer-cell velox-checkbox-cell");
1093
+ n.appendChild(o);
1094
+ }
1095
+ t.getFixedLeftColumns().forEach(
1096
+ (o) => n.appendChild(this.createFooterCell(o))
1097
+ ), t.fixedLeftFooter.appendChild(n);
1098
+ }
1099
+ if (t.footerElement) {
1100
+ t.footerElement.innerHTML = "";
1101
+ const n = f("div", "velox-footer-row");
1102
+ if (e.showRowNumbers) {
1103
+ const o = f("div", "velox-footer-cell velox-rownumber-cell");
1104
+ n.appendChild(o);
1105
+ }
1106
+ t.getScrollableColumns().forEach(
1107
+ (o) => n.appendChild(this.createFooterCell(o))
1108
+ ), t.footerElement.appendChild(n);
1109
+ }
1110
+ if (t.fixedRightFooter) {
1111
+ t.fixedRightFooter.innerHTML = "";
1112
+ const n = f("div", "velox-footer-row");
1113
+ t.getFixedRightColumns().forEach(
1114
+ (o) => n.appendChild(this.createFooterCell(o))
1115
+ ), t.fixedRightFooter.appendChild(n);
1116
+ }
1117
+ }
1118
+ }
1119
+ /**
1120
+ * Footer 셀 생성 (Phase 13)
1121
+ */
1122
+ createFooterCell(t) {
1123
+ var l, a;
1124
+ const e = this.ctx, i = e.getOptions(), s = f("div", "velox-footer-cell");
1125
+ s.dataset.field = t.field;
1126
+ const n = t.align || "left";
1127
+ y(s, `velox-footer-cell--align-${n}`), t.width ? (s.style.width = `${t.width}px`, s.style.minWidth = `${t.minWidth || t.width}px`) : (s.style.flex = "1", s.style.minWidth = `${t.minWidth || 100}px`);
1128
+ const o = ((a = (l = i.footerSummary) == null ? void 0 : l.columns) == null ? void 0 : a[t.field]) || t.summary;
1129
+ if (o) {
1130
+ const r = e.getSummaryValue(t.field), h = f("span", "velox-footer-content");
1131
+ if (o.label) {
1132
+ const c = f("span", "velox-footer-label");
1133
+ c.textContent = o.label, h.appendChild(c);
1134
+ }
1135
+ const p = f("span", "velox-footer-value");
1136
+ o.formatter ? p.textContent = o.formatter(r) : p.textContent = k(r, t.type), h.appendChild(p), s.appendChild(h), o.className && y(s, o.className);
1137
+ }
1138
+ return s;
1139
+ }
1140
+ /**
1141
+ * Loading 상태 업데이트
1142
+ */
1143
+ updateLoadingState() {
1144
+ const t = this.ctx, e = t.getOptions();
1145
+ t.loadingOverlay && (t.loadingOverlay.style.display = e.loading ? "flex" : "none");
1146
+ }
1147
+ /**
1148
+ * Row validation 상태 업데이트
1149
+ */
1150
+ updateRowValidationState(t) {
1151
+ const e = this.ctx, i = e.getState(), s = e.rootElement.querySelector(`[data-row-index="${t}"]`);
1152
+ if (!s) return;
1153
+ s.querySelectorAll(".velox-cell").forEach((o) => {
1154
+ const a = o.dataset.field;
1155
+ if (!a) return;
1156
+ const r = i.columns.find((h) => h.field === a);
1157
+ r != null && r.validation;
1158
+ });
1159
+ }
1160
+ }
1161
+ class nt {
1162
+ constructor(t) {
1163
+ this.ctx = t, this.filterPopup = null, this.boundHandleOutsideClick = this.handleOutsideClick.bind(this);
1164
+ }
1165
+ /**
1166
+ * Filter 팝업 표시
1167
+ */
1168
+ showFilterPopup(t, e) {
1169
+ var C;
1170
+ this.closeFilterPopup();
1171
+ const i = this.ctx, s = i.getState(), n = f("div", "velox-filter-popup"), o = e.getBoundingClientRect(), l = i.rootElement.getBoundingClientRect();
1172
+ n.style.top = `${o.bottom - l.top + 5}px`, n.style.left = `${Math.max(0, o.left - l.left - 100)}px`;
1173
+ const a = [...new Set(s.data.map((x) => x[t.field]))].filter((x) => x != null).sort(), r = (C = s.filter) == null ? void 0 : C.conditions.find((x) => x.field === t.field), h = f("select", "velox-filter-operator");
1174
+ [
1175
+ { value: "contains", label: "포함" },
1176
+ { value: "equals", label: "같음" },
1177
+ { value: "notEquals", label: "같지 않음" },
1178
+ { value: "startsWith", label: "시작" },
1179
+ { value: "endsWith", label: "끝" },
1180
+ { value: "greaterThan", label: ">" },
1181
+ { value: "lessThan", label: "<" },
1182
+ { value: "greaterThanOrEqual", label: ">=" },
1183
+ { value: "lessThanOrEqual", label: "<=" },
1184
+ { value: "isEmpty", label: "비어있음" },
1185
+ { value: "isNotEmpty", label: "비어있지 않음" }
1186
+ ].forEach((x) => {
1187
+ const v = f("option");
1188
+ v.value = x.value, v.textContent = x.label, (r == null ? void 0 : r.operator) === x.value && (v.selected = !0), h.appendChild(v);
1189
+ }), n.appendChild(h);
1190
+ const c = f("input", "velox-filter-input");
1191
+ if (c.type = t.type === "number" ? "number" : "text", c.placeholder = "값 입력...", (r == null ? void 0 : r.value) !== void 0 && (c.value = String(r.value)), n.appendChild(c), a.length > 0 && a.length <= 15) {
1192
+ const x = f("div", "velox-filter-list"), v = f("div", "velox-filter-list-label");
1193
+ v.textContent = "빠른 선택:", x.appendChild(v), a.slice(0, 10).forEach((w) => {
1194
+ const S = f("div", "velox-filter-list-item");
1195
+ S.textContent = k(w, t.type), S.addEventListener("click", () => {
1196
+ this.applyColumnFilter(t.field, "equals", w), this.closeFilterPopup();
1197
+ }), x.appendChild(S);
1198
+ }), n.appendChild(x);
1199
+ }
1200
+ const u = f("div", "velox-filter-buttons"), g = f("button", "velox-filter-apply");
1201
+ g.textContent = "적용", g.addEventListener("click", () => {
1202
+ const x = h.value, v = t.type === "number" ? parseFloat(c.value) : c.value;
1203
+ this.applyColumnFilter(t.field, x, v), this.closeFilterPopup();
1204
+ }), u.appendChild(g);
1205
+ const m = f("button", "velox-filter-clear");
1206
+ m.textContent = "해제", m.addEventListener("click", () => {
1207
+ this.removeColumnFilter(t.field), this.closeFilterPopup();
1208
+ }), u.appendChild(m), n.appendChild(u), this.filterPopup = n, i.rootElement.appendChild(n), setTimeout(() => document.addEventListener("click", this.boundHandleOutsideClick), 0), c.focus();
1209
+ }
1210
+ /**
1211
+ * Filter 팝업 닫기
1212
+ */
1213
+ closeFilterPopup() {
1214
+ this.filterPopup && (this.filterPopup.remove(), this.filterPopup = null, document.removeEventListener("click", this.boundHandleOutsideClick));
1215
+ }
1216
+ /**
1217
+ * 외부 클릭 핸들러
1218
+ */
1219
+ handleOutsideClick(t) {
1220
+ this.filterPopup && !this.filterPopup.contains(t.target) && this.closeFilterPopup();
1221
+ }
1222
+ /**
1223
+ * Column filter 적용
1224
+ */
1225
+ applyColumnFilter(t, e, i) {
1226
+ const s = this.ctx, n = s.getState(), o = { field: t, operator: e, value: i };
1227
+ if (n.filter) {
1228
+ const l = n.filter.conditions.filter((a) => a.field !== t);
1229
+ l.push(o), n.filter = { conditions: l, logic: "and" };
1230
+ } else
1231
+ n.filter = { conditions: [o], logic: "and" };
1232
+ s.clearSelectionState(), s.applyDataTransformations(), s.render(), s.emitEvent("onFilter", n.filter);
1233
+ }
1234
+ /**
1235
+ * Column filter 제거
1236
+ */
1237
+ removeColumnFilter(t) {
1238
+ const e = this.ctx, i = e.getState();
1239
+ if (i.filter) {
1240
+ const s = i.filter.conditions.filter((n) => n.field !== t);
1241
+ i.filter = s.length === 0 ? null : { conditions: s, logic: "and" }, e.clearSelectionState(), e.applyDataTransformations(), e.render(), i.filter && e.emitEvent("onFilter", i.filter);
1242
+ }
1243
+ }
1244
+ /**
1245
+ * Filter 팝업이 열려있는지 확인
1246
+ */
1247
+ isOpen() {
1248
+ return this.filterPopup !== null;
1249
+ }
1250
+ }
1251
+ class ot {
1252
+ constructor(t) {
1253
+ this.ctx = t, this.columnMenuPopup = null, this.boundHandleOutsideClick = this.handleOutsideClick.bind(this);
1254
+ }
1255
+ /**
1256
+ * Column 메뉴 표시
1257
+ */
1258
+ showColumnMenu(t, e) {
1259
+ this.closeColumnMenu();
1260
+ const i = this.ctx, s = i.getOptions(), n = f("div", "velox-column-menu"), o = e.getBoundingClientRect(), l = i.rootElement.getBoundingClientRect();
1261
+ n.style.top = `${o.bottom - l.top + 5}px`, n.style.left = `${o.left - l.left}px`;
1262
+ const a = {
1263
+ field: t.field,
1264
+ column: t,
1265
+ selectedRows: i.getSelectedRows(),
1266
+ selectedCells: i.getSelectedCells(),
1267
+ grid: i
1268
+ }, r = s.contextMenu, h = (r == null ? void 0 : r.showDefaultItems) !== !1, p = (r == null ? void 0 : r.headerItems) || [], c = [
1269
+ {
1270
+ id: "sort-asc",
1271
+ label: "오름차순 정렬",
1272
+ icon: "↑",
1273
+ action: () => i.sort(t.field, "asc")
1274
+ },
1275
+ {
1276
+ id: "sort-desc",
1277
+ label: "내림차순 정렬",
1278
+ icon: "↓",
1279
+ action: () => i.sort(t.field, "desc")
1280
+ },
1281
+ {
1282
+ id: "sort-clear",
1283
+ label: "정렬 해제",
1284
+ icon: "✕",
1285
+ action: () => i.clearSort()
1286
+ },
1287
+ { type: "separator" },
1288
+ {
1289
+ id: "hide",
1290
+ label: "컬럼 숨기기",
1291
+ icon: "👁",
1292
+ action: () => i.hideColumn(t.field)
1293
+ },
1294
+ {
1295
+ id: "autofit",
1296
+ label: "컬럼 너비 자동",
1297
+ icon: "↔",
1298
+ action: () => i.autoFitColumn(t.field)
1299
+ },
1300
+ {
1301
+ id: "autofit-all",
1302
+ label: "모든 컬럼 자동",
1303
+ icon: "⇔",
1304
+ action: () => i.autoFitAllColumns()
1305
+ },
1306
+ { type: "separator" },
1307
+ {
1308
+ id: "fix-left",
1309
+ label: "왼쪽에 고정",
1310
+ icon: "◀",
1311
+ action: () => i.fixColumn(t.field, "left")
1312
+ },
1313
+ {
1314
+ id: "unfix",
1315
+ label: "고정 해제",
1316
+ icon: "◇",
1317
+ action: () => i.fixColumn(t.field, !1)
1318
+ }
1319
+ ];
1320
+ let u = [];
1321
+ h ? (u = [...c], p.length > 0 && (u.push({ type: "separator" }), u.push(...p))) : u = p, u.forEach((g) => {
1322
+ if (typeof g.visible == "function" ? g.visible(a) : g.visible !== !1)
1323
+ if (g.type === "separator") {
1324
+ const C = f("div", "velox-column-menu-separator");
1325
+ n.appendChild(C);
1326
+ } else {
1327
+ const C = f("div", "velox-column-menu-item");
1328
+ g.className && y(C, g.className);
1329
+ const x = typeof g.disabled == "function" ? g.disabled(a) : g.disabled === !0;
1330
+ x && y(C, "velox-column-menu-item--disabled");
1331
+ let v = "";
1332
+ g.icon && (v += `<span class="velox-column-menu-icon">${g.icon}</span>`), v += `<span class="velox-column-menu-label">${g.label || ""}</span>`, g.shortcut && (v += `<span class="velox-column-menu-shortcut">${g.shortcut}</span>`), C.innerHTML = v, x || C.addEventListener("click", () => {
1333
+ var w;
1334
+ (w = g.action) == null || w.call(g, a), this.closeColumnMenu();
1335
+ }), n.appendChild(C);
1336
+ }
1337
+ }), this.columnMenuPopup = n, i.rootElement.appendChild(n), setTimeout(() => document.addEventListener("click", this.boundHandleOutsideClick), 0);
1338
+ }
1339
+ /**
1340
+ * Column 메뉴 닫기
1341
+ */
1342
+ closeColumnMenu() {
1343
+ this.columnMenuPopup && (this.columnMenuPopup.remove(), this.columnMenuPopup = null, document.removeEventListener("click", this.boundHandleOutsideClick));
1344
+ }
1345
+ /**
1346
+ * 외부 클릭 핸들러
1347
+ */
1348
+ handleOutsideClick(t) {
1349
+ this.columnMenuPopup && !this.columnMenuPopup.contains(t.target) && this.closeColumnMenu();
1350
+ }
1351
+ /**
1352
+ * Column 메뉴가 열려있는지 확인
1353
+ */
1354
+ isOpen() {
1355
+ return this.columnMenuPopup !== null;
1356
+ }
1357
+ }
1358
+ class lt {
1359
+ constructor(t) {
1360
+ this.ctx = t, this.columnDragging = null, this.rowDragging = null, this.resizing = null, this.boundHandleColumnDragMove = this.handleColumnDragMove.bind(this), this.boundHandleColumnDragEnd = this.handleColumnDragEnd.bind(this), this.boundHandleRowDragMove = this.handleRowDragMove.bind(this), this.boundHandleRowDragEnd = this.handleRowDragEnd.bind(this), this.boundHandleResizeMove = this.handleResizeMove.bind(this), this.boundHandleResizeEnd = this.handleResizeEnd.bind(this);
1361
+ }
1362
+ // ============================================
1363
+ // Column Drag & Drop
1364
+ // ============================================
1365
+ /**
1366
+ * Column 드래그 시작
1367
+ */
1368
+ startColumnDrag(t, e) {
1369
+ t.preventDefault(), t.stopPropagation(), this.columnDragging = {
1370
+ field: e.field,
1371
+ startX: t.clientX,
1372
+ element: null
1373
+ };
1374
+ const i = f("div", "velox-column-drag-indicator");
1375
+ i.textContent = e.header, i.style.position = "fixed", i.style.left = `${t.clientX}px`, i.style.top = `${t.clientY}px`, document.body.appendChild(i), this.columnDragging.element = i, document.addEventListener("mousemove", this.boundHandleColumnDragMove), document.addEventListener("mouseup", this.boundHandleColumnDragEnd), y(document.body, "velox-no-select");
1376
+ }
1377
+ /**
1378
+ * Column 드래그 이동
1379
+ */
1380
+ handleColumnDragMove(t) {
1381
+ var n;
1382
+ const e = this.ctx;
1383
+ if (!((n = this.columnDragging) != null && n.element)) return;
1384
+ this.columnDragging.element.style.left = `${t.clientX + 10}px`, this.columnDragging.element.style.top = `${t.clientY + 10}px`;
1385
+ const i = document.elementFromPoint(t.clientX, t.clientY), s = i == null ? void 0 : i.closest(".velox-header-cell");
1386
+ e.headerElement.querySelectorAll(".velox-header-cell--drop-target").forEach((o) => {
1387
+ b(o, "velox-header-cell--drop-target");
1388
+ }), s && s.dataset.field !== this.columnDragging.field && y(s, "velox-header-cell--drop-target");
1389
+ }
1390
+ /**
1391
+ * Column 드래그 종료
1392
+ */
1393
+ handleColumnDragEnd(t) {
1394
+ const e = this.ctx;
1395
+ if (!this.columnDragging) return;
1396
+ const i = this.columnDragging.field, s = document.elementFromPoint(t.clientX, t.clientY), n = s == null ? void 0 : s.closest(".velox-header-cell"), o = n == null ? void 0 : n.dataset.field;
1397
+ this.columnDragging.element && this.columnDragging.element.remove(), e.headerElement.querySelectorAll(".velox-header-cell--drop-target").forEach((l) => {
1398
+ b(l, "velox-header-cell--drop-target");
1399
+ }), document.removeEventListener("mousemove", this.boundHandleColumnDragMove), document.removeEventListener("mouseup", this.boundHandleColumnDragEnd), b(document.body, "velox-no-select"), o && o !== i && e.reorderColumn(i, o), this.columnDragging = null;
1400
+ }
1401
+ /**
1402
+ * Column 드래그 중인지 확인
1403
+ */
1404
+ isColumnDragging() {
1405
+ return this.columnDragging !== null;
1406
+ }
1407
+ // ============================================
1408
+ // Row Drag & Drop
1409
+ // ============================================
1410
+ /**
1411
+ * Row 드래그 시작
1412
+ */
1413
+ startRowDrag(t, e, i) {
1414
+ t.preventDefault(), t.stopPropagation(), this.rowDragging = {
1415
+ index: e,
1416
+ startY: t.clientY,
1417
+ element: null
1418
+ };
1419
+ const s = f("div", "velox-row-drag-indicator");
1420
+ s.textContent = `행 ${e + 1}`, s.style.position = "fixed", s.style.left = `${t.clientX}px`, s.style.top = `${t.clientY}px`, document.body.appendChild(s), this.rowDragging.element = s, y(i, "velox-row--dragging"), document.addEventListener("mousemove", this.boundHandleRowDragMove), document.addEventListener("mouseup", this.boundHandleRowDragEnd), y(document.body, "velox-no-select");
1421
+ }
1422
+ /**
1423
+ * Row 드래그 이동
1424
+ */
1425
+ handleRowDragMove(t) {
1426
+ var n, o;
1427
+ const e = this.ctx;
1428
+ if (!((n = this.rowDragging) != null && n.element)) return;
1429
+ this.rowDragging.element.style.left = `${t.clientX + 10}px`, this.rowDragging.element.style.top = `${t.clientY + 10}px`;
1430
+ const i = document.elementFromPoint(t.clientX, t.clientY), s = i == null ? void 0 : i.closest(".velox-row");
1431
+ if (e.bodyInner.querySelectorAll(".velox-row--drop-target").forEach((l) => {
1432
+ b(l, "velox-row--drop-target");
1433
+ }), (o = e.fixedLeftBodyInner) == null || o.querySelectorAll(".velox-row--drop-target").forEach((l) => {
1434
+ b(l, "velox-row--drop-target");
1435
+ }), s) {
1436
+ const l = parseInt(s.dataset.rowIndex || "-1", 10);
1437
+ l !== -1 && l !== this.rowDragging.index && y(s, "velox-row--drop-target");
1438
+ }
1439
+ }
1440
+ /**
1441
+ * Row 드래그 종료
1442
+ */
1443
+ handleRowDragEnd(t) {
1444
+ var l;
1445
+ const e = this.ctx;
1446
+ if (!this.rowDragging) return;
1447
+ const i = this.rowDragging.index, s = document.elementFromPoint(t.clientX, t.clientY), n = s == null ? void 0 : s.closest(".velox-row"), o = n ? parseInt(n.dataset.rowIndex || "-1", 10) : -1;
1448
+ this.rowDragging.element && this.rowDragging.element.remove(), e.bodyInner.querySelectorAll(".velox-row--dragging, .velox-row--drop-target").forEach((a) => {
1449
+ b(a, "velox-row--dragging"), b(a, "velox-row--drop-target");
1450
+ }), (l = e.fixedLeftBodyInner) == null || l.querySelectorAll(".velox-row--dragging, .velox-row--drop-target").forEach((a) => {
1451
+ b(a, "velox-row--dragging"), b(a, "velox-row--drop-target");
1452
+ }), document.removeEventListener("mousemove", this.boundHandleRowDragMove), document.removeEventListener("mouseup", this.boundHandleRowDragEnd), b(document.body, "velox-no-select"), o !== -1 && o !== i && e.moveRow(i, o), this.rowDragging = null;
1453
+ }
1454
+ /**
1455
+ * Row 드래그 중인지 확인
1456
+ */
1457
+ isRowDragging() {
1458
+ return this.rowDragging !== null;
1459
+ }
1460
+ // ============================================
1461
+ // Column Resize
1462
+ // ============================================
1463
+ /**
1464
+ * Column 리사이즈 시작
1465
+ */
1466
+ startResize(t, e) {
1467
+ const i = this.ctx;
1468
+ t.preventDefault(), t.stopPropagation();
1469
+ const s = i.headerElement.querySelector(`[data-field="${e.field}"]`);
1470
+ s && (this.resizing = {
1471
+ column: e,
1472
+ startX: t.clientX,
1473
+ startWidth: s.offsetWidth
1474
+ }, document.addEventListener("mousemove", this.boundHandleResizeMove), document.addEventListener("mouseup", this.boundHandleResizeEnd), y(document.body, "velox-no-select"));
1475
+ }
1476
+ /**
1477
+ * Column 리사이즈 이동
1478
+ */
1479
+ handleResizeMove(t) {
1480
+ const e = this.ctx;
1481
+ if (!this.resizing) return;
1482
+ const i = t.clientX - this.resizing.startX, s = Math.max(50, this.resizing.startWidth + i);
1483
+ this.resizing.column.width = s, e.invalidateColumnCache(), e.render();
1484
+ }
1485
+ /**
1486
+ * Column 리사이즈 종료
1487
+ */
1488
+ handleResizeEnd() {
1489
+ const t = this.ctx;
1490
+ this.resizing && (document.removeEventListener("mousemove", this.boundHandleResizeMove), document.removeEventListener("mouseup", this.boundHandleResizeEnd), b(document.body, "velox-no-select"), t.emitEvent("onColumnResize", this.resizing.column.field, this.resizing.column.width || 0), this.resizing = null);
1491
+ }
1492
+ /**
1493
+ * 리사이즈 중인지 확인
1494
+ */
1495
+ isResizing() {
1496
+ return this.resizing !== null;
1497
+ }
1498
+ /**
1499
+ * 모든 드래그 상태 초기화
1500
+ */
1501
+ cleanup() {
1502
+ this.columnDragging && (this.columnDragging.element && this.columnDragging.element.remove(), document.removeEventListener("mousemove", this.boundHandleColumnDragMove), document.removeEventListener("mouseup", this.boundHandleColumnDragEnd), this.columnDragging = null), this.rowDragging && (this.rowDragging.element && this.rowDragging.element.remove(), document.removeEventListener("mousemove", this.boundHandleRowDragMove), document.removeEventListener("mouseup", this.boundHandleRowDragEnd), this.rowDragging = null), this.resizing && (document.removeEventListener("mousemove", this.boundHandleResizeMove), document.removeEventListener("mouseup", this.boundHandleResizeEnd), this.resizing = null), b(document.body, "velox-no-select");
1503
+ }
1504
+ /**
1505
+ * 리소스 정리 (destroy 별칭)
1506
+ */
1507
+ destroy() {
1508
+ this.cleanup();
1509
+ }
1510
+ }
1511
+ class at {
1512
+ constructor(t) {
1513
+ this.context = t, this.summaryCache = /* @__PURE__ */ new Map();
1514
+ }
1515
+ /**
1516
+ * Footer Summary 값 계산
1517
+ * @param field 컬럼 필드명
1518
+ * @returns 계산된 집계 값
1519
+ */
1520
+ calculateFooterSummary(t) {
1521
+ var p, c;
1522
+ const e = this.context.getOptions(), i = this.context.getState().columns.find((u) => u.field === t);
1523
+ if (!i) return null;
1524
+ const s = i.summary || ((c = (p = e.footerSummary) == null ? void 0 : p.columns) == null ? void 0 : c[t]);
1525
+ if (!s) return null;
1526
+ const n = `footer:${t}`;
1527
+ if (this.summaryCache.has(n))
1528
+ return this.summaryCache.get(n);
1529
+ const o = this.context.getDisplayData(), l = o.map((u) => u[t]), a = this.executeAggregation(s, l, o), r = this.formatSummaryValue(a, s, i), h = {
1530
+ field: t,
1531
+ function: s.function,
1532
+ value: a,
1533
+ formattedValue: r
1534
+ };
1535
+ return this.summaryCache.set(n, h), h;
1536
+ }
1537
+ /**
1538
+ * 모든 Footer Summary 값 계산
1539
+ * @returns field -> SummaryResult 맵
1540
+ */
1541
+ calculateAllFooterSummaries() {
1542
+ var s;
1543
+ const t = this.context.getOptions(), e = /* @__PURE__ */ new Map();
1544
+ return (s = t.footerSummary) != null && s.visible && this.context.getVisibleColumns().forEach((n) => {
1545
+ const o = this.calculateFooterSummary(n.field);
1546
+ o && e.set(n.field, o);
1547
+ }), e;
1548
+ }
1549
+ /**
1550
+ * Group Summary 값 계산
1551
+ * @param field 컬럼 필드명
1552
+ * @param _groupValue 그룹 값 (reserved for future use)
1553
+ * @param groupData 그룹 데이터
1554
+ * @returns 계산된 집계 값
1555
+ */
1556
+ calculateGroupSummary(t, e, i) {
1557
+ var h, p;
1558
+ const s = this.context.getOptions(), n = this.context.getState().columns.find((c) => c.field === t);
1559
+ if (!n || !((h = s.groupSummary) != null && h.enabled)) return null;
1560
+ const o = (p = s.groupSummary.columns) == null ? void 0 : p[t];
1561
+ if (!o) return null;
1562
+ const l = i.map((c) => c[t]), a = this.executeAggregation(o, l, i), r = this.formatSummaryValue(a, o, n);
1563
+ return {
1564
+ field: t,
1565
+ function: o.function,
1566
+ value: a,
1567
+ formattedValue: r
1568
+ };
1569
+ }
1570
+ /**
1571
+ * 집계 함수 실행
1572
+ * @param config Summary 설정
1573
+ * @param values 값 배열
1574
+ * @param data 원본 데이터 배열
1575
+ * @returns 집계된 값
1576
+ */
1577
+ executeAggregation(t, e, i) {
1578
+ const s = e.filter((n) => n != null);
1579
+ switch (t.function) {
1580
+ case "sum":
1581
+ return this.calculateSum(s);
1582
+ case "avg":
1583
+ return this.calculateAverage(s);
1584
+ case "count":
1585
+ return this.calculateCount(s);
1586
+ case "min":
1587
+ return this.calculateMin(s);
1588
+ case "max":
1589
+ return this.calculateMax(s);
1590
+ case "custom":
1591
+ return t.customFunction ? t.customFunction(e, i) : null;
1592
+ default:
1593
+ return null;
1594
+ }
1595
+ }
1596
+ /**
1597
+ * 합계 계산
1598
+ */
1599
+ calculateSum(t) {
1600
+ return t.reduce((e, i) => {
1601
+ const s = this.toNumber(i);
1602
+ return e + (s !== null ? s : 0);
1603
+ }, 0);
1604
+ }
1605
+ /**
1606
+ * 평균 계산
1607
+ */
1608
+ calculateAverage(t) {
1609
+ if (t.length === 0) return null;
1610
+ const e = this.calculateSum(t), i = t.filter((s) => this.toNumber(s) !== null).length;
1611
+ return i > 0 ? e / i : null;
1612
+ }
1613
+ /**
1614
+ * 개수 계산
1615
+ */
1616
+ calculateCount(t) {
1617
+ return t.length;
1618
+ }
1619
+ /**
1620
+ * 최소값 계산
1621
+ */
1622
+ calculateMin(t) {
1623
+ const e = t.map((i) => this.toNumber(i)).filter((i) => i !== null);
1624
+ return e.length > 0 ? Math.min(...e) : null;
1625
+ }
1626
+ /**
1627
+ * 최대값 계산
1628
+ */
1629
+ calculateMax(t) {
1630
+ const e = t.map((i) => this.toNumber(i)).filter((i) => i !== null);
1631
+ return e.length > 0 ? Math.max(...e) : null;
1632
+ }
1633
+ /**
1634
+ * CellValue를 숫자로 변환
1635
+ */
1636
+ toNumber(t) {
1637
+ if (typeof t == "number") return t;
1638
+ if (typeof t == "string") {
1639
+ const e = t.replace(/,/g, ""), i = parseFloat(e);
1640
+ return isNaN(i) ? null : i;
1641
+ }
1642
+ return typeof t == "boolean" ? t ? 1 : 0 : null;
1643
+ }
1644
+ /**
1645
+ * Summary 값 포맷팅
1646
+ */
1647
+ formatSummaryValue(t, e, i) {
1648
+ return e.formatter ? e.formatter(t) : i.formatter ? i.formatter(t, {}, i) : t == null ? "" : typeof t == "number" ? e.format ? this.applyNumberFormat(t, e.format) : this.formatNumber(t) : String(t);
1649
+ }
1650
+ /**
1651
+ * 숫자 포맷 적용
1652
+ * @param value 숫자 값
1653
+ * @param format 포맷 문자열 (예: '0,0.00')
1654
+ */
1655
+ applyNumberFormat(t, e) {
1656
+ const i = e.includes(","), s = e.match(/\.(\d+)/), n = s ? s[1].length : 0;
1657
+ let o = t.toFixed(n);
1658
+ if (i) {
1659
+ const l = o.split(".");
1660
+ l[0] = l[0].replace(/\B(?=(\d{3})+(?!\d))/g, ","), o = l.join(".");
1661
+ }
1662
+ return o;
1663
+ }
1664
+ /**
1665
+ * 기본 숫자 포맷팅
1666
+ */
1667
+ formatNumber(t) {
1668
+ return Number.isInteger(t) ? t.toLocaleString("en-US") : t.toLocaleString("en-US", {
1669
+ minimumFractionDigits: 0,
1670
+ maximumFractionDigits: 2
1671
+ });
1672
+ }
1673
+ /**
1674
+ * 캐시 무효화
1675
+ */
1676
+ invalidateCache() {
1677
+ this.summaryCache.clear();
1678
+ }
1679
+ /**
1680
+ * 특정 필드의 캐시만 무효화
1681
+ */
1682
+ invalidateFieldCache(t) {
1683
+ this.summaryCache.delete(`footer:${t}`);
1684
+ }
1685
+ /**
1686
+ * Summary 값 가져오기 (캐시 우선)
1687
+ */
1688
+ getSummaryValue(t) {
1689
+ const e = this.calculateFooterSummary(t);
1690
+ return (e == null ? void 0 : e.value) ?? null;
1691
+ }
1692
+ /**
1693
+ * 모든 Summary 값 가져오기
1694
+ */
1695
+ getAllSummaryValues() {
1696
+ const t = this.calculateAllFooterSummaries(), e = {};
1697
+ return t.forEach((i, s) => {
1698
+ e[s] = i.value;
1699
+ }), e;
1700
+ }
1701
+ }
1702
+ const rt = {
1703
+ rowHeight: 40,
1704
+ headerHeight: 44,
1705
+ showRowNumbers: !1,
1706
+ rowDraggable: !1,
1707
+ selectable: !0,
1708
+ selectionMode: "multiple",
1709
+ selectionStyle: "row",
1710
+ showCheckbox: !1,
1711
+ sortable: !0,
1712
+ filterable: !1,
1713
+ editable: !1,
1714
+ resizable: !0,
1715
+ virtualScroll: !1,
1716
+ bufferSize: 5,
1717
+ theme: "default",
1718
+ locale: "ko-KR",
1719
+ emptyMessage: "데이터가 없습니다.",
1720
+ loading: !1,
1721
+ loadingMessage: "로딩 중...",
1722
+ undoable: !0,
1723
+ undoStackSize: 50
1724
+ }, M = {
1725
+ visible: !1,
1726
+ exclusive: !1,
1727
+ showAll: !0
1728
+ };
1729
+ class xt {
1730
+ constructor(t, e, i = {}) {
1731
+ var s, n, o, l, a;
1732
+ if (this.footerElement = null, this.fixedLeftFooter = null, this.loadingOverlay = null, this.fixedLeftContainer = null, this.fixedLeftHeader = null, this.fixedLeftBody = null, this.fixedLeftBodyInner = null, this.fixedRightContainer = null, this.fixedRightHeader = null, this.fixedRightBody = null, this.fixedRightBodyInner = null, this.fixedRightFooter = null, this.paginationContainer = null, this.infiniteScrollLoading = !1, this.infiniteScrollAllLoaded = !1, this.blockSelecting = null, this.measureCanvas = null, this.measureContext = null, this.virtualState = {
1733
+ startIndex: 0,
1734
+ endIndex: 0,
1735
+ visibleCount: 0,
1736
+ totalHeight: 0
1737
+ }, this.dataIndexMap = /* @__PURE__ */ new Map(), this.columnCache = {
1738
+ visible: null,
1739
+ fixedLeft: null,
1740
+ scrollable: null,
1741
+ fixedRight: null,
1742
+ // Phase 14: Columns fixed to right
1743
+ dirty: !0
1744
+ }, this.tooltip = null, this.editModeCleanup = null, this.scrollHandlers = [], this.wheelHandler = null, typeof t == "string") {
1745
+ const r = document.querySelector(t);
1746
+ if (!r) throw new Error(`Container not found: ${t}`);
1747
+ this.container = r;
1748
+ } else
1749
+ this.container = t;
1750
+ this.options = { ...rt, ...e }, this.options.checkBar ? this.options.checkBar = { ...M, ...this.options.checkBar } : this.options.showCheckbox && (this.options.checkBar = { ...M, visible: !0 }), this.events = i, this.gridId = _("velox-grid"), this.boundHandleBlockSelectionEnd = this.handleBlockSelectionEnd.bind(this), this.boundHandleKeyDown = this.handleKeyDown.bind(this), this.history = new Z({
1751
+ enabled: this.options.undoable ?? !0,
1752
+ maxSize: this.options.undoStackSize ?? 50
1753
+ }), this.renderer = new st(this), this.filterPopupManager = new nt(this), this.columnMenuManager = new ot(this), this.dragManager = new lt(this), this.summary = new at(this), this.state = {
1754
+ data: [],
1755
+ displayData: [],
1756
+ columns: this.options.columns.map((r) => ({ ...r })),
1757
+ selection: {
1758
+ selectedRows: /* @__PURE__ */ new Set(),
1759
+ selectedCells: /* @__PURE__ */ new Set(),
1760
+ focusedCell: null,
1761
+ selections: [],
1762
+ lastSelectedRow: null
1763
+ },
1764
+ checkBar: {
1765
+ checkedRows: /* @__PURE__ */ new Set(),
1766
+ checkableRows: /* @__PURE__ */ new Set()
1767
+ },
1768
+ rowStates: /* @__PURE__ */ new Map(),
1769
+ // Phase 15: Row state tracking
1770
+ sort: [],
1771
+ filter: null,
1772
+ edit: {
1773
+ editing: !1,
1774
+ rowIndex: null,
1775
+ field: null,
1776
+ originalValue: null
1777
+ },
1778
+ pagination: {
1779
+ currentPage: 1,
1780
+ pageSize: ((s = this.options.pagination) == null ? void 0 : s.pageSize) || 20,
1781
+ totalCount: ((n = this.options.dataSource) == null ? void 0 : n.totalCount) || 0,
1782
+ totalPages: 0,
1783
+ loading: !1
1784
+ },
1785
+ scroll: { top: 0, left: 0 }
1786
+ }, this.options.data && (this.state.data = this.options.data.map((r) => ({ ...r })), this.rebuildDataIndexMap(), this.state.displayData = [...this.state.data], this.initCheckableRows(), this.state.data.forEach((r) => {
1787
+ this.state.rowStates.set(r, "none");
1788
+ })), this.build(), this.tooltip = new it(this.rootElement), (o = this.options.pagination) != null && o.enabled ? this.isRemoteDataSource() ? (this.render(), this.fetchData()) : (this.state.pagination.totalCount = this.state.data.length, this.updateTotalPages(), this.options.pagination.mode === "infinite" ? this.applyLocalInfiniteScroll() : this.applyLocalPagination(), this.render()) : this.render(), this.attachEvents(), (a = (l = this.events).onReady) == null || a.call(l, this);
1789
+ }
1790
+ // GridContext: Data index methods (public for module access)
1791
+ rebuildDataIndexMap() {
1792
+ this.dataIndexMap.clear(), this.state.data.forEach((t, e) => {
1793
+ this.dataIndexMap.set(t, e);
1794
+ });
1795
+ }
1796
+ initCheckableRows() {
1797
+ this.state.checkBar.checkableRows.clear();
1798
+ const t = this.options.checkBar;
1799
+ this.state.displayData.forEach((e, i) => {
1800
+ t != null && t.checkableCallback ? t.checkableCallback(e, i) && this.state.checkBar.checkableRows.add(i) : this.state.checkBar.checkableRows.add(i);
1801
+ });
1802
+ }
1803
+ // GridContext: Column cache methods (public for module access)
1804
+ invalidateColumnCache() {
1805
+ this.columnCache.dirty = !0, this.columnCache.visible = null, this.columnCache.fixedLeft = null, this.columnCache.scrollable = null, this.columnCache.fixedRight = null;
1806
+ }
1807
+ /**
1808
+ * Get special columns with displayOrder
1809
+ * Phase 14.1: Helper method to generate special columns sorted by displayOrder
1810
+ */
1811
+ getSpecialColumnsWithOrder() {
1812
+ var e;
1813
+ const t = [];
1814
+ if (typeof this.options.rowDraggable == "object" && this.options.rowDraggable.enabled) {
1815
+ const i = this.options.rowDraggable.displayOrder ?? 0;
1816
+ t.push({
1817
+ col: { field: "__drag", header: "", width: 44, visible: !0 },
1818
+ order: i
1819
+ });
1820
+ } else this.options.rowDraggable === !0 && t.push({
1821
+ col: { field: "__drag", header: "", width: 44, visible: !0 },
1822
+ order: 0
1823
+ // default order
1824
+ });
1825
+ if ((e = this.options.checkBar) != null && e.visible) {
1826
+ const i = this.options.checkBar.displayOrder ?? 10;
1827
+ t.push({
1828
+ col: { field: "__checkbox", header: "", width: 44, visible: !0 },
1829
+ order: i
1830
+ });
1831
+ }
1832
+ if (typeof this.options.showRowNumbers == "object" && this.options.showRowNumbers.visible) {
1833
+ const i = this.options.showRowNumbers.displayOrder ?? 20;
1834
+ t.push({
1835
+ col: { field: "__rownum", header: "#", width: 50, visible: !0 },
1836
+ order: i
1837
+ });
1838
+ } else this.options.showRowNumbers === !0 && t.push({
1839
+ col: { field: "__rownum", header: "#", width: 50, visible: !0 },
1840
+ order: 20
1841
+ // default order
1842
+ });
1843
+ return t.sort((i, s) => i.order - s.order), t.map((i) => i.col);
1844
+ }
1845
+ /**
1846
+ * Get fixed left columns
1847
+ * Phase 14: Only include special columns when fixedOptions.colCount > 0
1848
+ * Phase 14.1: Sort special columns by displayOrder
1849
+ *
1850
+ * Logic:
1851
+ * - If colCount = 0: Fixed left is empty (special columns go to scrollable)
1852
+ * - If colCount > 0: Fixed left = special columns (sorted by displayOrder) + first N data columns
1853
+ */
1854
+ getFixedLeftColumns() {
1855
+ if (this.columnCache.dirty || !this.columnCache.fixedLeft) {
1856
+ const { colCount: t = 0 } = this.options.fixedOptions || {};
1857
+ if (t > 0) {
1858
+ const e = this.getSpecialColumnsWithOrder(), s = this.getDataColumns().slice(0, t);
1859
+ this.columnCache.fixedLeft = [...e, ...s];
1860
+ } else
1861
+ this.columnCache.fixedLeft = [];
1862
+ }
1863
+ return this.columnCache.fixedLeft;
1864
+ }
1865
+ /**
1866
+ * Get columns fixed to right (based on fixedOptions.rightCount)
1867
+ * Phase 14: New method for right fixed columns
1868
+ */
1869
+ getFixedRightColumns() {
1870
+ if (this.columnCache.dirty || !this.columnCache.fixedRight) {
1871
+ const { rightCount: t = 0 } = this.options.fixedOptions || {}, e = this.getDataColumns(), i = e.length;
1872
+ this.columnCache.fixedRight = t > 0 ? e.slice(i - t) : [];
1873
+ }
1874
+ return this.columnCache.fixedRight;
1875
+ }
1876
+ /**
1877
+ * Get scrollable columns (middle area between fixed left and fixed right)
1878
+ * Phase 14: Include special columns when colCount = 0
1879
+ * Phase 14.1: Sort special columns by displayOrder
1880
+ *
1881
+ * Logic:
1882
+ * - If colCount = 0: Scrollable = special columns (sorted by displayOrder) + all data columns (except fixed right)
1883
+ * - If colCount > 0: Scrollable = middle data columns only (between fixed left and fixed right)
1884
+ */
1885
+ getScrollableColumns() {
1886
+ if (this.columnCache.dirty || !this.columnCache.scrollable) {
1887
+ const { colCount: t = 0, rightCount: e = 0 } = this.options.fixedOptions || {}, i = this.getDataColumns(), s = i.length;
1888
+ if (t === 0) {
1889
+ const n = this.getSpecialColumnsWithOrder(), o = s - e, l = o > 0 ? i.slice(0, o) : [];
1890
+ this.columnCache.scrollable = [...n, ...l];
1891
+ } else {
1892
+ const n = t, o = s - e;
1893
+ this.columnCache.scrollable = n < o ? i.slice(n, o) : [];
1894
+ }
1895
+ this.columnCache.dirty = !1;
1896
+ }
1897
+ return this.columnCache.scrollable;
1898
+ }
1899
+ /**
1900
+ * Get data columns (exclude special columns)
1901
+ * Phase 14: Helper method to filter out special columns
1902
+ */
1903
+ getDataColumns() {
1904
+ return this.state.columns.filter(
1905
+ (t) => t.visible !== !1 && !this.isSpecialColumn(t)
1906
+ );
1907
+ }
1908
+ /**
1909
+ * Check if column is special (CheckBar, RowNumbers, DragHandle)
1910
+ * Phase 14: Helper method to identify special columns
1911
+ */
1912
+ isSpecialColumn(t) {
1913
+ return t.field === "__checkbox" || t.field === "__rownum" || t.field === "__drag";
1914
+ }
1915
+ getVisibleColumns() {
1916
+ return (this.columnCache.dirty || !this.columnCache.visible) && (this.columnCache.visible = this.state.columns.filter((t) => t.visible !== !1)), this.columnCache.visible;
1917
+ }
1918
+ /**
1919
+ * Check if grid has fixed left columns
1920
+ * Phase 14: Only true when fixedOptions.colCount > 0
1921
+ */
1922
+ hasFixedLeft() {
1923
+ const { colCount: t = 0 } = this.options.fixedOptions || {};
1924
+ return t > 0;
1925
+ }
1926
+ /**
1927
+ * Check if grid has fixed right columns
1928
+ * Phase 14: New method for hasFixedRight
1929
+ */
1930
+ hasFixedRight() {
1931
+ const { rightCount: t = 0 } = this.options.fixedOptions || {};
1932
+ return t > 0;
1933
+ }
1934
+ build() {
1935
+ var i, s, n, o;
1936
+ this.rootElement = f("div", "velox-grid"), this.rootElement.id = this.gridId, this.rootElement.tabIndex = 0, this.options.className && y(this.rootElement, this.options.className), this.hasFixedRight() && y(this.rootElement, "has-fixed-right"), this.options.width && (this.rootElement.style.width = typeof this.options.width == "number" ? `${this.options.width}px` : this.options.width), this.options.height && (this.rootElement.style.height = typeof this.options.height == "number" ? `${this.options.height}px` : this.options.height);
1937
+ const t = f("div", "velox-wrapper");
1938
+ this.hasFixedLeft() && (this.fixedLeftContainer = f("div", "velox-fixed-left"), this.fixedLeftHeader = f("div", "velox-header velox-header--fixed"), this.fixedLeftBody = f("div", "velox-body--fixed"), this.fixedLeftBodyInner = f("div", "velox-body-inner"), this.fixedLeftBody.appendChild(this.fixedLeftBodyInner), this.fixedLeftContainer.appendChild(this.fixedLeftHeader), this.fixedLeftContainer.appendChild(this.fixedLeftBody), (i = this.options.footerSummary) != null && i.visible && (this.fixedLeftFooter = f("div", "velox-footer velox-footer--fixed"), this.fixedLeftContainer.appendChild(this.fixedLeftFooter)), t.appendChild(this.fixedLeftContainer));
1939
+ const e = f("div", "velox-main");
1940
+ this.headerElement = f("div", "velox-header"), this.bodyElement = f("div", "velox-body"), this.bodyInner = f("div", "velox-body-inner"), this.bodyElement.appendChild(this.bodyInner), e.appendChild(this.headerElement), e.appendChild(this.bodyElement), (s = this.options.footerSummary) != null && s.visible && (this.footerElement = f("div", "velox-footer"), e.appendChild(this.footerElement)), t.appendChild(e), this.hasFixedRight() && (this.fixedRightContainer = f("div", "velox-fixed-right"), this.fixedRightHeader = f("div", "velox-header velox-header--fixed-right"), this.fixedRightBody = f("div", "velox-body--fixed"), this.fixedRightBodyInner = f("div", "velox-body-inner"), this.fixedRightBody.appendChild(this.fixedRightBodyInner), this.fixedRightContainer.appendChild(this.fixedRightHeader), this.fixedRightContainer.appendChild(this.fixedRightBody), (n = this.options.footerSummary) != null && n.visible && (this.fixedRightFooter = f("div", "velox-footer velox-footer--fixed-right"), this.fixedRightContainer.appendChild(this.fixedRightFooter)), t.appendChild(this.fixedRightContainer)), this.rootElement.appendChild(t), (o = this.options.pagination) != null && o.enabled && (this.paginationContainer = f("div", "velox-pagination"), this.rootElement.appendChild(this.paginationContainer)), this.container.innerHTML = "", this.container.appendChild(this.rootElement), this.buildLoadingOverlay();
1941
+ }
1942
+ buildLoadingOverlay() {
1943
+ this.loadingOverlay = f("div", "velox-loading-overlay"), this.loadingOverlay.style.display = "none";
1944
+ const t = f("div", "velox-loading-spinner"), e = f("div", "velox-loading-message");
1945
+ e.textContent = this.options.loadingMessage || "로딩 중...", this.loadingOverlay.appendChild(t), this.loadingOverlay.appendChild(e), this.rootElement.appendChild(this.loadingOverlay);
1946
+ }
1947
+ // GridContext: Virtual scroll methods (public for module access)
1948
+ calculateVirtualState() {
1949
+ if (!this.options.virtualScroll) return;
1950
+ const t = this.options.rowHeight || 40, e = this.bodyElement.clientHeight, i = this.bodyElement.scrollTop, s = this.options.bufferSize || 5;
1951
+ this.virtualState.visibleCount = Math.ceil(e / t), this.virtualState.startIndex = Math.max(0, Math.floor(i / t) - s), this.virtualState.endIndex = Math.min(
1952
+ this.state.displayData.length,
1953
+ this.virtualState.startIndex + this.virtualState.visibleCount + s * 2
1954
+ ), this.virtualState.totalHeight = this.state.displayData.length * t;
1955
+ }
1956
+ getVisibleRows() {
1957
+ if (!this.options.virtualScroll)
1958
+ return this.state.displayData.map((e, i) => ({ data: e, index: i }));
1959
+ this.calculateVirtualState();
1960
+ const t = [];
1961
+ for (let e = this.virtualState.startIndex; e < this.virtualState.endIndex; e++)
1962
+ this.state.displayData[e] && t.push({ data: this.state.displayData[e], index: e });
1963
+ return t;
1964
+ }
1965
+ // GridContext: Rendering methods - delegated to GridRenderer
1966
+ render() {
1967
+ console.log("🔄 render() called", { editing: this.state.edit.editing, rowIndex: this.state.edit.rowIndex, field: this.state.edit.field }), this.renderer.render(), this.paginationContainer && this.renderPagination();
1968
+ }
1969
+ renderHeader() {
1970
+ this.renderer.renderHeader();
1971
+ }
1972
+ renderBody() {
1973
+ this.renderer.renderBody();
1974
+ }
1975
+ updateLoadingState() {
1976
+ this.renderer.updateLoadingState();
1977
+ }
1978
+ // GridContext: Filter popup methods - delegated to GridFilterPopup
1979
+ showFilterPopup(t, e) {
1980
+ this.filterPopupManager.showFilterPopup(t, e);
1981
+ }
1982
+ closeFilterPopup() {
1983
+ this.filterPopupManager.closeFilterPopup();
1984
+ }
1985
+ applyColumnFilter(t, e, i) {
1986
+ this.filterPopupManager.applyColumnFilter(t, e, i);
1987
+ }
1988
+ removeColumnFilter(t) {
1989
+ this.filterPopupManager.removeColumnFilter(t);
1990
+ }
1991
+ detachEvents() {
1992
+ this.scrollHandlers.forEach((t) => {
1993
+ this.bodyElement.removeEventListener("scroll", t), this.fixedRightBody && this.fixedRightBody.removeEventListener("scroll", t);
1994
+ }), this.scrollHandlers = [], this.wheelHandler && (this.bodyElement.removeEventListener("wheel", this.wheelHandler), this.wheelHandler = null);
1995
+ }
1996
+ attachEvents() {
1997
+ this.detachEvents();
1998
+ let t = !1, e = null;
1999
+ const i = (n) => {
2000
+ var a, r;
2001
+ if (t || e !== null) return;
2002
+ e = window.setTimeout(() => {
2003
+ e = null;
2004
+ }, 16), t = !0;
2005
+ const o = n === "fixedRight" && this.fixedRightBody ? this.fixedRightBody.scrollTop : this.bodyElement.scrollTop, l = this.bodyElement.scrollLeft;
2006
+ this.state.scroll.top = o, this.state.scroll.left = l, this.fixedLeftBody && this.fixedLeftBody.scrollTop !== o && (this.fixedLeftBody.scrollTop = o), n !== "fixedRight" && this.fixedRightBody && this.fixedRightBody.scrollTop !== o && (this.fixedRightBody.scrollTop = o), n !== "body" && this.bodyElement.scrollTop !== o && (this.bodyElement.scrollTop = o), this.headerElement.scrollLeft !== l && (this.headerElement.scrollLeft = l), this.footerElement && this.footerElement.scrollLeft !== l && (this.footerElement.scrollLeft = l), this.options.virtualScroll && this.renderBody(), (r = (a = this.events).onScroll) == null || r.call(a, this.state.scroll.top, this.state.scroll.left), (n === "body" || n === "fixedRight") && this.checkInfiniteScroll(), t = !1;
2007
+ }, s = V(() => {
2008
+ const n = this.headerElement.scrollLeft;
2009
+ this.bodyElement.scrollLeft = n, this.footerElement && (this.footerElement.scrollLeft = n);
2010
+ }, 16);
2011
+ if (this.hasFixedRight()) {
2012
+ this.wheelHandler = (l) => {
2013
+ l.preventDefault(), this.fixedRightBody && (this.fixedRightBody.scrollTop += l.deltaY);
2014
+ }, this.bodyElement.addEventListener("wheel", this.wheelHandler, { passive: !1 });
2015
+ const n = () => i("fixedRight");
2016
+ this.fixedRightBody.addEventListener("scroll", n), this.scrollHandlers.push(n);
2017
+ const o = () => i("body");
2018
+ this.bodyElement.addEventListener("scroll", o), this.scrollHandlers.push(o);
2019
+ } else {
2020
+ const n = () => i("body");
2021
+ this.bodyElement.addEventListener("scroll", n), this.scrollHandlers.push(n);
2022
+ }
2023
+ this.headerElement.addEventListener("scroll", s), this.scrollHandlers.push(s), document.addEventListener("mouseup", this.boundHandleBlockSelectionEnd), this.rootElement.addEventListener("keydown", this.boundHandleKeyDown);
2024
+ }
2025
+ handleSort(t) {
2026
+ var s, n, o;
2027
+ const e = this.state.sort.findIndex((l) => l.field === t);
2028
+ let i = "asc";
2029
+ if (e >= 0) {
2030
+ const l = this.state.sort[e].direction;
2031
+ l === "asc" ? i = "desc" : l === "desc" && (i = null);
2032
+ }
2033
+ this.state.sort = i ? [{ field: t, direction: i }] : [], this.clearSelectionState(), this.applyDataTransformations(), (!this.isRemoteDataSource() || !((s = this.options.pagination) != null && s.enabled)) && this.render(), (o = (n = this.events).onSort) == null || o.call(n, this.state.sort);
2034
+ }
2035
+ // GridContext: Event handlers (public for module access)
2036
+ handleRowClick(t, e) {
2037
+ var s, n;
2038
+ if (!this.options.selectable) return;
2039
+ (this.options.selectionStyle || "row") === "row" && this.handleRowSelection(t, e), (n = (s = this.events).onRowClick) == null || n.call(s, t, this.state.displayData[t]);
2040
+ }
2041
+ handleRowSelection(t, e) {
2042
+ var s, n;
2043
+ const i = this.options.selectionMode || "multiple";
2044
+ if (i !== "none")
2045
+ if (i === "multiple" && (e.ctrlKey || e.metaKey)) {
2046
+ const o = this.state.selection.selectedRows.has(t);
2047
+ this.selectRow(t, !o);
2048
+ } else if (i === "multiple" && e.shiftKey) {
2049
+ const o = Array.from(this.state.selection.selectedRows);
2050
+ if (o.length > 0) {
2051
+ const l = o[o.length - 1], a = Math.min(l, t), r = Math.max(l, t);
2052
+ for (let h = a; h <= r; h++) this.state.selection.selectedRows.add(h);
2053
+ this.render(), (n = (s = this.events).onSelectionChange) == null || n.call(s, this.getSelectedRows());
2054
+ } else
2055
+ this.selectRow(t, !0);
2056
+ } else if (i === "extended" && (e.ctrlKey || e.metaKey)) {
2057
+ const o = this.state.selection.selectedRows.has(t);
2058
+ this.selectRow(t, !o);
2059
+ } else
2060
+ this.state.selection.selectedRows.clear(), this.selectRow(t, !0);
2061
+ }
2062
+ handleCellClick(t, e, i, s) {
2063
+ var o, l;
2064
+ if (console.log("🔍 handleCellClick", { rowIndex: t, field: e, editing: this.state.edit.editing, editRow: this.state.edit.rowIndex, editField: this.state.edit.field }), this.state.edit.editing && this.state.edit.rowIndex === t && this.state.edit.field === e) {
2065
+ console.log("✅ Same cell clicked - maintaining edit mode");
2066
+ return;
2067
+ }
2068
+ const n = this.options.selectionStyle || "row";
2069
+ n === "cell" || n === "block" ? this.handleCellSelection(t, e, s) : n === "row" && (this.state.selection.focusedCell = { rowIndex: t, field: e }), (l = (o = this.events).onCellClick) == null || l.call(o, t, e, i);
2070
+ }
2071
+ handleCellSelection(t, e, i) {
2072
+ var o, l;
2073
+ const s = this.options.selectionMode || "multiple", n = `${t}:${e}`;
2074
+ if (s !== "none") {
2075
+ if (this.state.selection.focusedCell = { rowIndex: t, field: e }, s === "multiple" && (i.ctrlKey || i.metaKey))
2076
+ this.state.selection.selectedCells.has(n) ? this.state.selection.selectedCells.delete(n) : this.state.selection.selectedCells.add(n);
2077
+ else if (s === "multiple" && i.shiftKey) {
2078
+ const a = this.state.selection.focusedCell;
2079
+ a && this.selectCellRange(a.rowIndex, a.field, t, e);
2080
+ } else
2081
+ this.state.selection.selectedCells.clear(), this.state.selection.selectedCells.add(n);
2082
+ this.render(), (l = (o = this.events).onCellSelectionChange) == null || l.call(o, this.getSelectedCells());
2083
+ }
2084
+ }
2085
+ selectCellRange(t, e, i, s) {
2086
+ const n = this.getVisibleColumns(), o = n.findIndex((c) => c.field === e), l = n.findIndex((c) => c.field === s), a = Math.min(t, i), r = Math.max(t, i), h = Math.min(o, l), p = Math.max(o, l);
2087
+ this.state.selection.selectedCells.clear();
2088
+ for (let c = a; c <= r; c++)
2089
+ for (let u = h; u <= p; u++) {
2090
+ const g = n[u].field;
2091
+ this.state.selection.selectedCells.add(`${c}:${g}`);
2092
+ }
2093
+ }
2094
+ // GridContext: Block selection methods (public for module access)
2095
+ startBlockSelection(t, e) {
2096
+ this.options.selectionStyle === "block" && (this.blockSelecting = { startRow: t, startField: e }, this.state.selection.focusedCell = { rowIndex: t, field: e }, this.state.selection.selectedCells.clear(), this.state.selection.selectedCells.add(`${t}:${e}`), this.render());
2097
+ }
2098
+ updateBlockSelection(t, e) {
2099
+ this.blockSelecting && (this.selectCellRange(
2100
+ this.blockSelecting.startRow,
2101
+ this.blockSelecting.startField,
2102
+ t,
2103
+ e
2104
+ ), this.render());
2105
+ }
2106
+ handleBlockSelectionEnd() {
2107
+ var t, e;
2108
+ this.blockSelecting && (this.blockSelecting = null, (e = (t = this.events).onCellSelectionChange) == null || e.call(t, this.getSelectedCells()));
2109
+ }
2110
+ handleRowDoubleClick(t, e) {
2111
+ var i, s;
2112
+ (s = (i = this.events).onRowDoubleClick) == null || s.call(i, t, this.state.displayData[t]);
2113
+ }
2114
+ handleKeyDown(t) {
2115
+ var a, r, h, p;
2116
+ if (this.state.edit.editing) {
2117
+ t.key === "Escape" ? this.cancelEdit() : t.key === "Enter" ? (t.preventDefault(), this.endEditAndMove(t.shiftKey ? "up" : "down")) : t.key === "Tab" && (t.preventDefault(), this.endEditAndMove(t.shiftKey ? "left" : "right"));
2118
+ return;
2119
+ }
2120
+ if ((t.ctrlKey || t.metaKey) && t.key === "z") {
2121
+ t.preventDefault(), this.undo();
2122
+ return;
2123
+ }
2124
+ if ((t.ctrlKey || t.metaKey) && t.key === "y") {
2125
+ t.preventDefault(), this.redo();
2126
+ return;
2127
+ }
2128
+ if ((t.ctrlKey || t.metaKey) && t.key === "c") {
2129
+ t.preventDefault(), this.copy();
2130
+ return;
2131
+ }
2132
+ if ((t.ctrlKey || t.metaKey) && t.key === "v") {
2133
+ t.preventDefault(), this.paste();
2134
+ return;
2135
+ }
2136
+ if ((t.ctrlKey || t.metaKey) && t.key === "x") {
2137
+ t.preventDefault(), this.cut();
2138
+ return;
2139
+ }
2140
+ if ((t.key === "Delete" || t.key === "Backspace") && this.options.editable) {
2141
+ t.preventDefault(), this.deleteSelectedCells();
2142
+ return;
2143
+ }
2144
+ const e = this.state.selection.focusedCell;
2145
+ if (!this.state.edit.editing && this.options.editable && e) {
2146
+ const c = this.state.columns.find((u) => u.field === e.field);
2147
+ if ((c == null ? void 0 : c.editable) !== !1 && t.key.length === 1 && !t.ctrlKey && !t.metaKey && !t.altKey) {
2148
+ t.preventDefault(), this.startEdit(e.rowIndex, e.field), setTimeout(() => {
2149
+ const u = document.querySelector(".velox-edit-input");
2150
+ u && (u.value = t.key, u.setSelectionRange(1, 1));
2151
+ }, 0);
2152
+ return;
2153
+ }
2154
+ }
2155
+ if ((r = (a = this.events).onKeyDown) == null || r.call(a, t, e), !e) return;
2156
+ const i = this.getVisibleColumns(), s = i.findIndex((c) => c.field === e.field);
2157
+ let n = e.rowIndex, o = s, l = !1;
2158
+ switch (t.key) {
2159
+ case "ArrowUp":
2160
+ n > 0 && (n--, l = !0);
2161
+ break;
2162
+ case "ArrowDown":
2163
+ n < this.state.displayData.length - 1 && (n++, l = !0);
2164
+ break;
2165
+ case "ArrowLeft":
2166
+ o > 0 && (o--, l = !0);
2167
+ break;
2168
+ case "ArrowRight":
2169
+ o < i.length - 1 && (o++, l = !0);
2170
+ break;
2171
+ case "Tab":
2172
+ t.shiftKey ? o > 0 ? o-- : n > 0 && (n--, o = i.length - 1) : o < i.length - 1 ? o++ : n < this.state.displayData.length - 1 && (n++, o = 0), l = !0;
2173
+ break;
2174
+ case "Home":
2175
+ t.ctrlKey && (n = 0), o = 0, l = !0;
2176
+ break;
2177
+ case "End":
2178
+ t.ctrlKey && (n = this.state.displayData.length - 1), o = i.length - 1, l = !0;
2179
+ break;
2180
+ case "PageUp":
2181
+ n = Math.max(0, n - this.virtualState.visibleCount), l = !0;
2182
+ break;
2183
+ case "PageDown":
2184
+ n = Math.min(this.state.displayData.length - 1, n + this.virtualState.visibleCount), l = !0;
2185
+ break;
2186
+ case "Enter":
2187
+ case "F2":
2188
+ this.options.editable && (this.startEdit(e.rowIndex, e.field), l = !0);
2189
+ break;
2190
+ case " ":
2191
+ (h = this.options.checkBar) != null && h.visible && (this.checkItem(e.rowIndex, !this.isItemChecked(e.rowIndex)), l = !0);
2192
+ break;
2193
+ case "a":
2194
+ case "A":
2195
+ (t.ctrlKey || t.metaKey) && (this.selectAllCells(), l = !0);
2196
+ break;
2197
+ }
2198
+ if (l) {
2199
+ t.preventDefault();
2200
+ const c = (p = i[o]) == null ? void 0 : p.field;
2201
+ c && (n !== e.rowIndex || c !== e.field) && (this.setFocusedCell(n, c), t.shiftKey && (this.options.selectionStyle === "cell" || this.options.selectionStyle === "block") ? this.selectCellRange(e.rowIndex, e.field, n, c) : !t.shiftKey && !t.ctrlKey && (this.state.selection.selectedCells.clear(), this.state.selection.selectedCells.add(`${n}:${c}`), this.options.selectionStyle === "row" && (this.state.selection.selectedRows.clear(), this.state.selection.selectedRows.add(n))), this.render(), this.scrollToCell(n, c));
2202
+ }
2203
+ }
2204
+ selectAllCells() {
2205
+ var e, i;
2206
+ const t = this.getVisibleColumns();
2207
+ this.state.selection.selectedCells.clear();
2208
+ for (let s = 0; s < this.state.displayData.length; s++)
2209
+ for (const n of t)
2210
+ this.state.selection.selectedCells.add(`${s}:${n.field}`);
2211
+ this.render(), (i = (e = this.events).onCellSelectionChange) == null || i.call(e, this.getSelectedCells());
2212
+ }
2213
+ // GridContext: Resize methods - delegated to GridDragManager
2214
+ startResize(t, e) {
2215
+ this.dragManager.startResize(t, e);
2216
+ }
2217
+ // GridContext: Data transformation (public for module access)
2218
+ applyDataTransformations() {
2219
+ var e;
2220
+ if ((e = this.options.pagination) != null && e.enabled)
2221
+ if (this.infiniteScrollLoading = !1, this.infiniteScrollAllLoaded = !1, this.state.pagination.currentPage = 1, this.isRemoteDataSource()) {
2222
+ this.options.pagination.mode === "infinite" && (this.state.data = [], this.state.displayData = []), this.fetchData();
2223
+ return;
2224
+ } else {
2225
+ this.options.pagination.mode === "infinite" ? this.applyLocalInfiniteScroll() : this.applyLocalPagination();
2226
+ return;
2227
+ }
2228
+ let t = [...this.state.data];
2229
+ if (this.state.filter && (t = T(t, this.state.filter)), this.state.sort.length > 0) {
2230
+ const i = {};
2231
+ this.state.columns.forEach((s) => {
2232
+ i[s.field] = s.type || "text";
2233
+ }), t = L(t, this.state.sort, i);
2234
+ }
2235
+ this.state.displayData = t, this.initCheckableRows();
2236
+ }
2237
+ /**
2238
+ * Clear all selection state (rows, cells, focused cell)
2239
+ * Extracted to reduce code duplication
2240
+ */
2241
+ // GridContext: Selection state clearing (public for module access)
2242
+ clearSelectionState() {
2243
+ this.state.selection.selectedRows.clear(), this.state.selection.selectedCells.clear(), this.state.selection.focusedCell = null;
2244
+ }
2245
+ // --- Public API: Data Methods ---
2246
+ getData() {
2247
+ return this.state.data.map((t) => ({ ...t }));
2248
+ }
2249
+ setData(t) {
2250
+ var e, i, s;
2251
+ this.state.data = t.map((n) => ({ ...n })), this.rebuildDataIndexMap(), this.clearSelectionState(), this.state.checkBar.checkedRows.clear(), this.state.rowStates.clear(), this.state.data.forEach((n) => {
2252
+ this.state.rowStates.set(n, "none");
2253
+ }), (e = this.options.pagination) != null && e.enabled && !this.isRemoteDataSource() && (this.state.pagination.totalCount = this.state.data.length, this.state.pagination.currentPage = 1, this.updateTotalPages()), this.summary.invalidateCache(), this.applyDataTransformations(), this.render(), (s = (i = this.events).onDataChange) == null || s.call(i, this.state.data);
2254
+ }
2255
+ getRow(t) {
2256
+ return this.state.displayData[t] ? { ...this.state.displayData[t] } : null;
2257
+ }
2258
+ getRowCount() {
2259
+ return this.state.data.length;
2260
+ }
2261
+ getVisibleRowCount() {
2262
+ return this.state.displayData.length;
2263
+ }
2264
+ addRow(t, e) {
2265
+ var n, o, l, a;
2266
+ const i = { ...t }, s = e !== void 0 ? e : this.state.data.length;
2267
+ this.state.data.splice(s, 0, i), this.rebuildDataIndexMap(), this.state.rowStates.set(i, "created"), this.summary.invalidateCache(), this.applyDataTransformations(), this.render(), (o = (n = this.events).onRowAdd) == null || o.call(n, i, s), (a = (l = this.events).onDataChange) == null || a.call(l, this.state.data);
2268
+ }
2269
+ updateRow(t, e) {
2270
+ var n, o, l, a;
2271
+ const i = this.state.displayData[t];
2272
+ if (!i) return;
2273
+ const s = this.state.data.indexOf(i);
2274
+ s >= 0 && (Object.assign(this.state.data[s], e), (this.state.rowStates.get(this.state.data[s]) || "none") === "none" && this.state.rowStates.set(this.state.data[s], "updated"), this.summary.invalidateCache(), this.applyDataTransformations(), this.render(), (o = (n = this.events).onRowUpdate) == null || o.call(n, this.state.data[s], t, e), (a = (l = this.events).onDataChange) == null || a.call(l, this.state.data));
2275
+ }
2276
+ removeRow(t) {
2277
+ var s, n, o, l;
2278
+ const e = this.state.displayData[t];
2279
+ if (!e) return;
2280
+ const i = this.state.data.indexOf(e);
2281
+ if (i >= 0) {
2282
+ const a = this.state.data[i];
2283
+ (this.state.rowStates.get(a) || "none") === "created" ? this.state.rowStates.set(a, "createAndDeleted") : this.state.rowStates.set(a, "deleted"), this.state.data.splice(i, 1), this.rebuildDataIndexMap(), this.state.selection.selectedRows.delete(t);
2284
+ const h = /* @__PURE__ */ new Set();
2285
+ this.state.selection.selectedRows.forEach((c) => {
2286
+ c > t ? h.add(c - 1) : c < t && h.add(c);
2287
+ }), this.state.selection.selectedRows = h, this.state.checkBar.checkedRows.delete(t);
2288
+ const p = /* @__PURE__ */ new Set();
2289
+ this.state.checkBar.checkedRows.forEach((c) => {
2290
+ c > t ? p.add(c - 1) : c < t && p.add(c);
2291
+ }), this.state.checkBar.checkedRows = p, this.summary.invalidateCache(), this.applyDataTransformations(), this.render(), (n = (s = this.events).onRowRemove) == null || n.call(s, a, t), (l = (o = this.events).onDataChange) == null || l.call(o, this.state.data);
2292
+ }
2293
+ }
2294
+ clearData() {
2295
+ var t, e;
2296
+ this.state.data = [], this.state.displayData = [], this.dataIndexMap.clear(), this.state.selection.selectedRows.clear(), this.state.selection.selectedCells.clear(), this.state.selection.focusedCell = null, this.state.checkBar.checkedRows.clear(), this.state.checkBar.checkableRows.clear(), this.summary.invalidateCache(), this.render(), (e = (t = this.events).onDataChange) == null || e.call(t, []);
2297
+ }
2298
+ // --- Public API: Row Selection ---
2299
+ getSelectedRows() {
2300
+ return Array.from(this.state.selection.selectedRows).sort((t, e) => t - e);
2301
+ }
2302
+ getSelectedData() {
2303
+ return this.getSelectedRows().map((t) => this.state.displayData[t]).filter(Boolean).map((t) => ({ ...t }));
2304
+ }
2305
+ selectRow(t, e = !0) {
2306
+ var i, s, n, o;
2307
+ e ? (this.options.selectionMode === "single" && this.state.selection.selectedRows.clear(), this.state.selection.selectedRows.add(t)) : this.state.selection.selectedRows.delete(t), this.render(), (s = (i = this.events).onRowSelect) == null || s.call(i, t, e), (o = (n = this.events).onSelectionChange) == null || o.call(n, this.getSelectedRows());
2308
+ }
2309
+ selectAll(t = !0) {
2310
+ var e, i, s, n;
2311
+ if (t)
2312
+ for (let o = 0; o < this.state.displayData.length; o++)
2313
+ this.state.selection.selectedRows.add(o);
2314
+ else
2315
+ this.state.selection.selectedRows.clear();
2316
+ this.render(), (i = (e = this.events).onAllSelect) == null || i.call(e, t), (n = (s = this.events).onSelectionChange) == null || n.call(s, this.getSelectedRows());
2317
+ }
2318
+ clearSelection() {
2319
+ var t, e, i, s;
2320
+ this.clearSelectionState(), this.render(), (e = (t = this.events).onSelectionChange) == null || e.call(t, []), (s = (i = this.events).onCellSelectionChange) == null || s.call(i, []);
2321
+ }
2322
+ isRowSelected(t) {
2323
+ return this.state.selection.selectedRows.has(t);
2324
+ }
2325
+ // --- Public API: Cell Selection ---
2326
+ selectCell(t, e, i = !0) {
2327
+ var n, o, l, a;
2328
+ const s = `${t}:${e}`;
2329
+ i ? this.state.selection.selectedCells.add(s) : this.state.selection.selectedCells.delete(s), this.render(), (o = (n = this.events).onCellSelect) == null || o.call(n, { rowIndex: t, field: e }, i), (a = (l = this.events).onCellSelectionChange) == null || a.call(l, this.getSelectedCells());
2330
+ }
2331
+ getSelectedCells() {
2332
+ return Array.from(this.state.selection.selectedCells).map((t) => {
2333
+ const [e, i] = t.split(":");
2334
+ return { rowIndex: parseInt(e, 10), field: i };
2335
+ });
2336
+ }
2337
+ setFocusedCell(t, e) {
2338
+ this.state.selection.focusedCell = { rowIndex: t, field: e }, this.render();
2339
+ }
2340
+ getFocusedCell() {
2341
+ return this.state.selection.focusedCell;
2342
+ }
2343
+ setSelection(t) {
2344
+ if (this.state.selection.selectedCells.clear(), this.state.selection.selectedRows.clear(), t.style === "row")
2345
+ for (let e = t.startRow; e <= t.endRow; e++)
2346
+ this.state.selection.selectedRows.add(e);
2347
+ else (t.style === "cell" || t.style === "block") && t.startColumn && t.endColumn && this.selectCellRange(t.startRow, t.startColumn, t.endRow, t.endColumn);
2348
+ this.state.selection.selections = [t], this.render();
2349
+ }
2350
+ getSelection() {
2351
+ const t = this.state.selection.selections;
2352
+ return t.length > 0 ? t[0] : null;
2353
+ }
2354
+ getSelectionData() {
2355
+ const t = this.getSelectedCells();
2356
+ if (t.length === 0) return [];
2357
+ const e = [...new Set(t.map((l) => l.rowIndex))].sort((l, a) => l - a), i = [...new Set(t.map((l) => l.field))], n = this.getVisibleColumns().filter((l) => i.includes(l.field)).map((l) => l.field), o = [];
2358
+ for (const l of e) {
2359
+ const a = this.state.displayData[l];
2360
+ if (a) {
2361
+ const r = n.map((h) => a[h]);
2362
+ o.push(r);
2363
+ }
2364
+ }
2365
+ return o;
2366
+ }
2367
+ // --- Public API: CheckBar ---
2368
+ checkItem(t, e = !0) {
2369
+ var n, o;
2370
+ if (console.log("🔵 checkItem START", { index: t, checked: e, currentEditState: this.state.edit }), !this.state.checkBar.checkableRows.has(t)) return;
2371
+ const i = this.options.checkBar;
2372
+ i != null && i.exclusive && e && this.state.checkBar.checkedRows.clear(), e ? this.state.checkBar.checkedRows.add(t) : this.state.checkBar.checkedRows.delete(t);
2373
+ const s = { ...this.state.edit };
2374
+ console.log("💾 Saved edit state before render", s), this.render(), console.log("🔄 Render complete, edit state after render", this.state.edit), s.editing && s.rowIndex !== null && s.field !== null && (console.log("♻️ Restoring edit state", s), this.state.edit = s, this.renderEditCell(s.rowIndex, s.field, s.originalValue)), (o = (n = this.events).onCheckChange) == null || o.call(n, t, e), console.log("🔵 checkItem END");
2375
+ }
2376
+ checkItems(t, e = !0) {
2377
+ t.forEach((i) => {
2378
+ this.state.checkBar.checkableRows.has(i) && (e ? this.state.checkBar.checkedRows.add(i) : this.state.checkBar.checkedRows.delete(i));
2379
+ }), this.render();
2380
+ }
2381
+ checkAll(t = !0) {
2382
+ var e, i, s;
2383
+ (e = this.options.checkBar) != null && e.exclusive || (t ? this.state.checkBar.checkableRows.forEach((n) => {
2384
+ this.state.checkBar.checkedRows.add(n);
2385
+ }) : this.state.checkBar.checkedRows.clear(), this.render(), (s = (i = this.events).onCheckAllChange) == null || s.call(i, t));
2386
+ }
2387
+ uncheckAll() {
2388
+ this.checkAll(!1);
2389
+ }
2390
+ getCheckedItems() {
2391
+ return Array.from(this.state.checkBar.checkedRows).sort((t, e) => t - e);
2392
+ }
2393
+ getCheckedData() {
2394
+ return this.getCheckedItems().map((t) => this.state.displayData[t]).filter(Boolean).map((t) => ({ ...t }));
2395
+ }
2396
+ isItemChecked(t) {
2397
+ return this.state.checkBar.checkedRows.has(t);
2398
+ }
2399
+ isItemCheckable(t) {
2400
+ return this.state.checkBar.checkableRows.has(t);
2401
+ }
2402
+ checkRow(t, e = !0) {
2403
+ this.checkItem(t, e);
2404
+ }
2405
+ getCheckedRows() {
2406
+ return this.getCheckedItems();
2407
+ }
2408
+ // --- Public API: Sort ---
2409
+ sort(t, e = "asc") {
2410
+ var i, s;
2411
+ this.state.sort = e ? [{ field: t, direction: e }] : [], this.clearSelectionState(), this.applyDataTransformations(), this.render(), (s = (i = this.events).onSort) == null || s.call(i, this.state.sort);
2412
+ }
2413
+ clearSort() {
2414
+ var t, e;
2415
+ this.state.sort = [], this.applyDataTransformations(), this.render(), (e = (t = this.events).onSort) == null || e.call(t, []);
2416
+ }
2417
+ getSortState() {
2418
+ return [...this.state.sort];
2419
+ }
2420
+ // --- Public API: Filter ---
2421
+ filter(t) {
2422
+ var i, s;
2423
+ const e = Array.isArray(t) ? t : [t];
2424
+ this.state.filter = { conditions: e, logic: "and" }, this.clearSelectionState(), this.applyDataTransformations(), this.render(), (s = (i = this.events).onFilter) == null || s.call(i, this.state.filter);
2425
+ }
2426
+ clearFilter() {
2427
+ this.state.filter = null, this.clearSelectionState(), this.applyDataTransformations(), this.render();
2428
+ }
2429
+ getFilterState() {
2430
+ return this.state.filter ? { ...this.state.filter } : null;
2431
+ }
2432
+ // --- Public API: Edit ---
2433
+ startEdit(t, e) {
2434
+ var n, o, l;
2435
+ if (console.log("🎬 startEdit called", { rowIndex: t, field: e, editable: this.options.editable }), !this.options.editable) return;
2436
+ if (this.state.edit.editing && this.state.edit.rowIndex === t && this.state.edit.field === e) {
2437
+ console.log("⚠️ Already editing this cell - ignoring startEdit");
2438
+ return;
2439
+ }
2440
+ const i = this.state.columns.find((a) => a.field === e);
2441
+ if (console.log("📋 Column found", { column: i == null ? void 0 : i.field, editable: i == null ? void 0 : i.editable }), !i || i.editable === !1) return;
2442
+ this.state.edit.editing && this.endEdit(!0);
2443
+ const s = (n = this.state.displayData[t]) == null ? void 0 : n[e];
2444
+ this.state.edit = { editing: !0, rowIndex: t, field: e, originalValue: s }, console.log("✅ Edit state updated", this.state.edit), (l = (o = this.events).onCellEditStart) == null || l.call(o, t, e, s), this.renderEditCell(t, e, s);
2445
+ }
2446
+ renderEditCell(t, e, i) {
2447
+ console.log("🎨 renderEditCell START", { rowIndex: t, field: e, value: i, currentEditState: this.state.edit }), this.editModeCleanup && (console.log("🧹 Cleaning up previous edit mode listener"), this.editModeCleanup(), this.editModeCleanup = null);
2448
+ const s = this.bodyInner.querySelector(`[data-row-index="${t}"]`), n = s == null ? void 0 : s.querySelector(`[data-field="${e}"]`);
2449
+ if (console.log("🔍 Cell element found", { cell: !!n, hasClass: n == null ? void 0 : n.classList.contains("velox-cell--editing") }), !n) return;
2450
+ const o = this.state.columns.find((a) => a.field === e);
2451
+ if (!o) return;
2452
+ y(n, "velox-cell--editing"), console.log("✅ Added velox-cell--editing class", { classList: Array.from(n.classList) });
2453
+ const l = (a) => {
2454
+ const r = a.target;
2455
+ n.contains(r) ? console.log("📦 Inside cell click - maintaining edit mode") : (console.log("🌍 Outside click detected - ending edit"), this.editModeCleanup = null, document.removeEventListener("mousedown", l), this.endEdit(!0));
2456
+ };
2457
+ if (this.editModeCleanup = () => {
2458
+ document.removeEventListener("mousedown", l);
2459
+ }, setTimeout(() => {
2460
+ document.addEventListener("mousedown", l);
2461
+ }, 0), o.editor) {
2462
+ const a = et.createEditor(
2463
+ i,
2464
+ o.editor,
2465
+ (r) => {
2466
+ var p;
2467
+ if (console.log("💾 Editor save callback", { newValue: r, editing: this.state.edit.editing }), this.setCellValue(t, e, r), ((p = o.editor) == null ? void 0 : p.type) === "checkbox") {
2468
+ console.log("✅ Checkbox editor - maintaining edit mode, new value:", r), this.state.edit.originalValue = r;
2469
+ const c = { ...this.state.edit };
2470
+ c.editing && (this.state.edit = c, this.renderEditCell(c.rowIndex, c.field, r));
2471
+ } else
2472
+ console.log("🛑 Other editor - ending edit mode"), this.state.edit.editing = !1, this.applyDataTransformations(), this.render();
2473
+ const h = this.events;
2474
+ h.onCellEditEnd && h.onCellEditEnd({
2475
+ rowIndex: t,
2476
+ field: e,
2477
+ oldValue: i,
2478
+ newValue: r,
2479
+ row: this.state.displayData[t]
2480
+ });
2481
+ },
2482
+ () => {
2483
+ this.cancelEdit();
2484
+ },
2485
+ (r) => {
2486
+ this.endEditAndMove(r);
2487
+ }
2488
+ );
2489
+ n.innerHTML = "", n.appendChild(a), a.addEventListener("mousedown", () => {
2490
+ console.log("🖱️ Editor mousedown");
2491
+ }), setTimeout(() => {
2492
+ (a instanceof HTMLInputElement || a instanceof HTMLSelectElement) && (a.focus(), a instanceof HTMLInputElement && a.type === "text" && a.select());
2493
+ }, 0);
2494
+ } else {
2495
+ const a = f("input", "velox-edit-input");
2496
+ a.type = o.type === "number" ? "number" : "text", a.value = i != null ? String(i) : "", n.innerHTML = "", n.appendChild(a), a.focus(), a.select(), a.addEventListener("mousedown", () => {
2497
+ console.log("🖱️ Input mousedown");
2498
+ }), a.addEventListener("keydown", (r) => {
2499
+ console.log("⌨️ Input keydown:", r.key, { shiftKey: r.shiftKey }), r.key === "Enter" ? (r.preventDefault(), r.stopPropagation(), console.log("✅ Enter detected - calling endEditAndMove"), this.endEditAndMove(r.shiftKey ? "up" : "down")) : r.key === "Tab" ? (r.preventDefault(), r.stopPropagation(), console.log("✅ Tab detected - calling endEditAndMove"), this.endEditAndMove(r.shiftKey ? "left" : "right")) : r.key === "Escape" && (r.preventDefault(), r.stopPropagation(), console.log("✅ Escape detected - calling cancelEdit"), this.cancelEdit());
2500
+ });
2501
+ }
2502
+ }
2503
+ endEdit(t = !0) {
2504
+ var l, a, r, h, p, c;
2505
+ if (console.log("🛑 endEdit called", { save: t, editing: this.state.edit.editing }), this.editModeCleanup && (console.log("🧹 Cleaning up edit mode listener on endEdit"), this.editModeCleanup(), this.editModeCleanup = null), !this.state.edit.editing) return;
2506
+ const { rowIndex: e, field: i, originalValue: s } = this.state.edit;
2507
+ if (e === null || i === null) return;
2508
+ const n = this.bodyInner.querySelector(`[data-row-index="${e}"]`), o = n == null ? void 0 : n.querySelector(`[data-field="${i}"]`);
2509
+ if (t && o) {
2510
+ const u = this.state.columns.find((E) => E.field === i), g = this.state.displayData[e];
2511
+ let m = s;
2512
+ const C = o.querySelector(".velox-edit-input"), x = o.querySelector(".velox-editor--select"), v = o.querySelector('.velox-editor--checkbox input[type="checkbox"]'), w = o.querySelector(".velox-editor--date"), S = o.querySelector(".velox-editor--number");
2513
+ if (v ? m = v.checked : x ? x.multiple ? m = Array.from(x.selectedOptions).map((E) => E.value) : m = x.value : w ? m = w.value : S ? m = S.value === "" ? null : Number(S.value) : C && (m = C.value), JSON.stringify(m) !== JSON.stringify(s)) {
2514
+ if (u != null && u.validation && u.validation.length > 0) {
2515
+ const H = u.type === "number" && typeof m == "string" ? parseFloat(m) : m, R = tt.validate(H, u.validation, g);
2516
+ if (!R.valid) {
2517
+ if (o) {
2518
+ y(o, "velox-cell--invalid");
2519
+ const D = R.errors.map(($) => $.message).join(", ");
2520
+ o.title = D;
2521
+ }
2522
+ (a = (l = this.events).onValidationError) == null || a.call(l, {
2523
+ rowIndex: e,
2524
+ field: i,
2525
+ value: H,
2526
+ errors: R.errors.map((D) => D.message)
2527
+ }), C ? C.focus() : x ? x.focus() : w ? w.focus() : S && S.focus();
2528
+ return;
2529
+ }
2530
+ }
2531
+ const E = (u == null ? void 0 : u.type) === "number" && typeof m == "string" ? parseFloat(m) : m;
2532
+ this.setCellValue(e, i, E), (h = (r = this.events).onCellEditEnd) == null || h.call(r, {
2533
+ rowIndex: e,
2534
+ field: i,
2535
+ oldValue: s,
2536
+ newValue: E,
2537
+ row: this.state.displayData[e]
2538
+ });
2539
+ }
2540
+ } else
2541
+ (c = (p = this.events).onCellEditCancel) == null || c.call(p, e, i);
2542
+ this.state.edit = { editing: !1, rowIndex: null, field: null, originalValue: null }, this.applyDataTransformations(), this.render();
2543
+ }
2544
+ cancelEdit() {
2545
+ this.endEdit(!1);
2546
+ }
2547
+ isEditing() {
2548
+ return this.state.edit.editing;
2549
+ }
2550
+ /**
2551
+ * End edit and move to adjacent cell (Phase 9)
2552
+ */
2553
+ endEditAndMove(t) {
2554
+ var r;
2555
+ const { rowIndex: e, field: i } = this.state.edit;
2556
+ if (e === null || i === null) return;
2557
+ this.endEdit(!0);
2558
+ const s = this.getVisibleColumns(), n = s.findIndex((h) => h.field === i);
2559
+ let o = e, l = n;
2560
+ switch (t) {
2561
+ case "up":
2562
+ o > 0 && o--;
2563
+ break;
2564
+ case "down":
2565
+ o < this.state.displayData.length - 1 && o++;
2566
+ break;
2567
+ case "left":
2568
+ l > 0 ? l-- : o > 0 && (o--, l = s.length - 1);
2569
+ break;
2570
+ case "right":
2571
+ l < s.length - 1 ? l++ : o < this.state.displayData.length - 1 && (o++, l = 0);
2572
+ break;
2573
+ }
2574
+ const a = (r = s[l]) == null ? void 0 : r.field;
2575
+ a && (this.setFocusedCell(o, a), this.state.selection.selectedCells.clear(), this.state.selection.selectedCells.add(`${o}:${a}`), this.scrollToCell(o, a), this.render(), this.rootElement.focus());
2576
+ }
2577
+ // --- Public API: Column ---
2578
+ getColumn(t) {
2579
+ return this.state.columns.find((e) => e.field === t) || null;
2580
+ }
2581
+ setColumnWidth(t, e) {
2582
+ var s, n;
2583
+ const i = this.state.columns.find((o) => o.field === t);
2584
+ i && (i.width = e, this.invalidateColumnCache(), this.render(), (n = (s = this.events).onColumnResize) == null || n.call(s, t, e));
2585
+ }
2586
+ showColumn(t) {
2587
+ const e = this.state.columns.find((i) => i.field === t);
2588
+ e && (e.visible = !0, this.invalidateColumnCache(), this.render());
2589
+ }
2590
+ hideColumn(t) {
2591
+ const e = this.state.columns.find((i) => i.field === t);
2592
+ e && (e.visible = !1, this.invalidateColumnCache(), this.render());
2593
+ }
2594
+ setColumns(t) {
2595
+ this.state.columns = t.map((e) => ({ ...e })), this.invalidateColumnCache(), this.render();
2596
+ }
2597
+ autoFitColumn(t) {
2598
+ var n, o;
2599
+ const e = this.state.columns.find((l) => l.field === t);
2600
+ if (!e) return;
2601
+ let i = 100;
2602
+ const s = e.header || "";
2603
+ i = Math.max(i, this.measureTextWidth(s) + 40), this.state.displayData.forEach((l) => {
2604
+ const a = l[e.field], r = k(a, e.type), h = this.measureTextWidth(r) + 20;
2605
+ i = Math.max(i, h);
2606
+ }), e.width = Math.min(i, 500), this.invalidateColumnCache(), this.render(), (o = (n = this.events).onColumnResize) == null || o.call(n, t, e.width);
2607
+ }
2608
+ autoFitAllColumns() {
2609
+ this.getVisibleColumns().forEach((t) => this.autoFitColumn(t.field));
2610
+ }
2611
+ // GridContext: Text measurement (public for module access)
2612
+ measureTextWidth(t, e) {
2613
+ return this.measureCanvas || (this.measureCanvas = document.createElement("canvas"), this.measureContext = this.measureCanvas.getContext("2d")), this.measureContext ? (this.measureContext.font = e || "14px sans-serif", this.measureContext.measureText(t).width) : 100;
2614
+ }
2615
+ // --- Public API: Scroll ---
2616
+ scrollToRow(t) {
2617
+ const e = this.options.rowHeight || 40;
2618
+ this.bodyElement.scrollTop = t * e, this.fixedLeftBody && (this.fixedLeftBody.scrollTop = t * e);
2619
+ }
2620
+ scrollToTop() {
2621
+ this.bodyElement.scrollTop = 0, this.fixedLeftBody && (this.fixedLeftBody.scrollTop = 0);
2622
+ }
2623
+ scrollToBottom() {
2624
+ this.bodyElement.scrollTop = this.bodyElement.scrollHeight, this.fixedLeftBody && (this.fixedLeftBody.scrollTop = this.fixedLeftBody.scrollHeight);
2625
+ }
2626
+ scrollToCell(t, e) {
2627
+ const i = this.options.rowHeight || 40, s = this.bodyElement.clientHeight, n = this.bodyElement.scrollTop, o = t * i, l = o + i;
2628
+ o < n ? this.bodyElement.scrollTop = o : l > n + s && (this.bodyElement.scrollTop = l - s), this.fixedLeftBody && (this.fixedLeftBody.scrollTop = this.bodyElement.scrollTop);
2629
+ const a = this.bodyInner.querySelector(`[data-field="${e}"]`);
2630
+ if (a) {
2631
+ const r = a.offsetLeft, h = r + a.offsetWidth, p = this.bodyElement.clientWidth, c = this.bodyElement.scrollLeft;
2632
+ r < c ? this.bodyElement.scrollLeft = r : h > c + p && (this.bodyElement.scrollLeft = h - p);
2633
+ }
2634
+ }
2635
+ // --- Public API: Clipboard ---
2636
+ copy() {
2637
+ const t = this.getSelectionData();
2638
+ if (t.length === 0) return;
2639
+ const e = t.map((i) => i.join(" ")).join(`
2640
+ `);
2641
+ navigator.clipboard.writeText(e).then(() => {
2642
+ var i, s;
2643
+ (s = (i = this.events).onCopy) == null || s.call(i, t.map((n) => n.map((o) => String(o ?? ""))));
2644
+ });
2645
+ }
2646
+ paste() {
2647
+ const t = this.state.selection.focusedCell;
2648
+ t && navigator.clipboard.readText().then((e) => {
2649
+ var o, l, a, r;
2650
+ const i = e.split(`
2651
+ `).map((h) => h.split(" "));
2652
+ (l = (o = this.events).onPaste) == null || l.call(o, i, t);
2653
+ const s = this.getVisibleColumns(), n = s.findIndex((h) => h.field === t.field);
2654
+ i.forEach((h, p) => {
2655
+ const c = t.rowIndex + p;
2656
+ c >= this.state.displayData.length || h.forEach((u, g) => {
2657
+ const m = n + g;
2658
+ if (m >= s.length) return;
2659
+ const C = s[m].field, x = s[m];
2660
+ if (x.editable !== !1) {
2661
+ const v = this.state.displayData[c], w = this.state.data.indexOf(v);
2662
+ w >= 0 && (this.state.data[w][C] = x.type === "number" ? parseFloat(u) : u);
2663
+ }
2664
+ });
2665
+ }), this.summary.invalidateCache(), this.applyDataTransformations(), this.render(), (r = (a = this.events).onDataChange) == null || r.call(a, this.state.data);
2666
+ });
2667
+ }
2668
+ cut() {
2669
+ var s, n, o, l;
2670
+ const t = this.getSelectionData();
2671
+ if (t.length === 0) return;
2672
+ this.copy();
2673
+ const e = [];
2674
+ this.getSelectedCells().forEach((a) => {
2675
+ const r = this.state.columns.find((h) => h.field === a.field);
2676
+ if ((r == null ? void 0 : r.editable) !== !1) {
2677
+ const h = this.state.displayData[a.rowIndex], p = this.state.data.indexOf(h);
2678
+ if (p >= 0) {
2679
+ const c = this.state.data[p][a.field];
2680
+ e.push({
2681
+ rowIndex: a.rowIndex,
2682
+ field: a.field,
2683
+ oldValue: c,
2684
+ newValue: ""
2685
+ }), this.state.data[p][a.field] = "";
2686
+ }
2687
+ }
2688
+ }), e.length > 0 && this.pushUndo({ type: "cut", timestamp: Date.now(), data: { changes: e } }), this.summary.invalidateCache(), this.applyDataTransformations(), this.render(), (n = (s = this.events).onCut) == null || n.call(s, t.map((a) => a.map((r) => String(r ?? "")))), (l = (o = this.events).onDataChange) == null || l.call(o, this.state.data);
2689
+ }
2690
+ // --- Public API: Undo/Redo ---
2691
+ pushUndo(t) {
2692
+ this.history.push(t);
2693
+ }
2694
+ undo() {
2695
+ var e, i, s, n;
2696
+ const t = this.history.popUndo();
2697
+ if (!t) return !1;
2698
+ switch (t.type) {
2699
+ case "cell_edit": {
2700
+ const o = t.data, l = this.state.displayData[o.rowIndex], a = this.state.data.indexOf(l);
2701
+ a >= 0 && (this.state.data[a][o.field] = o.oldValue);
2702
+ break;
2703
+ }
2704
+ case "bulk_edit":
2705
+ case "paste":
2706
+ case "cut":
2707
+ case "delete": {
2708
+ t.data.changes.forEach((l) => {
2709
+ const a = this.state.displayData[l.rowIndex], r = this.state.data.indexOf(a);
2710
+ r >= 0 && (this.state.data[r][l.field] = l.oldValue);
2711
+ });
2712
+ break;
2713
+ }
2714
+ case "row_add": {
2715
+ const o = t.data;
2716
+ this.state.data.splice(o.index, 1), this.rebuildDataIndexMap();
2717
+ break;
2718
+ }
2719
+ case "row_remove": {
2720
+ const o = t.data;
2721
+ this.state.data.splice(o.index, 0, { ...o.row }), this.rebuildDataIndexMap();
2722
+ break;
2723
+ }
2724
+ }
2725
+ return this.summary.invalidateCache(), this.applyDataTransformations(), this.render(), (i = (e = this.events).onUndo) == null || i.call(e, t), (n = (s = this.events).onDataChange) == null || n.call(s, this.state.data), !0;
2726
+ }
2727
+ redo() {
2728
+ var e, i, s, n;
2729
+ const t = this.history.popRedo();
2730
+ if (!t) return !1;
2731
+ switch (t.type) {
2732
+ case "cell_edit": {
2733
+ const o = t.data, l = this.state.displayData[o.rowIndex], a = this.state.data.indexOf(l);
2734
+ a >= 0 && (this.state.data[a][o.field] = o.newValue);
2735
+ break;
2736
+ }
2737
+ case "bulk_edit":
2738
+ case "paste":
2739
+ case "cut":
2740
+ case "delete": {
2741
+ t.data.changes.forEach((l) => {
2742
+ const a = this.state.displayData[l.rowIndex], r = this.state.data.indexOf(a);
2743
+ r >= 0 && (this.state.data[r][l.field] = l.newValue);
2744
+ });
2745
+ break;
2746
+ }
2747
+ case "row_add": {
2748
+ const o = t.data;
2749
+ this.state.data.splice(o.index, 0, { ...o.row }), this.rebuildDataIndexMap();
2750
+ break;
2751
+ }
2752
+ case "row_remove": {
2753
+ const o = t.data;
2754
+ this.state.data.splice(o.index, 1), this.rebuildDataIndexMap();
2755
+ break;
2756
+ }
2757
+ }
2758
+ return this.summary.invalidateCache(), this.applyDataTransformations(), this.render(), (i = (e = this.events).onRedo) == null || i.call(e, t), (n = (s = this.events).onDataChange) == null || n.call(s, this.state.data), !0;
2759
+ }
2760
+ canUndo() {
2761
+ return this.history.canUndo();
2762
+ }
2763
+ canRedo() {
2764
+ return this.history.canRedo();
2765
+ }
2766
+ clearHistory() {
2767
+ this.history.clear();
2768
+ }
2769
+ // --- Public API: Delete ---
2770
+ deleteSelectedCells() {
2771
+ var i, s;
2772
+ if (!this.options.editable) return;
2773
+ const t = this.getSelectedCells();
2774
+ if (t.length === 0) return;
2775
+ const e = [];
2776
+ t.forEach((n) => {
2777
+ const o = this.state.columns.find((l) => l.field === n.field);
2778
+ if ((o == null ? void 0 : o.editable) !== !1) {
2779
+ const l = this.state.displayData[n.rowIndex], a = this.state.data.indexOf(l);
2780
+ if (a >= 0) {
2781
+ const r = this.state.data[a][n.field];
2782
+ r !== "" && r !== null && r !== void 0 && (e.push({
2783
+ rowIndex: n.rowIndex,
2784
+ field: n.field,
2785
+ oldValue: r,
2786
+ newValue: ""
2787
+ }), this.state.data[a][n.field] = "");
2788
+ }
2789
+ }
2790
+ }), e.length > 0 && (this.pushUndo({ type: "delete", timestamp: Date.now(), data: { changes: e } }), this.applyDataTransformations(), this.summary.invalidateCache(), this.render(), (s = (i = this.events).onDataChange) == null || s.call(i, this.state.data));
2791
+ }
2792
+ deleteSelectedRows() {
2793
+ const t = this.getSelectedRows();
2794
+ if (t.length === 0) return;
2795
+ [...t].sort((i, s) => s - i).forEach((i) => {
2796
+ this.removeRow(i);
2797
+ });
2798
+ }
2799
+ // --- Public API: Export/Import ---
2800
+ createExportContext(t = {}) {
2801
+ return {
2802
+ data: this.state.data,
2803
+ displayData: this.state.displayData,
2804
+ columns: this.state.columns,
2805
+ selectedRows: this.getSelectedRows(),
2806
+ options: t
2807
+ };
2808
+ }
2809
+ /**
2810
+ * Export grid data to Excel (.xlsx) file
2811
+ * Requires SheetJS library to be loaded via CDN:
2812
+ * <script src="https://cdn.sheetjs.com/xlsx-0.20.1/package/dist/xlsx.full.min.js"><\/script>
2813
+ */
2814
+ exportToExcel(t = {}) {
2815
+ const e = this.createExportContext(t);
2816
+ K(e);
2817
+ }
2818
+ /**
2819
+ * Export grid data to CSV format
2820
+ * @returns CSV string
2821
+ */
2822
+ exportToCSV(t = {}) {
2823
+ const e = this.createExportContext(t);
2824
+ return O(e);
2825
+ }
2826
+ /**
2827
+ * Export grid data to JSON format
2828
+ * @returns JSON string
2829
+ */
2830
+ exportToJSON(t = {}) {
2831
+ const e = this.createExportContext(t);
2832
+ return A(e);
2833
+ }
2834
+ /**
2835
+ * Download grid data as CSV file
2836
+ */
2837
+ downloadCSV(t = {}) {
2838
+ const e = this.createExportContext(t);
2839
+ Y(e);
2840
+ }
2841
+ /**
2842
+ * Download grid data as JSON file
2843
+ */
2844
+ downloadJSON(t = {}) {
2845
+ const e = this.createExportContext(t);
2846
+ J(e);
2847
+ }
2848
+ /**
2849
+ * Import data from CSV string
2850
+ * @param csvString CSV content
2851
+ * @param hasHeader Whether first row is header (default: true)
2852
+ */
2853
+ importFromCSV(t, e = !0) {
2854
+ const i = j(t, e);
2855
+ return i.errors.length === 0 && i.data.length > 0 && this.setData(i.data), i;
2856
+ }
2857
+ /**
2858
+ * Import data from Excel file
2859
+ * Requires SheetJS library to be loaded via CDN
2860
+ * @param file Excel file (File object)
2861
+ * @param sheetIndex Sheet index to import (default: 0)
2862
+ */
2863
+ async importFromExcel(t, e = 0) {
2864
+ const i = await G(t, e);
2865
+ return i.errors.length === 0 && i.data.length > 0 && this.setData(i.data), i;
2866
+ }
2867
+ /**
2868
+ * Check if SheetJS library is available for Excel operations
2869
+ */
2870
+ static isExcelSupported() {
2871
+ return B();
2872
+ }
2873
+ // ============================================
2874
+ // Public API - Utility Methods
2875
+ // ============================================
2876
+ getCellValue(t, e) {
2877
+ var i;
2878
+ return (i = this.state.displayData[t]) == null ? void 0 : i[e];
2879
+ }
2880
+ setCellValue(t, e, i) {
2881
+ var o, l;
2882
+ const s = this.state.displayData[t];
2883
+ if (!s) return;
2884
+ const n = this.state.data.indexOf(s);
2885
+ n >= 0 && (this.state.data[n][e] = i, (this.state.rowStates.get(this.state.data[n]) || "none") === "none" && this.state.rowStates.set(this.state.data[n], "updated"), this.summary.invalidateCache(), this.applyDataTransformations(), this.render(), (l = (o = this.events).onDataChange) == null || l.call(o, this.state.data));
2886
+ }
2887
+ // ============================================
2888
+ // Phase 13: Summary Methods
2889
+ // ============================================
2890
+ /**
2891
+ * Get summary value for a specific field
2892
+ * @param field Column field name
2893
+ * @returns Calculated summary value
2894
+ */
2895
+ getSummaryValue(t) {
2896
+ return this.summary.getSummaryValue(t);
2897
+ }
2898
+ /**
2899
+ * Get all summary values
2900
+ * @returns Object with field names as keys and summary values
2901
+ */
2902
+ getSummaryValues() {
2903
+ return this.summary.getAllSummaryValues();
2904
+ }
2905
+ /**
2906
+ * Refresh summary calculations (clear cache)
2907
+ */
2908
+ refreshSummary() {
2909
+ this.summary.invalidateCache(), this.render();
2910
+ }
2911
+ setOptions(t) {
2912
+ this.options = { ...this.options, ...t }, t.checkBar && (this.options.checkBar = { ...M, ...t.checkBar }), t.columns && (this.state.columns = t.columns.map((e) => ({ ...e }))), t.loading !== void 0 && this.updateLoadingState(), this.render();
2913
+ }
2914
+ getOptions() {
2915
+ return { ...this.options };
2916
+ }
2917
+ setLoading(t) {
2918
+ this.options.loading = t, this.updateLoadingState();
2919
+ }
2920
+ refresh() {
2921
+ this.applyDataTransformations(), this.render();
2922
+ }
2923
+ // ============================================
2924
+ // Phase 10: Column Reorder & Menu
2925
+ // ============================================
2926
+ /**
2927
+ * Fix/unfix column to a position
2928
+ */
2929
+ fixColumn(t, e) {
2930
+ const i = this.state.columns.find((s) => s.field === t);
2931
+ i && (i.fixed = e, this.invalidateColumnCache(), this.render());
2932
+ }
2933
+ /**
2934
+ * Reorder column to new position
2935
+ */
2936
+ reorderColumn(t, e) {
2937
+ var o, l;
2938
+ const i = this.state.columns.findIndex((a) => a.field === t), s = this.state.columns.findIndex((a) => a.field === e);
2939
+ if (i === -1 || s === -1) return;
2940
+ const [n] = this.state.columns.splice(i, 1);
2941
+ this.state.columns.splice(s, 0, n), this.invalidateColumnCache(), this.render(), (l = (o = this.events).onColumnReorder) == null || l.call(o, t, i, s);
2942
+ }
2943
+ // GridContext: Column menu methods - delegated to GridColumnMenu
2944
+ showColumnMenu(t, e) {
2945
+ this.closeFilterPopup(), this.columnMenuManager.showColumnMenu(t, e);
2946
+ }
2947
+ closeColumnMenu() {
2948
+ this.columnMenuManager.closeColumnMenu();
2949
+ }
2950
+ // GridContext: Column drag methods - delegated to GridDragManager
2951
+ startColumnDrag(t, e) {
2952
+ this.dragManager.startColumnDrag(t, e);
2953
+ }
2954
+ // ============================================
2955
+ // Phase 11: Row Drag & Drop
2956
+ // ============================================
2957
+ /**
2958
+ * Move row to new position
2959
+ */
2960
+ moveRow(t, e) {
2961
+ var r, h;
2962
+ const i = this.state.displayData[t];
2963
+ if (!i) return;
2964
+ const s = this.state.data.indexOf(i);
2965
+ if (s === -1) return;
2966
+ const n = this.state.displayData[e], o = n ? this.state.data.indexOf(n) : this.state.data.length, [l] = this.state.data.splice(s, 1), a = o > s ? o - 1 : o;
2967
+ this.state.data.splice(a, 0, l), this.rebuildDataIndexMap(), this.applyDataTransformations(), this.render(), (h = (r = this.events).onDataChange) == null || h.call(r, this.state.data);
2968
+ }
2969
+ // GridContext: Row drag methods - delegated to GridDragManager
2970
+ startRowDrag(t, e, i) {
2971
+ this.dragManager.startRowDrag(t, e, i);
2972
+ }
2973
+ // ============================================
2974
+ // GridContext Implementation
2975
+ // ============================================
2976
+ /** GridContext: 그리드 상태 반환 */
2977
+ getState() {
2978
+ return this.state;
2979
+ }
2980
+ /** GridContext: 이벤트 핸들러 반환 */
2981
+ getEvents() {
2982
+ return this.events;
2983
+ }
2984
+ /** GridContext: 그리드 고유 ID 반환 */
2985
+ getGridId() {
2986
+ return this.gridId;
2987
+ }
2988
+ /** GridContext: 표시 데이터 반환 */
2989
+ getDisplayData() {
2990
+ return this.state.displayData;
2991
+ }
2992
+ /** GridContext: 가상 스크롤 상태 반환 */
2993
+ getVirtualState() {
2994
+ return this.virtualState;
2995
+ }
2996
+ /** GridContext: 이벤트 발행 헬퍼 */
2997
+ emitEvent(t, ...e) {
2998
+ const i = this.events[t];
2999
+ i && i(...e);
3000
+ }
3001
+ // ============================================
3002
+ // GridContext: Internal Handlers
3003
+ // ============================================
3004
+ /** GridContext: Block selection 상태 확인 */
3005
+ isBlockSelecting() {
3006
+ return this.blockSelecting !== null;
3007
+ }
3008
+ /** GridContext: Tooltip 표시 */
3009
+ showTooltip(t, e, i, s) {
3010
+ this.tooltip && this.tooltip.show(t, e, i, s);
3011
+ }
3012
+ /** GridContext: Tooltip 숨기기 */
3013
+ hideTooltip() {
3014
+ this.tooltip && this.tooltip.hide();
3015
+ }
3016
+ // ============================================
3017
+ // Phase 14: Fixed Columns
3018
+ // ============================================
3019
+ /**
3020
+ * Set fixed columns options
3021
+ * @param options - Fixed options (colCount, rightCount)
3022
+ */
3023
+ setFixedOptions(t) {
3024
+ var n, o;
3025
+ const e = this.options.fixedOptions || { colCount: 0, rightCount: 0 }, i = {
3026
+ colCount: t.colCount ?? ((n = this.options.fixedOptions) == null ? void 0 : n.colCount) ?? 0,
3027
+ rightCount: t.rightCount ?? ((o = this.options.fixedOptions) == null ? void 0 : o.rightCount) ?? 0
3028
+ }, s = (
3029
+ // Left fixed changes
3030
+ (e.colCount || 0) === 0 && i.colCount > 0 || // Left 추가
3031
+ (e.colCount || 0) > 0 && i.colCount === 0 || // Left 제거
3032
+ // Right fixed changes
3033
+ (e.rightCount || 0) === 0 && i.rightCount > 0 || // Right 추가
3034
+ (e.rightCount || 0) > 0 && i.rightCount === 0
3035
+ );
3036
+ this.options.fixedOptions = i, this.invalidateColumnCache(), s && this.rebuildDOM(), this.render();
3037
+ }
3038
+ /**
3039
+ * Rebuild DOM structure and re-attach events
3040
+ * Phase 14: For dynamic fixed columns
3041
+ */
3042
+ rebuildDOM() {
3043
+ var i, s;
3044
+ const t = ((i = this.bodyElement) == null ? void 0 : i.scrollTop) || 0, e = ((s = this.bodyElement) == null ? void 0 : s.scrollLeft) || 0;
3045
+ this.detachEvents(), this.build(), this.attachEvents(), this.bodyElement && (this.bodyElement.scrollTop = t, this.bodyElement.scrollLeft = e), this.fixedRightBody && (this.fixedRightBody.scrollTop = t);
3046
+ }
3047
+ /**
3048
+ * Get current fixed columns options
3049
+ * @returns Fixed options
3050
+ */
3051
+ getFixedOptions() {
3052
+ return this.options.fixedOptions || { colCount: 0, rightCount: 0 };
3053
+ }
3054
+ // ============================================
3055
+ // Phase 15: Row State Management
3056
+ // ============================================
3057
+ /**
3058
+ * Get row state by display index
3059
+ * @param index - Display index (in displayData array)
3060
+ * @returns Row state type
3061
+ */
3062
+ getRowState(t) {
3063
+ const e = this.state.displayData[t];
3064
+ return e && this.state.rowStates.get(e) || "none";
3065
+ }
3066
+ /**
3067
+ * Get row state by row data object
3068
+ * @param row - Row data object
3069
+ * @returns Row state type
3070
+ */
3071
+ getRowStateByData(t) {
3072
+ return this.state.rowStates.get(t) || "none";
3073
+ }
3074
+ /**
3075
+ * Set row state manually
3076
+ * @param index - Display index
3077
+ * @param state - New state
3078
+ */
3079
+ setRowState(t, e) {
3080
+ const i = this.state.displayData[t];
3081
+ i && (this.state.rowStates.set(i, e), this.render());
3082
+ }
3083
+ /**
3084
+ * Get all changes (created, updated, deleted rows)
3085
+ * @returns Changes result with separated arrays
3086
+ */
3087
+ getChanges() {
3088
+ const t = [], e = [], i = [];
3089
+ return this.state.rowStates.forEach((s, n) => {
3090
+ s === "created" ? t.push(n) : s === "updated" ? e.push(n) : s === "deleted" && i.push(n);
3091
+ }), { created: t, updated: e, deleted: i };
3092
+ }
3093
+ /**
3094
+ * Get newly created rows
3095
+ * @returns Array of created rows
3096
+ */
3097
+ getCreatedRows() {
3098
+ const t = [];
3099
+ return this.state.rowStates.forEach((e, i) => {
3100
+ e === "created" && t.push(i);
3101
+ }), t;
3102
+ }
3103
+ /**
3104
+ * Get updated rows
3105
+ * @returns Array of updated rows
3106
+ */
3107
+ getUpdatedRows() {
3108
+ const t = [];
3109
+ return this.state.rowStates.forEach((e, i) => {
3110
+ e === "updated" && t.push(i);
3111
+ }), t;
3112
+ }
3113
+ /**
3114
+ * Get deleted rows (marked for deletion)
3115
+ * @returns Array of deleted rows
3116
+ */
3117
+ getDeletedRows() {
3118
+ const t = [];
3119
+ return this.state.rowStates.forEach((e, i) => {
3120
+ e === "deleted" && t.push(i);
3121
+ }), t;
3122
+ }
3123
+ /**
3124
+ * Clear all row states (reset to 'none')
3125
+ */
3126
+ clearRowStates() {
3127
+ this.state.rowStates.clear(), this.state.data.forEach((t) => {
3128
+ this.state.rowStates.set(t, "none");
3129
+ }), this.render();
3130
+ }
3131
+ /**
3132
+ * Commit changes (mark all rows as 'none')
3133
+ * This is typically called after successfully saving changes to server
3134
+ */
3135
+ commit() {
3136
+ const t = [];
3137
+ this.state.rowStates.forEach((e, i) => {
3138
+ e === "createAndDeleted" && t.push(i);
3139
+ }), t.forEach((e) => {
3140
+ const i = this.state.data.indexOf(e);
3141
+ i >= 0 && this.state.data.splice(i, 1);
3142
+ }), this.state.rowStates.clear(), this.state.data.forEach((e) => {
3143
+ this.state.rowStates.set(e, "none");
3144
+ }), this.rebuildDataIndexMap(), this.applyDataTransformations(), this.render();
3145
+ }
3146
+ // ============================================
3147
+ // Phase 18: Data Source & Pagination
3148
+ // ============================================
3149
+ /**
3150
+ * Remote 데이터 소스 여부 확인
3151
+ */
3152
+ isRemoteDataSource() {
3153
+ var t;
3154
+ return ((t = this.options.dataSource) == null ? void 0 : t.type) === "remote";
3155
+ }
3156
+ /**
3157
+ * 페이지네이션 상태 조회
3158
+ */
3159
+ getPaginationState() {
3160
+ return { ...this.state.pagination };
3161
+ }
3162
+ /**
3163
+ * 특정 페이지로 이동
3164
+ * @param page - 이동할 페이지 번호 (1-based)
3165
+ */
3166
+ goToPage(t) {
3167
+ var s, n;
3168
+ const { totalPages: e } = this.state.pagination, i = Math.max(1, Math.min(t, e || 1));
3169
+ i === this.state.pagination.currentPage && this.state.displayData.length > 0 || (this.state.pagination.currentPage = i, this.isRemoteDataSource() ? this.fetchData() : (this.applyLocalPagination(), this.render()), (n = (s = this.events).onPageChange) == null || n.call(s, i, this.state.pagination.pageSize));
3170
+ }
3171
+ /**
3172
+ * 페이지 크기 변경
3173
+ * @param pageSize - 새 페이지 크기
3174
+ */
3175
+ setPageSize(t) {
3176
+ var e, i;
3177
+ t < 1 || t === this.state.pagination.pageSize || (this.state.pagination.pageSize = t, this.state.pagination.currentPage = 1, this.updateTotalPages(), this.isRemoteDataSource() ? this.fetchData() : (this.applyLocalPagination(), this.render()), (i = (e = this.events).onPageSizeChange) == null || i.call(e, t));
3178
+ }
3179
+ /**
3180
+ * 서버에서 데이터 가져오기 (remote 모드)
3181
+ */
3182
+ async fetchData() {
3183
+ const t = this.options.dataSource;
3184
+ if (!(t != null && t.fetch)) return;
3185
+ const e = {
3186
+ page: this.state.pagination.currentPage,
3187
+ pageSize: this.state.pagination.pageSize,
3188
+ sort: this.state.sort.length > 0 ? this.state.sort : void 0,
3189
+ filter: this.state.filter
3190
+ };
3191
+ this.state.pagination.loading = !0, this.setLoading(!0);
3192
+ try {
3193
+ const i = await t.fetch(e);
3194
+ this.state.data = i.data.map((s) => ({ ...s })), this.state.displayData = this.state.data, this.state.pagination.totalCount = i.totalCount, this.updateTotalPages(), this.state.rowStates.clear(), this.state.data.forEach((s) => {
3195
+ this.state.rowStates.set(s, "none");
3196
+ }), this.rebuildDataIndexMap(), this.summary.invalidateCache(), this.clearSelectionState();
3197
+ } catch (i) {
3198
+ console.error("VeloxGrid: fetchData error", i);
3199
+ } finally {
3200
+ this.state.pagination.loading = !1, this.setLoading(!1), this.render();
3201
+ }
3202
+ }
3203
+ /**
3204
+ * 전체 페이지 수 재계산
3205
+ */
3206
+ updateTotalPages() {
3207
+ const { pageSize: t, totalCount: e } = this.state.pagination;
3208
+ this.state.pagination.totalPages = Math.max(1, Math.ceil(e / t));
3209
+ }
3210
+ /**
3211
+ * 로컬 데이터에 페이지네이션 적용
3212
+ * sort/filter 적용 후 현재 페이지 데이터만 displayData에 설정
3213
+ */
3214
+ applyLocalPagination() {
3215
+ let t = [...this.state.data];
3216
+ if (this.state.filter && (t = T(t, this.state.filter)), this.state.sort.length > 0) {
3217
+ const n = {};
3218
+ this.state.columns.forEach((o) => {
3219
+ n[o.field] = o.type || "text";
3220
+ }), t = L(t, this.state.sort, n);
3221
+ }
3222
+ this.state.pagination.totalCount = t.length, this.updateTotalPages();
3223
+ const { currentPage: e, pageSize: i } = this.state.pagination, s = (e - 1) * i;
3224
+ this.state.displayData = t.slice(s, s + i), this.initCheckableRows();
3225
+ }
3226
+ /**
3227
+ * Infinite Scroll: 스크롤 바닥 감지 (Phase 18)
3228
+ */
3229
+ checkInfiniteScroll() {
3230
+ const t = this.options.pagination;
3231
+ if (!(t != null && t.enabled) || t.mode !== "infinite" || this.infiniteScrollLoading || this.infiniteScrollAllLoaded) return;
3232
+ const e = t.infiniteScrollThreshold || 100, { scrollTop: i, scrollHeight: s, clientHeight: n } = this.bodyElement;
3233
+ s - i - n <= e && this.loadNextPage();
3234
+ }
3235
+ /**
3236
+ * Infinite Scroll: 다음 페이지 로드 (Phase 18)
3237
+ */
3238
+ async loadNextPage() {
3239
+ const { currentPage: t, totalPages: e } = this.state.pagination;
3240
+ if (t >= e) {
3241
+ this.infiniteScrollAllLoaded = !0, this.renderInfiniteScrollStatus();
3242
+ return;
3243
+ }
3244
+ if (this.infiniteScrollLoading = !0, this.state.pagination.currentPage++, this.renderInfiniteScrollStatus(), this.isRemoteDataSource()) {
3245
+ const i = this.options.dataSource;
3246
+ if (!(i != null && i.fetch)) return;
3247
+ const s = {
3248
+ page: this.state.pagination.currentPage,
3249
+ pageSize: this.state.pagination.pageSize,
3250
+ sort: this.state.sort.length > 0 ? this.state.sort : void 0,
3251
+ filter: this.state.filter
3252
+ };
3253
+ try {
3254
+ const n = await i.fetch(s), o = n.data.map((l) => ({ ...l }));
3255
+ this.state.data.push(...o), this.state.displayData = [...this.state.data], this.state.pagination.totalCount = n.totalCount, this.updateTotalPages(), o.forEach((l) => this.state.rowStates.set(l, "none")), this.rebuildDataIndexMap(), this.summary.invalidateCache(), this.state.pagination.currentPage >= this.state.pagination.totalPages && (this.infiniteScrollAllLoaded = !0);
3256
+ } catch (n) {
3257
+ console.error("VeloxGrid: infinite scroll fetch error", n), this.state.pagination.currentPage--;
3258
+ }
3259
+ } else
3260
+ this.applyLocalInfiniteScroll();
3261
+ this.infiniteScrollLoading = !1, this.render();
3262
+ }
3263
+ /**
3264
+ * Local Infinite Scroll: 다음 페이지 데이터 추가 (Phase 18)
3265
+ */
3266
+ applyLocalInfiniteScroll() {
3267
+ let t = [...this.state.data];
3268
+ if (this.state.filter && (t = T(t, this.state.filter)), this.state.sort.length > 0) {
3269
+ const n = {};
3270
+ this.state.columns.forEach((o) => {
3271
+ n[o.field] = o.type || "text";
3272
+ }), t = L(t, this.state.sort, n);
3273
+ }
3274
+ const { currentPage: e, pageSize: i } = this.state.pagination, s = e * i;
3275
+ this.state.displayData = t.slice(0, s), s >= t.length && (this.infiniteScrollAllLoaded = !0), this.initCheckableRows();
3276
+ }
3277
+ /**
3278
+ * Infinite Scroll 상태 표시 렌더링 (Phase 18)
3279
+ */
3280
+ renderInfiniteScrollStatus() {
3281
+ if (this.paginationContainer) {
3282
+ if (this.paginationContainer.innerHTML = "", this.infiniteScrollLoading) {
3283
+ const t = f("div", "velox-infinite-status");
3284
+ t.textContent = "Loading...", this.paginationContainer.appendChild(t);
3285
+ } else if (this.infiniteScrollAllLoaded) {
3286
+ const t = f("div", "velox-infinite-status velox-infinite-status--done"), { totalCount: e } = this.state.pagination;
3287
+ t.textContent = `All ${e.toLocaleString()} items loaded`, this.paginationContainer.appendChild(t);
3288
+ }
3289
+ }
3290
+ }
3291
+ /**
3292
+ * Pagination UI 렌더링 (Phase 18)
3293
+ */
3294
+ renderPagination() {
3295
+ var p;
3296
+ if (!this.paginationContainer) return;
3297
+ if (((p = this.options.pagination) == null ? void 0 : p.mode) === "infinite") {
3298
+ this.renderInfiniteScrollStatus();
3299
+ return;
3300
+ }
3301
+ const { currentPage: t, totalPages: e, totalCount: i, pageSize: s } = this.state.pagination, n = this.options.pagination, o = (n == null ? void 0 : n.maxPageButtons) || 5;
3302
+ if (this.paginationContainer.innerHTML = "", (n == null ? void 0 : n.showInfo) !== !1) {
3303
+ const c = f("div", "velox-pagination-info"), u = (t - 1) * s + 1, g = Math.min(t * s, i);
3304
+ c.textContent = i > 0 ? `${u}-${g} / ${i.toLocaleString()}` : "0 items", this.paginationContainer.appendChild(c);
3305
+ }
3306
+ const l = f("div", "velox-pagination-nav");
3307
+ l.appendChild(this.createPageButton("«", 1, t === 1)), l.appendChild(this.createPageButton("‹", t - 1, t === 1));
3308
+ const a = Math.floor(o / 2);
3309
+ let r = Math.max(1, t - a);
3310
+ const h = Math.min(e, r + o - 1);
3311
+ if (h - r + 1 < o && (r = Math.max(1, h - o + 1)), r > 1 && (l.appendChild(this.createPageButton("1", 1, !1)), r > 2)) {
3312
+ const c = f("span", "velox-pagination-ellipsis");
3313
+ c.textContent = "…", l.appendChild(c);
3314
+ }
3315
+ for (let c = r; c <= h; c++) {
3316
+ const u = this.createPageButton(String(c), c, !1);
3317
+ c === t && y(u, "velox-pagination-btn--active"), l.appendChild(u);
3318
+ }
3319
+ if (h < e) {
3320
+ if (h < e - 1) {
3321
+ const c = f("span", "velox-pagination-ellipsis");
3322
+ c.textContent = "…", l.appendChild(c);
3323
+ }
3324
+ l.appendChild(this.createPageButton(String(e), e, !1));
3325
+ }
3326
+ if (l.appendChild(this.createPageButton("›", t + 1, t === e)), l.appendChild(this.createPageButton("»", e, t === e)), this.paginationContainer.appendChild(l), n != null && n.showSizeChanger) {
3327
+ const c = f("div", "velox-pagination-sizer"), u = document.createElement("select");
3328
+ u.className = "velox-pagination-select", (n.pageSizeOptions || [10, 20, 50, 100]).forEach((m) => {
3329
+ const C = document.createElement("option");
3330
+ C.value = String(m), C.textContent = `${m} / page`, m === s && (C.selected = !0), u.appendChild(C);
3331
+ }), u.addEventListener("change", () => {
3332
+ this.setPageSize(Number(u.value));
3333
+ }), c.appendChild(u), this.paginationContainer.appendChild(c);
3334
+ }
3335
+ }
3336
+ /**
3337
+ * 페이지 버튼 생성 헬퍼 (Phase 18)
3338
+ */
3339
+ createPageButton(t, e, i) {
3340
+ const s = f("button", "velox-pagination-btn");
3341
+ return s.textContent = t, i ? (s.setAttribute("disabled", "true"), y(s, "velox-pagination-btn--disabled")) : s.addEventListener("click", () => this.goToPage(e)), s;
3342
+ }
3343
+ destroy() {
3344
+ var t, e;
3345
+ this.dragManager.destroy(), this.detachEvents(), document.removeEventListener("mouseup", this.boundHandleBlockSelectionEnd), this.closeFilterPopup(), this.closeColumnMenu(), this.rootElement.removeEventListener("keydown", this.boundHandleKeyDown), this.measureCanvas = null, this.measureContext = null, this.tooltip && (this.tooltip.destroy(), this.tooltip = null), this.container.innerHTML = "", (e = (t = this.events).onDestroy) == null || e.call(t);
3346
+ }
3347
+ }
3348
+ /**
3349
+ * VeloxGrid
3350
+ * A fast, lightweight, and framework-agnostic data grid library
3351
+ *
3352
+ * @author bumki
3353
+ * @license MIT
3354
+ * @version 0.9.1
3355
+ */
3356
+ const yt = "0.11.0";
3357
+ export {
3358
+ yt as VERSION,
3359
+ xt as VeloxGrid,
3360
+ y as addClass,
3361
+ z as compareValues,
3362
+ f as createElement,
3363
+ ft as debounce,
3364
+ P as deepClone,
3365
+ Y as downloadCSV,
3366
+ N as downloadFile,
3367
+ J as downloadJSON,
3368
+ gt as escapeHtml,
3369
+ O as exportToCSV,
3370
+ K as exportToExcel,
3371
+ A as exportToJSON,
3372
+ T as filterData,
3373
+ k as formatValue,
3374
+ _ as generateId,
3375
+ ht as hasClass,
3376
+ G as importFromExcel,
3377
+ mt as importFromExcelBySheetName,
3378
+ B as isSheetJSAvailable,
3379
+ j as parseCSV,
3380
+ pt as parseValue,
3381
+ b as removeClass,
3382
+ ut as setStyles,
3383
+ L as sortData,
3384
+ V as throttle,
3385
+ ct as toggleClass
3386
+ };
3387
+ //# sourceMappingURL=velox-grid.esm.js.map