@extable/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,4445 @@
1
+ function zt(c) {
2
+ const t = c.payload;
3
+ if (!t) return null;
4
+ const e = t.batchId;
5
+ return typeof e == "string" && e.length ? e : null;
6
+ }
7
+ class Gt {
8
+ constructor(t = 100) {
9
+ this.applied = [], this.undone = [], this.cap = t;
10
+ }
11
+ enqueue(t) {
12
+ const e = zt(t), i = this.applied.at(-1);
13
+ for (e && i && i.batchId === e ? i.commands.push(t) : this.applied.push({ batchId: e, commands: [t] }); this.applied.length > this.cap; ) this.applied.shift();
14
+ this.undone = [];
15
+ }
16
+ canUndo() {
17
+ return this.applied.length > 0;
18
+ }
19
+ canRedo() {
20
+ return this.undone.length > 0;
21
+ }
22
+ listApplied() {
23
+ const t = [];
24
+ for (const e of this.applied) t.push(...e.commands);
25
+ return t;
26
+ }
27
+ listUndoGroups() {
28
+ return this.applied.slice().reverse().map((t) => ({ batchId: t.batchId, commands: t.commands.slice() }));
29
+ }
30
+ listRedoGroups() {
31
+ return this.undone.slice().reverse().map((t) => ({ batchId: t.batchId, commands: t.commands.slice() }));
32
+ }
33
+ undo() {
34
+ const t = this.applied.pop();
35
+ return t && this.undone.push(t), t?.commands ?? null;
36
+ }
37
+ redo() {
38
+ const t = this.undone.pop();
39
+ return t && this.applied.push(t), t?.commands ?? null;
40
+ }
41
+ clear() {
42
+ this.applied = [], this.undone = [];
43
+ }
44
+ }
45
+ function vt() {
46
+ return typeof crypto < "u" && "randomUUID" in crypto ? crypto.randomUUID() : `row_${Math.random().toString(16).slice(2)}`;
47
+ }
48
+ function Ct(c) {
49
+ if (c !== void 0)
50
+ return Array.isArray(c) ? c : [c];
51
+ }
52
+ function _(c) {
53
+ if (c)
54
+ try {
55
+ c.remove();
56
+ } catch {
57
+ }
58
+ }
59
+ const j = (c, t = 2) => c.toString().padStart(t, "0"), Et = {
60
+ yyyy: (c) => j(c.getFullYear(), 4),
61
+ MM: (c) => j(c.getMonth() + 1),
62
+ dd: (c) => j(c.getDate()),
63
+ HH: (c) => j(c.getHours()),
64
+ hh: (c) => {
65
+ const t = c.getHours() % 12 || 12;
66
+ return j(t);
67
+ },
68
+ mm: (c) => j(c.getMinutes()),
69
+ ss: (c) => j(c.getSeconds()),
70
+ a: (c) => c.getHours() >= 12 ? "PM" : "AM"
71
+ }, Tt = Object.keys(Et).sort((c, t) => t.length - c.length), Dt = /* @__PURE__ */ new Set(["yyyy", "MM", "dd"]), Ht = /* @__PURE__ */ new Set(["HH", "hh", "mm", "ss", "a"]), Qt = /* @__PURE__ */ new Set([...Dt, ...Ht]), tt = {
72
+ iso: "yyyy-MM-dd",
73
+ us: "MM/dd/yyyy",
74
+ eu: "dd.MM.yyyy"
75
+ }, et = {
76
+ iso: "HH:mm:ss",
77
+ "24h": "HH:mm",
78
+ "12h": "hh:mm a"
79
+ }, it = {
80
+ iso: "yyyy-MM-dd'T'HH:mm:ss'Z'",
81
+ "iso-24h": "yyyy-MM-dd'T'HH:mm:ss'Z'",
82
+ "iso-12h": "yyyy-MM-dd hh:mm a",
83
+ us: "MM/dd/yyyy HH:mm",
84
+ "us-24h": "MM/dd/yyyy HH:mm",
85
+ "us-12h": "MM/dd/yyyy hh:mm a",
86
+ eu: "dd.MM.yyyy HH:mm",
87
+ "eu-24h": "dd.MM.yyyy HH:mm",
88
+ "eu-12h": "dd.MM.yyyy hh:mm a"
89
+ };
90
+ function dt(c) {
91
+ const t = new Date(c);
92
+ return Number.isNaN(t.getTime()) ? null : t;
93
+ }
94
+ function At(c, t) {
95
+ let e = "", i = 0, n = !1;
96
+ for (; i < t.length; ) {
97
+ const s = t[i];
98
+ if (s === "'") {
99
+ n = !n, i += 1;
100
+ continue;
101
+ }
102
+ if (n) {
103
+ e += s, i += 1;
104
+ continue;
105
+ }
106
+ let o = !1;
107
+ for (const r of Tt)
108
+ if (t.startsWith(r, i)) {
109
+ e += Et[r](c), i += r.length, o = !0;
110
+ break;
111
+ }
112
+ o || (e += s, i += 1);
113
+ }
114
+ return e;
115
+ }
116
+ function Zt(c, t) {
117
+ return t === "date" ? Dt.has(c) : t === "time" ? Ht.has(c) : Qt.has(c);
118
+ }
119
+ function Jt(c, t) {
120
+ let e = 0, i = !1;
121
+ for (; e < c.length; ) {
122
+ if (c[e] === "'") {
123
+ i = !i, e += 1;
124
+ continue;
125
+ }
126
+ if (i) {
127
+ e += 1;
128
+ continue;
129
+ }
130
+ let s = !1;
131
+ for (const o of Tt)
132
+ if (c.startsWith(o, e)) {
133
+ if (!Zt(o, t)) return !1;
134
+ e += o.length, s = !0;
135
+ break;
136
+ }
137
+ s || (e += 1);
138
+ }
139
+ return !0;
140
+ }
141
+ function Y(c, t) {
142
+ if (!c)
143
+ return t === "date" ? tt.iso : t === "time" ? et.iso : it.iso;
144
+ if (t === "date" && tt[c]) return tt[c];
145
+ if (t === "time" && et[c]) return et[c];
146
+ if (t === "datetime" && it[c]) return it[c];
147
+ const e = t === "date" ? tt.iso : t === "time" ? et.iso : it.iso;
148
+ return Jt(c, t) ? c : e;
149
+ }
150
+ const St = (c) => !Number.isNaN(c.getTime()), te = (c) => {
151
+ if (typeof c == "string") return c;
152
+ if (c && typeof c == "object") {
153
+ const t = c;
154
+ if (t.kind === "enum")
155
+ return typeof t.value == "string" ? t.value : null;
156
+ }
157
+ return null;
158
+ }, ee = (c) => {
159
+ if (Array.isArray(c) && c.every((t) => typeof t == "string")) return c;
160
+ if (c && typeof c == "object") {
161
+ const t = c;
162
+ if (t.kind === "tags") {
163
+ const e = t.values;
164
+ return Array.isArray(e) && e.every((i) => typeof i == "string") ? e : null;
165
+ }
166
+ }
167
+ return null;
168
+ };
169
+ function ie(c, t) {
170
+ if (c == null) return null;
171
+ switch (t.type) {
172
+ case "string": {
173
+ if (typeof c != "string") return "Expected a string";
174
+ if (t.string?.maxLength !== void 0 && c.length > t.string.maxLength)
175
+ return `Too long (max ${t.string.maxLength})`;
176
+ if (t.string?.regex)
177
+ try {
178
+ if (!new RegExp(t.string.regex).test(c)) return "Does not match pattern";
179
+ } catch {
180
+ }
181
+ return null;
182
+ }
183
+ case "number":
184
+ return typeof c != "number" || !Number.isFinite(c) ? "Expected a number" : t.number?.signed === !1 && c < 0 ? "Expected a non-negative number" : null;
185
+ case "boolean":
186
+ return typeof c != "boolean" ? "Expected a boolean" : null;
187
+ case "enum": {
188
+ const e = te(c);
189
+ if (!e) return "Expected an enum value";
190
+ const i = t.enum?.allowCustom ?? !1, n = t.enum?.options ?? [];
191
+ return !i && n.length && !n.includes(e) ? "Not in allowed options" : null;
192
+ }
193
+ case "tags": {
194
+ const e = ee(c);
195
+ if (!e) return "Expected a list of tags";
196
+ const i = t.tags?.allowCustom ?? !1, n = t.tags?.options ?? [];
197
+ if (!i && n.length) {
198
+ for (const s of e)
199
+ if (!n.includes(s)) return "Contains an unknown tag";
200
+ }
201
+ return null;
202
+ }
203
+ case "date":
204
+ case "time":
205
+ case "datetime": {
206
+ if (t.type === "date" ? Y(t.dateFormat, "date") : t.type === "time" ? Y(t.timeFormat, "time") : Y(t.dateTimeFormat, "datetime"), c instanceof Date) return St(c) ? null : "Invalid date";
207
+ if (typeof c == "string") {
208
+ const e = dt(c);
209
+ return e && St(e) ? null : "Invalid date/time";
210
+ }
211
+ return "Expected a date/time string";
212
+ }
213
+ default:
214
+ return null;
215
+ }
216
+ }
217
+ class ne {
218
+ constructor(t, e, i) {
219
+ this.rows = [], this.baseIndexById = /* @__PURE__ */ new Map(), this.dataVersion = 0, this.visibleRowsCache = null, this.distinctValueCache = /* @__PURE__ */ new Map(), this.pending = /* @__PURE__ */ new Map(), this.rowVersion = /* @__PURE__ */ new Map(), this.listeners = /* @__PURE__ */ new Set(), this.cellStyles = /* @__PURE__ */ new Map(), this.cellConditionalStyles = /* @__PURE__ */ new Map(), this.computedCache = /* @__PURE__ */ new Map(), this.conditionalCache = /* @__PURE__ */ new Map(), this.rowConditionalCache = /* @__PURE__ */ new Map(), this.formulaDiagnostics = /* @__PURE__ */ new Map(), this.conditionalDiagnostics = /* @__PURE__ */ new Map(), this.baseValidationErrors = /* @__PURE__ */ new Map(), this.uniqueValidationErrors = /* @__PURE__ */ new Map(), this.notifySuspended = !1, this.notifyDirty = !1, this.schema = e, this.view = i, this.setData(t ?? []);
220
+ }
221
+ subscribe(t) {
222
+ return this.listeners.add(t), () => this.listeners.delete(t);
223
+ }
224
+ notify() {
225
+ if (this.visibleRowsCache = null, this.distinctValueCache.clear(), this.notifySuspended) {
226
+ this.notifyDirty = !0;
227
+ return;
228
+ }
229
+ for (const t of this.listeners) t();
230
+ }
231
+ batchUpdate(t) {
232
+ this.notifySuspended = !0;
233
+ try {
234
+ t();
235
+ } finally {
236
+ this.notifySuspended = !1, this.notifyDirty && (this.notifyDirty = !1, this.notify());
237
+ }
238
+ }
239
+ setData(t) {
240
+ this.dataVersion += 1, this.pending.clear(), this.cellStyles.clear(), this.cellConditionalStyles.clear(), this.computedCache.clear(), this.conditionalCache.clear(), this.rowConditionalCache.clear(), this.formulaDiagnostics.clear(), this.conditionalDiagnostics.clear(), this.baseValidationErrors.clear(), this.uniqueValidationErrors.clear(), this.rows = (t ?? []).map((e, i) => {
241
+ const n = vt();
242
+ return this.rowVersion.set(n, 0), {
243
+ id: n,
244
+ raw: e,
245
+ displayIndex: i + 1
246
+ };
247
+ }), this.rebuildBaseIndex(), this.recomputeValidationErrors(), this.notify();
248
+ }
249
+ setSchema(t) {
250
+ this.dataVersion += 1, this.schema = t, this.computedCache.clear(), this.conditionalCache.clear(), this.rowConditionalCache.clear(), this.formulaDiagnostics.clear(), this.conditionalDiagnostics.clear(), this.recomputeValidationErrors(), this.notify();
251
+ }
252
+ setView(t) {
253
+ this.dataVersion += 1, Array.isArray(t.sorts) && t.sorts.length > 1 ? this.view = { ...t, sorts: t.sorts.slice(0, 1) } : this.view = t, this.notify();
254
+ }
255
+ getSchema() {
256
+ return { ...this.schema, columns: this.getColumns() };
257
+ }
258
+ getColumns() {
259
+ return this.schema.columns;
260
+ }
261
+ getRowConditionalStyleFn() {
262
+ return this.schema.row?.conditionalStyle ?? null;
263
+ }
264
+ getView() {
265
+ return this.view;
266
+ }
267
+ getDataVersion() {
268
+ return this.dataVersion;
269
+ }
270
+ getFullSchema() {
271
+ return this.schema;
272
+ }
273
+ listRows() {
274
+ return this.computeVisibleRows().rows;
275
+ }
276
+ listAllRows() {
277
+ return this.rows;
278
+ }
279
+ getAllRowCount() {
280
+ return this.rows.length;
281
+ }
282
+ findRow(t) {
283
+ const e = this.baseIndexById.get(t);
284
+ if (e === void 0) return null;
285
+ const i = this.rows[e];
286
+ if (i && i.id === t) return { row: i, index: e };
287
+ const n = this.rows.findIndex((o) => o.id === t);
288
+ if (n < 0) return null;
289
+ const s = this.rows[n];
290
+ return s ? (this.rebuildBaseIndex(), { row: s, index: n }) : null;
291
+ }
292
+ getRowHeight(t) {
293
+ return this.view.rowHeights?.[t];
294
+ }
295
+ setRowHeight(t, e) {
296
+ this.view.rowHeights || (this.view.rowHeights = {}), this.view.rowHeights[t] !== e && (this.view.rowHeights[t] = e, this.notify());
297
+ }
298
+ setRowHeightsBulk(t) {
299
+ const e = Object.entries(t);
300
+ if (e.length === 0) return;
301
+ this.view.rowHeights || (this.view.rowHeights = {});
302
+ let i = !1;
303
+ for (const [n, s] of e)
304
+ this.view.rowHeights[n] !== s && (this.view.rowHeights[n] = s, i = !0);
305
+ i && this.notify();
306
+ }
307
+ getCell(t, e) {
308
+ const i = this.findRow(t);
309
+ if (!i) return;
310
+ const n = i.row, s = this.pending.get(t);
311
+ return s && e in s ? s[e] : n.raw[e];
312
+ }
313
+ getRawCell(t, e) {
314
+ const i = this.findRow(t);
315
+ return i ? i.row.raw[e] : void 0;
316
+ }
317
+ isRowReadonly(t) {
318
+ const e = this.findRow(t);
319
+ return e ? !!e.row.raw._readonly : !1;
320
+ }
321
+ isColumnReadonly(t) {
322
+ const e = this.schema.columns.find((i) => i.key === t);
323
+ return !!(e?.readonly || e?.formula);
324
+ }
325
+ isReadonly(t, e) {
326
+ return this.isRowReadonly(t) || this.isColumnReadonly(e);
327
+ }
328
+ setCell(t, e, i, n) {
329
+ this.dataVersion += 1;
330
+ const s = this.findRow(t);
331
+ if (!s) return;
332
+ const o = s.row, r = () => {
333
+ const l = this.rowVersion.get(t) ?? 0;
334
+ this.rowVersion.set(t, l + 1);
335
+ };
336
+ if (n)
337
+ o.raw[e] = i, this.pending.delete(t), r();
338
+ else {
339
+ const l = this.getRawCell(t, e), h = this.pending.get(t) ?? {};
340
+ this.isEqual(l, i) ? delete h[e] : h[e] = i, Object.keys(h).length > 0 ? this.pending.set(t, h) : this.pending.delete(t), r();
341
+ }
342
+ this.updateValidationForCell(t, e, this.getCell(t, e)), this.schema.columns.find((l) => l.key === e)?.unique && this.recomputeUniqueValidationForColumn(e), this.clearDiagnosticsForCell(t, e), this.notify();
343
+ }
344
+ applyPending(t) {
345
+ this.dataVersion += 1;
346
+ const e = this.pending.get(t);
347
+ if (!e) return;
348
+ const i = this.findRow(t);
349
+ if (!i) return;
350
+ const n = i.row, s = /* @__PURE__ */ new Set();
351
+ for (const [o, r] of Object.entries(e)) {
352
+ n.raw[o] = r, this.updateValidationForCell(t, o, r);
353
+ const a = this.schema.columns.find((l) => String(l.key) === String(o));
354
+ a?.unique && s.add(a.key), this.clearDiagnosticsForCell(t, o);
355
+ }
356
+ this.pending.delete(t);
357
+ for (const o of s) this.recomputeUniqueValidationForColumn(o);
358
+ this.notify();
359
+ }
360
+ clearPending(t) {
361
+ this.dataVersion += 1, this.pending.delete(t), this.notify();
362
+ }
363
+ getPending() {
364
+ return this.pending;
365
+ }
366
+ hasPending(t, e) {
367
+ const i = this.pending.get(t);
368
+ return i ? e in i : !1;
369
+ }
370
+ insertRow(t) {
371
+ return this.insertRowAt(t, this.rows.length);
372
+ }
373
+ insertRowAt(t, e, i) {
374
+ this.dataVersion += 1;
375
+ const n = i ?? vt(), s = Math.max(0, Math.min(e, this.rows.length));
376
+ return this.rows.splice(s, 0, { id: n, raw: t, displayIndex: 0 }), this.reindexRows(), this.rowVersion.set(n, 0), this.rebuildBaseIndex(), this.recomputeValidationErrors(), this.notify(), n;
377
+ }
378
+ removeRow(t) {
379
+ this.dataVersion += 1;
380
+ const e = this.findRow(t);
381
+ if (!e) return null;
382
+ const i = this.rows.splice(e.index, 1)[0];
383
+ return i ? (this.pending.delete(t), this.rowVersion.delete(t), this.reindexRows(), this.rebuildBaseIndex(), this.recomputeValidationErrors(), this.notify(), { row: i, index: e.index }) : null;
384
+ }
385
+ getDisplayIndex(t) {
386
+ return this.findRow(t)?.row.displayIndex ?? null;
387
+ }
388
+ getRowIndex(t) {
389
+ return this.computeVisibleRows().indexById.get(t) ?? -1;
390
+ }
391
+ getBaseRowIndex(t) {
392
+ return this.baseIndexById.get(t) ?? -1;
393
+ }
394
+ getColumnIndex(t) {
395
+ return this.getColumns().findIndex((e) => e.key === t);
396
+ }
397
+ getColumnByIndex(t) {
398
+ return this.getColumns()[t] ?? null;
399
+ }
400
+ getRowByIndex(t) {
401
+ return this.listRows()[t] ?? null;
402
+ }
403
+ rebuildBaseIndex() {
404
+ this.baseIndexById.clear();
405
+ for (let t = 0; t < this.rows.length; t += 1) {
406
+ const e = this.rows[t];
407
+ e && this.baseIndexById.set(e.id, t);
408
+ }
409
+ }
410
+ getFilterSortKey(t) {
411
+ const e = t?.excludeColumnKey, i = t?.includeSort ?? !0, n = this.view, s = (n.filters ?? []).filter((a) => {
412
+ if (e === void 0) return !0;
413
+ const l = a?.key;
414
+ return String(l) !== String(e);
415
+ }).map((a) => {
416
+ if (a?.kind === "values") {
417
+ const u = a;
418
+ return {
419
+ kind: "values",
420
+ key: String(u.key),
421
+ includeBlanks: !!u.includeBlanks,
422
+ values: (u.values ?? []).map((f) => this.stableValueKey(f))
423
+ };
424
+ }
425
+ const h = a;
426
+ return {
427
+ kind: "op",
428
+ key: String(h.key),
429
+ op: String(h.op ?? ""),
430
+ value: this.stableValueKey(h.value)
431
+ };
432
+ }).sort((a, l) => a.key === l.key ? a.kind < l.kind ? -1 : 1 : a.key < l.key ? -1 : 1), o = Object.entries(n.columnDiagnostics ?? {}).filter(([a]) => e !== void 0 ? String(a) !== String(e) : !0).map(([a, l]) => ({
433
+ key: String(a),
434
+ errors: !!l?.errors,
435
+ warnings: !!l?.warnings
436
+ })).filter((a) => a.errors || a.warnings).sort((a, l) => a.key < l.key ? -1 : a.key > l.key ? 1 : 0), r = i ? (n.sorts ?? []).slice(0, 1).map((a) => ({ key: String(a.key), dir: a.dir })) : [];
437
+ return JSON.stringify({ filters: s, columnDiagnosticsEntries: o, sorts: r });
438
+ }
439
+ stableValueKey(t) {
440
+ if (t === null) return "null";
441
+ if (t === void 0) return "undefined";
442
+ if (t instanceof Date) return `date:${t.getTime()}`;
443
+ if (typeof t == "string") return `s:${t}`;
444
+ if (typeof t == "number") return `n:${Number.isNaN(t) ? "NaN" : String(t)}`;
445
+ if (typeof t == "boolean") return `b:${t ? "1" : "0"}`;
446
+ if (typeof t == "object") {
447
+ const e = t, i = e.kind;
448
+ if (i === "enum" && typeof e.value == "string") return `enum:${e.value}`;
449
+ if (i === "tags" && Array.isArray(e.values))
450
+ return `tags:${e.values.filter((n) => typeof n == "string").join("|")}`;
451
+ }
452
+ try {
453
+ return `json:${JSON.stringify(t)}`;
454
+ } catch {
455
+ return `str:${String(t)}`;
456
+ }
457
+ }
458
+ isBlankValue(t) {
459
+ return t == null || t === "";
460
+ }
461
+ resolveFilterColumnKey(t) {
462
+ return this.getColumns().find((n) => String(n.key) === String(t))?.key ?? t;
463
+ }
464
+ getFilterCellValue(t, e) {
465
+ const i = this.getColumns().find((n) => String(n.key) === String(e));
466
+ return i ? this.resolveCellValue(t, i).value : this.getCell(t, e);
467
+ }
468
+ valuesFilterMatches(t, e) {
469
+ const i = this.resolveFilterColumnKey(e.key), n = this.getFilterCellValue(t, i);
470
+ if (this.isBlankValue(n)) return !!e.includeBlanks;
471
+ if (!e.values || e.values.length === 0) return !1;
472
+ const s = this.stableValueKey(n);
473
+ for (const o of e.values)
474
+ if (this.stableValueKey(o) === s) return !0;
475
+ return !1;
476
+ }
477
+ rowPassesColumnDiagnostics(t, e, i) {
478
+ const n = !!i.errors, s = !!i.warnings;
479
+ if (!n && !s) return !0;
480
+ const o = this.resolveFilterColumnKey(e), r = this.getColumns().find((l) => String(l.key) === String(o));
481
+ r && (this.resolveCellValue(t, r), this.resolveConditionalStyle(t, r));
482
+ const a = this.getCellMarker(t, o);
483
+ return a ? a.level === "error" ? n : s : !1;
484
+ }
485
+ computeRowsAfterFilter(t) {
486
+ const e = t?.excludeColumnKey, i = t?.includeSort ?? !0, n = this.view, s = this.getSchema(), o = (n.filters ?? []).filter((d) => {
487
+ if (e === void 0) return !0;
488
+ const m = d?.key;
489
+ return String(m) !== String(e);
490
+ }), r = Object.entries(n.columnDiagnostics ?? {}).filter(
491
+ ([d]) => e !== void 0 ? String(d) !== String(e) : !0
492
+ ), a = [];
493
+ for (const d of this.rows) {
494
+ let m = !0;
495
+ for (const g of o)
496
+ if (g?.kind === "values" && !this.valuesFilterMatches(d.id, g)) {
497
+ m = !1;
498
+ break;
499
+ }
500
+ if (m) {
501
+ for (const [g, y] of r)
502
+ if (!this.rowPassesColumnDiagnostics(d.id, g, y)) {
503
+ m = !1;
504
+ break;
505
+ }
506
+ m && a.push(d);
507
+ }
508
+ }
509
+ if (!i) return a;
510
+ const l = (n.sorts ?? []).slice(0, 1)[0];
511
+ if (!l) return a;
512
+ const h = s.columns.find((d) => String(d.key) === String(l.key));
513
+ if (!h) return a;
514
+ const u = l.dir === "desc" ? -1 : 1, f = a.map((d) => {
515
+ const m = this.getFilterCellValue(d.id, h.key), g = this.isBlankValue(m), y = this.baseIndexById.get(d.id) ?? 0;
516
+ return { row: d, v: m, blank: g, baseIndex: y };
517
+ }), p = (d, m) => {
518
+ if (d === m) return 0;
519
+ if (d instanceof Date && m instanceof Date) return d.getTime() - m.getTime();
520
+ if (typeof d == "number" && typeof m == "number") return d - m;
521
+ if (typeof d == "boolean" && typeof m == "boolean") return d === m ? 0 : d ? 1 : -1;
522
+ const g = d instanceof Date ? d.toISOString() : typeof d == "string" ? d : String(d), y = m instanceof Date ? m.toISOString() : typeof m == "string" ? m : String(m);
523
+ return g === y ? 0 : g < y ? -1 : 1;
524
+ };
525
+ return f.sort((d, m) => {
526
+ if (d.blank && m.blank) return d.baseIndex - m.baseIndex;
527
+ if (d.blank) return 1;
528
+ if (m.blank) return -1;
529
+ const g = p(d.v, m.v);
530
+ return g !== 0 ? g * u : d.baseIndex - m.baseIndex;
531
+ }), f.map((d) => d.row);
532
+ }
533
+ computeVisibleRows() {
534
+ const t = this.getFilterSortKey(), e = this.visibleRowsCache;
535
+ if (e && e.version === this.dataVersion && e.key === t) return e;
536
+ const n = this.computeRowsAfterFilter({ includeSort: !0 }), s = /* @__PURE__ */ new Map();
537
+ for (let r = 0; r < n.length; r += 1) {
538
+ const a = n[r];
539
+ a && s.set(a.id, r);
540
+ }
541
+ const o = { version: this.dataVersion, key: t, rows: n, indexById: s };
542
+ return this.visibleRowsCache = o, o;
543
+ }
544
+ getDistinctValuesForColumn(t) {
545
+ const e = `${String(t)}|${this.getFilterSortKey({ excludeColumnKey: t, includeSort: !1 })}`, i = this.distinctValueCache.get(e);
546
+ if (i && i.version === this.dataVersion) return i;
547
+ const n = this.computeRowsAfterFilter({ excludeColumnKey: t, includeSort: !1 }), s = this.getSchema().columns.find((h) => String(h.key) === String(t));
548
+ if (!s) {
549
+ const h = { version: this.dataVersion, values: [], hasBlanks: !1, total: 0 };
550
+ return this.distinctValueCache.set(e, h), h;
551
+ }
552
+ const o = /* @__PURE__ */ new Map();
553
+ let r = !1;
554
+ for (const h of n) {
555
+ const u = this.getFilterCellValue(h.id, s.key);
556
+ if (this.isBlankValue(u)) {
557
+ r = !0;
558
+ continue;
559
+ }
560
+ const f = this.stableValueKey(u);
561
+ if (o.has(f)) continue;
562
+ const p = (() => {
563
+ if (u instanceof Date) return u.toISOString();
564
+ if (typeof u == "string") return u;
565
+ if (typeof u == "number" || typeof u == "boolean") return String(u);
566
+ if (typeof u == "object" && u) {
567
+ const d = u, m = d.kind;
568
+ if (m === "enum" && typeof d.value == "string") return d.value;
569
+ if (m === "tags" && Array.isArray(d.values))
570
+ return d.values.filter((g) => typeof g == "string").join(", ");
571
+ }
572
+ return String(u);
573
+ })();
574
+ o.set(f, { value: u, label: p });
575
+ }
576
+ const a = [...o.values()].sort(
577
+ (h, u) => h.label < u.label ? -1 : h.label > u.label ? 1 : 0
578
+ ), l = {
579
+ version: this.dataVersion,
580
+ values: a,
581
+ hasBlanks: r,
582
+ total: a.length + (r ? 1 : 0)
583
+ };
584
+ return this.distinctValueCache.set(e, l), l;
585
+ }
586
+ cellStyleKey(t, e) {
587
+ return `${t}::${String(e)}`;
588
+ }
589
+ clearDiagnosticsForCell(t, e) {
590
+ const i = this.cellStyleKey(t, e);
591
+ this.formulaDiagnostics.delete(i), this.conditionalDiagnostics.delete(i);
592
+ }
593
+ getCellDiagnostic(t, e) {
594
+ const i = this.cellStyleKey(t, e), n = this.formulaDiagnostics.get(i) ?? null, s = this.conditionalDiagnostics.get(i) ?? null;
595
+ return n ? !s || n.level === "error" ? n : s.level === "error" ? s : n : s;
596
+ }
597
+ getDiagnostics() {
598
+ const t = /* @__PURE__ */ new Set();
599
+ for (const e of this.formulaDiagnostics.keys()) t.add(e);
600
+ for (const e of this.conditionalDiagnostics.keys()) t.add(e);
601
+ return [...t].map((e) => {
602
+ const i = e.indexOf("::"), n = i >= 0 ? e.slice(0, i) : e, s = i >= 0 ? e.slice(i + 2) : "";
603
+ return { rowId: n, colKey: s, diag: this.getCellDiagnostic(n, s) };
604
+ });
605
+ }
606
+ getRowObjectEffective(t) {
607
+ const e = this.findRow(t);
608
+ if (!e) return null;
609
+ const i = e.row, n = this.pending.get(t);
610
+ if (!n || Object.keys(n).length === 0)
611
+ return i.raw;
612
+ const s = { ...i.raw };
613
+ for (const [o, r] of Object.entries(n))
614
+ s[o] = r;
615
+ return s;
616
+ }
617
+ resolveCellValue(t, e) {
618
+ if (!e.formula)
619
+ return { value: this.getCell(t, e.key), diagnostic: null };
620
+ const i = this.getRowVersion(t), n = this.cellStyleKey(t, e.key), s = this.computedCache.get(n);
621
+ if (s && s.version === i && s.formulaRef === e.formula)
622
+ return s.diagnostic && this.formulaDiagnostics.set(n, s.diagnostic), {
623
+ value: s.value,
624
+ textOverride: s.textOverride,
625
+ diagnostic: s.diagnostic
626
+ };
627
+ const o = this.getRowObjectEffective(t);
628
+ if (!o) {
629
+ const r = this.getCell(t, e.key), a = {
630
+ version: i,
631
+ formulaRef: e.formula,
632
+ value: r,
633
+ diagnostic: null
634
+ };
635
+ return this.computedCache.set(n, a), { value: r, diagnostic: null };
636
+ }
637
+ try {
638
+ const r = e.formula(o);
639
+ if (Array.isArray(r) && r.length >= 2 && r[1] instanceof Error) {
640
+ const l = {
641
+ level: "warning",
642
+ message: r[1].message,
643
+ source: "formula"
644
+ }, h = r[0];
645
+ return this.computedCache.set(n, { version: i, formulaRef: e.formula, value: h, diagnostic: l }), this.formulaDiagnostics.set(n, l), { value: h, diagnostic: l };
646
+ }
647
+ const a = r;
648
+ return this.computedCache.set(n, { version: i, formulaRef: e.formula, value: a, diagnostic: null }), this.formulaDiagnostics.delete(n), { value: a, diagnostic: null };
649
+ } catch (r) {
650
+ const l = { level: "error", message: r instanceof Error ? r.message : String(r), source: "formula" };
651
+ return this.computedCache.set(n, {
652
+ version: i,
653
+ formulaRef: e.formula,
654
+ value: null,
655
+ textOverride: "#ERROR",
656
+ diagnostic: l
657
+ }), this.formulaDiagnostics.set(n, l), { value: null, textOverride: "#ERROR", diagnostic: l };
658
+ }
659
+ }
660
+ setCellConditionalStyle(t, e, i) {
661
+ const n = this.cellStyleKey(t, e);
662
+ i ? this.cellConditionalStyles.set(n, i) : this.cellConditionalStyles.delete(n), this.conditionalCache.delete(n), this.clearDiagnosticsForCell(t, e), this.notify();
663
+ }
664
+ evalConditionalStyleFn(t, e) {
665
+ try {
666
+ const i = t(e);
667
+ return i instanceof Error ? {
668
+ delta: null,
669
+ diagnostic: { level: "warning", message: i.message, source: "conditionalStyle" },
670
+ forceErrorText: !1
671
+ } : { delta: i ?? null, diagnostic: null, forceErrorText: !1 };
672
+ } catch (i) {
673
+ return {
674
+ delta: null,
675
+ diagnostic: { level: "error", message: i instanceof Error ? i.message : String(i), source: "conditionalStyle" },
676
+ forceErrorText: !0
677
+ };
678
+ }
679
+ }
680
+ resolveRowConditionalStyle(t) {
681
+ const e = this.getRowConditionalStyleFn();
682
+ if (!e) return { delta: null, diagnostic: null, forceErrorText: !1 };
683
+ const i = this.getRowVersion(t), n = this.rowConditionalCache.get(t);
684
+ if (n && n.version === i && n.fnRef === e)
685
+ return {
686
+ delta: n.delta,
687
+ diagnostic: n.diagnostic,
688
+ forceErrorText: n.forceErrorText
689
+ };
690
+ const s = this.getRowObjectEffective(t);
691
+ if (!s) {
692
+ const r = { version: i, fnRef: e, delta: null, diagnostic: null, forceErrorText: !1 };
693
+ return this.rowConditionalCache.set(t, r), { delta: null, diagnostic: null, forceErrorText: !1 };
694
+ }
695
+ const o = this.evalConditionalStyleFn(e, s);
696
+ return this.rowConditionalCache.set(t, { version: i, fnRef: e, ...o }), o;
697
+ }
698
+ resolveConditionalStyle(t, e) {
699
+ const i = this.getRowVersion(t), n = this.cellStyleKey(t, e.key), s = this.getRowObjectEffective(t), o = this.resolveRowConditionalStyle(t), r = (f, p) => p ? f ? { ...f, ...p } : { ...p } : f;
700
+ let a = null, l = null, h = !1;
701
+ if (a = r(a, o.delta), o.diagnostic && (l = o.diagnostic), h = h || o.forceErrorText, e.conditionalStyle && s) {
702
+ const f = `${t}::${String(e.key)}::col`, p = this.conditionalCache.get(f);
703
+ if (p && p.version === i && p.fnRef === e.conditionalStyle)
704
+ a = r(a, p.delta), p.diagnostic && (l === null || l.level !== "error") && (l = p.diagnostic), h = h || p.forceErrorText;
705
+ else {
706
+ const d = this.evalConditionalStyleFn(e.conditionalStyle, s);
707
+ this.conditionalCache.set(f, { version: i, fnRef: e.conditionalStyle, ...d }), a = r(a, d.delta), d.diagnostic && (l === null || l.level !== "error") && (l = d.diagnostic), h = h || d.forceErrorText;
708
+ }
709
+ }
710
+ const u = this.cellConditionalStyles.get(n);
711
+ if (u && s) {
712
+ const f = this.conditionalCache.get(n);
713
+ if (f && f.version === i && f.fnRef === u)
714
+ a = r(a, f.delta), f.diagnostic && (l === null || l.level !== "error") && (l = f.diagnostic), h = h || f.forceErrorText;
715
+ else {
716
+ const p = this.evalConditionalStyleFn(u, s);
717
+ this.conditionalCache.set(n, { version: i, fnRef: u, ...p }), a = r(a, p.delta), p.diagnostic && (l === null || l.level !== "error") && (l = p.diagnostic), h = h || p.forceErrorText;
718
+ }
719
+ }
720
+ return l ? this.conditionalDiagnostics.set(n, l) : this.conditionalDiagnostics.delete(n), { delta: a, diagnostic: l, forceErrorText: h };
721
+ }
722
+ getValidationErrors() {
723
+ const t = /* @__PURE__ */ new Map();
724
+ for (const [i, n] of this.baseValidationErrors.entries()) t.set(i, { ...n });
725
+ for (const [i, n] of this.uniqueValidationErrors.entries()) {
726
+ const s = t.get(i);
727
+ s ? t.set(i, { ...s, message: `${s.message}
728
+ ${n.message}` }) : t.set(i, { ...n });
729
+ }
730
+ const e = [...t.values()];
731
+ return e.sort((i, n) => {
732
+ const s = this.getRowIndex(i.rowId), o = this.getRowIndex(n.rowId);
733
+ if (s !== o) return s - o;
734
+ const r = this.getColumnIndex(i.colKey), a = this.getColumnIndex(n.colKey);
735
+ return r - a;
736
+ }), e;
737
+ }
738
+ getCellValidationMessage(t, e) {
739
+ const i = this.cellStyleKey(t, e), n = this.baseValidationErrors.get(i)?.message ?? null, s = this.uniqueValidationErrors.get(i)?.message ?? null;
740
+ return n ? s ? `${n}
741
+ ${s}` : n : s;
742
+ }
743
+ getCellMarker(t, e) {
744
+ const i = this.getCellDiagnostic(t, e), n = this.getCellValidationMessage(t, e);
745
+ if (!i && !n) return null;
746
+ const s = n ? "error" : i?.level ?? "warning", o = [i?.message ?? null, n].filter(Boolean).join(`
747
+ `);
748
+ return { level: s, message: o };
749
+ }
750
+ updateValidationForCell(t, e, i) {
751
+ const n = this.schema.columns.find((r) => String(r.key) === String(e));
752
+ if (!n) return;
753
+ const s = ie(i, n), o = this.cellStyleKey(t, e);
754
+ s ? this.baseValidationErrors.set(o, { rowId: t, colKey: e, message: s }) : this.baseValidationErrors.delete(o);
755
+ }
756
+ recomputeValidationErrors() {
757
+ this.baseValidationErrors.clear(), this.uniqueValidationErrors.clear();
758
+ for (const t of this.rows)
759
+ for (const e of this.schema.columns) {
760
+ const i = this.getCell(t.id, e.key);
761
+ this.updateValidationForCell(t.id, e.key, i);
762
+ }
763
+ for (const t of this.getColumns())
764
+ t.unique && this.recomputeUniqueValidationForColumn(t.key);
765
+ }
766
+ normalizeUniqueValue(t) {
767
+ if (t == null) return null;
768
+ if (typeof t == "string") return t === "" ? null : t;
769
+ if (typeof t == "number" || typeof t == "boolean") return String(t);
770
+ if (t instanceof Date) return String(t.getTime());
771
+ if (typeof t == "object") {
772
+ const e = t, i = e.kind;
773
+ if (i === "enum" && typeof e.value == "string")
774
+ return e.value === "" ? null : e.value;
775
+ if (i === "tags" && Array.isArray(e.values)) {
776
+ const n = e.values.filter((s) => typeof s == "string").join(",");
777
+ return n === "" ? null : n;
778
+ }
779
+ }
780
+ return String(t);
781
+ }
782
+ recomputeUniqueValidationForColumn(t) {
783
+ const e = String(t);
784
+ for (const n of Array.from(this.uniqueValidationErrors.keys())) {
785
+ const s = n.indexOf("::");
786
+ (s >= 0 ? n.slice(s + 2) : "") === e && this.uniqueValidationErrors.delete(n);
787
+ }
788
+ const i = /* @__PURE__ */ new Map();
789
+ for (const n of this.rows) {
790
+ const s = this.getCell(n.id, t), o = this.normalizeUniqueValue(s);
791
+ if (!o) continue;
792
+ const r = i.get(o) ?? [];
793
+ r.push(n.id), i.set(o, r);
794
+ }
795
+ for (const n of i.values()) {
796
+ if (n.length < 2) continue;
797
+ const s = n.map((r) => this.getDisplayIndex(r)).filter((r) => typeof r == "number" && Number.isFinite(r)).sort((r, a) => r - a), o = s.length ? s.join(", ") : n.join(", ");
798
+ for (const r of n) {
799
+ const a = this.cellStyleKey(r, t);
800
+ this.uniqueValidationErrors.set(a, {
801
+ rowId: r,
802
+ colKey: t,
803
+ message: `Duplicate value
804
+ Rows: ${o}`
805
+ });
806
+ }
807
+ }
808
+ }
809
+ getCellStyle(t, e) {
810
+ return this.cellStyles.get(this.cellStyleKey(t, e)) ?? null;
811
+ }
812
+ setCellStyle(t, e, i) {
813
+ const n = this.cellStyleKey(t, e);
814
+ !i || Object.keys(i).length === 0 ? this.cellStyles.delete(n) : this.cellStyles.set(n, i), this.notify();
815
+ }
816
+ updateColumnStyle(t, e) {
817
+ const i = this.schema.columns.find((s) => String(s.key) === String(t));
818
+ if (!i) return;
819
+ const n = typeof e == "function" ? e(i.style) : e;
820
+ i.style = n, this.notify();
821
+ }
822
+ isEqual(t, e) {
823
+ return t instanceof Date && e instanceof Date ? t.getTime() === e.getTime() : Object.is(t, e);
824
+ }
825
+ getRowVersion(t) {
826
+ return this.rowVersion.get(t) ?? 0;
827
+ }
828
+ reindexRows() {
829
+ let t = 1;
830
+ for (const e of this.rows)
831
+ e.displayIndex = t, t += 1;
832
+ }
833
+ }
834
+ function se(c, t) {
835
+ const e = c.getSchema(), i = c.listRows(), n = (o, r) => {
836
+ if (!Number.isInteger(o) || !Number.isInteger(r) || o < 0 || o >= i.length || r < 0 || r >= e.columns.length) return null;
837
+ const a = i[o], l = e.columns[r];
838
+ return { rowId: a.id, colKey: l.key, rowIndex: o, colIndex: r };
839
+ }, s = (o, r) => {
840
+ const a = c.getRowIndex(o);
841
+ if (a < 0) return null;
842
+ const l = e.columns.findIndex((h) => h.key === r);
843
+ return l < 0 ? null : { rowId: o, colKey: r, rowIndex: a, colIndex: l };
844
+ };
845
+ if ("rowId" in t && "colKey" in t)
846
+ return s(t.rowId, t.colKey);
847
+ if ("rowIndex" in t && "colIndex" in t)
848
+ return n(t.rowIndex, t.colIndex);
849
+ if ("rowId" in t && "colIndex" in t) {
850
+ const o = c.getRowIndex(t.rowId);
851
+ return o < 0 ? null : n(o, t.colIndex);
852
+ }
853
+ if ("rowIndex" in t && "colKey" in t) {
854
+ const o = e.columns.findIndex((r) => r.key === t.colKey);
855
+ return o < 0 ? null : n(t.rowIndex, o);
856
+ }
857
+ return null;
858
+ }
859
+ function oe() {
860
+ return { caseInsensitive: !1, regex: !1 };
861
+ }
862
+ function at(c, t) {
863
+ if (c == null) return "";
864
+ if (c instanceof Date) return c.toISOString();
865
+ if (typeof c == "string") return c;
866
+ if (typeof c == "number" || typeof c == "boolean") return String(c);
867
+ if (typeof c == "object") {
868
+ const e = c, i = e.kind;
869
+ if (i === "enum" && typeof e.value == "string") return e.value;
870
+ if (i === "tags" && Array.isArray(e.values))
871
+ return e.values.filter((n) => typeof n == "string").join(", ");
872
+ }
873
+ return t.type === "boolean" ? String(c).toLowerCase() === "true" ? "TRUE" : "FALSE" : String(c);
874
+ }
875
+ function re(c) {
876
+ return c.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
877
+ }
878
+ function xt(c, t) {
879
+ return t <= 0 ? -1 : Math.max(0, Math.min(t - 1, c));
880
+ }
881
+ class le {
882
+ constructor(t, e, i, n) {
883
+ this.dataModel = t, this.navigateToCell = e, this.applyEdit = i, this.canEdit = n, this.state = {
884
+ query: "",
885
+ replace: "",
886
+ mode: "find",
887
+ options: oe(),
888
+ matches: [],
889
+ activeIndex: -1,
890
+ error: null
891
+ }, this.listeners = /* @__PURE__ */ new Set(), this.debounceId = null, this.unsubscribeData = null, this.unsubscribeData = this.dataModel.subscribe(() => {
892
+ this.state.query && this.scheduleRecompute();
893
+ });
894
+ }
895
+ destroy() {
896
+ this.debounceId !== null && window.clearTimeout(this.debounceId), this.debounceId = null, this.unsubscribeData?.(), this.unsubscribeData = null, this.listeners.clear();
897
+ }
898
+ getState() {
899
+ return this.state;
900
+ }
901
+ subscribe(t) {
902
+ return this.listeners.add(t), t(this.state), () => this.listeners.delete(t);
903
+ }
904
+ setMode(t) {
905
+ this.setState({ ...this.state, mode: t });
906
+ }
907
+ setQuery(t) {
908
+ this.setState({ ...this.state, query: t }), this.scheduleRecompute();
909
+ }
910
+ setReplace(t) {
911
+ this.setState({ ...this.state, replace: t });
912
+ }
913
+ setOptions(t) {
914
+ this.setState({ ...this.state, options: { ...this.state.options, ...t } }), this.scheduleRecompute();
915
+ }
916
+ next() {
917
+ if (!this.state.matches.length) return;
918
+ const t = (this.state.activeIndex + 1) % this.state.matches.length;
919
+ this.activateIndex(t);
920
+ }
921
+ prev() {
922
+ if (!this.state.matches.length) return;
923
+ const t = (this.state.activeIndex - 1 + this.state.matches.length) % this.state.matches.length;
924
+ this.activateIndex(t);
925
+ }
926
+ activateIndex(t) {
927
+ const e = xt(t, this.state.matches.length);
928
+ if (e < 0) return;
929
+ const i = this.state.matches[e];
930
+ i && (this.setState({ ...this.state, activeIndex: e }), this.navigateToCell(i.rowId, i.colKey));
931
+ }
932
+ replaceCurrent() {
933
+ const t = this.state.activeIndex;
934
+ if (t < 0) return;
935
+ const e = this.state.matches[t];
936
+ if (!e || !this.canEdit(e.rowId, e.colKey)) return;
937
+ const n = this.dataModel.getSchema().columns[e.colIndex];
938
+ if (!n) return;
939
+ const s = this.dataModel.getCell(e.rowId, e.colKey), o = at(s, n), r = this.applyReplacement(o, e);
940
+ this.applyEdit(e.rowId, e.colKey, r), this.recompute();
941
+ }
942
+ replaceAll() {
943
+ if (!this.state.matches.length) return;
944
+ const t = this.dataModel.getSchema(), e = /* @__PURE__ */ new Map();
945
+ for (const i of this.state.matches)
946
+ this.canEdit(i.rowId, i.colKey) && e.set(`${i.rowId}|${i.colKey}`, [...e.get(`${i.rowId}|${i.colKey}`) ?? [], i]);
947
+ for (const [i, n] of e.entries()) {
948
+ const [s, o] = i.split("|"), r = n[0];
949
+ if (!r) continue;
950
+ const a = r.colIndex, l = t.columns[a];
951
+ if (!l || !s || !o) continue;
952
+ const h = o, u = this.dataModel.getCell(s, h), f = at(u, l), p = this.applyReplacementAllInCell(f, n);
953
+ this.applyEdit(s, h, p);
954
+ }
955
+ this.recompute();
956
+ }
957
+ applyReplacement(t, e) {
958
+ const { query: i, replace: n, options: s } = this.state;
959
+ if (!i) return t;
960
+ if (s.regex)
961
+ try {
962
+ const o = `g${s.caseInsensitive ? "i" : ""}`, r = new RegExp(i, o);
963
+ let a = -1;
964
+ return t.replace(r, (l, ...h) => {
965
+ const u = h[h.length - 2];
966
+ return typeof u != "number" || u !== e.start || a === u ? l : (a = u, n);
967
+ });
968
+ } catch {
969
+ return t;
970
+ }
971
+ return t.slice(0, e.start) + n + t.slice(e.end);
972
+ }
973
+ applyReplacementAllInCell(t, e) {
974
+ if (!e.length) return t;
975
+ const { query: i, replace: n, options: s } = this.state;
976
+ if (!i) return t;
977
+ if (s.regex)
978
+ try {
979
+ const a = `g${s.caseInsensitive ? "i" : ""}`, l = new RegExp(i, a);
980
+ return t.replace(l, n);
981
+ } catch {
982
+ return t;
983
+ }
984
+ const o = [...e].sort((a, l) => l.start - a.start);
985
+ let r = t;
986
+ for (const a of o)
987
+ r = r.slice(0, a.start) + n + r.slice(a.end);
988
+ return r;
989
+ }
990
+ scheduleRecompute() {
991
+ this.debounceId !== null && window.clearTimeout(this.debounceId), this.debounceId = window.setTimeout(() => {
992
+ this.debounceId = null, this.recompute();
993
+ }, 180);
994
+ }
995
+ recompute() {
996
+ const { query: t, options: e } = this.state;
997
+ if (!t) {
998
+ this.setState({ ...this.state, matches: [], activeIndex: -1, error: null });
999
+ return;
1000
+ }
1001
+ const i = this.dataModel.getSchema(), n = this.dataModel.getView(), s = this.dataModel.listRows(), o = [];
1002
+ let r = null, a = null;
1003
+ if (e.regex)
1004
+ try {
1005
+ const h = `g${e.caseInsensitive ? "i" : ""}`;
1006
+ a = new RegExp(t, h);
1007
+ } catch (h) {
1008
+ r = h instanceof Error ? h.message : "Invalid pattern";
1009
+ }
1010
+ else {
1011
+ const h = e.caseInsensitive ? "gi" : "g";
1012
+ a = new RegExp(re(t), h);
1013
+ }
1014
+ if (!a || r) {
1015
+ this.setState({
1016
+ ...this.state,
1017
+ matches: [],
1018
+ activeIndex: -1,
1019
+ error: r ?? "Invalid pattern"
1020
+ });
1021
+ return;
1022
+ }
1023
+ for (let h = 0; h < s.length; h += 1) {
1024
+ const u = s[h];
1025
+ if (u)
1026
+ for (let f = 0; f < i.columns.length; f += 1) {
1027
+ const p = i.columns[f];
1028
+ if (!p || n.hiddenColumns?.some((y) => String(y) === String(p.key))) continue;
1029
+ const d = this.dataModel.getCell(u.id, p.key), m = at(d, p);
1030
+ if (!m) continue;
1031
+ a.lastIndex = 0;
1032
+ let g = a.exec(m);
1033
+ for (; g; ) {
1034
+ const y = g.index, w = y + (g[0]?.length ?? 0);
1035
+ if (o.push({
1036
+ rowId: u.id,
1037
+ colKey: p.key,
1038
+ rowIndex: h,
1039
+ colIndex: f,
1040
+ start: y,
1041
+ end: w,
1042
+ text: m
1043
+ }), g[0] === "") break;
1044
+ g = a.exec(m);
1045
+ }
1046
+ }
1047
+ }
1048
+ const l = o.length ? xt(this.state.activeIndex, o.length) : -1;
1049
+ this.setState({ ...this.state, matches: o, activeIndex: l, error: null });
1050
+ }
1051
+ setState(t) {
1052
+ this.state = t;
1053
+ for (const e of this.listeners) e(this.state);
1054
+ }
1055
+ }
1056
+ class ae {
1057
+ constructor(t, e, i) {
1058
+ this.lockedRows = /* @__PURE__ */ new Set(), this.mode = "none", this.mode = t, this.server = e, this.user = i;
1059
+ }
1060
+ setMode(t) {
1061
+ this.mode = t;
1062
+ }
1063
+ setUser(t) {
1064
+ this.user = t;
1065
+ }
1066
+ async selectRow(t) {
1067
+ if (this.mode === "row" && !this.lockedRows.has(t) && (this.lockedRows.add(t), this.server && this.user))
1068
+ try {
1069
+ await this.server.lockRow(t, this.user);
1070
+ } catch (e) {
1071
+ console.warn("lockRow failed", e);
1072
+ }
1073
+ }
1074
+ async unlockOnMove(t) {
1075
+ this.mode === "row" && t && await this.unlockRows([t]);
1076
+ }
1077
+ async unlockOnCommit(t) {
1078
+ if (this.mode !== "row") return;
1079
+ const e = [];
1080
+ for (const i of this.lockedRows)
1081
+ i !== t && e.push(i);
1082
+ e.length && await this.unlockRows(e);
1083
+ }
1084
+ async unlockRows(t) {
1085
+ for (const e of t) this.lockedRows.delete(e);
1086
+ if (this.server && this.user)
1087
+ try {
1088
+ await this.server.unlockRows(t, this.user);
1089
+ } catch (e) {
1090
+ console.warn("unlockRows failed", e);
1091
+ }
1092
+ }
1093
+ getLockedRows() {
1094
+ return new Set(this.lockedRows);
1095
+ }
1096
+ }
1097
+ const ct = 864e5;
1098
+ function Rt(c) {
1099
+ const t = dt(c);
1100
+ return t ? Number.isNaN(t.getTime()) ? null : t : null;
1101
+ }
1102
+ function ce(c) {
1103
+ const t = /* @__PURE__ */ new Date(`1970-01-01T${c}`);
1104
+ return Number.isNaN(t.getTime()) ? null : t;
1105
+ }
1106
+ function de(c, t, e) {
1107
+ if (t == null || e.type === "string") return null;
1108
+ if (e.type === "boolean") return String(!!t);
1109
+ if (e.type === "number" && typeof t == "number") return String(t);
1110
+ if (e.type === "datetime") {
1111
+ const i = t instanceof Date ? t : Rt(String(t));
1112
+ return i ? String(i.getTime()) : null;
1113
+ }
1114
+ if (e.type === "date") {
1115
+ const i = t instanceof Date ? t : Rt(String(t));
1116
+ if (!i) return null;
1117
+ const n = Math.floor(i.getTime() / ct) * ct;
1118
+ return String(n);
1119
+ }
1120
+ if (e.type === "time") {
1121
+ const i = t instanceof Date ? t : ce(String(t));
1122
+ return i ? String(i.getTime() % ct) : null;
1123
+ }
1124
+ return null;
1125
+ }
1126
+ const Ft = 24, ht = 48, ut = 24;
1127
+ function W(c, t, e = 100) {
1128
+ const i = {
1129
+ // Reserve extra space for browser UI affordances (e.g. date picker icon) and formatting.
1130
+ date: 8
1131
+ };
1132
+ return c.columns.map((n) => {
1133
+ const s = t.columnWidths?.[String(n.key)] ?? n.width ?? e, o = i[n.type] ?? 0;
1134
+ return s + o;
1135
+ });
1136
+ }
1137
+ function he(c) {
1138
+ return {
1139
+ ...c,
1140
+ startRow: Math.min(c.startRow, c.endRow),
1141
+ endRow: Math.max(c.startRow, c.endRow),
1142
+ startCol: Math.min(c.startCol, c.endCol),
1143
+ endCol: Math.max(c.startCol, c.endCol)
1144
+ };
1145
+ }
1146
+ function st(c) {
1147
+ const t = /^(.*?)(\d+)$/.exec(c);
1148
+ if (!t) return null;
1149
+ const e = t[1] ?? "", i = t[2] ?? "", n = Number(i);
1150
+ return Number.isFinite(n) ? { prefix: e, num: n, width: i.length } : null;
1151
+ }
1152
+ function Mt(c) {
1153
+ if (c instanceof Date) return c;
1154
+ if (typeof c == "number" && Number.isFinite(c)) return new Date(c);
1155
+ if (typeof c == "string") {
1156
+ const t = c.trim();
1157
+ if (/^\d{1,2}:\d{2}(:\d{2}(\.\d{1,3})?)?$/.test(t)) {
1158
+ const i = /* @__PURE__ */ new Date(`1970-01-01T${t}`);
1159
+ return Number.isNaN(i.getTime()) ? null : i;
1160
+ }
1161
+ const e = new Date(t);
1162
+ return Number.isNaN(e.getTime()) ? null : e;
1163
+ }
1164
+ return null;
1165
+ }
1166
+ function kt(c) {
1167
+ if (c instanceof Date) return c;
1168
+ if (typeof c == "number" && Number.isFinite(c)) return new Date(c);
1169
+ if (typeof c == "string") {
1170
+ const t = new Date(c);
1171
+ return Number.isNaN(t.getTime()) ? null : t;
1172
+ }
1173
+ return null;
1174
+ }
1175
+ function ue(c, t) {
1176
+ return new Date(Date.UTC(c, t + 1, 0)).getUTCDate();
1177
+ }
1178
+ function Kt(c, t) {
1179
+ const e = c.getUTCFullYear(), i = c.getUTCMonth(), n = c.getUTCDate(), s = c.getUTCHours(), o = c.getUTCMinutes(), r = c.getUTCSeconds(), a = c.getUTCMilliseconds(), l = e * 12 + i + t, h = Math.floor(l / 12), u = (l % 12 + 12) % 12, f = ue(h, u), p = Math.min(n, f);
1180
+ return new Date(Date.UTC(h, u, p, s, o, r, a));
1181
+ }
1182
+ function fe(c, t) {
1183
+ return Kt(c, t * 12);
1184
+ }
1185
+ function Lt(c, t) {
1186
+ const e = c.getFullYear(), i = c.getMonth(), n = c.getDate(), s = c.getHours(), o = c.getMinutes(), r = c.getSeconds(), a = c.getMilliseconds(), l = e * 12 + i + t, h = Math.floor(l / 12), u = (l % 12 + 12) % 12, f = new Date(h, u + 1, 0).getDate(), p = Math.min(n, f);
1187
+ return new Date(h, u, p, s, o, r, a);
1188
+ }
1189
+ function pe(c, t) {
1190
+ return Lt(c, t * 12);
1191
+ }
1192
+ function Vt(c, t) {
1193
+ if (t.length !== 1) return null;
1194
+ const e = he(t[0]);
1195
+ if (e.kind !== "cells") return null;
1196
+ const i = e.endCol - e.startCol + 1, n = e.endRow - e.startRow + 1;
1197
+ if (i !== 1 || n !== 1 && n !== 2) return null;
1198
+ const o = c.getSchema().columns[e.startCol];
1199
+ if (!o) return null;
1200
+ if (o.type === "boolean")
1201
+ return n !== 1 ? null : {
1202
+ colKey: o.key,
1203
+ colIndex: e.startCol,
1204
+ startRowIndex: e.startRow,
1205
+ endRowIndex: e.endRow,
1206
+ mode: "copy"
1207
+ };
1208
+ if (n === 1)
1209
+ return {
1210
+ colKey: o.key,
1211
+ colIndex: e.startCol,
1212
+ startRowIndex: e.startRow,
1213
+ endRowIndex: e.endRow,
1214
+ mode: "copy"
1215
+ };
1216
+ if (o.type === "string") {
1217
+ const r = c.listRows(), a = r[e.startRow], l = r[e.endRow];
1218
+ if (!a || !l) return null;
1219
+ const h = String(c.getCell(a.id, o.key) ?? ""), u = String(c.getCell(l.id, o.key) ?? ""), f = st(h), p = st(u);
1220
+ return !f || !p || f.prefix !== p.prefix || f.width !== p.width ? null : {
1221
+ colKey: o.key,
1222
+ colIndex: e.startCol,
1223
+ startRowIndex: e.startRow,
1224
+ endRowIndex: e.endRow,
1225
+ mode: "sequence"
1226
+ };
1227
+ }
1228
+ return o.type === "number" || o.type === "date" || o.type === "time" || o.type === "datetime" ? {
1229
+ colKey: o.key,
1230
+ colIndex: e.startCol,
1231
+ startRowIndex: e.startRow,
1232
+ endRowIndex: e.endRow,
1233
+ mode: "sequence"
1234
+ } : null;
1235
+ }
1236
+ function ge(c, t) {
1237
+ const e = c.getSchema(), i = c.listRows(), n = e.columns[t.colIndex];
1238
+ if (!n) return null;
1239
+ const s = i[t.startRowIndex], o = i[t.endRowIndex];
1240
+ if (!s || !o) return null;
1241
+ const r = c.getCell(s.id, n.key), a = c.getCell(o.id, n.key);
1242
+ if (t.mode === "copy")
1243
+ return () => a;
1244
+ if (n.type === "number") {
1245
+ const l = typeof r == "number" ? r : Number(r), h = typeof a == "number" ? a : Number(a);
1246
+ if (!Number.isFinite(l) || !Number.isFinite(h)) return null;
1247
+ const u = h - l;
1248
+ return (f) => h + u * f;
1249
+ }
1250
+ if (n.type === "date" || n.type === "time" || n.type === "datetime") {
1251
+ const l = n.type === "time" ? Mt(r) : kt(r), h = n.type === "time" ? Mt(a) : kt(a);
1252
+ if (!l || !h) return null;
1253
+ const u = l.getTime(), f = h.getTime(), p = f - u;
1254
+ if (n.type === "time")
1255
+ return (d) => new Date(f + p * d);
1256
+ if (n.type === "date") {
1257
+ const d = l.getUTCDate() === h.getUTCDate();
1258
+ if (d && l.getUTCMonth() === h.getUTCMonth()) {
1259
+ const g = h.getUTCFullYear() - l.getUTCFullYear();
1260
+ if (g !== 0) {
1261
+ const y = new Date(Date.UTC(h.getUTCFullYear(), h.getUTCMonth(), h.getUTCDate()));
1262
+ return (w) => fe(y, g * w);
1263
+ }
1264
+ } else if (d) {
1265
+ const g = (h.getUTCFullYear() - l.getUTCFullYear()) * 12 + (h.getUTCMonth() - l.getUTCMonth());
1266
+ if (g !== 0) {
1267
+ const y = new Date(Date.UTC(h.getUTCFullYear(), h.getUTCMonth(), h.getUTCDate()));
1268
+ return (w) => Kt(y, g * w);
1269
+ }
1270
+ }
1271
+ } else {
1272
+ const d = l.getDate() === h.getDate();
1273
+ if (d && l.getMonth() === h.getMonth()) {
1274
+ const g = h.getFullYear() - l.getFullYear();
1275
+ if (g !== 0)
1276
+ return (y) => pe(h, g * y);
1277
+ } else if (d) {
1278
+ const g = (h.getFullYear() - l.getFullYear()) * 12 + (h.getMonth() - l.getMonth());
1279
+ if (g !== 0)
1280
+ return (y) => Lt(h, g * y);
1281
+ }
1282
+ }
1283
+ return (d) => new Date(f + p * d);
1284
+ }
1285
+ if (n.type === "string") {
1286
+ const l = String(r ?? ""), h = String(a ?? ""), u = st(l), f = st(h);
1287
+ if (!u || !f || u.prefix !== f.prefix || u.width !== f.width) return null;
1288
+ const p = f.num - u.num;
1289
+ return (d) => {
1290
+ const m = f.num + p * d, g = m < 0 ? "-" : "", y = Math.abs(m), w = String(y).padStart(f.width, "0");
1291
+ return `${f.prefix}${g}${w}`;
1292
+ };
1293
+ }
1294
+ return null;
1295
+ }
1296
+ const _t = 12, Pt = 14;
1297
+ function Bt(c, t = _t) {
1298
+ return new DOMRect(c.right - t - 1, c.bottom - t - 1, t, t);
1299
+ }
1300
+ function $t(c, t, e) {
1301
+ return c >= e.left && c <= e.right && t >= e.top && t <= e.bottom;
1302
+ }
1303
+ function ot(c, t, e, i, n) {
1304
+ if (n === "readonly") return !1;
1305
+ const s = Vt(c, t);
1306
+ return !(!s || !e || e === "__all__" || i === null || i === "__all__" || i === null || i !== s.colKey || c.isReadonly(e, i));
1307
+ }
1308
+ function ft(c) {
1309
+ const t = c.style, e = t?.decorations;
1310
+ return {
1311
+ backgroundColor: t?.backgroundColor,
1312
+ textColor: t?.textColor,
1313
+ bold: e?.bold,
1314
+ italic: e?.italic,
1315
+ underline: e?.underline,
1316
+ strike: e?.strike
1317
+ };
1318
+ }
1319
+ function X(c, t) {
1320
+ return t ? {
1321
+ backgroundColor: t.backgroundColor ?? c.backgroundColor,
1322
+ textColor: t.textColor ?? c.textColor,
1323
+ bold: t.bold ?? c.bold,
1324
+ italic: t.italic ?? c.italic,
1325
+ underline: t.underline ?? c.underline,
1326
+ strike: t.strike ?? c.strike
1327
+ } : c;
1328
+ }
1329
+ function me(c, t, e) {
1330
+ const i = ft(e), n = c.resolveConditionalStyle(t, e).delta ?? {}, s = c.getCellStyle(t, e.key) ?? {}, o = X(X(i, n), s);
1331
+ return { columnStyle: i, cellStyle: s, resolved: o };
1332
+ }
1333
+ function It(c) {
1334
+ let t = "";
1335
+ c.backgroundColor && (t += `background-color:${c.backgroundColor};`), c.textColor && (t += `color:${c.textColor};`), c.bold && (t += "font-weight:600;"), c.italic && (t += "font-style:italic;");
1336
+ const e = [];
1337
+ return c.underline && e.push("underline"), c.strike && e.push("line-through"), e.length && (t += `text-decoration-line:${e.join(" ")};`), t;
1338
+ }
1339
+ function Nt(c, t) {
1340
+ const e = c.sorts?.[0];
1341
+ return e && e.key === t ? e.dir : null;
1342
+ }
1343
+ function Ot(c, t) {
1344
+ if ((c.filters ?? []).some((n) => {
1345
+ const s = n;
1346
+ return s?.kind === "values" && s.key === t;
1347
+ })) return !0;
1348
+ const i = c.columnDiagnostics?.[t];
1349
+ return !!(i?.errors || i?.warnings);
1350
+ }
1351
+ function ye() {
1352
+ return `
1353
+ <svg viewBox="0 0 24 24" width="16" height="16" aria-hidden="true" focusable="false">
1354
+ <path d="M3 5h18l-7 8v6l-4 2v-8L3 5z" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>
1355
+ </svg>
1356
+ `.trim();
1357
+ }
1358
+ function we(c) {
1359
+ return `
1360
+ <svg viewBox="0 0 24 24" width="16" height="16" aria-hidden="true" focusable="false">
1361
+ <path d="${c === "asc" ? "M12 6l6 8H6l6-8z" : "M12 18l-6-8h12l-6 8z"}" fill="currentColor"/>
1362
+ </svg>
1363
+ `.trim();
1364
+ }
1365
+ function be(c, t, e, i, n, s) {
1366
+ const o = Math.min(10, Math.floor(Math.min(i, n) / 2));
1367
+ if (o <= 0) return;
1368
+ const r = s === "error" ? "#ef4444" : "#f59e0b";
1369
+ c.save(), c.fillStyle = r, c.beginPath(), c.moveTo(t + i, e), c.lineTo(t + i - o, e), c.lineTo(t + i, e + o), c.closePath(), c.fill(), c.restore();
1370
+ }
1371
+ class qt {
1372
+ constructor() {
1373
+ this.numberFormatCache = /* @__PURE__ */ new Map(), this.dateParseCache = /* @__PURE__ */ new Map();
1374
+ }
1375
+ getNumberFormatter(t) {
1376
+ const e = JSON.stringify(t);
1377
+ let i = this.numberFormatCache.get(e);
1378
+ return i || (i = new Intl.NumberFormat("en-US", t), this.numberFormatCache.set(e, i)), i;
1379
+ }
1380
+ parseIsoDate(t) {
1381
+ const e = this.dateParseCache.get(t);
1382
+ if (e) return e;
1383
+ const i = dt(t);
1384
+ return !i || Number.isNaN(i.getTime()) ? null : (this.dateParseCache.set(t, i), i);
1385
+ }
1386
+ }
1387
+ function ve(c, t, e, i, n) {
1388
+ c.save(), c.globalAlpha = n, c.strokeStyle = "rgba(15,23,42,1)", c.lineWidth = 2, c.lineJoin = "round", c.beginPath(), c.moveTo(t, e), c.lineTo(t + i, e), c.lineTo(t + Math.round(i * 0.62), e + Math.round(i * 0.46)), c.lineTo(t + Math.round(i * 0.38), e + Math.round(i * 0.46)), c.closePath(), c.stroke(), c.beginPath(), c.moveTo(t + Math.round(i * 0.46), e + Math.round(i * 0.46)), c.lineTo(t + Math.round(i * 0.46), e + i), c.lineTo(t + Math.round(i * 0.54), e + i - 2), c.lineTo(t + Math.round(i * 0.54), e + Math.round(i * 0.46)), c.stroke(), c.restore();
1389
+ }
1390
+ function Ce(c, t, e, i, n, s) {
1391
+ c.save(), c.globalAlpha = n, c.fillStyle = "rgba(15,23,42,1)", c.beginPath(), s === "asc" ? (c.moveTo(t + i / 2, e), c.lineTo(t + i, e + i), c.lineTo(t, e + i)) : (c.moveTo(t, e), c.lineTo(t + i, e), c.lineTo(t + i / 2, e + i)), c.closePath(), c.fill(), c.restore();
1392
+ }
1393
+ class pt {
1394
+ constructor(t) {
1395
+ this.n = t, this.tree = new Array(t + 1).fill(0);
1396
+ }
1397
+ static from(t) {
1398
+ const e = new pt(t.length);
1399
+ for (let i = 0; i < t.length; i += 1) e.add(i, t[i] ?? 0);
1400
+ return e;
1401
+ }
1402
+ sum(t) {
1403
+ let e = Math.max(0, Math.min(this.n, t)), i = 0;
1404
+ for (; e > 0; )
1405
+ i += this.tree[e] ?? 0, e -= e & -e;
1406
+ return i;
1407
+ }
1408
+ total() {
1409
+ return this.sum(this.n);
1410
+ }
1411
+ add(t, e) {
1412
+ let i = t + 1;
1413
+ if (!(i <= 0 || i > this.n))
1414
+ for (; i <= this.n; )
1415
+ this.tree[i] = (this.tree[i] ?? 0) + e, i += i & -i;
1416
+ }
1417
+ /**
1418
+ * Returns the smallest index `i` (0-based) such that `sum(i + 1) >= target`.
1419
+ * If `target <= 0`, returns 0. If `target > total`, returns `n - 1`.
1420
+ */
1421
+ lowerBound(t) {
1422
+ if (this.n <= 0 || t <= 0) return 0;
1423
+ const e = this.total();
1424
+ if (t > e) return this.n - 1;
1425
+ let i = 0, n = 1;
1426
+ for (; n <= this.n; ) n <<= 1;
1427
+ let s = 0;
1428
+ for (let o = n; o !== 0; o >>= 1) {
1429
+ const r = i + o;
1430
+ if (r <= this.n) {
1431
+ const a = s + (this.tree[r] ?? 0);
1432
+ a < t && (i = r, s = a);
1433
+ }
1434
+ }
1435
+ return Math.min(this.n - 1, i);
1436
+ }
1437
+ }
1438
+ class nt {
1439
+ constructor(t) {
1440
+ this.dataModel = t, this.tableEl = null, this.defaultRowHeight = ut, this.rowHeaderWidth = ht, this.activeRowId = null, this.activeColKey = null, this.selection = [], this.valueFormatCache = new qt(), this.measureCache = /* @__PURE__ */ new Map(), this.frame = 0;
1441
+ }
1442
+ mount(t) {
1443
+ this.tableEl = document.createElement("table"), this.tableEl.dataset.extableRenderer = "html", t.innerHTML = "", t.appendChild(this.tableEl), this.render();
1444
+ }
1445
+ setActiveCell(t, e) {
1446
+ this.activeRowId = t, this.activeColKey = e, this.updateActiveClasses();
1447
+ }
1448
+ setSelection(t) {
1449
+ this.selection = t, this.applySelectionClasses();
1450
+ }
1451
+ render(t) {
1452
+ if (this.frame += 1, !this.tableEl) return;
1453
+ const e = this.tableEl.parentElement, i = e?.scrollTop ?? 0, n = e?.scrollLeft ?? 0, s = this.dataModel.getSchema(), o = this.dataModel.getView(), r = this.dataModel.listRows();
1454
+ this.tableEl.innerHTML = "";
1455
+ const a = W(s, o), l = s.columns.map((m) => ft(m)), h = l.map((m) => It(m)), u = this.rowHeaderWidth + a.reduce((m, g) => m + (g ?? 0), 0), f = document.createElement("colgroup"), p = document.createElement("col");
1456
+ p.style.width = `${this.rowHeaderWidth}px`, f.appendChild(p);
1457
+ for (const m of a) {
1458
+ const g = document.createElement("col");
1459
+ m && (g.style.width = `${m}px`), f.appendChild(g);
1460
+ }
1461
+ this.tableEl.appendChild(f), this.tableEl.style.width = `${u}px`, this.tableEl.appendChild(this.renderHeader(s, a));
1462
+ const d = document.createElement("tbody");
1463
+ for (const m of r)
1464
+ d.appendChild(this.renderRow(m, s, a, l, h));
1465
+ this.tableEl.appendChild(d), this.updateActiveClasses(), this.applySelectionClasses(), e && (e.scrollTop = i, e.scrollLeft = n);
1466
+ for (const [m, g] of Array.from(this.measureCache.entries()))
1467
+ g.frame !== this.frame && this.measureCache.delete(m);
1468
+ }
1469
+ destroy() {
1470
+ _(this.tableEl), this.tableEl = null;
1471
+ }
1472
+ getCellElements() {
1473
+ return this.tableEl?.querySelectorAll("tr[data-row-id] td[data-col-key]") ?? null;
1474
+ }
1475
+ hitTest(t) {
1476
+ const e = t.target;
1477
+ if (!e) return null;
1478
+ const i = e.closest("th.extable-corner");
1479
+ if (i)
1480
+ return {
1481
+ rowId: "__all__",
1482
+ colKey: "__all__",
1483
+ element: i,
1484
+ rect: i.getBoundingClientRect()
1485
+ };
1486
+ const n = e.closest("th.extable-row-header:not(.extable-corner)");
1487
+ if (n) {
1488
+ const r = n.closest("tr[data-row-id]");
1489
+ if (r)
1490
+ return {
1491
+ rowId: r.dataset.rowId ?? "",
1492
+ colKey: null,
1493
+ element: n,
1494
+ rect: n.getBoundingClientRect()
1495
+ };
1496
+ }
1497
+ const s = e.closest("td[data-col-key]"), o = s?.closest("tr[data-row-id]");
1498
+ return !s || !o ? null : {
1499
+ rowId: o.dataset.rowId ?? "",
1500
+ colKey: s.dataset.colKey ?? "",
1501
+ element: s,
1502
+ rect: s.getBoundingClientRect()
1503
+ };
1504
+ }
1505
+ renderHeader(t, e) {
1506
+ const i = document.createElement("thead"), n = document.createElement("tr"), s = document.createElement("th");
1507
+ s.classList.add("extable-row-header", "extable-corner"), s.textContent = "", s.style.width = `${this.rowHeaderWidth}px`, this.activeRowId && s.classList.toggle("extable-active-row-header", !0), s.dataset.colKey = "", n.appendChild(s);
1508
+ const o = this.dataModel.getView();
1509
+ for (let r = 0; r < t.columns.length; r += 1) {
1510
+ const a = t.columns[r];
1511
+ if (!a) continue;
1512
+ const l = document.createElement("th");
1513
+ l.dataset.colKey = a.key;
1514
+ const h = Nt(o, a.key), u = Ot(o, a.key);
1515
+ h ? l.dataset.extableSortDir = h : l.removeAttribute("data-extable-sort-dir"), u ? l.dataset.extableFsActive = "1" : l.removeAttribute("data-extable-fs-active");
1516
+ const f = document.createElement("div");
1517
+ f.className = "extable-col-header";
1518
+ const p = document.createElement("span");
1519
+ p.className = "extable-col-header-text", p.textContent = a.header ?? a.key;
1520
+ const d = document.createElement("button");
1521
+ d.type = "button", d.className = "extable-filter-sort-trigger", d.dataset.extableFsOpen = "1", d.dataset.extableColKey = a.key, d.title = "Filter / Sort", d.innerHTML = h ? we(h) : ye(), f.appendChild(p), f.appendChild(d), l.appendChild(f);
1522
+ const m = e[r] ?? a.width;
1523
+ m && (l.style.width = `${m}px`), l.dataset.colKey = a.key, this.activeColKey !== null && this.activeColKey === a.key && l.classList.add("extable-active-col-header"), n.appendChild(l);
1524
+ }
1525
+ return i.appendChild(n), i;
1526
+ }
1527
+ renderRow(t, e, i, n, s) {
1528
+ const o = document.createElement("tr");
1529
+ o.dataset.rowId = t.id;
1530
+ const r = this.dataModel.getView(), a = document.createElement("th");
1531
+ a.scope = "row", a.classList.add("extable-row-header");
1532
+ const l = this.dataModel.getDisplayIndex(t.id) ?? "";
1533
+ a.textContent = String(l), a.style.width = `${this.rowHeaderWidth}px`, this.activeRowId === t.id && a.classList.add("extable-active-row-header"), o.appendChild(a);
1534
+ for (let u = 0; u < e.columns.length; u += 1) {
1535
+ const f = e.columns[u];
1536
+ if (!f) continue;
1537
+ const p = document.createElement("td");
1538
+ p.classList.add("extable-cell"), p.dataset.colKey = f.key, f.type === "boolean" && p.classList.add("extable-boolean");
1539
+ const d = this.dataModel.hasPending(t.id, f.key), m = this.dataModel.resolveConditionalStyle(t.id, f), g = this.dataModel.getCellStyle(t.id, f.key);
1540
+ if (!g && !m.delta && !d) {
1541
+ const M = s[u] ?? "";
1542
+ M && (p.style.cssText = M);
1543
+ } else {
1544
+ const M = n[u] ?? {}, A = m.delta ? X(M, m.delta) : M, K = g ? X(A, g) : A, H = d ? { ...K, textColor: void 0 } : K, P = It(H);
1545
+ P && (p.style.cssText = P);
1546
+ }
1547
+ const y = i[u] ?? r.columnWidths?.[f.key] ?? f.width;
1548
+ y && (p.style.width = `${y}px`);
1549
+ const w = r.wrapText?.[f.key] ?? f.wrapText;
1550
+ p.classList.add(w ? "cell-wrap" : "cell-nowrap");
1551
+ const S = this.dataModel.getRawCell(t.id, f.key), b = this.dataModel.resolveCellValue(t.id, f), C = b.textOverride ?? (m.forceErrorText ? "#ERROR" : void 0), v = C ? { text: C } : this.formatValue(b.value, f);
1552
+ p.textContent = v.text, v.color && (p.style.color = v.color);
1553
+ const R = this.dataModel.getCellMarker(t.id, f.key);
1554
+ R ? (p.classList.toggle("extable-diag-warning", R.level === "warning"), p.classList.toggle("extable-diag-error", R.level === "error"), p.dataset.extableDiagMessage = R.message) : (p.classList.remove("extable-diag-warning", "extable-diag-error"), p.removeAttribute("data-extable-diag-message"));
1555
+ const I = f.style?.align ?? (f.type === "number" ? "right" : "left");
1556
+ p.classList.add(I === "right" ? "align-right" : "align-left");
1557
+ const x = de(S, b.value, f);
1558
+ if (x !== null)
1559
+ p.dataset.raw = x;
1560
+ else {
1561
+ const M = S == null ? "" : String(S);
1562
+ p.dataset.raw = M;
1563
+ }
1564
+ d && p.classList.add("pending"), this.dataModel.isReadonly(t.id, f.key) ? p.classList.add("extable-readonly", "extable-readonly-muted") : p.classList.add("extable-editable"), this.activeRowId === t.id && this.activeColKey !== null && this.activeColKey === f.key && p.classList.add("extable-active-cell"), o.appendChild(p);
1565
+ }
1566
+ if (e.columns.some((u) => r.wrapText?.[u.key] ?? u.wrapText)) {
1567
+ let u = this.defaultRowHeight;
1568
+ for (let f = 0; f < e.columns.length; f += 1) {
1569
+ const p = e.columns[f];
1570
+ if (!p || !p.wrapText) continue;
1571
+ const d = i[f] ?? r.columnWidths?.[p.key] ?? p.width ?? 100, m = this.dataModel.resolveCellValue(t.id, p), g = this.dataModel.resolveConditionalStyle(t.id, p), w = m.textOverride ?? (g.forceErrorText ? "#ERROR" : void 0) ? "#ERROR" : m.value === null || m.value === void 0 ? "" : String(m.value), S = this.dataModel.getRowVersion(t.id), b = `${t.id}|${p.key}|${S}|${d}|${w}`, C = this.measureCache.get(b);
1572
+ if (C) {
1573
+ C.frame = this.frame, this.measureCache.set(b, C), u = Math.max(u, C.height);
1574
+ continue;
1575
+ }
1576
+ const v = document.createElement("span");
1577
+ v.style.visibility = "hidden", v.style.position = "absolute", v.style.left = "-10000px", v.style.top = "0", v.style.whiteSpace = "pre-wrap", v.style.overflowWrap = "anywhere", v.style.display = "inline-block", v.style.width = `${d}px`, v.textContent = w;
1578
+ const R = this.tableEl?.parentElement;
1579
+ if (!R) continue;
1580
+ R.appendChild(v);
1581
+ const I = v.clientHeight + 10;
1582
+ v.remove(), this.measureCache.set(b, { height: I, frame: this.frame }), u = Math.max(u, I);
1583
+ }
1584
+ o.style.height = `${u}px`, this.dataModel.setRowHeight(t.id, u);
1585
+ } else
1586
+ o.style.height = `${this.defaultRowHeight}px`, this.dataModel.setRowHeight(t.id, this.defaultRowHeight);
1587
+ return o;
1588
+ }
1589
+ updateActiveClasses() {
1590
+ if (!this.tableEl) return;
1591
+ const t = this.activeRowId === "__all__" && this.activeColKey === "__all__";
1592
+ this.tableEl.classList.toggle("extable-all-selected", t);
1593
+ for (const e of Array.from(this.tableEl.querySelectorAll(".extable-active-row-header")))
1594
+ e.classList.remove("extable-active-row-header");
1595
+ for (const e of Array.from(this.tableEl.querySelectorAll(".extable-active-col-header")))
1596
+ e.classList.remove("extable-active-col-header");
1597
+ for (const e of Array.from(this.tableEl.querySelectorAll(".extable-active-cell")))
1598
+ e.classList.remove("extable-active-cell");
1599
+ if (this.activeRowId)
1600
+ for (const e of Array.from(
1601
+ this.tableEl.querySelectorAll(
1602
+ `tr[data-row-id="${this.activeRowId}"] .extable-row-header`
1603
+ )
1604
+ ))
1605
+ e.classList.add("extable-active-row-header");
1606
+ if (this.activeColKey !== null) {
1607
+ for (const e of Array.from(
1608
+ this.tableEl.querySelectorAll(`th[data-col-key="${this.activeColKey}"]`)
1609
+ ))
1610
+ e.classList.add("extable-active-col-header");
1611
+ if (this.activeRowId)
1612
+ for (const e of Array.from(
1613
+ this.tableEl.querySelectorAll(
1614
+ `tr[data-row-id="${this.activeRowId}"] td[data-col-key="${this.activeColKey}"]`
1615
+ )
1616
+ ))
1617
+ e.classList.add("extable-active-cell");
1618
+ }
1619
+ }
1620
+ applySelectionClasses() {
1621
+ if (!this.tableEl) return;
1622
+ for (const i of Array.from(this.tableEl.querySelectorAll(".extable-selected")))
1623
+ i.classList.remove("extable-selected");
1624
+ if (!this.selection.length) return;
1625
+ const t = Array.from(this.tableEl.querySelectorAll("tbody tr")), e = this.dataModel.getSchema();
1626
+ for (const i of this.selection) {
1627
+ const n = Math.max(0, Math.min(i.startRow, i.endRow)), s = Math.min(t.length - 1, Math.max(i.startRow, i.endRow)), o = Math.max(0, Math.min(i.startCol, i.endCol)), r = Math.min(e.columns.length - 1, Math.max(i.startCol, i.endCol));
1628
+ for (let a = n; a <= s; a += 1) {
1629
+ const l = t[a];
1630
+ if (!l) continue;
1631
+ const h = l.querySelector("th.extable-row-header");
1632
+ h && h.classList.add("extable-selected");
1633
+ const u = Array.from(l.querySelectorAll("td"));
1634
+ for (let f = o; f <= r; f += 1) {
1635
+ const p = u[f];
1636
+ p && p.classList.add("extable-selected");
1637
+ }
1638
+ }
1639
+ }
1640
+ }
1641
+ formatValue(t, e) {
1642
+ if (t == null) return { text: "" };
1643
+ if (e.type === "boolean")
1644
+ return e.booleanDisplay === "checkbox" || !e.booleanDisplay ? { text: t ? "☑" : "☐" } : Array.isArray(e.booleanDisplay) && e.booleanDisplay.length >= 2 ? { text: String(t ? e.booleanDisplay[0] : e.booleanDisplay[1]) } : { text: t ? String(e.booleanDisplay) : "" };
1645
+ if (e.type === "number" && typeof t == "number") {
1646
+ const i = t, n = {};
1647
+ e.number?.scale !== void 0 && (n.minimumFractionDigits = e.number.scale, n.maximumFractionDigits = e.number.scale), n.useGrouping = !!e.number?.thousandSeparator;
1648
+ const s = this.valueFormatCache.getNumberFormatter(n).format(i), o = e.number?.negativeRed && i < 0 ? "#b91c1c" : void 0;
1649
+ return { text: s, color: o };
1650
+ }
1651
+ if ((e.type === "date" || e.type === "time" || e.type === "datetime") && (t instanceof Date || typeof t == "string")) {
1652
+ const i = e.type === "date" ? Y(e.dateFormat, "date") : e.type === "time" ? Y(e.timeFormat, "time") : Y(e.dateTimeFormat, "datetime");
1653
+ let n = null;
1654
+ return t instanceof Date ? n = t : n = this.valueFormatCache.parseIsoDate(t), n ? { text: At(n, i) } : { text: String(t) };
1655
+ }
1656
+ return { text: String(t) };
1657
+ }
1658
+ }
1659
+ const O = class O {
1660
+ constructor(t, e = () => "direct") {
1661
+ this.getEditMode = e, this.root = null, this.canvas = null, this.spacer = null, this.overlayLayer = null, this.tooltip = null, this.tooltipTarget = null, this.tooltipMessage = null, this.rowHeight = ut, this.headerHeight = Ft, this.lineHeight = 16, this.padding = 12, this.rowHeaderWidth = ht, this.activeRowId = null, this.activeColKey = null, this.selection = [], this.valueFormatCache = new qt(), this.textMeasureCache = /* @__PURE__ */ new Map(), this.frame = 0, this.cursorTimer = null, this.pendingCursorPoint = null, this.hoverHeaderColKey = null, this.hoverHeaderIcon = !1, this.rowHeightCacheKey = null, this.rowHeightMeasuredVersion = /* @__PURE__ */ new Map(), this.rowHeightMeasureRaf = null, this.rowHeightMeasureTask = null, this.heightIndex = null, this.handleClick = (i) => {
1662
+ if (!this.root || !this.canvas) return;
1663
+ const n = this.canvas.getBoundingClientRect(), s = i.clientX - n.left, o = i.clientY - n.top, r = s + this.root.scrollLeft, a = o;
1664
+ if (a >= this.headerHeight || r < this.rowHeaderWidth) return;
1665
+ const l = this.dataModel.getSchema(), h = this.dataModel.getView(), u = W(l, h);
1666
+ let f = this.rowHeaderWidth, p = -1;
1667
+ for (let C = 0; C < u.length; C += 1) {
1668
+ const v = u[C] ?? 100;
1669
+ if (r >= f && r <= f + v) {
1670
+ p = C;
1671
+ break;
1672
+ }
1673
+ f += v;
1674
+ }
1675
+ if (p < 0) return;
1676
+ const d = l.columns[p];
1677
+ if (!d) return;
1678
+ const m = u[p] ?? 100, g = 18, w = f + m - g - 4, S = Math.floor((this.headerHeight - g) / 2);
1679
+ r >= w && r <= w + g && a >= S && a <= S + g && this.root.dispatchEvent(
1680
+ new CustomEvent("extable:filter-sort-open", { bubbles: !0, detail: { colKey: d.key } })
1681
+ );
1682
+ }, this.handlePointerMove = (i) => {
1683
+ this.pendingCursorPoint = { x: i.clientX, y: i.clientY }, !this.cursorTimer && (this.cursorTimer = window.setTimeout(() => {
1684
+ this.cursorTimer = null;
1685
+ const n = this.pendingCursorPoint;
1686
+ n && this.updateCanvasCursor(n.x, n.y);
1687
+ }, 50));
1688
+ }, this.handlePointerLeave = () => {
1689
+ this.canvas && (this.canvas.style.cursor = "cell", this.tooltip && (this.tooltip.dataset.visible = "0"), this.tooltipTarget = null, this.tooltipMessage = null);
1690
+ }, this.dataModel = t;
1691
+ }
1692
+ mount(t) {
1693
+ this.root = t, this.canvas = document.createElement("canvas");
1694
+ const e = O.MAX_CANVAS_DIM_PX;
1695
+ this.canvas.width = Math.max(1, Math.min(e, Math.floor(t.clientWidth || 600))), this.canvas.height = Math.max(1, Math.min(e, Math.floor(t.clientHeight || 400))), this.canvas.style.width = `${this.canvas.width}px`, this.canvas.style.height = `${this.canvas.height}px`, this.canvas.dataset.extableRenderer = "canvas", this.canvas.style.position = "sticky", this.canvas.style.top = "0", this.canvas.style.left = "0", this.canvas.style.zIndex = "1", this.canvas.style.cursor = "cell", this.canvas.addEventListener("pointermove", this.handlePointerMove), this.canvas.addEventListener("pointerleave", this.handlePointerLeave), this.canvas.addEventListener("click", this.handleClick), this.spacer = document.createElement("div"), this.spacer.style.width = "1px", this.tooltip && this.tooltip.remove(), this.overlayLayer && this.overlayLayer.remove(), this.overlayLayer = document.createElement("div"), this.overlayLayer.className = "extable-overlay-layer", this.tooltip = document.createElement("div"), this.tooltip.className = "extable-tooltip", this.tooltip.dataset.visible = "0", this.overlayLayer.appendChild(this.tooltip), t.innerHTML = "", t.style.position = "relative", t.appendChild(this.overlayLayer), t.appendChild(this.canvas), t.appendChild(this.spacer), this.render();
1696
+ }
1697
+ setActiveCell(t, e) {
1698
+ this.activeRowId = t, this.activeColKey = e, this.render();
1699
+ }
1700
+ setSelection(t) {
1701
+ this.selection = t, this.render();
1702
+ }
1703
+ render(t) {
1704
+ try {
1705
+ if (this.frame += 1, !this.canvas || !this.root) return;
1706
+ const e = this.canvas.getContext("2d");
1707
+ if (!e) return;
1708
+ e.font = "14px sans-serif";
1709
+ let i = e.font;
1710
+ const n = this.activeRowId === "__all__" && this.activeColKey === "__all__", s = this.dataModel.getSchema(), o = this.dataModel.getView(), r = this.dataModel.listRows(), a = W(s, o), l = s.columns.map((k) => ft(k)), h = /* @__PURE__ */ new Map(), u = s.columns.some((k) => o.wrapText?.[k.key] ?? k.wrapText), f = u ? this.getRowHeightCacheKey(s, o, a) : null;
1711
+ f !== this.rowHeightCacheKey && (this.rowHeightCacheKey = f, this.rowHeightMeasuredVersion.clear(), this.rowHeightMeasureTask = null), u || this.cancelRowHeightMeasurement(), this.ensureHeightIndex(r, u ? f : null, u);
1712
+ const p = this.heightIndex;
1713
+ if (!p) return;
1714
+ const d = this.rowHeaderWidth + a.reduce((k, D) => k + (D ?? 0), 0), m = t?.clientWidth ?? (this.root.clientWidth || 600), g = t?.clientHeight ?? (this.root.clientHeight || this.canvas.height || 400), y = O.MAX_CANVAS_DIM_PX, w = Math.max(1, Math.min(y, Math.floor(m))), S = Math.max(1, Math.min(y, Math.floor(g)));
1715
+ this.canvas.width !== w && (this.canvas.width = w), this.canvas.height !== S && (this.canvas.height = S), this.canvas.style.width = `${w}px`, this.canvas.style.height = `${S}px`, e.font = "14px sans-serif", i = e.font, this.refreshTooltipPosition();
1716
+ const b = t?.scrollTop ?? this.root.scrollTop, C = t?.scrollLeft ?? this.root.scrollLeft, v = (k) => {
1717
+ const D = k + 1, E = Math.max(
1718
+ 0,
1719
+ Math.min(r.length - 1, p.fenwick.lowerBound(D))
1720
+ ), q = p.fenwick.sum(E);
1721
+ let F = E, $ = 0;
1722
+ const B = (this.canvas?.height ?? 0) + this.rowHeight * 2;
1723
+ for (let T = E; T < r.length && $ < B; T += 1)
1724
+ $ += p.heights[T] ?? this.rowHeight, F = T + 1;
1725
+ return { accum: q, visibleStart: E, visibleEnd: F };
1726
+ };
1727
+ let R = p.fenwick.total(), I = Math.max(
1728
+ 0,
1729
+ Math.min(b, Math.max(0, R - this.rowHeight))
1730
+ ), { accum: x, visibleStart: M, visibleEnd: A } = v(I);
1731
+ if (u && f) {
1732
+ const k = {};
1733
+ for (let D = M; D < A; D += 1) {
1734
+ const E = r[D];
1735
+ if (!E) continue;
1736
+ const q = this.dataModel.getRowVersion(E.id);
1737
+ if (this.rowHeightMeasuredVersion.get(E.id) === q) continue;
1738
+ const F = this.measureRowHeight(e, E, s, a);
1739
+ k[E.id] = F, this.rowHeightMeasuredVersion.set(E.id, q);
1740
+ }
1741
+ this.applyRowHeightUpdates(k), R = p.fenwick.total(), this.dataModel.setRowHeightsBulk(k), I = Math.max(
1742
+ 0,
1743
+ Math.min(b, Math.max(0, R - this.rowHeight))
1744
+ ), { accum: x, visibleStart: M, visibleEnd: A } = v(I), (this.rowHeightMeasureTask || Object.keys(k).length > 0) && this.scheduleRowHeightMeasurement();
1745
+ }
1746
+ this.spacer && (this.spacer.style.height = `${R + this.headerHeight}px`, this.spacer.style.width = `${d}px`);
1747
+ const K = this.rowHeaderWidth - C;
1748
+ e.clearRect(0, 0, this.canvas.width, this.canvas.height), e.fillStyle = "#e5e7eb", e.fillRect(0, 0, this.rowHeaderWidth, this.canvas.height);
1749
+ let H = this.headerHeight + x - I;
1750
+ for (let k = M; k < A; k += 1) {
1751
+ const D = r[k], E = p.heights[k] ?? this.rowHeight;
1752
+ e.strokeStyle = "#d0d7de", e.fillStyle = "#e5e7eb", e.fillRect(0, H, this.rowHeaderWidth, E), e.strokeRect(0, H, this.rowHeaderWidth, E);
1753
+ const q = this.dataModel.getDisplayIndex(D.id) ?? "";
1754
+ this.activeRowId === D.id && (e.fillStyle = "rgba(59,130,246,0.16)", e.fillRect(0, H, this.rowHeaderWidth, E)), e.fillStyle = "#0f172a", e.font = "bold 14px sans-serif", e.textAlign = "center", e.textBaseline = "middle", e.fillText(String(q), this.rowHeaderWidth / 2, H + E / 2), e.font = i, e.textAlign = "left", e.textBaseline = "alphabetic", e.save(), e.beginPath(), e.rect(
1755
+ this.rowHeaderWidth,
1756
+ this.headerHeight,
1757
+ this.canvas.width - this.rowHeaderWidth,
1758
+ this.canvas.height - this.headerHeight
1759
+ ), e.clip(), e.translate(K, 0);
1760
+ let F = 0, $ = "";
1761
+ for (let B = 0; B < s.columns.length; B += 1) {
1762
+ const T = s.columns[B], L = a[B] ?? 100;
1763
+ if (!T) {
1764
+ F += L;
1765
+ continue;
1766
+ }
1767
+ const V = this.dataModel.isReadonly(D.id, T.key);
1768
+ e.strokeStyle = "#d0d7de";
1769
+ const Z = this.dataModel.resolveConditionalStyle(D.id, T), z = this.dataModel.getCellStyle(D.id, T.key), G = l[B] ?? {}, gt = Z.delta ? X(G, Z.delta) : G, U = z ? X(gt, z) : gt, Ut = V ? "#f3f4f6" : U.backgroundColor ?? "#ffffff";
1770
+ e.fillStyle = Ut, e.fillRect(F, H, L, E), e.strokeRect(F, H, L, E);
1771
+ const mt = this.dataModel.resolveCellValue(D.id, T), lt = mt.textOverride ?? (Z.forceErrorText ? "#ERROR" : void 0) ? { text: "#ERROR" } : this.formatValue(mt.value, T), Wt = lt.text, jt = T.style?.align ?? (T.type === "number" ? "right" : "left");
1772
+ if (this.activeRowId === D.id && this.activeColKey !== null && this.activeColKey === T.key && (e.strokeStyle = "#3b82f6", e.lineWidth = 2, e.strokeRect(F + 1, H + 1, L - 2, E - 2), e.lineWidth = 1, ot(
1773
+ this.dataModel,
1774
+ this.selection,
1775
+ this.activeRowId,
1776
+ this.activeColKey,
1777
+ this.getEditMode()
1778
+ ))) {
1779
+ const N = _t, Q = F + L - N - 1, J = H + E - N - 1;
1780
+ e.fillStyle = "#3b82f6", e.fillRect(Q, J, N, N), e.strokeStyle = "#ffffff", e.strokeRect(Q + 0.5, J + 0.5, N - 1, N - 1);
1781
+ }
1782
+ e.fillStyle = this.dataModel.hasPending(D.id, T.key) ? "#b91c1c" : lt.color ? lt.color : V ? "#94a3b8" : U.textColor ?? "#0f172a";
1783
+ const Yt = o.wrapText?.[T.key] ?? T.wrapText ?? !1, yt = T.type === "boolean" && (!T.booleanDisplay || T.booleanDisplay === "checkbox"), Xt = T.type === "boolean" && !!(T.booleanDisplay && T.booleanDisplay !== "checkbox");
1784
+ if (yt)
1785
+ e.font = i, $ = "";
1786
+ else {
1787
+ const N = `${U.italic ? "i" : ""}${U.bold ? "b" : ""}`;
1788
+ if (N !== $) {
1789
+ const Q = h.get(N);
1790
+ if (Q) e.font = Q;
1791
+ else {
1792
+ const J = U.bold ? "600 " : "", bt = `${U.italic ? "italic " : ""}${J}14px sans-serif`.trim();
1793
+ h.set(N, bt), e.font = bt;
1794
+ }
1795
+ $ = N;
1796
+ }
1797
+ }
1798
+ this.drawCellText(
1799
+ e,
1800
+ Wt,
1801
+ F + 8,
1802
+ H + 6,
1803
+ L - 12,
1804
+ E - 12,
1805
+ Yt,
1806
+ jt,
1807
+ yt,
1808
+ Xt,
1809
+ { underline: !!U.underline, strike: !!U.strike }
1810
+ );
1811
+ const wt = this.dataModel.getCellMarker(D.id, T.key);
1812
+ wt && be(e, F, H, L, E, wt.level), F += L;
1813
+ }
1814
+ e.restore(), H += E;
1815
+ }
1816
+ e.fillStyle = "#e5e7eb", e.fillRect(0, 0, this.canvas.width, this.headerHeight), e.strokeStyle = "#d0d7de", e.strokeRect(0, 0, this.rowHeaderWidth, this.headerHeight), e.fillStyle = "#9ca3af", e.beginPath(), e.moveTo(4, 4), e.lineTo(16, 4), e.lineTo(4, 16), e.closePath(), e.fill(), this.activeRowId && (e.fillStyle = "rgba(59,130,246,0.16)", e.fillRect(0, 0, this.rowHeaderWidth, this.headerHeight)), e.save(), e.beginPath(), e.rect(this.rowHeaderWidth, 0, this.canvas.width - this.rowHeaderWidth, this.headerHeight), e.clip(), e.translate(K, 0);
1817
+ let P = 0;
1818
+ for (let k = 0; k < s.columns.length; k += 1) {
1819
+ const D = s.columns[k], E = a[k] ?? 100;
1820
+ if (!D) {
1821
+ P += E;
1822
+ continue;
1823
+ }
1824
+ this.activeColKey !== null && this.activeColKey === D.key && (e.fillStyle = "rgba(59,130,246,0.16)", e.fillRect(P, 0, E, this.headerHeight)), e.strokeStyle = "#d0d7de", e.strokeRect(P, 0, E, this.headerHeight), e.fillStyle = "#0f172a", e.font = "bold 14px sans-serif", e.fillText(D.header ?? D.key, P + 8, this.headerHeight - 8), e.font = i;
1825
+ const F = Nt(o, D.key), $ = Ot(o, D.key), B = this.hoverHeaderColKey !== null && this.hoverHeaderColKey === D.key;
1826
+ if (!!F || $ || B) {
1827
+ const L = B ? 0.9 : $ || F ? 0.75 : 0.45, V = 16, z = P + E - V - 6, G = Math.floor((this.headerHeight - V) / 2);
1828
+ F ? Ce(e, z + 3, G + 3, V - 6, L, F) : ve(e, z + 2, G + 2, V - 4, L);
1829
+ }
1830
+ P += E;
1831
+ }
1832
+ if (e.restore(), this.selection.length) {
1833
+ e.save(), e.strokeStyle = "#3b82f6", e.fillStyle = "rgba(59,130,246,0.12)";
1834
+ for (const k of this.selection) {
1835
+ const D = Math.max(0, Math.min(k.startRow, k.endRow)), E = Math.min(r.length - 1, Math.max(k.startRow, k.endRow)), q = Math.max(0, Math.min(k.startCol, k.endCol)), F = Math.min(
1836
+ s.columns.length - 1,
1837
+ Math.max(k.startCol, k.endCol)
1838
+ ), $ = this.headerHeight + p.fenwick.sum(D) - I, B = p.fenwick.sum(E + 1) - p.fenwick.sum(D);
1839
+ let T = this.rowHeaderWidth;
1840
+ for (let V = 0; V < q; V += 1)
1841
+ T += a[V] ?? 100;
1842
+ let L = 0;
1843
+ for (let V = q; V <= F; V += 1)
1844
+ L += a[V] ?? 100;
1845
+ T -= C, e.fillRect(T, $, L, B), e.strokeRect(T + 0.5, $ + 0.5, L - 1, B - 1);
1846
+ }
1847
+ e.restore();
1848
+ }
1849
+ n && (e.save(), e.fillStyle = "rgba(59,130,246,0.08)", e.fillRect(0, 0, this.canvas.width, this.canvas.height), e.restore());
1850
+ } catch {
1851
+ return;
1852
+ }
1853
+ }
1854
+ destroy() {
1855
+ this.cancelRowHeightMeasurement(), this.heightIndex = null, this.canvas && (this.canvas.removeEventListener("pointermove", this.handlePointerMove), this.canvas.removeEventListener("pointerleave", this.handlePointerLeave), this.canvas.removeEventListener("click", this.handleClick)), this.cursorTimer && (window.clearTimeout(this.cursorTimer), this.cursorTimer = null), this.pendingCursorPoint = null, _(this.canvas), _(this.spacer), _(this.overlayLayer), _(this.tooltip), this.canvas = null, this.spacer = null, this.overlayLayer = null, this.tooltip = null, this.tooltipTarget = null, this.tooltipMessage = null, this.root = null;
1856
+ }
1857
+ ensureHeightIndex(t, e, i) {
1858
+ const n = this.heightIndex;
1859
+ if (n && n.rowsRef === t && n.key === e) return;
1860
+ const s = new Array(t.length), o = /* @__PURE__ */ new Map();
1861
+ for (let r = 0; r < t.length; r += 1) {
1862
+ const a = t[r];
1863
+ if (!a) continue;
1864
+ o.set(a.id, r);
1865
+ let l = this.rowHeight;
1866
+ if (i && e) {
1867
+ const h = this.dataModel.getRowVersion(a.id), u = this.rowHeightMeasuredVersion.get(a.id), f = this.dataModel.getRowHeight(a.id);
1868
+ u === h && typeof f == "number" && (l = f);
1869
+ }
1870
+ s[r] = l;
1871
+ }
1872
+ this.heightIndex = {
1873
+ key: e,
1874
+ rowsRef: t,
1875
+ idToIndex: o,
1876
+ heights: s,
1877
+ fenwick: pt.from(s)
1878
+ };
1879
+ }
1880
+ applyRowHeightUpdates(t) {
1881
+ const e = this.heightIndex;
1882
+ if (e)
1883
+ for (const [i, n] of Object.entries(t)) {
1884
+ const s = e.idToIndex.get(i);
1885
+ if (s === void 0) continue;
1886
+ const o = e.heights[s] ?? this.rowHeight;
1887
+ o !== n && (e.heights[s] = n, e.fenwick.add(s, n - o));
1888
+ }
1889
+ }
1890
+ getRowHeightCacheKey(t, e, i) {
1891
+ const n = t.columns.map((o) => e.wrapText?.[o.key] ?? o.wrapText ? "1" : "0").join(""), s = i.map((o) => String(o ?? 0)).join(",");
1892
+ return `${n}|${s}`;
1893
+ }
1894
+ cancelRowHeightMeasurement() {
1895
+ this.rowHeightMeasureRaf !== null && (cancelAnimationFrame(this.rowHeightMeasureRaf), this.rowHeightMeasureRaf = null), this.rowHeightMeasureTask = null;
1896
+ }
1897
+ scheduleRowHeightMeasurement() {
1898
+ this.rowHeightMeasureRaf === null && (this.rowHeightMeasureRaf = requestAnimationFrame(() => {
1899
+ this.rowHeightMeasureRaf = null, this.runRowHeightMeasurement();
1900
+ }));
1901
+ }
1902
+ runRowHeightMeasurement() {
1903
+ if (!this.canvas) return;
1904
+ const t = this.canvas.getContext("2d");
1905
+ if (!t) return;
1906
+ t.font = "14px sans-serif";
1907
+ const e = this.dataModel.getSchema(), i = this.dataModel.getView(), n = W(e, i);
1908
+ if (!e.columns.some((f) => i.wrapText?.[f.key] ?? f.wrapText)) return;
1909
+ const o = this.getRowHeightCacheKey(e, i, n);
1910
+ this.rowHeightCacheKey !== o && (this.rowHeightCacheKey = o, this.rowHeightMeasuredVersion.clear(), this.rowHeightMeasureTask = null);
1911
+ const r = this.rowHeightMeasureTask ?? { key: o, nextIndex: 0 };
1912
+ if (r.key !== o) return;
1913
+ const a = this.dataModel.listRows(), l = {};
1914
+ let h = 0;
1915
+ const u = performance.now();
1916
+ for (; r.nextIndex < a.length && h < O.ROW_HEIGHT_MEASURE_CHUNK && !(performance.now() - u > O.ROW_HEIGHT_MEASURE_TIME_BUDGET_MS); ) {
1917
+ const f = a[r.nextIndex];
1918
+ if (r.nextIndex += 1, !f) continue;
1919
+ const p = this.dataModel.getRowVersion(f.id);
1920
+ if (this.rowHeightMeasuredVersion.get(f.id) === p) continue;
1921
+ const d = this.measureRowHeight(t, f, e, n);
1922
+ l[f.id] = d, this.rowHeightMeasuredVersion.set(f.id, p), h += 1;
1923
+ }
1924
+ this.rowHeightMeasureTask = r.nextIndex < a.length ? r : null, this.ensureHeightIndex(a, this.rowHeightCacheKey, !0), this.applyRowHeightUpdates(l), this.dataModel.setRowHeightsBulk(l), this.rowHeightMeasureTask && this.scheduleRowHeightMeasurement();
1925
+ }
1926
+ getCellElements() {
1927
+ return null;
1928
+ }
1929
+ hitTest(t) {
1930
+ if (!this.root || !this.canvas) return null;
1931
+ const e = this.canvas.getBoundingClientRect(), i = t.clientX - e.left, n = t.clientY - e.top, s = i + this.root.scrollLeft, o = this.dataModel.getSchema(), r = this.dataModel.getView(), a = this.dataModel.listRows(), l = this.headerHeight, h = W(o, r), u = o.columns.some((x) => r.wrapText?.[x.key] ?? x.wrapText), f = u ? this.getRowHeightCacheKey(o, r, h) : null;
1932
+ this.ensureHeightIndex(a, f, u);
1933
+ const p = this.heightIndex;
1934
+ if (!p) return null;
1935
+ const d = p.fenwick.total(), m = Math.max(
1936
+ 0,
1937
+ Math.min(
1938
+ this.root.scrollTop - this.headerHeight,
1939
+ Math.max(0, d - this.rowHeight)
1940
+ )
1941
+ );
1942
+ if (n < l && i < this.rowHeaderWidth)
1943
+ return {
1944
+ rowId: "__all__",
1945
+ colKey: "__all__",
1946
+ rect: new DOMRect(e.left, e.top, this.rowHeaderWidth, l)
1947
+ };
1948
+ if (n < l) {
1949
+ let x = this.rowHeaderWidth, M = -1;
1950
+ for (let A = 0; A < h.length; A += 1) {
1951
+ const K = h[A] ?? 100;
1952
+ if (s >= x && s <= x + K) {
1953
+ M = A;
1954
+ break;
1955
+ }
1956
+ x += K;
1957
+ }
1958
+ if (M >= 0) {
1959
+ const A = o.columns[M], K = new DOMRect(
1960
+ e.left + x - this.root.scrollLeft,
1961
+ e.top,
1962
+ h[M] ?? 100,
1963
+ l
1964
+ );
1965
+ return { rowId: "__header__", colKey: A.key, rect: K };
1966
+ }
1967
+ return null;
1968
+ }
1969
+ if (i < this.rowHeaderWidth) {
1970
+ const x = n - l + m, M = Math.max(
1971
+ 0,
1972
+ Math.min(a.length - 1, p.fenwick.lowerBound(x + 1))
1973
+ ), A = p.fenwick.sum(M);
1974
+ if (M < 0 || M >= a.length) return null;
1975
+ const K = a[M], H = e.top + l + A - m, P = new DOMRect(
1976
+ e.left,
1977
+ H,
1978
+ this.rowHeaderWidth,
1979
+ p.heights[M] ?? this.rowHeight
1980
+ );
1981
+ return { rowId: K.id, colKey: null, rect: P };
1982
+ }
1983
+ const g = n - l + m, y = Math.max(0, Math.min(a.length - 1, p.fenwick.lowerBound(g + 1))), w = p.fenwick.sum(y);
1984
+ if (y < 0 || y >= a.length) return null;
1985
+ let S = this.rowHeaderWidth, b = -1;
1986
+ for (let x = 0; x < h.length; x += 1) {
1987
+ const M = h[x] ?? 100;
1988
+ if (s >= S && s <= S + M) {
1989
+ b = x;
1990
+ break;
1991
+ }
1992
+ S += M;
1993
+ }
1994
+ if (b === -1) return null;
1995
+ const C = a[y], v = o.columns[b], R = w, I = new DOMRect(
1996
+ e.left + S - this.root.scrollLeft,
1997
+ e.top + l + R - this.root.scrollTop,
1998
+ h[b] ?? 100,
1999
+ p.heights[y] ?? this.rowHeight
2000
+ );
2001
+ return { rowId: C.id, colKey: v.key, rect: I };
2002
+ }
2003
+ isPointInSelection(t, e) {
2004
+ if (!this.selection.length) return !1;
2005
+ const i = this.dataModel.getSchema(), n = this.dataModel.getRowIndex(t), s = i.columns.findIndex((o) => o.key === e);
2006
+ if (n < 0 || s < 0) return !1;
2007
+ for (const o of this.selection) {
2008
+ if (o.kind !== "cells") continue;
2009
+ const r = Math.min(o.startRow, o.endRow), a = Math.max(o.startRow, o.endRow), l = Math.min(o.startCol, o.endCol), h = Math.max(o.startCol, o.endCol);
2010
+ if (n >= r && n <= a && s >= l && s <= h)
2011
+ return !0;
2012
+ }
2013
+ return !1;
2014
+ }
2015
+ positionTooltipAtRect(t) {
2016
+ if (!this.tooltip || !this.root) return;
2017
+ const e = 8, i = this.root.getBoundingClientRect(), n = this.root.clientWidth, s = this.root.clientHeight;
2018
+ let o = t.right - i.left + e, r = t.top - i.top + e, a = "right";
2019
+ const l = this.tooltip.getBoundingClientRect();
2020
+ o + l.width > n && (o = Math.max(0, t.left - i.left - l.width - e), a = "left"), r + l.height > s && (r = Math.max(0, s - l.height - e)), this.tooltip.style.left = `${o}px`, this.tooltip.style.top = `${r}px`, this.tooltip.dataset.side = a;
2021
+ }
2022
+ refreshTooltipPosition() {
2023
+ if (!this.tooltip || this.tooltip.dataset.visible !== "1" || !this.tooltipTarget || !this.tooltipMessage) return;
2024
+ const t = this.getCellRect(this.tooltipTarget.rowId, this.tooltipTarget.colKey);
2025
+ if (!t) {
2026
+ this.tooltip.dataset.visible = "0", this.tooltipTarget = null, this.tooltipMessage = null;
2027
+ return;
2028
+ }
2029
+ this.tooltip.textContent = this.tooltipMessage, this.positionTooltipAtRect(t);
2030
+ }
2031
+ getCellRect(t, e) {
2032
+ if (!this.root || !this.canvas) return null;
2033
+ const i = this.canvas.getBoundingClientRect(), n = this.dataModel.getSchema(), s = this.dataModel.getView(), o = this.dataModel.listRows(), r = this.dataModel.getRowIndex(t), a = n.columns.findIndex((m) => m.key === e);
2034
+ if (r < 0 || a < 0) return null;
2035
+ const l = W(n, s), h = n.columns.some((m) => s.wrapText?.[m.key] ?? m.wrapText), u = h ? this.getRowHeightCacheKey(n, s, l) : null;
2036
+ this.ensureHeightIndex(o, u, h);
2037
+ const f = this.heightIndex;
2038
+ if (!f) return null;
2039
+ const p = f.fenwick.sum(r);
2040
+ let d = this.rowHeaderWidth;
2041
+ for (let m = 0; m < a; m += 1)
2042
+ d += l[m] ?? 100;
2043
+ return new DOMRect(
2044
+ i.left + d - this.root.scrollLeft,
2045
+ i.top + this.headerHeight + p - this.root.scrollTop,
2046
+ l[a] ?? 100,
2047
+ f.heights[r] ?? this.rowHeight
2048
+ );
2049
+ }
2050
+ updateCanvasCursor(t, e) {
2051
+ if (!this.root || !this.canvas) return;
2052
+ if (this.root.dataset.extableFillDragging === "1") {
2053
+ this.canvas.style.cursor = "crosshair";
2054
+ return;
2055
+ }
2056
+ {
2057
+ const l = this.canvas.getBoundingClientRect(), h = t - l.left, u = e - l.top, f = h + this.root.scrollLeft, p = u, d = this.hoverHeaderColKey, m = this.hoverHeaderIcon;
2058
+ let g = null, y = !1;
2059
+ if (p >= 0 && p < this.headerHeight && f >= this.rowHeaderWidth) {
2060
+ const S = this.dataModel.getSchema(), b = this.dataModel.getView(), C = W(S, b);
2061
+ let v = this.rowHeaderWidth, R = -1;
2062
+ for (let x = 0; x < C.length; x += 1) {
2063
+ const M = C[x] ?? 100;
2064
+ if (f >= v && f <= v + M) {
2065
+ R = x;
2066
+ break;
2067
+ }
2068
+ v += M;
2069
+ }
2070
+ const I = R >= 0 ? S.columns[R] : null;
2071
+ if (I) {
2072
+ g = I.key;
2073
+ const x = C[R] ?? 100, M = 18, K = v + x - M - 4, H = Math.floor((this.headerHeight - M) / 2);
2074
+ y = f >= K && f <= K + M && p >= H && p <= H + M;
2075
+ }
2076
+ }
2077
+ const w = String(d ?? "") !== String(g ?? "") || !!m != !!y;
2078
+ if (this.hoverHeaderColKey = g, this.hoverHeaderIcon = y, g !== null) {
2079
+ this.canvas.style.cursor = y ? "pointer" : "default", this.tooltip && (this.tooltip.dataset.visible = "0"), this.tooltipTarget = null, this.tooltipMessage = null, w && this.render();
2080
+ return;
2081
+ }
2082
+ w && this.render();
2083
+ }
2084
+ let i = "cell";
2085
+ const n = this.hitTest(new MouseEvent("mousemove", { clientX: t, clientY: e }));
2086
+ if (!n) {
2087
+ this.canvas.style.cursor = i, this.tooltip && (this.tooltip.dataset.visible = "0"), this.tooltipTarget = null, this.tooltipMessage = null;
2088
+ return;
2089
+ }
2090
+ if (n.colKey === "__all__" || n.colKey === null) {
2091
+ this.canvas.style.cursor = "default", this.tooltip && (this.tooltip.dataset.visible = "0"), this.tooltipTarget = null, this.tooltipMessage = null;
2092
+ return;
2093
+ }
2094
+ if (this.getEditMode() === "readonly") {
2095
+ this.canvas.style.cursor = "cell";
2096
+ return;
2097
+ }
2098
+ const s = this.dataModel.getCellMarker(n.rowId, n.colKey);
2099
+ if (this.tooltip && s) {
2100
+ const l = this.tooltipTarget && this.tooltipTarget.rowId === n.rowId && this.tooltipTarget.colKey === n.colKey, h = this.tooltipMessage === s.message;
2101
+ if (!l || !h || this.tooltip.dataset.visible !== "1") {
2102
+ this.tooltipTarget = { rowId: n.rowId, colKey: n.colKey }, this.tooltipMessage = s.message, this.tooltip.textContent = s.message;
2103
+ const u = this.getCellRect(n.rowId, n.colKey);
2104
+ u ? (this.positionTooltipAtRect(u), this.tooltip.dataset.visible = "1") : this.tooltip.dataset.visible = "0";
2105
+ }
2106
+ } else this.tooltip && (this.tooltip.dataset.visible = "0", this.tooltipTarget = null, this.tooltipMessage = null);
2107
+ if (!this.isPointInSelection(n.rowId, n.colKey)) {
2108
+ this.canvas.style.cursor = "cell";
2109
+ return;
2110
+ }
2111
+ if (!(this.activeRowId === n.rowId && this.activeColKey !== null && this.activeColKey === n.colKey)) {
2112
+ this.canvas.style.cursor = "cell";
2113
+ return;
2114
+ }
2115
+ if (this.activeRowId && this.activeColKey !== null && this.activeRowId !== "__all__" && this.activeColKey !== "__all__" && ot(
2116
+ this.dataModel,
2117
+ this.selection,
2118
+ this.activeRowId,
2119
+ this.activeColKey,
2120
+ this.getEditMode()
2121
+ )) {
2122
+ const l = this.getCellRect(this.activeRowId, this.activeColKey);
2123
+ if (l) {
2124
+ const h = Bt(l, Pt);
2125
+ if ($t(t, e, h)) {
2126
+ this.canvas.style.cursor = "crosshair";
2127
+ return;
2128
+ }
2129
+ }
2130
+ }
2131
+ const r = this.dataModel.getSchema().columns.find((l) => l.key === n.colKey);
2132
+ this.dataModel.isReadonly(n.rowId, n.colKey) || r?.type === "boolean" ? i = "default" : i = "text", this.canvas.style.cursor = i;
2133
+ }
2134
+ measureRowHeight(t, e, i, n) {
2135
+ let s = this.rowHeight;
2136
+ const o = this.dataModel.getView();
2137
+ for (let r = 0; r < i.columns.length; r += 1) {
2138
+ const a = i.columns[r];
2139
+ if (!a || !(o.wrapText?.[a.key] ?? a.wrapText)) continue;
2140
+ const h = (n[r] ?? 100) - this.padding, u = this.dataModel.resolveCellValue(e.id, a), f = this.dataModel.resolveConditionalStyle(e.id, a), d = u.textOverride ?? (f.forceErrorText ? "#ERROR" : void 0) ? "#ERROR" : this.formatValue(u.value, a).text, g = this.wrapLines(t, d, h).length * this.lineHeight + this.padding;
2141
+ s = Math.max(s, g);
2142
+ }
2143
+ return s;
2144
+ }
2145
+ wrapLines(t, e, i) {
2146
+ const n = `${t.font}|${i}|${e}`, s = this.textMeasureCache.get(n);
2147
+ if (s)
2148
+ return this.textMeasureCache.delete(n), this.textMeasureCache.set(n, s), s.lines;
2149
+ const o = e.split(`
2150
+ `), r = [];
2151
+ for (const a of o) {
2152
+ let l = a;
2153
+ for (; t.measureText(l).width > i && l.length > 1; ) {
2154
+ let h = l.length;
2155
+ for (; h > 1 && t.measureText(l.slice(0, h)).width > i; )
2156
+ h -= 1;
2157
+ r.push(l.slice(0, h)), l = l.slice(h);
2158
+ }
2159
+ r.push(l);
2160
+ }
2161
+ for (this.textMeasureCache.set(n, { lines: r }); this.textMeasureCache.size > O.TEXT_MEASURE_CACHE_MAX; ) {
2162
+ const a = this.textMeasureCache.keys().next().value;
2163
+ if (!a) break;
2164
+ this.textMeasureCache.delete(a);
2165
+ }
2166
+ return r;
2167
+ }
2168
+ drawCellText(t, e, i, n, s, o, r, a = "left", l = !1, h = !1, u) {
2169
+ t.save(), t.beginPath(), t.rect(i - 4, n - 4, s + 8, o + 8), t.clip();
2170
+ const f = t.font;
2171
+ l ? t.font = "28px sans-serif" : h && (t.font = "14px sans-serif");
2172
+ const p = (d, m) => {
2173
+ const g = n + this.lineHeight * m;
2174
+ let y = i, w = i;
2175
+ if (a === "right")
2176
+ t.textAlign = "right", w = i + s, y = w - t.measureText(d).width, t.fillText(d, w, g);
2177
+ else if (a === "center") {
2178
+ t.textAlign = "center";
2179
+ const S = i + s / 2, b = t.measureText(d).width;
2180
+ y = S - b / 2, w = S + b / 2, t.fillText(d, S, g);
2181
+ } else
2182
+ t.textAlign = "left", y = i, w = i + t.measureText(d).width, t.fillText(d, i, g);
2183
+ if (u?.underline || u?.strike) {
2184
+ const S = t.strokeStyle, b = t.lineWidth;
2185
+ if (t.strokeStyle = t.fillStyle, t.lineWidth = 1, t.beginPath(), u.underline) {
2186
+ const C = g + 2;
2187
+ t.moveTo(y, C), t.lineTo(w, C);
2188
+ }
2189
+ if (u.strike) {
2190
+ const C = g - Math.floor(this.lineHeight / 2) + 2;
2191
+ t.moveTo(y, C), t.lineTo(w, C);
2192
+ }
2193
+ t.stroke(), t.strokeStyle = S, t.lineWidth = b;
2194
+ }
2195
+ };
2196
+ if (r) {
2197
+ const d = this.wrapLines(t, e, s);
2198
+ for (let m = 0; m < d.length; m += 1)
2199
+ p(d[m], m + 1);
2200
+ } else {
2201
+ let d = e;
2202
+ for (; t.measureText(d).width > s && d.length > 1; )
2203
+ d = `${d.slice(0, -2)}…`;
2204
+ p(d, 1);
2205
+ }
2206
+ t.textAlign = "left", t.font = f, t.restore();
2207
+ }
2208
+ formatValue(t, e) {
2209
+ if (t == null) return { text: "" };
2210
+ if (e.type === "boolean")
2211
+ return e.booleanDisplay === "checkbox" || !e.booleanDisplay ? { text: t ? "☑" : "☐" } : Array.isArray(e.booleanDisplay) && e.booleanDisplay.length >= 2 ? { text: String(t ? e.booleanDisplay[0] : e.booleanDisplay[1]) } : { text: t ? String(e.booleanDisplay) : "" };
2212
+ if (e.type === "number" && typeof t == "number") {
2213
+ const i = t, n = {};
2214
+ e.number?.scale !== void 0 && (n.minimumFractionDigits = e.number.scale, n.maximumFractionDigits = e.number.scale), n.useGrouping = !!e.number?.thousandSeparator;
2215
+ const s = this.valueFormatCache.getNumberFormatter(n).format(i), o = e.number?.negativeRed && i < 0 ? "#b91c1c" : void 0;
2216
+ return { text: s, color: o };
2217
+ }
2218
+ if ((e.type === "date" || e.type === "time" || e.type === "datetime") && (t instanceof Date || typeof t == "string")) {
2219
+ const i = e.type === "date" ? e.dateFormat ?? "yyyy-MM-dd" : e.type === "time" ? e.timeFormat ?? "HH:mm" : e.dateTimeFormat ?? "yyyy-MM-dd'T'HH:mm:ss'Z'";
2220
+ let n = null;
2221
+ return t instanceof Date ? n = t : n = this.valueFormatCache.parseIsoDate(t), n ? { text: At(n, i) } : { text: String(t) };
2222
+ }
2223
+ return { text: String(t) };
2224
+ }
2225
+ };
2226
+ O.MAX_CANVAS_DIM_PX = 8192, O.ROW_HEIGHT_MEASURE_CHUNK = 500, O.ROW_HEIGHT_MEASURE_TIME_BUDGET_MS = 8, O.TEXT_MEASURE_CACHE_MAX = 2e3;
2227
+ let rt = O;
2228
+ class Se {
2229
+ constructor(t, e, i, n, s, o, r, a, l, h, u, f, p) {
2230
+ this.dataModel = r, this.onActiveChange = l, this.onSelectionChange = u, this.onUndo = f, this.onRedo = p, this.handleDocumentContextMenu = null, this.selectionRanges = [], this.inputEl = null, this.floatingInputWrapper = null, this.selectionInput = null, this.copyToastEl = null, this.copyToastTimer = null, this.selectionMode = !0, this.lastBooleanCell = null, this.selectionAnchor = null, this.dragging = !1, this.dragStart = null, this.pointerDownClient = null, this.dragMoved = !1, this.dragSelectionChanged = !1, this.suppressNextClick = !1, this.fillDragging = !1, this.fillSource = null, this.fillEndRowIndex = null, this.rootCursorBackup = null, this.lastPointerClient = null, this.autoScrollRaf = null, this.autoScrollActive = !1, this.activeCell = null, this.activeHost = null, this.activeHostOriginalText = null, this.composing = !1, this.lastCompositionEnd = 0, this.handleSelectionBlur = () => this.teardownSelectionInput(), this.handlePointerDown = (d) => {
2231
+ if (d.button !== 0) return;
2232
+ const m = d.target;
2233
+ if (m?.closest('button[data-extable-fs-open="1"]') || m?.closest(".extable-filter-sort-trigger") || this.inputEl && d.target && this.inputEl.contains(d.target)) return;
2234
+ if (this.inputEl && this.activeCell && this.activeCell.colKey !== null) {
2235
+ const { rowId: R, colKey: I } = this.activeCell, x = this.readActiveValue();
2236
+ this.commitEdit(R, I, x), this.onMove(R), this.teardownInput(!1);
2237
+ }
2238
+ if (this.fillDragging) return;
2239
+ const g = this.hitTest(d);
2240
+ if (!g || g.rowId === "__all__" && g.colKey === "__all__" || g.colKey === "__all__") return;
2241
+ const y = Vt(this.dataModel, this.selectionRanges);
2242
+ if (y && this.activeCell) {
2243
+ const R = this.dataModel.getSchema(), I = this.dataModel.listRows(), x = y.endRowIndex, M = y.colIndex, A = I[x], K = R.columns[M];
2244
+ if (A && K) {
2245
+ const H = this.findHtmlCellElement(A.id, K.key)?.getBoundingClientRect() ?? this.computeCanvasCellRect(A.id, K.key);
2246
+ if (H && ot(
2247
+ this.dataModel,
2248
+ this.selectionRanges,
2249
+ this.activeCell.rowId,
2250
+ this.activeCell.colKey,
2251
+ this.editMode
2252
+ )) {
2253
+ const P = Bt(H, Pt);
2254
+ if ($t(d.clientX, d.clientY, P)) {
2255
+ d.preventDefault(), this.fillDragging = !0, this.fillSource = y, this.fillEndRowIndex = y.endRowIndex, this.root.dataset.extableFillDragging = "1", this.rootCursorBackup === null && (this.rootCursorBackup = this.root.style.cursor || ""), this.root.style.cursor = "crosshair", this.lastPointerClient = { x: d.clientX, y: d.clientY }, this.dragging = !1, this.dragStart = null, this.suppressNextClick = !0;
2256
+ try {
2257
+ d.target?.setPointerCapture?.(d.pointerId);
2258
+ } catch {
2259
+ }
2260
+ this.selectionRanges = [
2261
+ {
2262
+ kind: "cells",
2263
+ startRow: y.startRowIndex,
2264
+ endRow: y.endRowIndex,
2265
+ startCol: y.colIndex,
2266
+ endCol: y.colIndex
2267
+ }
2268
+ ], this.onSelectionChange(this.selectionRanges), this.startAutoScroll();
2269
+ return;
2270
+ }
2271
+ }
2272
+ }
2273
+ }
2274
+ const w = this.dataModel.getSchema(), S = this.dataModel.listRows(), b = g.rowId === "__header__" ? 0 : this.dataModel.getRowIndex(g.rowId), C = w.columns.findIndex((R) => String(R.key) === String(g.colKey));
2275
+ if (C < 0) return;
2276
+ if (g.rowId === "__header__") {
2277
+ if (!S.length) return;
2278
+ const R = {
2279
+ kind: "cells",
2280
+ startRow: 0,
2281
+ endRow: S.length - 1,
2282
+ startCol: C,
2283
+ endCol: C
2284
+ };
2285
+ this.selectionRanges = [R], this.onSelectionChange(this.selectionRanges), this.dragging = !1, this.selectionMode = !1, this.dragStart = null, this.selectionAnchor = null, this.suppressNextClick = !0;
2286
+ const I = S[0], x = w.columns[C];
2287
+ I && x && (this.activeCell = { rowId: I.id, colKey: x.key }, this.onActiveChange(I.id, x.key));
2288
+ return;
2289
+ }
2290
+ if (b < 0) return;
2291
+ const v = g.colKey === null ? "rows" : "cells";
2292
+ this.dragging = !0, this.pointerDownClient = { x: d.clientX, y: d.clientY }, this.dragMoved = !1, this.dragSelectionChanged = !1, this.dragStart = { rowIndex: b, colIndex: C, kind: v }, this.suppressNextClick = !1, this.selectionMode = !0, this.selectionAnchor = null, this.lastPointerClient = { x: d.clientX, y: d.clientY };
2293
+ try {
2294
+ d.target?.setPointerCapture?.(d.pointerId);
2295
+ } catch {
2296
+ }
2297
+ this.startAutoScroll();
2298
+ }, this.handlePointerMove = (d) => {
2299
+ if (this.lastPointerClient = { x: d.clientX, y: d.clientY }, this.fillDragging && this.fillSource) {
2300
+ this.updateFillDragFromClientPoint(d.clientX, d.clientY);
2301
+ return;
2302
+ }
2303
+ if (!(!this.dragging || !this.dragStart)) {
2304
+ if (!this.dragMoved && this.pointerDownClient) {
2305
+ const m = d.clientX - this.pointerDownClient.x, g = d.clientY - this.pointerDownClient.y;
2306
+ if (m * m + g * g < 9) return;
2307
+ this.dragMoved = !0;
2308
+ }
2309
+ this.updateDragFromClientPoint(d.clientX, d.clientY);
2310
+ }
2311
+ }, this.handlePointerUp = (d) => {
2312
+ if (this.fillDragging && this.fillSource) {
2313
+ const g = this.fillSource, y = this.fillEndRowIndex ?? g.endRowIndex;
2314
+ this.fillDragging = !1, this.fillSource = null, this.fillEndRowIndex = null, this.root.dataset.extableFillDragging = "", this.rootCursorBackup !== null && (this.root.style.cursor = this.rootCursorBackup, this.rootCursorBackup = null), this.stopAutoScroll();
2315
+ try {
2316
+ d.target?.releasePointerCapture?.(d.pointerId);
2317
+ } catch {
2318
+ }
2319
+ this.commitFill(g, y), this.suppressNextClick = !0;
2320
+ return;
2321
+ }
2322
+ if (!this.dragging) return;
2323
+ this.dragging = !1, this.dragStart = null;
2324
+ const m = this.dragSelectionChanged;
2325
+ this.pointerDownClient = null, this.dragMoved = !1, this.dragSelectionChanged = !1, this.stopAutoScroll();
2326
+ try {
2327
+ d.target?.releasePointerCapture?.(d.pointerId);
2328
+ } catch {
2329
+ }
2330
+ if (m && (this.suppressNextClick = !0), this.suppressNextClick && this.activeCell && this.activeCell.colKey !== null) {
2331
+ const g = this.dataModel.getCell(this.activeCell.rowId, this.activeCell.colKey);
2332
+ this.focusSelectionInput(this.cellToClipboardString(g));
2333
+ }
2334
+ }, this.handleSelectionCopy = (d) => {
2335
+ if (!this.selectionMode) return;
2336
+ const m = this.buildSelectionClipboardPayload();
2337
+ m && (d.preventDefault(), d.clipboardData?.setData("text/plain", m.text), d.clipboardData?.setData("text/tab-separated-values", m.text), d.clipboardData?.setData("text/html", m.html), this.showCopyToast(`Copied ${m.cellCount} cells`, "info"));
2338
+ }, this.handleSelectionCut = (d) => {
2339
+ if (!this.selectionMode) return;
2340
+ const m = this.buildSelectionClipboardPayload();
2341
+ m && (d.preventDefault(), d.clipboardData?.setData("text/plain", m.text), d.clipboardData?.setData("text/tab-separated-values", m.text), d.clipboardData?.setData("text/html", m.html), this.clearSelectionValues());
2342
+ }, this.handleSelectionPaste = (d) => {
2343
+ if (!this.selectionMode) return;
2344
+ d.preventDefault();
2345
+ const m = d.clipboardData?.getData("text/html") ?? "", g = d.clipboardData?.getData("text/tab-separated-values") ?? "", y = d.clipboardData?.getData("text/plain") ?? "", w = this.parseClipboardGrid({ html: m, tsv: g, text: y });
2346
+ w && this.applyClipboardGrid(w);
2347
+ }, this.handleSelectionCompositionStart = () => {
2348
+ this.selectionMode && (this.selectionMode = !1, this.teardownSelectionInput(), this.openEditorAtActiveCell());
2349
+ }, this.handleSelectionKeydown = (d) => {
2350
+ if (!this.selectionMode || !this.activeCell || d.isComposing || this.composing || d.key === "Shift" || d.key === "Control" || d.key === "Alt" || d.key === "Meta" || d.key === "CapsLock" || d.key === "NumLock" || d.key === "ScrollLock")
2351
+ return;
2352
+ const m = typeof navigator < "u" && /mac/i.test(navigator.platform);
2353
+ if (m ? d.metaKey : d.ctrlKey) {
2354
+ const b = d.key.toLowerCase();
2355
+ if (b === "z") {
2356
+ d.preventDefault(), m && d.shiftKey ? this.onRedo() : this.onUndo(), this.selectionAnchor = null;
2357
+ return;
2358
+ }
2359
+ if (b === "y") {
2360
+ d.preventDefault(), this.onRedo(), this.selectionAnchor = null;
2361
+ return;
2362
+ }
2363
+ if (b === "c") {
2364
+ d.preventDefault(), document.execCommand("copy"), this.selectionAnchor = null;
2365
+ return;
2366
+ }
2367
+ if (b === "x") {
2368
+ d.preventDefault(), document.execCommand("cut"), this.selectionAnchor = null;
2369
+ return;
2370
+ }
2371
+ if (b === "v") {
2372
+ this.selectionAnchor = null;
2373
+ return;
2374
+ }
2375
+ }
2376
+ if (d.key === " " && this.activeCell.colKey !== null) {
2377
+ this.findColumn(this.activeCell.colKey)?.type === "boolean" && (d.preventDefault(), this.toggleBoolean(this.activeCell.rowId, this.activeCell.colKey)), this.selectionAnchor = null;
2378
+ return;
2379
+ }
2380
+ const y = d.key === "Tab", w = d.key === "Enter", S = d.key === "ArrowLeft" || d.key === "ArrowRight" || d.key === "ArrowUp" || d.key === "ArrowDown";
2381
+ if (y || w || S) {
2382
+ if (d.preventDefault(), this.teardownSelectionInput(), y) {
2383
+ this.moveActiveCell(0, d.shiftKey ? -1 : 1, !1);
2384
+ return;
2385
+ }
2386
+ if (w) {
2387
+ this.moveActiveCell(d.shiftKey ? -1 : 1, 0, !1);
2388
+ return;
2389
+ }
2390
+ const b = d.shiftKey;
2391
+ d.key === "ArrowLeft" ? this.moveActiveCell(0, -1, b) : d.key === "ArrowRight" ? this.moveActiveCell(0, 1, b) : d.key === "ArrowUp" ? this.moveActiveCell(-1, 0, b) : d.key === "ArrowDown" && this.moveActiveCell(1, 0, b);
2392
+ return;
2393
+ }
2394
+ this.selectionMode = !1, this.selectionAnchor = null, this.teardownSelectionInput(), this.openEditorAtActiveCell();
2395
+ }, this.handleClick = (d) => {
2396
+ if (d.button !== 0 || d.target?.closest('button[data-extable-fs-open="1"]'))
2397
+ return;
2398
+ if (this.suppressNextClick) {
2399
+ this.suppressNextClick = !1;
2400
+ return;
2401
+ }
2402
+ if (this.inputEl && d.target && this.inputEl.contains(d.target))
2403
+ return;
2404
+ if (this.inputEl && this.activeCell && this.activeCell.colKey !== null) {
2405
+ const { rowId: v, colKey: R } = this.activeCell, I = this.readActiveValue();
2406
+ this.commitEdit(v, R, I), this.onMove(v), this.teardownInput(!1);
2407
+ }
2408
+ const g = typeof document.elementFromPoint == "function" ? this.getHitAtClientPoint(d.clientX, d.clientY) : this.hitTest(d);
2409
+ if (!g) return;
2410
+ const y = this.selectionMode && !d.shiftKey && !d.metaKey && !d.ctrlKey && this.activeCell?.rowId === g.rowId && String(this.activeCell?.colKey) === String(g.colKey);
2411
+ if (g.rowId === "__all__" && g.colKey === "__all__") {
2412
+ this.teardownInput(!1), this.activeCell = null, this.onActiveChange("__all__", "__all__"), this.selectionAnchor = null;
2413
+ return;
2414
+ }
2415
+ if (this.onRowSelect(g.rowId), this.applySelectionFromHit(d, g), g.colKey === null || g.colKey === "__all__")
2416
+ return;
2417
+ if (this.isCellReadonly(g.rowId, g.colKey)) {
2418
+ this.selectionMode = !0, this.selectionAnchor = null, this.teardownInput(!1), this.teardownSelectionInput();
2419
+ return;
2420
+ }
2421
+ if (this.findColumn(g.colKey)?.type === "boolean") {
2422
+ const v = this.lastBooleanCell?.rowId === g.rowId && String(this.lastBooleanCell?.colKey) === String(g.colKey);
2423
+ if (this.lastBooleanCell = { rowId: g.rowId, colKey: g.colKey }, this.selectionMode = !0, this.selectionAnchor = null, this.teardownInput(!1), this.teardownSelectionInput(), v) {
2424
+ this.toggleBoolean(g.rowId, g.colKey), this.focusSelectionInput("");
2425
+ return;
2426
+ }
2427
+ this.focusSelectionInput("");
2428
+ return;
2429
+ }
2430
+ this.lastBooleanCell = null, this.selectionAnchor = null, this.teardownInput(!1);
2431
+ const b = this.dataModel.getCell(g.rowId, g.colKey), C = this.cellToClipboardString(b);
2432
+ this.focusSelectionInput(C), y && (this.selectionMode = !1, this.teardownSelectionInput(), this.openEditorAtActiveCell());
2433
+ }, this.handleContextMenu = (d) => {
2434
+ const m = d.target;
2435
+ if (d.ctrlKey) {
2436
+ d.preventDefault();
2437
+ return;
2438
+ }
2439
+ if (!m || !this.root.contains(m)) return;
2440
+ d.preventDefault(), d.stopPropagation();
2441
+ const g = this.hitTest(d), y = g?.rowId ?? null, w = g?.colKey ?? null;
2442
+ this.onContextMenu(y, w, d.clientX, d.clientY);
2443
+ }, this.root = t, this.editMode = e, this.onEdit = i, this.onRowSelect = n, this.onMove = s, this.hitTest = o, this.isCellReadonly = a, this.onContextMenu = h, this.bind();
2444
+ }
2445
+ setEditMode(t) {
2446
+ this.editMode = t;
2447
+ }
2448
+ syncAfterRowsChanged() {
2449
+ const t = this.dataModel.getSchema(), e = this.dataModel.listRows();
2450
+ if (!e.length || !t.columns.length) {
2451
+ this.selectionRanges = [], this.activeCell = null, this.selectionAnchor = null, this.lastBooleanCell = null, this.teardownInput(!0), this.onActiveChange(null, null), this.onSelectionChange(this.selectionRanges), this.updateFillHandleFlag();
2452
+ return;
2453
+ }
2454
+ const i = e.length - 1, n = t.columns.length - 1, s = this.activeCell?.rowId ?? null, o = this.activeCell?.colKey ?? null, r = s ? this.dataModel.getRowIndex(s) : -1, a = o !== null ? t.columns.findIndex((w) => String(w.key) === String(o)) : -1;
2455
+ if (r >= 0 && a >= 0) {
2456
+ let w = !1;
2457
+ if (!this.selectionRanges.length)
2458
+ this.selectionRanges = [
2459
+ {
2460
+ kind: "cells",
2461
+ startRow: r,
2462
+ endRow: r,
2463
+ startCol: a,
2464
+ endCol: a
2465
+ }
2466
+ ], w = !0;
2467
+ else {
2468
+ const S = this.selectionRanges.map((b) => {
2469
+ const C = (R, I) => Math.max(0, Math.min(I, R)), v = {
2470
+ ...b,
2471
+ startRow: C(b.startRow, i),
2472
+ endRow: C(b.endRow, i),
2473
+ startCol: C(b.startCol, n),
2474
+ endCol: C(b.endCol, n)
2475
+ };
2476
+ return (v.startRow !== b.startRow || v.endRow !== b.endRow || v.startCol !== b.startCol || v.endCol !== b.endCol) && (w = !0), v;
2477
+ });
2478
+ this.selectionRanges = S;
2479
+ }
2480
+ w && this.onSelectionChange(this.selectionRanges), this.updateFillHandleFlag();
2481
+ return;
2482
+ }
2483
+ const l = o ?? t.columns[0]?.key ?? "";
2484
+ let h = t.columns.findIndex((w) => String(w.key) === String(l));
2485
+ h < 0 && (h = 0);
2486
+ const u = t.columns[h]?.key ?? "", f = s ? this.dataModel.getBaseRowIndex(s) : 0, p = e[e.length - 1] ?? e[0] ?? null, d = e.find((w) => this.dataModel.getBaseRowIndex(w.id) >= f) ?? p;
2487
+ if (!d) return;
2488
+ const m = d.id, g = this.dataModel.getRowIndex(m);
2489
+ this.selectionAnchor = null, this.lastBooleanCell = null, this.teardownInput(!1), this.selectionRanges = [
2490
+ {
2491
+ kind: "cells",
2492
+ startRow: g,
2493
+ endRow: g,
2494
+ startCol: h,
2495
+ endCol: h
2496
+ }
2497
+ ], this.activeCell = { rowId: m, colKey: u }, this.onActiveChange(m, u), this.onSelectionChange(this.selectionRanges), this.ensureVisibleCell(m, u);
2498
+ const y = this.dataModel.getCell(m, u);
2499
+ this.focusSelectionInput(this.cellToClipboardString(y)), this.updateFillHandleFlag();
2500
+ }
2501
+ navigateToCell(t, e) {
2502
+ const i = this.dataModel.getSchema(), n = this.dataModel.getRowIndex(t), s = i.columns.findIndex((a) => String(a.key) === String(e));
2503
+ if (n < 0 || s < 0) return;
2504
+ this.selectionAnchor = null, this.lastBooleanCell = null, this.teardownInput(!1);
2505
+ const o = {
2506
+ kind: "cells",
2507
+ startRow: n,
2508
+ endRow: n,
2509
+ startCol: s,
2510
+ endCol: s
2511
+ };
2512
+ this.selectionRanges = [o], this.activeCell = { rowId: t, colKey: e }, this.onActiveChange(t, e), this.onSelectionChange(this.selectionRanges), this.ensureVisibleCell(t, e);
2513
+ const r = this.dataModel.getCell(t, e);
2514
+ this.focusSelectionInput(this.cellToClipboardString(r)), this.updateFillHandleFlag();
2515
+ }
2516
+ cancelEditing() {
2517
+ this.teardownInput(!0);
2518
+ }
2519
+ destroy() {
2520
+ this.root.removeEventListener("click", this.handleClick), this.root.removeEventListener("pointerdown", this.handlePointerDown), this.root.removeEventListener("pointermove", this.handlePointerMove), this.root.removeEventListener("pointerup", this.handlePointerUp), this.root.removeEventListener("pointercancel", this.handlePointerUp), this.handleDocumentContextMenu && document.removeEventListener("contextmenu", this.handleDocumentContextMenu, !0), this.teardownInput(!0), this.teardownSelectionInput(), this.teardownCopyToast(), this.stopAutoScroll();
2521
+ }
2522
+ onScroll(t, e) {
2523
+ this.positionCopyToast();
2524
+ }
2525
+ bind() {
2526
+ this.root.addEventListener("click", this.handleClick), this.root.addEventListener("pointerdown", this.handlePointerDown), this.root.addEventListener("pointermove", this.handlePointerMove), this.root.addEventListener("pointerup", this.handlePointerUp), this.root.addEventListener("pointercancel", this.handlePointerUp), this.handleDocumentContextMenu = (t) => this.handleContextMenu(t), document.addEventListener("contextmenu", this.handleDocumentContextMenu, { capture: !0 });
2527
+ }
2528
+ updateFillHandleFlag() {
2529
+ const t = this.activeCell?.colKey ?? null, e = this.activeCell?.rowId ?? null;
2530
+ this.root.dataset.extableFillHandle = ot(
2531
+ this.dataModel,
2532
+ this.selectionRanges,
2533
+ e,
2534
+ t,
2535
+ this.editMode
2536
+ ) ? "1" : "";
2537
+ }
2538
+ findColumn(t) {
2539
+ return this.dataModel.getSchema().columns.find((i) => i.key === t);
2540
+ }
2541
+ pad2(t) {
2542
+ return String(t).padStart(2, "0");
2543
+ }
2544
+ formatLocalDateForInput(t) {
2545
+ return `${t.getFullYear()}-${this.pad2(t.getMonth() + 1)}-${this.pad2(t.getDate())}`;
2546
+ }
2547
+ formatLocalTimeForInput(t) {
2548
+ return `${this.pad2(t.getHours())}:${this.pad2(t.getMinutes())}`;
2549
+ }
2550
+ formatLocalDateTimeForInput(t) {
2551
+ return `${this.formatLocalDateForInput(t)}T${this.formatLocalTimeForInput(t)}`;
2552
+ }
2553
+ normalizeTemporalInitialValue(t, e) {
2554
+ const i = e.trim();
2555
+ if (!i) return "";
2556
+ if (t === "date") {
2557
+ const s = i.match(/^(\d{4}-\d{2}-\d{2})/);
2558
+ if (s) return s[1] ?? "";
2559
+ const o = new Date(i);
2560
+ return Number.isNaN(o.getTime()) ? "" : this.formatLocalDateForInput(o);
2561
+ }
2562
+ if (t === "time") {
2563
+ const s = i.match(/(\d{2}:\d{2})(?::\d{2})?/);
2564
+ if (s) return s[1] ?? "";
2565
+ const o = new Date(i);
2566
+ return Number.isNaN(o.getTime()) ? "" : this.formatLocalTimeForInput(o);
2567
+ }
2568
+ if (/Z$/.test(i) && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/.test(i))
2569
+ return i.replace(/Z$/, "").slice(0, 16);
2570
+ if (/^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}/.test(i) && !/[+-]\d{2}:\d{2}$/.test(i))
2571
+ return i.slice(0, 16);
2572
+ const n = new Date(i);
2573
+ return Number.isNaN(n.getTime()) ? "" : this.formatLocalDateTimeForInput(n);
2574
+ }
2575
+ ensureCopyToast() {
2576
+ if (this.copyToastEl) return this.copyToastEl;
2577
+ window.getComputedStyle(this.root).position === "static" && (this.root.style.position = "relative");
2578
+ const e = document.createElement("div");
2579
+ return e.className = "extable-toast", e.dataset.extableCopyToast = "1", e.setAttribute("popover", "manual"), e.style.position = "absolute", e.style.left = "0", e.style.top = "0", e.style.pointerEvents = "none", e.style.zIndex = "1000", e.style.margin = "0", this.root.appendChild(e), this.copyToastEl = e, e;
2580
+ }
2581
+ teardownCopyToast() {
2582
+ this.copyToastTimer && (window.clearTimeout(this.copyToastTimer), this.copyToastTimer = null), this.copyToastEl && (this.copyToastEl.hidePopover?.(), _(this.copyToastEl), this.copyToastEl = null);
2583
+ }
2584
+ positionCopyToast() {
2585
+ if (!this.copyToastEl) return;
2586
+ const t = this.copyToastEl.offsetWidth || 0, e = this.copyToastEl.offsetHeight || 0, i = 16, n = Math.max(0, this.root.scrollLeft + this.root.clientWidth - t - i), s = Math.max(0, this.root.scrollTop + this.root.clientHeight - e - i);
2587
+ this.copyToastEl.style.left = `${n}px`, this.copyToastEl.style.top = `${s}px`;
2588
+ }
2589
+ showCopyToast(t, e = "info", i = 1200) {
2590
+ const n = this.ensureCopyToast();
2591
+ n.textContent = t, n.dataset.variant = e, n.hidePopover?.(), n.showPopover?.(), this.positionCopyToast(), this.copyToastTimer && (window.clearTimeout(this.copyToastTimer), this.copyToastTimer = null), this.copyToastTimer = window.setTimeout(() => {
2592
+ n.hidePopover?.();
2593
+ }, i);
2594
+ }
2595
+ ensureSelectionInput() {
2596
+ if (this.selectionInput) return this.selectionInput;
2597
+ const t = document.createElement("input");
2598
+ return t.type = "text", t.autocomplete = "off", t.spellcheck = !1, t.style.position = "absolute", t.style.left = "0", t.style.top = "0", t.style.transform = "translate(-10000px, 0)", t.style.width = "1px", t.style.height = "1px", t.style.opacity = "0", t.style.pointerEvents = "none", t.addEventListener("keydown", this.handleSelectionKeydown), t.addEventListener("compositionstart", this.handleSelectionCompositionStart), t.addEventListener("copy", this.handleSelectionCopy), t.addEventListener("cut", this.handleSelectionCut), t.addEventListener("paste", this.handleSelectionPaste), t.addEventListener("blur", this.handleSelectionBlur), this.root.appendChild(t), this.selectionInput = t, t;
2599
+ }
2600
+ focusSelectionInput(t) {
2601
+ const e = this.ensureSelectionInput();
2602
+ e.value = t, this.selectionMode = !0, e.focus({ preventScroll: !0 }), e.select();
2603
+ }
2604
+ openEditorAtActiveCell(t) {
2605
+ if (!this.activeCell) return;
2606
+ const { rowId: e, colKey: i } = this.activeCell;
2607
+ if (i === null) return;
2608
+ const n = this.findHtmlCellElement(e, i);
2609
+ if (n) {
2610
+ this.activateCellElement(n, e, i, t);
2611
+ return;
2612
+ }
2613
+ const s = this.computeCanvasCellRect(e, i);
2614
+ s && this.activateFloating(s, e, i, t);
2615
+ }
2616
+ escapeCssAttrValue(t) {
2617
+ const i = globalThis.CSS?.escape;
2618
+ return typeof i == "function" ? i(t) : t.replace(/["\\]/g, "\\$&");
2619
+ }
2620
+ findHtmlCellElement(t, e) {
2621
+ if (e === null) return null;
2622
+ const i = String(e), n = this.escapeCssAttrValue(t), s = this.escapeCssAttrValue(i);
2623
+ return this.root.querySelector(
2624
+ `tr[data-row-id="${n}"] td[data-col-key="${s}"]`
2625
+ ) ?? null;
2626
+ }
2627
+ computeCanvasCellRect(t, e) {
2628
+ if (e === null) return null;
2629
+ const i = this.computeCanvasCellBoxContent(t, e);
2630
+ if (!i) return null;
2631
+ const n = this.root.getBoundingClientRect();
2632
+ return new DOMRect(
2633
+ n.left + i.left - this.root.scrollLeft,
2634
+ n.top + i.top - this.root.scrollTop,
2635
+ i.width,
2636
+ i.height
2637
+ );
2638
+ }
2639
+ computeCanvasCellBoxContent(t, e) {
2640
+ return this.root.querySelector(
2641
+ 'canvas[data-extable-renderer="canvas"]'
2642
+ ) ? this.getCanvasCellMetrics(t, e) : null;
2643
+ }
2644
+ getCanvasCellMetrics(t, e) {
2645
+ const i = this.dataModel.getSchema(), n = this.dataModel.getView(), s = this.dataModel.listRows(), o = s.findIndex((g) => g.id === t), r = i.columns.findIndex((g) => String(g.key) === String(e));
2646
+ if (o < 0 || r < 0) return null;
2647
+ const a = Ft, l = ht, h = ut, u = W(i, n);
2648
+ let f = l;
2649
+ for (let g = 0; g < r; g += 1) f += u[g] ?? 100;
2650
+ let p = a;
2651
+ for (let g = 0; g < o; g += 1) {
2652
+ const y = s[g];
2653
+ if (!y) return null;
2654
+ const w = this.dataModel.getRowHeight(y.id) ?? h;
2655
+ p += w;
2656
+ }
2657
+ const d = this.dataModel.getRowHeight(t) ?? h, m = u[r] ?? 100;
2658
+ return { left: f, top: p, width: m, height: d, rowIndex: o, colIndex: r };
2659
+ }
2660
+ ensureVisibleCell(t, e) {
2661
+ const i = this.findHtmlCellElement(t, e);
2662
+ if (i) {
2663
+ typeof i.scrollIntoView == "function" && i.scrollIntoView({ block: "nearest", inline: "nearest" });
2664
+ return;
2665
+ }
2666
+ const n = this.getCanvasCellMetrics(t, e);
2667
+ if (!n) return;
2668
+ const s = n.left, o = n.left + n.width, r = n.top, a = n.top + n.height, l = this.root.scrollLeft, h = this.root.scrollLeft + this.root.clientWidth, u = this.root.scrollTop, f = this.root.scrollTop + this.root.clientHeight;
2669
+ s < l ? this.root.scrollLeft = Math.max(0, s) : o > h && (this.root.scrollLeft = Math.max(0, o - this.root.clientWidth)), r < u ? this.root.scrollTop = Math.max(0, r) : a > f && (this.root.scrollTop = Math.max(0, a - this.root.clientHeight));
2670
+ }
2671
+ moveActiveCell(t, e, i = !1) {
2672
+ const n = this.dataModel.getSchema(), s = this.dataModel.listRows();
2673
+ if (!s.length || !n.columns.length) return;
2674
+ const { rowIndex: o, colIndex: r } = this.getActiveIndices();
2675
+ i || (this.selectionAnchor = null);
2676
+ const a = i && this.selectionAnchor ? this.selectionAnchor : i ? { rowIndex: o, colIndex: r } : null;
2677
+ i && !this.selectionAnchor && (this.selectionAnchor = { rowIndex: o, colIndex: r });
2678
+ const l = Math.max(0, Math.min(s.length - 1, o + t)), h = Math.max(0, Math.min(n.columns.length - 1, r + e));
2679
+ {
2680
+ const u = s[l];
2681
+ if (!u) return;
2682
+ const f = n.columns[h];
2683
+ if (!f) return;
2684
+ const p = u.id, d = f.key, m = a ? {
2685
+ kind: "cells",
2686
+ startRow: a.rowIndex,
2687
+ endRow: l,
2688
+ startCol: a.colIndex,
2689
+ endCol: h
2690
+ } : {
2691
+ kind: "cells",
2692
+ startRow: l,
2693
+ endRow: l,
2694
+ startCol: h,
2695
+ endCol: h
2696
+ };
2697
+ this.selectionRanges = [m], this.activeCell = { rowId: p, colKey: d }, this.onActiveChange(p, d), this.onSelectionChange(this.selectionRanges), this.ensureVisibleCell(p, d);
2698
+ const g = this.dataModel.getCell(p, d), y = this.cellToClipboardString(g);
2699
+ this.focusSelectionInput(y), this.updateFillHandleFlag();
2700
+ return;
2701
+ }
2702
+ }
2703
+ getHitAtClientPoint(t, e) {
2704
+ if (typeof document.elementFromPoint != "function") return null;
2705
+ const i = document.elementFromPoint(t, e);
2706
+ if (i && this.root.contains(i)) {
2707
+ const n = i.closest("th.extable-corner");
2708
+ if (n)
2709
+ return {
2710
+ rowId: "__all__",
2711
+ colKey: "__all__",
2712
+ element: n,
2713
+ rect: n.getBoundingClientRect()
2714
+ };
2715
+ const s = i.closest("th.extable-row-header:not(.extable-corner)");
2716
+ if (s) {
2717
+ const l = s.closest("tr[data-row-id]");
2718
+ if (l)
2719
+ return {
2720
+ rowId: l.dataset.rowId ?? "",
2721
+ colKey: null,
2722
+ element: s,
2723
+ rect: s.getBoundingClientRect()
2724
+ };
2725
+ }
2726
+ const o = i.closest("th[data-col-key]");
2727
+ if (o)
2728
+ return {
2729
+ rowId: "__header__",
2730
+ colKey: o.dataset.colKey ?? "",
2731
+ element: o,
2732
+ rect: o.getBoundingClientRect()
2733
+ };
2734
+ const r = i.closest("td[data-col-key]"), a = r?.closest("tr[data-row-id]");
2735
+ if (r && a)
2736
+ return {
2737
+ rowId: a.dataset.rowId ?? "",
2738
+ colKey: r.dataset.colKey ?? "",
2739
+ element: r,
2740
+ rect: r.getBoundingClientRect()
2741
+ };
2742
+ }
2743
+ return this.hitTest({ clientX: t, clientY: e });
2744
+ }
2745
+ updateDragFromClientPoint(t, e) {
2746
+ if (!this.dragging || !this.dragStart) return;
2747
+ const i = this.getHitAtClientPoint(t, e);
2748
+ if (!i || i.colKey === "__all__" || this.dragStart.kind === "cells" && i.colKey === null)
2749
+ return;
2750
+ const n = this.dataModel.getSchema(), s = this.dataModel.listRows(), o = this.dataModel.getRowIndex(i.rowId);
2751
+ if (o < 0) return;
2752
+ const r = this.dragStart.kind === "rows" ? n.columns.length - 1 : n.columns.findIndex((d) => String(d.key) === String(i.colKey));
2753
+ if (r < 0) return;
2754
+ const a = this.dragStart.rowIndex, l = this.dragStart.kind === "rows" ? 0 : this.dragStart.colIndex, h = this.dragStart.kind === "rows" ? n.columns.length - 1 : r;
2755
+ (o !== a || h !== l) && (this.dragSelectionChanged = !0);
2756
+ const u = {
2757
+ kind: this.dragStart.kind,
2758
+ startRow: a,
2759
+ endRow: o,
2760
+ startCol: l,
2761
+ endCol: h
2762
+ };
2763
+ this.selectionRanges = [u];
2764
+ const f = s[o]?.id ?? i.rowId, p = this.dragStart.kind === "rows" ? n.columns[0]?.key ?? i.colKey : n.columns[r]?.key ?? i.colKey;
2765
+ this.activeCell = { rowId: f, colKey: p }, this.onActiveChange(f, p), this.onSelectionChange(this.selectionRanges), this.updateFillHandleFlag();
2766
+ }
2767
+ updateFillDragFromClientPoint(t, e) {
2768
+ if (!this.fillDragging || !this.fillSource) return;
2769
+ const i = this.getHitAtClientPoint(t, e);
2770
+ if (!i || i.colKey === "__all__" || i.colKey === null) return;
2771
+ const n = this.dataModel.getSchema(), s = this.dataModel.listRows(), o = this.dataModel.getRowIndex(i.rowId);
2772
+ if (o < 0 || n.columns.findIndex((l) => String(l.key) === String(i.colKey)) !== this.fillSource.colIndex) return;
2773
+ const a = Math.max(this.fillSource.endRowIndex, o);
2774
+ if (this.fillEndRowIndex !== a) {
2775
+ this.fillEndRowIndex = a;
2776
+ const l = s[a]?.id ?? i.rowId, h = n.columns[this.fillSource.colIndex];
2777
+ if (!h) return;
2778
+ const u = h.key;
2779
+ this.activeCell = { rowId: l, colKey: u }, this.onActiveChange(l, u), this.selectionRanges = [
2780
+ {
2781
+ kind: "cells",
2782
+ startRow: this.fillSource.startRowIndex,
2783
+ endRow: a,
2784
+ startCol: this.fillSource.colIndex,
2785
+ endCol: this.fillSource.colIndex
2786
+ }
2787
+ ], this.onSelectionChange(this.selectionRanges);
2788
+ }
2789
+ this.suppressNextClick = !0;
2790
+ }
2791
+ computeAutoScrollDelta(t, e) {
2792
+ const i = this.root.getBoundingClientRect(), n = 24, s = 18;
2793
+ let o = 0, r = 0;
2794
+ return t < i.left + n ? o = -Math.ceil((i.left + n - t) / n * s) : t > i.right - n && (o = Math.ceil((t - (i.right - n)) / n * s)), e < i.top + n ? r = -Math.ceil((i.top + n - e) / n * s) : e > i.bottom - n && (r = Math.ceil((e - (i.bottom - n)) / n * s)), { dx: o, dy: r };
2795
+ }
2796
+ startAutoScroll() {
2797
+ if (this.autoScrollActive) return;
2798
+ this.autoScrollActive = !0;
2799
+ const t = () => {
2800
+ if (!this.autoScrollActive) return;
2801
+ const i = this.lastPointerClient;
2802
+ if (!i || !this.dragging && !this.fillDragging) {
2803
+ this.stopAutoScroll();
2804
+ return;
2805
+ }
2806
+ const { dx: n, dy: s } = this.computeAutoScrollDelta(i.x, i.y);
2807
+ if (n !== 0 || s !== 0) {
2808
+ const r = this.root.scrollTop, a = this.root.scrollLeft;
2809
+ this.root.scrollTop = Math.max(0, r + s), this.root.scrollLeft = Math.max(0, a + n), this.fillDragging ? this.updateFillDragFromClientPoint(i.x, i.y) : this.updateDragFromClientPoint(i.x, i.y);
2810
+ }
2811
+ const o = window.requestAnimationFrame ?? ((r) => window.setTimeout(() => r(performance.now()), 16));
2812
+ this.autoScrollRaf = o(t);
2813
+ }, e = window.requestAnimationFrame ?? ((i) => window.setTimeout(() => i(performance.now()), 16));
2814
+ this.autoScrollRaf = e(t);
2815
+ }
2816
+ stopAutoScroll() {
2817
+ this.autoScrollActive = !1, this.autoScrollRaf !== null && ((window.cancelAnimationFrame ?? ((e) => window.clearTimeout(e)))(this.autoScrollRaf), this.autoScrollRaf = null);
2818
+ }
2819
+ commitFill(t, e) {
2820
+ const i = this.dataModel.getSchema(), n = this.dataModel.listRows(), s = i.columns[t.colIndex];
2821
+ if (!s || e <= t.endRowIndex) return;
2822
+ const o = ge(this.dataModel, t);
2823
+ if (!o) return;
2824
+ const r = this.editMode === "direct", a = `fill:${Date.now()}:${Math.random().toString(16).slice(2)}`;
2825
+ for (let u = t.endRowIndex + 1; u <= e; u += 1) {
2826
+ const f = n[u];
2827
+ if (!f) break;
2828
+ if (this.isCellReadonly(f.id, s.key)) continue;
2829
+ const p = u - t.endRowIndex, d = o(p), m = {
2830
+ kind: "edit",
2831
+ rowId: f.id,
2832
+ colKey: s.key,
2833
+ next: d,
2834
+ payload: { batchId: a }
2835
+ };
2836
+ this.onEdit(m, r);
2837
+ }
2838
+ const l = n[e];
2839
+ if (!l) return;
2840
+ this.selectionRanges = [
2841
+ {
2842
+ kind: "cells",
2843
+ startRow: t.startRowIndex,
2844
+ endRow: e,
2845
+ startCol: t.colIndex,
2846
+ endCol: t.colIndex
2847
+ }
2848
+ ], this.activeCell = { rowId: l.id, colKey: s.key }, this.onActiveChange(l.id, s.key), this.onSelectionChange(this.selectionRanges), this.updateFillHandleFlag(), this.ensureVisibleCell(l.id, s.key);
2849
+ const h = this.dataModel.getCell(l.id, s.key);
2850
+ this.focusSelectionInput(this.cellToClipboardString(h));
2851
+ }
2852
+ teardownSelectionInput() {
2853
+ const t = this.selectionInput;
2854
+ t && (this.selectionInput = null, t.removeEventListener("keydown", this.handleSelectionKeydown), t.removeEventListener("compositionstart", this.handleSelectionCompositionStart), t.removeEventListener("copy", this.handleSelectionCopy), t.removeEventListener("cut", this.handleSelectionCut), t.removeEventListener("paste", this.handleSelectionPaste), t.removeEventListener("blur", this.handleSelectionBlur), _(t));
2855
+ }
2856
+ async copySelection() {
2857
+ const t = this.buildSelectionClipboardPayload();
2858
+ if (!t) return;
2859
+ const { text: e, html: i, cellCount: n } = t, s = async () => {
2860
+ if (typeof navigator > "u" || !navigator.clipboard) return !1;
2861
+ if (typeof ClipboardItem < "u" && navigator.clipboard.write)
2862
+ try {
2863
+ const a = new ClipboardItem({
2864
+ "text/plain": new Blob([e], { type: "text/plain" }),
2865
+ "text/html": new Blob([i], { type: "text/html" })
2866
+ });
2867
+ return await navigator.clipboard.write([a]), !0;
2868
+ } catch {
2869
+ }
2870
+ if (navigator.clipboard.writeText)
2871
+ try {
2872
+ return await navigator.clipboard.writeText(e), !0;
2873
+ } catch {
2874
+ }
2875
+ return !1;
2876
+ }, o = () => {
2877
+ const a = this.ensureSelectionInput(), l = a.value;
2878
+ a.value = e, a.select(), document.execCommand?.("copy"), a.value = l;
2879
+ };
2880
+ await s() || o(), this.showCopyToast(`Copied ${n} cells`, "info");
2881
+ }
2882
+ async pasteFromClipboard() {
2883
+ if (this.editMode !== "readonly" && this.selectionMode && !(typeof navigator > "u" || !navigator.clipboard?.readText))
2884
+ try {
2885
+ const t = await navigator.clipboard.readText();
2886
+ if (!t) return;
2887
+ const e = this.parseClipboardGrid({ html: "", tsv: t, text: t });
2888
+ if (!e) return;
2889
+ this.applyClipboardGrid(e);
2890
+ } catch {
2891
+ }
2892
+ }
2893
+ getActiveIndices() {
2894
+ const t = this.dataModel.getSchema(), e = this.dataModel.listRows(), i = { rowIndex: 0, colIndex: 0 }, n = this.activeCell;
2895
+ if (!n) return i;
2896
+ const s = e.findIndex((r) => r.id === n.rowId), o = t.columns.findIndex((r) => String(r.key) === String(n.colKey));
2897
+ return { rowIndex: s >= 0 ? s : 0, colIndex: o >= 0 ? o : 0 };
2898
+ }
2899
+ normalizeRange(t) {
2900
+ return {
2901
+ ...t,
2902
+ startRow: Math.min(t.startRow, t.endRow),
2903
+ endRow: Math.max(t.startRow, t.endRow),
2904
+ startCol: Math.min(t.startCol, t.endCol),
2905
+ endCol: Math.max(t.startCol, t.endCol)
2906
+ };
2907
+ }
2908
+ getCopyRange() {
2909
+ const t = this.dataModel.getSchema();
2910
+ if (this.selectionRanges.length > 0) {
2911
+ const s = this.selectionRanges[0];
2912
+ return s ? this.normalizeRange(s) : null;
2913
+ }
2914
+ if (!this.activeCell) return null;
2915
+ const e = this.activeCell;
2916
+ if (!e) return null;
2917
+ const i = this.dataModel.getRowIndex(e.rowId), n = t.columns.findIndex((s) => String(s.key) === String(e.colKey));
2918
+ return i < 0 || n < 0 ? null : { kind: "cells", startRow: i, endRow: i, startCol: n, endCol: n };
2919
+ }
2920
+ cellToClipboardString(t) {
2921
+ if (t == null) return "";
2922
+ if (t instanceof Date) return t.toISOString();
2923
+ if (typeof t == "boolean") return t ? "TRUE" : "FALSE";
2924
+ if (typeof t == "number") return String(t);
2925
+ if (typeof t == "object") {
2926
+ const e = t, i = e.kind;
2927
+ if (i === "enum" && typeof e.value == "string") return e.value;
2928
+ if (i === "tags" && Array.isArray(e.values))
2929
+ return e.values.filter((n) => typeof n == "string").join(", ");
2930
+ }
2931
+ return String(t);
2932
+ }
2933
+ escapeHtml(t) {
2934
+ return t.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
2935
+ }
2936
+ buildSelectionClipboardPayload() {
2937
+ const t = this.dataModel.getSchema(), e = this.dataModel.listRows(), i = this.getCopyRange();
2938
+ if (!i || i.kind !== "cells") return null;
2939
+ const n = [], s = [], o = "border-collapse:collapse;border-spacing:0;", r = "border:1px solid #d0d7de;padding:4px 6px;vertical-align:top;";
2940
+ let a = 0;
2941
+ for (let u = i.startRow; u <= i.endRow; u += 1) {
2942
+ const f = e[u];
2943
+ if (!f) continue;
2944
+ const p = [], d = [];
2945
+ for (let m = i.startCol; m <= i.endCol; m += 1) {
2946
+ const g = t.columns[m];
2947
+ if (!g) continue;
2948
+ const y = this.dataModel.getCell(f.id, g.key), w = this.cellToClipboardString(y);
2949
+ p.push(w), d.push(`<td style="${r}">${this.escapeHtml(w)}</td>`), a += 1;
2950
+ }
2951
+ n.push(p.join(" ")), s.push(`<tr>${d.join("")}</tr>`);
2952
+ }
2953
+ const l = n.join(`\r
2954
+ `), h = `<table style="${o}"><tbody>${s.join("")}</tbody></table>`;
2955
+ return { text: l, html: h, cellCount: a };
2956
+ }
2957
+ clearSelectionValues() {
2958
+ const t = this.dataModel.getSchema(), e = this.dataModel.listRows(), i = this.getCopyRange();
2959
+ if (!i || i.kind !== "cells") return;
2960
+ const n = this.editMode === "direct", s = `cut:${Date.now()}:${Math.random().toString(16).slice(2)}`;
2961
+ for (let o = i.startRow; o <= i.endRow; o += 1) {
2962
+ const r = e[o];
2963
+ if (r)
2964
+ for (let a = i.startCol; a <= i.endCol; a += 1) {
2965
+ const l = t.columns[a];
2966
+ if (!l || this.isCellReadonly(r.id, l.key)) continue;
2967
+ const h = l.type === "boolean" ? !1 : "", u = {
2968
+ kind: "edit",
2969
+ rowId: r.id,
2970
+ colKey: l.key,
2971
+ next: h,
2972
+ payload: { batchId: s }
2973
+ };
2974
+ this.onEdit(u, n);
2975
+ }
2976
+ }
2977
+ }
2978
+ parseClipboardGrid(t) {
2979
+ const e = this.parseHtmlTable(t.html);
2980
+ if (e) return e;
2981
+ const i = t.tsv || t.text;
2982
+ return this.parseTsv(i);
2983
+ }
2984
+ parseTsv(t) {
2985
+ const e = t.replace(/\r\n$/, "").replace(/\n$/, "");
2986
+ return e ? e.split(/\r\n|\n/).map((n) => n.split(" ")) : null;
2987
+ }
2988
+ parseHtmlTable(t) {
2989
+ if (!t) return null;
2990
+ try {
2991
+ const n = new DOMParser().parseFromString(t, "text/html").querySelector("table");
2992
+ if (!n) return null;
2993
+ const s = Array.from(n.querySelectorAll("tr"));
2994
+ if (s.length === 0) return null;
2995
+ const o = [];
2996
+ for (const r of s) {
2997
+ const a = Array.from(r.querySelectorAll("th,td"));
2998
+ if (a.some(
2999
+ (l) => l.rowSpan > 1 || l.colSpan > 1
3000
+ ))
3001
+ return null;
3002
+ o.push(a.map((l) => (l.textContent ?? "").trim()));
3003
+ }
3004
+ return o.length ? o : null;
3005
+ } catch {
3006
+ return null;
3007
+ }
3008
+ }
3009
+ coerceCellValue(t, e) {
3010
+ const i = this.findColumn(e);
3011
+ if (!i) return t;
3012
+ if (t === "") return "";
3013
+ if (i.type === "number") {
3014
+ const n = Number(t);
3015
+ return Number.isFinite(n) ? n : t;
3016
+ }
3017
+ if (i.type === "boolean") {
3018
+ const n = t.trim().toLowerCase();
3019
+ return n === "true" || n === "1" || n === "yes" ? !0 : n === "false" || n === "0" || n === "no" ? !1 : t;
3020
+ }
3021
+ return t;
3022
+ }
3023
+ applyClipboardGrid(t) {
3024
+ const e = this.dataModel.getSchema(), i = this.dataModel.listRows(), { rowIndex: n, colIndex: s } = this.getActiveIndices(), o = this.editMode === "direct", r = `paste:${Date.now()}:${Math.random().toString(16).slice(2)}`;
3025
+ for (let a = 0; a < t.length; a += 1) {
3026
+ const l = i[n + a];
3027
+ if (!l) break;
3028
+ const h = t[a] ?? [];
3029
+ for (let u = 0; u < h.length; u += 1) {
3030
+ const f = e.columns[s + u];
3031
+ if (!f) break;
3032
+ if (this.isCellReadonly(l.id, f.key)) continue;
3033
+ const p = this.coerceCellValue(h[u] ?? "", f.key), d = {
3034
+ kind: "edit",
3035
+ rowId: l.id,
3036
+ colKey: f.key,
3037
+ next: p,
3038
+ payload: { batchId: r }
3039
+ };
3040
+ this.onEdit(d, o);
3041
+ }
3042
+ }
3043
+ }
3044
+ createEditor(t, e) {
3045
+ const i = this.findColumn(t);
3046
+ if (i?.wrapText || e.includes(`
3047
+ `)) {
3048
+ const o = document.createElement("textarea");
3049
+ return o.value = e, o.style.resize = "none", o.style.whiteSpace = "pre-wrap", o.style.overflowWrap = "anywhere", o.style.overflow = "hidden", this.autosize(o), o.addEventListener("input", () => this.autosize(o)), { control: o, value: e };
3050
+ }
3051
+ if (i?.type === "boolean") {
3052
+ const o = document.createElement("input");
3053
+ return o.type = "checkbox", o.checked = e === "true" || e === "1" || e === "on", { control: o, value: e };
3054
+ }
3055
+ if (i?.type === "number") {
3056
+ const o = document.createElement("input");
3057
+ return o.type = "number", o.value = e, { control: o, value: e };
3058
+ }
3059
+ if (i?.type === "date" || i?.type === "time" || i?.type === "datetime") {
3060
+ const o = document.createElement("input");
3061
+ o.type = i.type === "date" ? "date" : i.type === "time" ? "time" : "datetime-local";
3062
+ const r = this.normalizeTemporalInitialValue(i.type, e);
3063
+ return o.value = r, { control: o, value: r };
3064
+ }
3065
+ if (i?.type === "enum" || i?.type === "tags") {
3066
+ const o = i.enum?.allowCustom ?? i.tags?.allowCustom, r = i.enum?.options ?? i.tags?.options ?? [];
3067
+ if (o === !1) {
3068
+ const u = document.createElement("select"), f = document.createElement("option");
3069
+ f.value = "", f.textContent = "", u.appendChild(f);
3070
+ for (const p of r) {
3071
+ const d = document.createElement("option");
3072
+ d.value = p, d.textContent = p, e === p && (d.selected = !0), u.appendChild(d);
3073
+ }
3074
+ return { control: u, value: e };
3075
+ }
3076
+ const a = document.createElement("input");
3077
+ a.type = "text";
3078
+ const l = `extable-datalist-${String(t)}`;
3079
+ a.setAttribute("list", l), a.value = e;
3080
+ let h = document.getElementById(l);
3081
+ if (!h) {
3082
+ h = document.createElement("datalist"), h.id = l;
3083
+ for (const u of r) {
3084
+ const f = document.createElement("option");
3085
+ f.value = u, h.appendChild(f);
3086
+ }
3087
+ this.root.appendChild(h);
3088
+ }
3089
+ return { control: a, value: e, datalistId: l };
3090
+ }
3091
+ const s = document.createElement("input");
3092
+ return s.type = "text", s.value = e, { control: s, value: e };
3093
+ }
3094
+ autosize(t) {
3095
+ const e = window.getComputedStyle(t);
3096
+ let i = Number.parseFloat(e.lineHeight);
3097
+ (!Number.isFinite(i) || i <= 0) && (i = 16), t.rows = 1;
3098
+ const n = Math.ceil(t.scrollHeight / i);
3099
+ t.rows = Math.max(1, n), t.style.minHeight = `${i}px`;
3100
+ }
3101
+ positionFloatingContentBox(t, e) {
3102
+ e.style.left = `${t.left + 2}px`, e.style.top = `${t.top + 2}px`, e.style.width = `${Math.max(8, t.width - 4)}px`, e.style.height = `${Math.max(8, t.height - 4)}px`;
3103
+ }
3104
+ toggleBoolean(t, e) {
3105
+ const i = this.dataModel.getCell(t, e), o = { kind: "edit", rowId: t, colKey: e, next: !(i === !0 || i === "true" || i === "1" || i === 1) }, r = this.editMode === "direct";
3106
+ this.onEdit(o, r), this.onMove(t);
3107
+ }
3108
+ applySelectionFromHit(t, e) {
3109
+ const i = this.dataModel.getSchema(), n = this.dataModel.getRowIndex(e.rowId), s = e.colKey === null, o = s ? i.columns.length > 0 ? 0 : -1 : i.columns.findIndex((h) => String(h.key) === String(e.colKey));
3110
+ if (o < 0) return;
3111
+ const r = s ? {
3112
+ kind: "rows",
3113
+ startRow: n,
3114
+ endRow: n,
3115
+ startCol: 0,
3116
+ endCol: i.columns.length - 1
3117
+ } : {
3118
+ kind: "cells",
3119
+ startRow: n,
3120
+ endRow: n,
3121
+ startCol: Math.max(0, o),
3122
+ endCol: Math.max(0, o)
3123
+ };
3124
+ let a = [];
3125
+ if (t.shiftKey && this.activeCell) {
3126
+ const h = this.activeCell, u = this.dataModel.getRowIndex(h.rowId), f = i.columns.findIndex((d) => String(d.key) === String(h.colKey));
3127
+ a = [s ? {
3128
+ kind: "rows",
3129
+ startRow: u,
3130
+ endRow: n,
3131
+ startCol: 0,
3132
+ endCol: i.columns.length - 1
3133
+ } : {
3134
+ kind: "cells",
3135
+ startRow: u,
3136
+ endRow: n,
3137
+ startCol: Math.max(0, f),
3138
+ endCol: Math.max(0, o)
3139
+ }];
3140
+ } else t.metaKey || t.ctrlKey ? a = [...this.selectionRanges, r] : a = [r];
3141
+ this.selectionRanges = this.mergeRanges(a);
3142
+ const l = r.kind === "rows" ? { rowId: e.rowId, colKey: i.columns[o]?.key ?? null } : { rowId: e.rowId, colKey: e.colKey };
3143
+ this.activeCell = l, this.onActiveChange(l.rowId, l.colKey), this.onSelectionChange(this.selectionRanges), this.updateFillHandleFlag();
3144
+ }
3145
+ mergeRanges(t) {
3146
+ const e = [], i = [...t].sort((n, s) => n.kind !== s.kind ? n.kind === "rows" ? -1 : 1 : n.startRow !== s.startRow ? n.startRow - s.startRow : n.startCol !== s.startCol ? n.startCol - s.startCol : n.endRow !== s.endRow ? n.endRow - s.endRow : n.endCol - s.endCol);
3147
+ for (const n of i) {
3148
+ const s = e.at(-1);
3149
+ if (!s) {
3150
+ e.push({ ...n });
3151
+ continue;
3152
+ }
3153
+ if (s.kind !== n.kind) {
3154
+ e.push({ ...n });
3155
+ continue;
3156
+ }
3157
+ if (n.kind === "rows") {
3158
+ if (s.startCol === n.startCol && s.endCol === n.endCol && s.startRow <= n.endRow + 1 && n.startRow <= s.endRow + 1) {
3159
+ s.startRow = Math.min(s.startRow, n.startRow), s.endRow = Math.max(s.endRow, n.endRow);
3160
+ continue;
3161
+ }
3162
+ } else {
3163
+ const o = s.startCol === n.startCol && s.endCol === n.endCol, r = s.startRow === n.startRow && s.endRow === n.endRow, a = s.startRow <= n.endRow + 1 && n.startRow <= s.endRow + 1, l = s.startCol <= n.endCol + 1 && n.startCol <= s.endCol + 1;
3164
+ if (o && a) {
3165
+ s.startRow = Math.min(s.startRow, n.startRow), s.endRow = Math.max(s.endRow, n.endRow);
3166
+ continue;
3167
+ }
3168
+ if (r && l) {
3169
+ s.startCol = Math.min(s.startCol, n.startCol), s.endCol = Math.max(s.endCol, n.endCol);
3170
+ continue;
3171
+ }
3172
+ }
3173
+ e.push({ ...n });
3174
+ }
3175
+ return e;
3176
+ }
3177
+ activateCellElement(t, e, i, n) {
3178
+ this.teardownInput(), this.activeCell = { rowId: e, colKey: i }, this.activeHost = t, this.activeHostOriginalText = t.textContent ?? "";
3179
+ const s = this.dataModel.getCell(e, i), o = n?.initialValueOverride ?? (s == null ? "" : String(s)), { control: r, value: a } = this.createEditor(i, o), l = r;
3180
+ l.value = a, l.style.width = "calc(100% - 4px)", l.style.boxSizing = "border-box", l.style.margin = "2px", l.style.padding = "4px 6px", l.style.border = "none", l.style.borderRadius = "0", l.style.boxShadow = "none", l.style.background = "#fff", l.style.outline = "none", l.style.fontSize = "14px", l.style.fontFamily = "inherit", l.style.lineHeight = "1.2", l.style.fontWeight = "inherit";
3181
+ const h = this.findColumn(i);
3182
+ if (l.style.textAlign = h?.style?.align ?? (h?.type === "number" ? "right" : "left"), l.addEventListener("keydown", (u) => this.handleKey(u, t)), l.addEventListener("focus", () => {
3183
+ (l instanceof HTMLInputElement || l instanceof HTMLTextAreaElement) && l.select();
3184
+ }), this.bindImmediateCommit(l), t.textContent = "", t.appendChild(l), l.tagName.toLowerCase() === "textarea" && requestAnimationFrame(() => {
3185
+ l instanceof HTMLTextAreaElement && this.autosize(l);
3186
+ }), l.focus({ preventScroll: !0 }), n?.placeCursorAtEnd && (l instanceof HTMLInputElement || l instanceof HTMLTextAreaElement)) {
3187
+ const u = l.value.length;
3188
+ l.setSelectionRange(u, u);
3189
+ }
3190
+ this.inputEl = l;
3191
+ }
3192
+ activateFloating(t, e, i, n) {
3193
+ this.teardownInput(), this.activeCell = { rowId: e, colKey: i };
3194
+ const s = this.computeCanvasCellBoxContent(e, i);
3195
+ if (!s) return;
3196
+ const o = document.createElement("div");
3197
+ this.activeHost = o, this.activeHostOriginalText = null, o.style.position = "absolute", o.dataset.extableFloating = "fixed", o.style.pointerEvents = "auto", o.style.padding = "0", o.style.zIndex = "10";
3198
+ const r = this.dataModel.getCell(e, i), a = n?.initialValueOverride ?? (r == null ? "" : String(r)), { control: l, value: h } = this.createEditor(i, a), u = l;
3199
+ u.value = h, u.style.width = "calc(100% - 4px)", u.style.height = "calc(100% - 4px)", u.style.boxSizing = "border-box", u.style.margin = "2px", u.style.padding = "4px 6px", u.style.border = "none", u.style.borderRadius = "0", u.style.boxShadow = "none", u.style.background = "#fff", u.style.outline = "none", u.style.fontSize = "14px", u.style.fontFamily = "inherit", u.style.lineHeight = "1.2", u.style.fontWeight = "inherit";
3200
+ const f = this.findColumn(i);
3201
+ if (u.style.textAlign = f?.style?.align ?? (f?.type === "number" ? "right" : "left"), u.style.pointerEvents = "auto", u.addEventListener("keydown", (p) => this.handleKey(p, o)), u.addEventListener("focus", () => {
3202
+ (u instanceof HTMLInputElement || u instanceof HTMLTextAreaElement) && u.select();
3203
+ }), this.bindImmediateCommit(u), o.appendChild(u), u.tagName.toLowerCase() === "textarea" && requestAnimationFrame(() => {
3204
+ u instanceof HTMLTextAreaElement && this.autosize(u);
3205
+ }), this.root.appendChild(o), this.positionFloatingContentBox(s, o), u.focus({ preventScroll: !0 }), n?.placeCursorAtEnd && (u instanceof HTMLInputElement || u instanceof HTMLTextAreaElement)) {
3206
+ const p = u.value.length;
3207
+ u.setSelectionRange(p, p);
3208
+ }
3209
+ this.inputEl = u, this.floatingInputWrapper = o;
3210
+ }
3211
+ handleKey(t, e) {
3212
+ if (!this.activeCell || !this.inputEl) return;
3213
+ const i = Date.now();
3214
+ if (t.isComposing || this.composing || i - this.lastCompositionEnd < 24) return;
3215
+ const { rowId: n, colKey: s } = this.activeCell;
3216
+ if (s === null) return;
3217
+ const o = this.inputEl.tagName.toLowerCase() === "textarea", r = t.key === "Enter" && t.altKey, a = (l, h) => {
3218
+ const u = this.readActiveValue();
3219
+ this.commitEdit(n, s, u), this.onMove(n), this.teardownInput(!1), this.moveActiveCell(l, h);
3220
+ };
3221
+ if (t.key === "Tab") {
3222
+ t.preventDefault(), a(0, t.shiftKey ? -1 : 1);
3223
+ return;
3224
+ }
3225
+ if (t.key === "Enter") {
3226
+ if (o && r)
3227
+ return;
3228
+ t.preventDefault(), a(t.shiftKey ? -1 : 1, 0);
3229
+ return;
3230
+ }
3231
+ if (t.key === "Escape") {
3232
+ if (t.preventDefault(), this.cancelEdit(e), this.onMove(), this.activeCell && this.activeCell.colKey !== null) {
3233
+ const l = this.dataModel.getCell(this.activeCell.rowId, this.activeCell.colKey), h = this.cellToClipboardString(l);
3234
+ this.focusSelectionInput(h);
3235
+ }
3236
+ } else t.key === "Backspace" && this.inputEl.value === "" && (t.preventDefault(), this.commitEdit(n, s, ""), this.onMove(n), this.teardownInput(!1), this.moveActiveCell(0, 0));
3237
+ }
3238
+ commitEdit(t, e, i) {
3239
+ if (e === null) return;
3240
+ const n = {
3241
+ kind: "edit",
3242
+ rowId: t,
3243
+ colKey: e,
3244
+ next: i
3245
+ }, s = this.editMode === "direct";
3246
+ this.onEdit(n, s);
3247
+ }
3248
+ readActiveValue() {
3249
+ if (!this.inputEl || !this.activeCell) return this.inputEl?.value ?? "";
3250
+ if (this.inputEl instanceof HTMLInputElement) {
3251
+ if (this.inputEl.type === "checkbox")
3252
+ return this.inputEl.checked;
3253
+ if (this.inputEl.type === "number") {
3254
+ if (this.inputEl.value === "") return "";
3255
+ const t = Number(this.inputEl.value);
3256
+ return Number.isNaN(t) ? "" : t;
3257
+ }
3258
+ return this.inputEl.value;
3259
+ }
3260
+ return this.inputEl instanceof HTMLSelectElement ? this.inputEl.value : this.inputEl.value;
3261
+ }
3262
+ bindImmediateCommit(t) {
3263
+ !this.activeCell || !(t instanceof HTMLInputElement ? t.type === "checkbox" || t.type === "number" || t.type === "date" || t.type === "time" || t.type === "datetime-local" : t instanceof HTMLSelectElement) || t.addEventListener("change", () => {
3264
+ const i = this.activeCell;
3265
+ if (!i) return;
3266
+ const { rowId: n, colKey: s } = i;
3267
+ if (s === null) return;
3268
+ const o = this.readActiveValue();
3269
+ this.commitEdit(n, s, o), this.onMove(n), this.teardownInput(!1);
3270
+ });
3271
+ }
3272
+ cancelEdit(t) {
3273
+ if (this.activeCell) {
3274
+ const { rowId: e, colKey: i } = this.activeCell;
3275
+ if (i === null) return;
3276
+ const n = t.dataset?.original ?? t.dataset?.value ?? (() => {
3277
+ const o = this.dataModel.getCell(e, i);
3278
+ return o == null ? "" : String(o);
3279
+ })(), s = {
3280
+ kind: "edit",
3281
+ rowId: e,
3282
+ colKey: i,
3283
+ next: n,
3284
+ prev: n
3285
+ };
3286
+ this.onEdit(s, !0);
3287
+ }
3288
+ this.teardownInput(!1), (t.dataset?.original !== void 0 || t.dataset?.value !== void 0) && (t.textContent = t.dataset.original ?? t.dataset.value ?? ""), t.blur();
3289
+ }
3290
+ teardownInput(t = !1) {
3291
+ _(this.inputEl), _(this.floatingInputWrapper), this.activeHost && this.activeHostOriginalText !== null && (this.activeHost.textContent = this.activeHostOriginalText), this.inputEl = null, this.floatingInputWrapper = null, this.activeHost = null, this.activeHostOriginalText = null, t && (this.activeCell = null, this.onActiveChange(null, null));
3292
+ }
3293
+ }
3294
+ const Ie = (c) => c;
3295
+ class xe {
3296
+ constructor(t) {
3297
+ this.shell = null, this.viewportEl = null, this.viewportResizeObserver = null, this.selectionManager = null, this.resizeHandler = null, this.scrollHandler = null, this.viewportState = null, this.rafId = null, this.contextMenu = null, this.contextMenuRowId = null, this.handleGlobalPointer = null, this.toast = null, this.toastTimer = null, this.findReplace = null, this.findReplaceSidebar = null, this.findReplaceSidebarUnsub = null, this.findReplaceEnabled = !0, this.findReplaceUiEnabled = !0, this.findReplaceEnableSearch = !1, this.mounted = !1, this.filterSortSidebar = null, this.filterSortSidebarUnsub = null, this.filterSortKeydown = null, this.filterSortClickCapture = null, this.filterSortOpenEvent = null, this.filterSortActiveColumnKey = null, this.filterSortDraft = null, this.tableStateListeners = /* @__PURE__ */ new Set(), this.selectionListeners = /* @__PURE__ */ new Set(), this.lastTableState = null, this.lastSelectionSnapshot = null, this.selectionRanges = [], this.activeCell = null, this.activeErrors = [], this.rowStateListeners = /* @__PURE__ */ new Set(), this.lastRowStates = /* @__PURE__ */ new Map(), this.root = t.root, this.root.classList.add("extable-root"), this.renderMode = t.options?.renderMode ?? "auto", this.editMode = t.options?.editMode ?? "direct", this.lockMode = t.options?.lockMode ?? "none", this.loadingEnabled = t.options?.loading?.enabled ?? !0, this.findReplaceEnabled = t.options?.findReplace?.enabled ?? !0, this.findReplaceUiEnabled = t.options?.findReplace?.sidebar ?? t.options?.findReplace?.dialog ?? !0, this.findReplaceEnableSearch = t.options?.findReplace?.enableSearch ?? !0, this.server = t.options?.server, this.user = t.options?.user;
3298
+ const e = t.defaultData ?? null, i = e ?? [];
3299
+ this.dataLoaded = e != null, this.dataModel = new ne(
3300
+ i,
3301
+ t.schema,
3302
+ t.defaultView
3303
+ ), this.commandQueue = new Gt(), this.lockManager = new ae(this.lockMode, this.server, this.user), this.renderer = this.chooseRenderer(this.renderMode), this.applyRootDecor(t.options), this.applyReadonlyClass(), this.loadInitial();
3304
+ }
3305
+ isCellReadonly(t, e) {
3306
+ return this.editMode === "readonly" || this.dataModel.isReadonly(t, e);
3307
+ }
3308
+ resolveRowId(t) {
3309
+ return typeof t == "string" ? this.findRowById(t)?.id ?? null : (this.dataModel.listAllRows()[t] ?? null)?.id ?? null;
3310
+ }
3311
+ isSearchPanelVisible() {
3312
+ return this.root.classList.contains("extable-search-open");
3313
+ }
3314
+ isFilterSortPanelVisible() {
3315
+ return this.root.classList.contains("extable-filter-sort-open");
3316
+ }
3317
+ safeRender(t) {
3318
+ try {
3319
+ this.renderer.render(t), this.activeErrors.some((i) => i.scope === "render") && (this.activeErrors = this.activeErrors.filter((i) => i.scope !== "render"), this.emitTableState());
3320
+ } catch (e) {
3321
+ const i = e instanceof Error ? e.message : String(e);
3322
+ this.activeErrors = [
3323
+ ...this.activeErrors.filter((n) => n.scope !== "render"),
3324
+ { scope: "render", message: i }
3325
+ ], this.emitTableState();
3326
+ }
3327
+ }
3328
+ async loadInitial() {
3329
+ if (this.server?.fetchInitial)
3330
+ try {
3331
+ const t = await this.server.fetchInitial();
3332
+ this.dataModel.setData(t.data), t.schema && this.dataModel.setSchema(t.schema), t.view && this.dataModel.setView(t.view), this.user = t.user ?? this.user, this.lockManager.setUser(this.user);
3333
+ } catch (t) {
3334
+ console.warn("fetchInitial failed", t);
3335
+ }
3336
+ this.mount();
3337
+ }
3338
+ applyRootDecor(t) {
3339
+ const e = Ct(t?.defaultClass);
3340
+ if (e?.length && this.root.classList.add(...e), t?.defaultStyle)
3341
+ for (const [i, n] of Object.entries(t.defaultStyle))
3342
+ this.root.style[i] = n ?? "";
3343
+ }
3344
+ applyReadonlyClass() {
3345
+ this.root.classList.toggle("extable-readonly-all", this.editMode === "readonly");
3346
+ }
3347
+ chooseRenderer(t) {
3348
+ if (t === "auto") {
3349
+ const e = typeof navigator < "u" ? navigator.userAgent : "";
3350
+ return /bot|crawl|spider/i.test(e) || typeof navigator < "u" && "userAgentData" in navigator && navigator.userAgentData?.brands?.some(
3351
+ (n) => /bot/i.test(n.brand ?? "")
3352
+ ) ? new nt(this.dataModel) : new rt(this.dataModel, () => this.editMode);
3353
+ }
3354
+ return t === "html" ? new nt(this.dataModel) : new rt(this.dataModel, () => this.editMode);
3355
+ }
3356
+ ensureShell() {
3357
+ if (this.shell && this.viewportEl && this.shell.parentElement === this.root) return;
3358
+ this.root.innerHTML = "";
3359
+ const t = document.createElement("div");
3360
+ t.className = "extable-shell";
3361
+ const e = document.createElement("div");
3362
+ e.className = "extable-viewport", t.appendChild(e), this.root.appendChild(t), this.shell = t, this.viewportEl = e, this.viewportResizeObserver?.disconnect();
3363
+ const i = globalThis.ResizeObserver;
3364
+ typeof i == "function" ? (this.viewportResizeObserver = new i(() => {
3365
+ this.updateViewportFromRoot();
3366
+ }), this.viewportResizeObserver.observe(e)) : this.viewportResizeObserver = null;
3367
+ }
3368
+ getScrollHost() {
3369
+ return this.viewportEl ?? this.root;
3370
+ }
3371
+ mount(t = !1) {
3372
+ if (this.mounted && !t) return;
3373
+ this.ensureShell();
3374
+ const e = this.viewportEl ?? this.root;
3375
+ if (this.renderer.mount(e), this.ensureContextMenu(), this.ensureToast(), this.initViewportState(), this.root.classList.toggle("extable-loading", !this.dataLoaded && this.loadingEnabled), !this.dataLoaded && this.loadingEnabled) {
3376
+ this.root.dataset.extable = "loading", this.bindViewport(), this.ensureFindReplace(), this.ensureFilterSort(), this.emitTableState(), this.emitSelection("data");
3377
+ return;
3378
+ }
3379
+ this.selectionManager = new Se(
3380
+ e,
3381
+ this.editMode,
3382
+ (i, n) => this.handleEdit(i, n),
3383
+ (i) => {
3384
+ this.lockManager.selectRow(i);
3385
+ },
3386
+ (i) => {
3387
+ this.lockManager.unlockOnMove(i);
3388
+ },
3389
+ (i) => this.renderer.hitTest(i),
3390
+ this.dataModel,
3391
+ (i, n) => this.isCellReadonly(i, n),
3392
+ (i, n) => {
3393
+ const s = i && n !== null ? { rowId: i, colKey: n } : null;
3394
+ s && this.renderer.setActiveCell(s.rowId, s.colKey), this.activeCell = s, this.emitSelection("selection");
3395
+ },
3396
+ (i, n, s, o) => this.showContextMenu(i, s, o),
3397
+ (i) => {
3398
+ this.selectionRanges = i, this.renderer.setSelection(i), this.emitSelection("selection");
3399
+ },
3400
+ () => this.undo(),
3401
+ () => this.redo()
3402
+ ), this.root.dataset.extable = "ready", this.bindViewport(), this.ensureFindReplace(), this.ensureFilterSort(), this.server && (this.unsubscribe = this.server.subscribe((i) => this.handleServerEvent(i))), this.emitTableState(), this.emitSelection("selection"), this.mounted = !0;
3403
+ }
3404
+ destroy() {
3405
+ this.teardownFilterSort(), this.teardownFindReplace(), this.selectionManager?.destroy(), this.renderer.destroy(), this.unsubscribe?.(), this.unbindViewport(), this.viewportResizeObserver?.disconnect(), this.viewportResizeObserver = null, this.mounted = !1;
3406
+ }
3407
+ // editMode/lockMode are configured only at construction time for consistency.
3408
+ setRootClass(t) {
3409
+ this.root.className = "", this.root.classList.add(...Ct(t) ?? []);
3410
+ }
3411
+ setRootStyle(t) {
3412
+ for (const [e, i] of Object.entries(t))
3413
+ this.root.style[e] = i ?? "";
3414
+ }
3415
+ setData(t) {
3416
+ if (t == null) {
3417
+ if (this.dataLoaded) return;
3418
+ this.dataLoaded = !1, this.safeRender(this.viewportState ?? void 0), this.emitSelection("data"), this.emitTableState();
3419
+ return;
3420
+ }
3421
+ const e = this.dataLoaded;
3422
+ this.dataLoaded = !0, this.dataModel.setData(t), this.safeRender(this.viewportState ?? void 0), this.selectionManager?.syncAfterRowsChanged(), this.emitSelection("data"), this.emitTableState(), !e && this.loadingEnabled && (this.root.classList.remove("extable-loading"), this.selectionManager?.destroy(), this.selectionManager = null, this.mount());
3423
+ }
3424
+ setView(t) {
3425
+ this.dataModel.setView(t), this.safeRender(this.viewportState ?? void 0), this.selectionManager?.syncAfterRowsChanged(), this.emitSelection("view"), this.emitTableState();
3426
+ }
3427
+ // Public API: configuration getters (mirror setters)
3428
+ getSchema() {
3429
+ return this.dataModel.getSchema();
3430
+ }
3431
+ getView() {
3432
+ return this.dataModel.getView();
3433
+ }
3434
+ /**
3435
+ * Returns the current table data (includes pending edits in commit mode and formula results).
3436
+ * Note: This is the full dataset (not affected by view filters/sorts). Use `listRows()` for the visible rows.
3437
+ */
3438
+ getData() {
3439
+ return this.getTableData();
3440
+ }
3441
+ /**
3442
+ * Returns the raw dataset without pending edits and without formula results.
3443
+ * Note: This is the full dataset (not affected by view filters/sorts). Use `listRows()` for the visible rows.
3444
+ */
3445
+ getRawData() {
3446
+ return this.dataModel.listAllRows().map((t) => ({ ...t.raw }));
3447
+ }
3448
+ // Public API: cell-level access
3449
+ getCell(t, e) {
3450
+ return this.dataModel.getCell(t, e);
3451
+ }
3452
+ // Public API: row/column index utilities
3453
+ getRowIndex(t) {
3454
+ return this.dataModel.getRowIndex(t);
3455
+ }
3456
+ getColumnIndex(t) {
3457
+ return this.dataModel.getColumnIndex(t);
3458
+ }
3459
+ // Public API: row-level access
3460
+ findRowById(t) {
3461
+ const e = this.dataModel.getBaseRowIndex(t);
3462
+ return e < 0 ? null : this.dataModel.listAllRows()[e] ?? null;
3463
+ }
3464
+ listRows() {
3465
+ return this.dataModel.listRows();
3466
+ }
3467
+ getAllRows() {
3468
+ return this.dataModel.listAllRows();
3469
+ }
3470
+ /**
3471
+ * Returns a single row as an object (includes pending edits in commit mode and formula results).
3472
+ * When a number is passed, it is interpreted as the base row index (not affected by view filters/sorts).
3473
+ */
3474
+ getRow(t) {
3475
+ const e = this.resolveRowId(t);
3476
+ return e ? this.buildRow(e) : null;
3477
+ }
3478
+ buildRow(t) {
3479
+ const e = this.findRowById(t);
3480
+ if (!e) return null;
3481
+ const i = this.dataModel.getSchema(), n = { ...e.raw };
3482
+ for (const s of i.columns)
3483
+ n[s.key] = this.dataModel.resolveCellValue(t, s).value;
3484
+ return n;
3485
+ }
3486
+ // Public API: bulk data access
3487
+ getTableData() {
3488
+ return this.dataModel.listAllRows().map((e) => this.buildRow(e.id)).filter(Boolean);
3489
+ }
3490
+ getColumnData(t) {
3491
+ const i = this.dataModel.getSchema().columns.find((s) => s.key === t);
3492
+ return i ? this.dataModel.listAllRows().map((s) => this.dataModel.resolveCellValue(s.id, i).value) : [];
3493
+ }
3494
+ // Public API: commit-mode pending helpers
3495
+ getPending() {
3496
+ return this.editMode !== "commit" ? /* @__PURE__ */ new Map() : new Map(this.dataModel.getPending());
3497
+ }
3498
+ getPendingRowIds() {
3499
+ return [...this.getPending().keys()];
3500
+ }
3501
+ hasPendingChanges() {
3502
+ return this.getPending().size > 0;
3503
+ }
3504
+ getPendingCellCount() {
3505
+ const t = this.getPending();
3506
+ let e = 0;
3507
+ for (const i of t.values()) e += Object.keys(i).length;
3508
+ return e;
3509
+ }
3510
+ getCellPending(t, e) {
3511
+ if (this.editMode !== "commit") return !1;
3512
+ const i = this.resolveRowId(t);
3513
+ return i ? this.dataModel.hasPending(i, e) : !1;
3514
+ }
3515
+ getDisplayValue(t, e) {
3516
+ const i = this.resolveRowId(t);
3517
+ if (!i) return "";
3518
+ const s = this.dataModel.getSchema().columns.find((a) => String(a.key) === String(e));
3519
+ if (!s) return "";
3520
+ const o = this.dataModel.resolveCellValue(i, s);
3521
+ if (o.textOverride) return o.textOverride;
3522
+ const r = o.value;
3523
+ if (r == null) return "";
3524
+ if (r instanceof Date) return r.toISOString();
3525
+ if (typeof r == "string") return r;
3526
+ if (typeof r == "number" || typeof r == "boolean") return String(r);
3527
+ if (typeof r == "object") {
3528
+ const a = r;
3529
+ if (a.kind === "enum" && typeof a.value == "string") return a.value;
3530
+ if (a.kind === "tags" && Array.isArray(a.values))
3531
+ return a.values.filter((h) => typeof h == "string").join(", ");
3532
+ }
3533
+ return String(r);
3534
+ }
3535
+ insertRow(t, e) {
3536
+ let i = null;
3537
+ if (typeof e == "number" && Number.isFinite(e))
3538
+ i = Math.max(0, Math.min(Math.floor(e), this.dataModel.getAllRowCount()));
3539
+ else if (typeof e == "string") {
3540
+ const o = this.dataModel.getBaseRowIndex(e);
3541
+ o >= 0 && (i = o);
3542
+ }
3543
+ i === null && (i = this.dataModel.getAllRowCount());
3544
+ const n = this.dataModel.insertRowAt(t, i), s = {
3545
+ kind: "insertRow",
3546
+ rowId: n,
3547
+ rowData: t,
3548
+ payload: { index: i }
3549
+ };
3550
+ return this.commandQueue.enqueue(s), this.emitRowState(n, "new"), this.safeRender(this.viewportState ?? void 0), this.selectionManager?.syncAfterRowsChanged(), this.emitSelection("data"), this.emitTableState(), this.editMode === "direct" && this.sendCommit([s]), n;
3551
+ }
3552
+ deleteRow(t) {
3553
+ const e = this.resolveRowId(t);
3554
+ if (!e) return !1;
3555
+ const i = this.dataModel.removeRow(e);
3556
+ if (!i) return !1;
3557
+ const n = {
3558
+ kind: "deleteRow",
3559
+ rowId: i.row.id,
3560
+ rowData: i.row.raw,
3561
+ payload: { index: i.index }
3562
+ };
3563
+ return this.commandQueue.enqueue(n), this.emitRowState(e, "delete"), this.safeRender(this.viewportState ?? void 0), this.selectionManager?.syncAfterRowsChanged(), this.emitSelection("data"), this.emitTableState(), this.editMode === "direct" && this.sendCommit([n]), !0;
3564
+ }
3565
+ handleEdit(t, e) {
3566
+ if (this.editMode === "readonly" || !t.rowId || t.colKey === void 0) return;
3567
+ const i = this.dataModel.getCell(t.rowId, t.colKey);
3568
+ this.commandQueue.enqueue({ ...t, prev: i }), this.dataModel.setCell(t.rowId, t.colKey, t.next, e), this.emitRowState(t.rowId, "edit"), this.safeRender(this.viewportState ?? void 0), this.selectionManager?.syncAfterRowsChanged(), this.emitTableState(), this.emitSelection("edit"), e && this.sendCommit([t]);
3569
+ }
3570
+ undo() {
3571
+ this.selectionManager?.cancelEditing();
3572
+ const t = this.commandQueue.undo();
3573
+ if (!(!t || !t.length)) {
3574
+ for (let e = t.length - 1; e >= 0; e -= 1) {
3575
+ const i = t[e];
3576
+ i && this.applyInverse(i);
3577
+ }
3578
+ this.safeRender(this.viewportState ?? void 0), this.selectionManager?.syncAfterRowsChanged(), this.emitTableState(), this.emitSelection("edit");
3579
+ }
3580
+ }
3581
+ redo() {
3582
+ this.selectionManager?.cancelEditing();
3583
+ const t = this.commandQueue.redo();
3584
+ if (!(!t || !t.length)) {
3585
+ for (const e of t)
3586
+ this.applyForward(e);
3587
+ this.safeRender(this.viewportState ?? void 0), this.selectionManager?.syncAfterRowsChanged(), this.emitTableState(), this.emitSelection("edit");
3588
+ }
3589
+ }
3590
+ getUndoRedoHistory() {
3591
+ const t = (n) => {
3592
+ const s = /* @__PURE__ */ new Set(), o = [];
3593
+ for (const r of n)
3594
+ s.has(r.kind) || (s.add(r.kind), o.push(r.kind));
3595
+ return o;
3596
+ }, e = (n, s) => n.length === 1 && n[0] === "edit" ? `Edit ${s} cell(s)` : n.length === 1 && n[0] === "insertRow" ? s === 1 ? "Insert row" : `Insert row (${s})` : n.length === 1 && n[0] === "deleteRow" ? s === 1 ? "Delete row" : `Delete row (${s})` : n.length === 1 && n[0] === "updateView" ? "Update view" : `Command: ${n.join(", ")} (${s})`, i = (n) => n.map((s) => {
3597
+ const o = t(s.commands), r = s.commands.length;
3598
+ return {
3599
+ batchId: s.batchId,
3600
+ kinds: o,
3601
+ commandCount: r,
3602
+ label: e(o, r)
3603
+ };
3604
+ });
3605
+ return {
3606
+ undo: i(this.commandQueue.listUndoGroups()),
3607
+ redo: i(this.commandQueue.listRedoGroups())
3608
+ };
3609
+ }
3610
+ applyInverse(t) {
3611
+ switch (t.kind) {
3612
+ case "edit":
3613
+ t.rowId && t.colKey !== void 0 && (this.dataModel.setCell(t.rowId, t.colKey, t.prev, this.editMode === "direct"), this.emitRowState(t.rowId, "edit"));
3614
+ return;
3615
+ case "insertRow":
3616
+ t.rowId && this.dataModel.removeRow(t.rowId), t.rowId && this.emitRowState(t.rowId, "delete");
3617
+ return;
3618
+ case "deleteRow":
3619
+ if (t.rowId && t.rowData) {
3620
+ const e = t.payload?.index, i = typeof e == "number" ? e : this.dataModel.getAllRowCount();
3621
+ this.dataModel.insertRowAt(t.rowData, i, t.rowId), this.emitRowState(t.rowId, "new");
3622
+ }
3623
+ return;
3624
+ default:
3625
+ return;
3626
+ }
3627
+ }
3628
+ applyForward(t) {
3629
+ switch (t.kind) {
3630
+ case "edit":
3631
+ t.rowId && t.colKey !== void 0 && (this.dataModel.setCell(t.rowId, t.colKey, t.next, this.editMode === "direct"), this.emitRowState(t.rowId, "edit"));
3632
+ return;
3633
+ case "insertRow":
3634
+ if (t.rowData) {
3635
+ const e = t.payload?.index, i = typeof e == "number" ? e : this.dataModel.getAllRowCount(), n = typeof t.rowId == "string" ? t.rowId : void 0;
3636
+ this.dataModel.insertRowAt(t.rowData, i, n), n && this.emitRowState(n, "new");
3637
+ }
3638
+ return;
3639
+ case "deleteRow":
3640
+ t.rowId && (this.dataModel.removeRow(t.rowId), this.emitRowState(t.rowId, "delete"));
3641
+ return;
3642
+ default:
3643
+ return;
3644
+ }
3645
+ }
3646
+ async commit() {
3647
+ const t = this.commandQueue.listApplied();
3648
+ if (!t.length) return [];
3649
+ const e = /* @__PURE__ */ new Set();
3650
+ for (const n of t)
3651
+ n.rowId && (this.dataModel.applyPending(n.rowId), e.add(n.rowId));
3652
+ await this.sendCommit(t), await this.lockManager.unlockOnCommit(this.commandQueue.listApplied().at(-1)?.rowId), this.commandQueue.clear();
3653
+ const i = [];
3654
+ for (const n of e) {
3655
+ this.emitRowState(n, "edit");
3656
+ const s = this.getRowStateSnapshot(n);
3657
+ s && i.push(s);
3658
+ }
3659
+ return this.safeRender(this.viewportState ?? void 0), this.selectionManager?.syncAfterRowsChanged(), this.emitTableState(), this.emitSelection("edit"), i;
3660
+ }
3661
+ async sendCommit(t) {
3662
+ if (this.server && this.user)
3663
+ try {
3664
+ await this.server.commit(t, this.user), this.activeErrors = this.activeErrors.filter((e) => e.scope !== "commit"), this.emitTableState();
3665
+ } catch (e) {
3666
+ console.warn("commit failed", e);
3667
+ const i = e instanceof Error ? e.message : String(e);
3668
+ this.activeErrors = [
3669
+ ...this.activeErrors.filter((n) => n.scope !== "commit"),
3670
+ { scope: "commit", message: i }
3671
+ ], this.emitTableState();
3672
+ }
3673
+ }
3674
+ handleServerEvent(t) {
3675
+ for (const e of t.commands)
3676
+ this.applyCommand(e);
3677
+ this.safeRender(this.viewportState ?? void 0), this.selectionManager?.syncAfterRowsChanged(), this.emitSelection("data"), this.emitTableState();
3678
+ }
3679
+ applyCommand(t) {
3680
+ switch (t.kind) {
3681
+ case "edit":
3682
+ t.rowId && t.colKey !== void 0 && (this.dataModel.setCell(t.rowId, t.colKey, t.next, !0), this.emitRowState(t.rowId, "edit"));
3683
+ break;
3684
+ case "insertRow":
3685
+ if (t.rowData) {
3686
+ const e = this.dataModel.insertRow(t.rowData);
3687
+ e && this.emitRowState(e, "new");
3688
+ }
3689
+ break;
3690
+ case "deleteRow":
3691
+ t.rowId && (this.dataModel.removeRow(t.rowId), this.emitRowState(t.rowId, "delete"));
3692
+ break;
3693
+ case "updateView":
3694
+ t.next && typeof t.next == "object" && this.dataModel.setView(t.next);
3695
+ break;
3696
+ }
3697
+ }
3698
+ ensureContextMenu() {
3699
+ if (this.contextMenu?.isConnected) return;
3700
+ this.contextMenu = null;
3701
+ const t = document.createElement("div");
3702
+ t.className = "extable-context-menu", t.setAttribute("popover", "manual"), t.addEventListener("contextmenu", (s) => s.preventDefault());
3703
+ const i = this.editMode === "readonly" ? [{ key: "copy", label: "Copy" }] : [
3704
+ { key: "copy", label: "Copy" },
3705
+ { key: "paste", label: "Paste" },
3706
+ { key: "sep-top", kind: "sep" },
3707
+ { key: "undo", label: "Undo" },
3708
+ { key: "redo", label: "Redo" },
3709
+ { key: "sep-mid", kind: "sep" },
3710
+ { key: "insert-above", label: "Insert row above" },
3711
+ { key: "insert-below", label: "Insert row below" },
3712
+ { key: "delete-row", label: "Delete row" }
3713
+ ], n = document.createElement("div");
3714
+ for (const s of i) {
3715
+ if (s.kind === "sep") {
3716
+ const r = document.createElement("hr");
3717
+ r.className = "extable-context-sep", n.appendChild(r);
3718
+ continue;
3719
+ }
3720
+ const o = document.createElement("button");
3721
+ o.type = "button", o.dataset.action = s.key, o.textContent = s.label ?? "", o.addEventListener("click", () => {
3722
+ this.handleContextAction(s.key), this.closeContextMenu();
3723
+ }), n.appendChild(o);
3724
+ }
3725
+ t.appendChild(n), (this.viewportEl ?? this.root).appendChild(t), this.contextMenu = t;
3726
+ }
3727
+ showContextMenu(t, e, i) {
3728
+ if (this.ensureContextMenu(), !this.contextMenu || !this.contextMenu.isConnected && (this.contextMenu = null, this.ensureContextMenu(), !this.contextMenu))
3729
+ return;
3730
+ this.contextMenuRowId = t;
3731
+ for (const l of Array.from(
3732
+ this.contextMenu.querySelectorAll("button[data-action]")
3733
+ )) {
3734
+ const h = l.dataset.action;
3735
+ h === "undo" ? l.disabled = !this.commandQueue.canUndo() : h === "redo" && (l.disabled = !this.commandQueue.canRedo());
3736
+ }
3737
+ const n = window.innerWidth, s = window.innerHeight, o = { width: 220, height: 160 };
3738
+ let r = e, a = i;
3739
+ r + o.width > n && (r = Math.max(0, n - o.width - 8)), a + o.height > s && (a = Math.max(0, s - o.height - 8)), this.contextMenu.style.left = `${r}px`, this.contextMenu.style.top = `${a}px`, this.contextMenu.hidePopover?.(), this.contextMenu.showPopover?.();
3740
+ }
3741
+ handleContextAction(t) {
3742
+ if (t === "copy") {
3743
+ this.selectionManager?.copySelection();
3744
+ return;
3745
+ }
3746
+ if (t === "paste") {
3747
+ this.selectionManager?.pasteFromClipboard();
3748
+ return;
3749
+ }
3750
+ if (t === "undo") {
3751
+ this.undo();
3752
+ return;
3753
+ }
3754
+ if (t === "redo") {
3755
+ this.redo();
3756
+ return;
3757
+ }
3758
+ if (!this.contextMenuRowId) return;
3759
+ const e = this.dataModel.getBaseRowIndex(this.contextMenuRowId), i = e >= 0 ? e : this.dataModel.getAllRowCount();
3760
+ if (t === "insert-above" || t === "insert-below") {
3761
+ const n = t === "insert-above" ? i : i + 1, s = this.createBlankRow(), o = this.dataModel.insertRowAt(s, n);
3762
+ this.commandQueue.enqueue({
3763
+ kind: "insertRow",
3764
+ rowId: o,
3765
+ rowData: s,
3766
+ payload: { index: n }
3767
+ }), this.emitRowState(o, "new"), this.safeRender(this.viewportState ?? void 0), this.selectionManager?.syncAfterRowsChanged(), this.showToast("Row inserted", "info");
3768
+ return;
3769
+ }
3770
+ if (t === "delete-row") {
3771
+ const n = this.dataModel.removeRow(this.contextMenuRowId);
3772
+ if (!n) return;
3773
+ this.commandQueue.enqueue({
3774
+ kind: "deleteRow",
3775
+ rowId: n.row.id,
3776
+ rowData: n.row.raw,
3777
+ payload: { index: n.index }
3778
+ }), this.emitRowState(n.row.id, "delete"), this.safeRender(this.viewportState ?? void 0), this.selectionManager?.syncAfterRowsChanged(), this.showToast("Row deleted", "info");
3779
+ return;
3780
+ }
3781
+ }
3782
+ createBlankRow() {
3783
+ const t = this.dataModel.getSchema(), e = {};
3784
+ for (const i of t.columns)
3785
+ e[String(i.key)] = null;
3786
+ return e;
3787
+ }
3788
+ closeContextMenu() {
3789
+ this.contextMenu?.hidePopover?.();
3790
+ }
3791
+ ensureToast() {
3792
+ if (this.toast?.isConnected) return;
3793
+ this.toast = null;
3794
+ const t = document.createElement("div");
3795
+ t.className = "extable-toast", t.setAttribute("popover", "manual"), t.style.right = "16px", t.style.bottom = "16px", t.style.position = "fixed", (this.viewportEl ?? this.root).appendChild(t), this.toast = t;
3796
+ }
3797
+ showToast(t, e = "info", i = 2500) {
3798
+ this.ensureToast(), this.toast && (this.toast.textContent = t, this.toast.dataset.variant = e, this.toast.hidePopover?.(), this.toast.showPopover?.(), this.toastTimer && (window.clearTimeout(this.toastTimer), this.toastTimer = null), this.toastTimer = window.setTimeout(() => {
3799
+ this.toast?.hidePopover?.();
3800
+ }, i));
3801
+ }
3802
+ bindViewport() {
3803
+ this.resizeHandler = () => this.updateViewportFromRoot(), this.scrollHandler = () => this.updateViewportFromRoot(), this.getScrollHost().addEventListener("scroll", this.scrollHandler, { passive: !0 }), window.addEventListener("resize", this.resizeHandler), this.handleGlobalPointer = (t) => {
3804
+ this.contextMenu && !this.contextMenu.contains(t.target) && this.contextMenu.hidePopover?.();
3805
+ }, document.addEventListener("pointerdown", this.handleGlobalPointer, !0);
3806
+ }
3807
+ unbindViewport() {
3808
+ this.resizeHandler && window.removeEventListener("resize", this.resizeHandler), this.scrollHandler && this.getScrollHost().removeEventListener("scroll", this.scrollHandler), this.rafId !== null && (cancelAnimationFrame(this.rafId), this.rafId = null), this.handleGlobalPointer && (document.removeEventListener("pointerdown", this.handleGlobalPointer, !0), this.handleGlobalPointer = null), this.contextMenu && (this.contextMenu.hidePopover?.(), this.contextMenu.parentElement && _(this.contextMenu), this.contextMenu = null), this.toast && (this.toast.hidePopover?.(), this.toast.parentElement && _(this.toast), this.toast = null);
3809
+ }
3810
+ remount(t) {
3811
+ this.unbindViewport(), this.teardownFilterSort(), this.teardownFindReplace(), this.selectionManager?.destroy(), this.renderer.destroy(), this.root = t, this.applyReadonlyClass(), this.shell = null, this.viewportEl = null, this.viewportResizeObserver?.disconnect(), this.viewportResizeObserver = null, this.renderer = this.chooseRenderer(this.renderMode), this.mounted = !1, this.mount(!0);
3812
+ }
3813
+ showSearchPanel(t = "find") {
3814
+ if (!this.findReplaceEnabled || !this.findReplaceUiEnabled || (this.ensureFindReplace(), !this.findReplace || !this.findReplaceSidebar)) return;
3815
+ this.hideFilterSortPanel(), this.findReplace.setMode(t), this.root.classList.toggle("extable-search-open", !0), this.updateViewportFromRoot(), this.safeRender(this.viewportState ?? void 0), this.emitTableState();
3816
+ const e = this.findReplaceSidebar.querySelector(
3817
+ 'input[data-extable-fr="query"]'
3818
+ );
3819
+ e?.focus({ preventScroll: !0 }), e?.select();
3820
+ }
3821
+ hideSearchPanel() {
3822
+ this.findReplaceSidebar && (this.root.classList.toggle("extable-search-open", !1), this.updateViewportFromRoot(), this.safeRender(this.viewportState ?? void 0), this.emitTableState(), this.getScrollHost().querySelector(
3823
+ 'input[data-extable-selection="1"]'
3824
+ )?.focus?.({
3825
+ preventScroll: !0
3826
+ }));
3827
+ }
3828
+ // External toggle for host apps.
3829
+ toggleSearchPanel(t = "find") {
3830
+ this.isSearchPanelVisible() ? this.hideSearchPanel() : this.showSearchPanel(t);
3831
+ }
3832
+ updateFindReplaceReadonlyUI() {
3833
+ if (!this.findReplaceSidebar) return;
3834
+ const t = this.findReplaceSidebar, e = t.querySelector('input[data-extable-fr="replace-toggle"]');
3835
+ if (!e) return;
3836
+ const i = this.editMode === "readonly";
3837
+ t.dataset.extableFrReadonly = i ? "1" : "0", i ? (e.checked = !1, this.findReplace?.setMode("find"), t.dataset.extableFrMode = "find") : t.dataset.extableFrMode = e.checked ? "replace" : "find";
3838
+ }
3839
+ // Public API: table-level state callbacks
3840
+ getTableState() {
3841
+ const t = this.renderer instanceof nt ? "html" : "canvas", e = this.commandQueue.listApplied(), i = this.editMode === "commit" ? e : [], n = i.length, s = (() => {
3842
+ const l = /* @__PURE__ */ new Set();
3843
+ for (const h of i)
3844
+ h.kind === "edit" && (!h.rowId || h.colKey === void 0 || l.add(`${h.rowId}::${String(h.colKey)}`));
3845
+ return l.size;
3846
+ })(), o = this.dataModel.getValidationErrors().map((l) => ({
3847
+ scope: "validation",
3848
+ message: l.message,
3849
+ target: { rowId: l.rowId, colKey: l.colKey }
3850
+ })), r = this.dataModel.getDiagnostics().map((l) => l.diag ? {
3851
+ scope: l.diag.source,
3852
+ message: l.diag.message,
3853
+ target: { rowId: l.rowId, colKey: l.colKey }
3854
+ } : null).filter(Boolean);
3855
+ r.sort((l, h) => {
3856
+ const u = l.target?.rowId ? this.dataModel.getRowIndex(l.target.rowId) : -1, f = h.target?.rowId ? this.dataModel.getRowIndex(h.target.rowId) : -1;
3857
+ if (u !== f) return u - f;
3858
+ const p = l.target?.colKey !== void 0 ? this.dataModel.getColumnIndex(l.target.colKey) : -1, d = h.target?.colKey !== void 0 ? this.dataModel.getColumnIndex(h.target.colKey) : -1;
3859
+ return p !== d ? p - d : l.scope !== h.scope ? l.scope < h.scope ? -1 : 1 : l.message < h.message ? -1 : l.message > h.message ? 1 : 0;
3860
+ });
3861
+ const a = [
3862
+ ...o,
3863
+ ...r.slice(0, 200),
3864
+ ...this.activeErrors
3865
+ ];
3866
+ return {
3867
+ canCommit: this.editMode === "commit" && n > 0,
3868
+ pendingCommandCount: n,
3869
+ pendingCellCount: s,
3870
+ undoRedo: { canUndo: this.commandQueue.canUndo(), canRedo: this.commandQueue.canRedo() },
3871
+ renderMode: t,
3872
+ ui: { searchPanelOpen: this.isSearchPanelVisible() },
3873
+ activeErrors: a
3874
+ };
3875
+ }
3876
+ subscribeTableState(t) {
3877
+ return this.tableStateListeners.add(t), t(this.getTableState(), null), () => this.tableStateListeners.delete(t);
3878
+ }
3879
+ emitTableState() {
3880
+ const t = this.getTableState(), e = this.lastTableState, i = (() => {
3881
+ if (!e || e.activeErrors.length !== t.activeErrors.length) return !1;
3882
+ for (let s = 0; s < t.activeErrors.length; s += 1) {
3883
+ const o = e.activeErrors[s], r = t.activeErrors[s];
3884
+ if (!o || !r || o.scope !== r.scope || o.message !== r.message) return !1;
3885
+ const a = o.target, l = r.target;
3886
+ if (!(!a && !l) && (!a || !l || (a.rowId ?? null) !== (l.rowId ?? null) || String(a.colKey ?? "") !== String(l.colKey ?? "")))
3887
+ return !1;
3888
+ }
3889
+ return !0;
3890
+ })();
3891
+ if (!(e && e.canCommit === t.canCommit && e.pendingCommandCount === t.pendingCommandCount && e.pendingCellCount === t.pendingCellCount && e.undoRedo.canUndo === t.undoRedo.canUndo && e.undoRedo.canRedo === t.undoRedo.canRedo && e.renderMode === t.renderMode && e.ui.searchPanelOpen === t.ui.searchPanelOpen && i)) {
3892
+ this.lastTableState = t;
3893
+ for (const s of this.tableStateListeners) s(t, e);
3894
+ }
3895
+ }
3896
+ // Public API: selection callbacks
3897
+ getSelectionSnapshot() {
3898
+ const t = this.selectionRanges, e = this.dataModel.getSchema(), i = this.activeCell, n = i?.rowId ?? null, s = i?.colKey ?? null, o = n ? this.dataModel.getRowIndex(n) : null, r = s !== null ? e.columns.findIndex((y) => String(y.key) === String(s)) : null, a = r !== null && r >= 0 ? e.columns[r] : null, l = n && a ? this.dataModel.resolveCellValue(n, a) : null, h = n && a ? this.dataModel.resolveConditionalStyle(n, a) : null, u = l?.textOverride ?? (h?.forceErrorText ? "#ERROR" : void 0), f = l ? l.value : null, p = (() => {
3899
+ if (u) return u;
3900
+ const y = f;
3901
+ if (y == null) return "";
3902
+ if (y instanceof Date) return y.toISOString();
3903
+ if (typeof y == "string") return y;
3904
+ if (typeof y == "number" || typeof y == "boolean") return String(y);
3905
+ if (typeof y == "object") {
3906
+ const w = y, S = w.kind;
3907
+ if (S === "enum" && typeof w.value == "string") return w.value;
3908
+ if (S === "tags" && Array.isArray(w.values))
3909
+ return w.values.filter((b) => typeof b == "string").join(", ");
3910
+ }
3911
+ return String(y);
3912
+ })(), d = a?.type ?? null, m = n && s !== null ? this.dataModel.getCellDiagnostic(n, s) : null, g = (() => {
3913
+ if (!n || !a)
3914
+ return { columnStyle: {}, cellStyle: {}, resolved: {} };
3915
+ const { columnStyle: y, cellStyle: w, resolved: S } = me(
3916
+ this.dataModel,
3917
+ n,
3918
+ a
3919
+ );
3920
+ return { columnStyle: y, cellStyle: w, resolved: S };
3921
+ })();
3922
+ return {
3923
+ ranges: [...t],
3924
+ activeRowIndex: o !== null && o >= 0 ? o : null,
3925
+ activeRowKey: n,
3926
+ activeColumnIndex: r !== null && r >= 0 ? r : null,
3927
+ activeColumnKey: s,
3928
+ activeValueRaw: f,
3929
+ activeValueDisplay: p,
3930
+ activeValueType: d,
3931
+ diagnostic: m,
3932
+ styles: g
3933
+ };
3934
+ }
3935
+ subscribeSelection(t) {
3936
+ return this.selectionListeners.add(t), t(this.getSelectionSnapshot(), null, "selection"), () => this.selectionListeners.delete(t);
3937
+ }
3938
+ emitSelection(t) {
3939
+ const e = this.getSelectionSnapshot(), i = this.lastSelectionSnapshot;
3940
+ this.lastSelectionSnapshot = e;
3941
+ for (const n of this.selectionListeners) n(e, i, t);
3942
+ }
3943
+ getRowStateSnapshot(t) {
3944
+ const e = this.dataModel.getRowIndex(t);
3945
+ if (e < 0) return null;
3946
+ const i = this.buildRow(t);
3947
+ if (!i) return null;
3948
+ const n = this.editMode === "commit" ? this.dataModel.getPending().get(t) ?? void 0 : void 0, s = this.getTableState().activeErrors.filter((o) => o.target?.rowId === t);
3949
+ return {
3950
+ rowId: t,
3951
+ rowIndex: e,
3952
+ data: i,
3953
+ pending: n,
3954
+ diagnostics: s.length ? s : void 0
3955
+ };
3956
+ }
3957
+ subscribeRowState(t) {
3958
+ this.rowStateListeners.add(t);
3959
+ const e = this.dataModel.listAllRows();
3960
+ for (const i of e) {
3961
+ const n = this.getRowStateSnapshot(i.id);
3962
+ n && (this.lastRowStates.set(i.id, n), t(i.id, n, null, "new"));
3963
+ }
3964
+ return () => this.rowStateListeners.delete(t);
3965
+ }
3966
+ emitRowState(t, e) {
3967
+ const i = this.lastRowStates.get(t) ?? null;
3968
+ if (e === "delete") {
3969
+ this.lastRowStates.delete(t);
3970
+ for (const s of this.rowStateListeners) s(t, null, i, e);
3971
+ return;
3972
+ }
3973
+ const n = this.getRowStateSnapshot(t);
3974
+ if (n) {
3975
+ this.lastRowStates.set(t, n);
3976
+ for (const s of this.rowStateListeners) s(t, n, i, e);
3977
+ }
3978
+ }
3979
+ // Public API: value updates
3980
+ setCellValue(t, e, i) {
3981
+ const n = typeof t == "string" ? t : this.dataModel.listRows()[t]?.id, s = n ? se(this.dataModel, { rowId: n, colKey: e }) : null;
3982
+ if (!s || this.isCellReadonly(s.rowId, s.colKey)) return;
3983
+ const o = this.dataModel.getCell(s.rowId, s.colKey), r = typeof i == "function" ? i(o) : i;
3984
+ this.handleEdit(
3985
+ { kind: "edit", rowId: s.rowId, colKey: s.colKey, next: r },
3986
+ this.editMode === "direct"
3987
+ );
3988
+ }
3989
+ setValueToSelection(t) {
3990
+ const e = this.dataModel.getSchema(), i = this.dataModel.listRows(), n = /* @__PURE__ */ new Set();
3991
+ for (const s of this.selectionRanges) {
3992
+ if (s.kind !== "cells") continue;
3993
+ const o = Math.min(s.startRow, s.endRow), r = Math.max(s.startRow, s.endRow), a = Math.min(s.startCol, s.endCol), l = Math.max(s.startCol, s.endCol);
3994
+ for (let h = o; h <= r; h += 1) {
3995
+ const u = i[h];
3996
+ if (u)
3997
+ for (let f = a; f <= l; f += 1) {
3998
+ const p = e.columns[f];
3999
+ if (!p) continue;
4000
+ const d = `${u.id}::${String(p.key)}`;
4001
+ if (n.has(d) || (n.add(d), this.isCellReadonly(u.id, p.key))) continue;
4002
+ const m = this.dataModel.getCell(u.id, p.key), g = typeof t == "function" ? t(m) : t;
4003
+ this.handleEdit(
4004
+ { kind: "edit", rowId: u.id, colKey: p.key, next: g },
4005
+ this.editMode === "direct"
4006
+ );
4007
+ }
4008
+ }
4009
+ }
4010
+ }
4011
+ // Backward compatible aliases.
4012
+ openFindReplaceDialog(t = "find") {
4013
+ this.showSearchPanel(t);
4014
+ }
4015
+ closeFindReplaceDialog() {
4016
+ this.hideSearchPanel();
4017
+ }
4018
+ ensureFindReplace() {
4019
+ this.findReplaceEnabled && (this.findReplace || (this.findReplace = new le(
4020
+ this.dataModel,
4021
+ (t, e) => this.selectionManager?.navigateToCell(t, e),
4022
+ (t, e, i) => this.handleEdit({ kind: "edit", rowId: t, colKey: e, next: i }, this.editMode === "direct"),
4023
+ (t, e) => !this.isCellReadonly(t, e)
4024
+ )), this.findReplaceUiEnabled && this.ensureFindReplaceSidebar());
4025
+ }
4026
+ ensureFindReplaceSidebar() {
4027
+ if (this.findReplaceSidebar) {
4028
+ if (this.findReplaceSidebar.isConnected) return;
4029
+ this.findReplaceSidebar = null;
4030
+ }
4031
+ this.ensureShell();
4032
+ const t = this.shell ?? this.root, e = document.createElement("aside");
4033
+ e.className = "extable-search-sidebar", e.innerHTML = `
4034
+ <div class="extable-search-header">
4035
+ <div class="extable-search-row">
4036
+ <label class="extable-search-label">
4037
+ <span class="extable-search-title">Find</span>
4038
+ <input data-extable-fr="query" type="text" />
4039
+ </label>
4040
+ <button type="button" data-extable-fr="close" class="extable-search-close">×</button>
4041
+ </div>
4042
+ <div class="extable-search-row">
4043
+ <label><input data-extable-fr="case" type="checkbox" /> <span>Case</span></label>
4044
+ <label><input data-extable-fr="regex" type="checkbox" /> <span>Regex</span></label>
4045
+ <label data-extable-fr="replace-toggle-row"><input data-extable-fr="replace-toggle" type="checkbox" /> <span>Replace</span></label>
4046
+ </div>
4047
+ <div class="extable-search-row extable-search-replace-row" data-extable-fr="replace-row">
4048
+ <label class="extable-search-label">
4049
+ <span class="extable-search-title">Replace</span>
4050
+ <input data-extable-fr="replace" type="text" />
4051
+ </label>
4052
+ </div>
4053
+ <div class="extable-search-row extable-search-status">
4054
+ <span data-extable-fr="status"></span>
4055
+ <span data-extable-fr="error" class="extable-search-error"></span>
4056
+ </div>
4057
+ </div>
4058
+ <div class="extable-search-body">
4059
+ <div class="extable-search-actions">
4060
+ <button type="button" data-extable-fr="prev">Prev</button>
4061
+ <button type="button" data-extable-fr="next">Next</button>
4062
+ <button type="button" data-extable-fr="replace-current" data-extable-fr-only="replace">Replace</button>
4063
+ <button type="button" data-extable-fr="replace-all" data-extable-fr-only="replace">Replace All</button>
4064
+ </div>
4065
+ <div class="extable-search-results" data-extable-fr="results">
4066
+ <table class="extable-search-table" data-extable-fr="results-table">
4067
+ <thead>
4068
+ <tr><th>Cell</th><th>Text</th></tr>
4069
+ </thead>
4070
+ <tbody data-extable-fr="results-tbody"></tbody>
4071
+ </table>
4072
+ </div>
4073
+ </div>
4074
+ `, t.appendChild(e), this.findReplaceSidebar = e;
4075
+ const i = e.querySelector('input[data-extable-fr="query"]'), n = e.querySelector('input[data-extable-fr="replace"]'), s = e.querySelector(
4076
+ 'input[data-extable-fr="replace-toggle"]'
4077
+ ), o = e.querySelector(
4078
+ '[data-extable-fr="replace-toggle-row"]'
4079
+ ), r = e.querySelector('input[data-extable-fr="case"]'), a = e.querySelector('input[data-extable-fr="regex"]'), l = e.querySelector('button[data-extable-fr="prev"]'), h = e.querySelector('button[data-extable-fr="next"]'), u = e.querySelector(
4080
+ 'button[data-extable-fr="replace-current"]'
4081
+ ), f = e.querySelector(
4082
+ 'button[data-extable-fr="replace-all"]'
4083
+ ), p = e.querySelector('button[data-extable-fr="close"]'), d = e.querySelector('[data-extable-fr="results-tbody"]');
4084
+ if (!i || !n || !s || !o || !r || !a || !l || !h || !u || !f || !p || !d) {
4085
+ _(e), this.findReplaceSidebar = null;
4086
+ return;
4087
+ }
4088
+ i.addEventListener("input", () => this.findReplace?.setQuery(i.value)), n.addEventListener("input", () => this.findReplace?.setReplace(n.value)), r.addEventListener(
4089
+ "change",
4090
+ () => this.findReplace?.setOptions({ caseInsensitive: r.checked })
4091
+ ), a.addEventListener(
4092
+ "change",
4093
+ () => this.findReplace?.setOptions({ regex: a.checked })
4094
+ ), s.addEventListener("change", () => {
4095
+ if (this.editMode === "readonly") {
4096
+ s.checked = !1, this.findReplace?.setMode("find"), e.dataset.extableFrMode = "find";
4097
+ return;
4098
+ }
4099
+ const g = s.checked ? "replace" : "find";
4100
+ this.findReplace?.setMode(g), e.dataset.extableFrMode = g;
4101
+ }), this.updateFindReplaceReadonlyUI(), l.addEventListener("click", () => this.findReplace?.prev()), h.addEventListener("click", () => this.findReplace?.next()), u.addEventListener("click", () => this.findReplace?.replaceCurrent()), f.addEventListener("click", () => this.findReplace?.replaceAll()), p.addEventListener("click", () => this.hideSearchPanel());
4102
+ const m = (g, y = 140) => g.length > y ? `${g.slice(0, y - 1)}…` : g;
4103
+ d.addEventListener("click", (g) => {
4104
+ const y = g.target?.closest("tr[data-index]"), w = y ? Number(y.dataset.index) : -1;
4105
+ Number.isFinite(w) && w >= 0 && this.findReplace?.activateIndex(w);
4106
+ }), d.addEventListener("keydown", (g) => {
4107
+ if (g.key !== "Enter") return;
4108
+ const y = g.target?.closest("tr[data-index]"), w = y ? Number(y.dataset.index) : -1;
4109
+ Number.isFinite(w) && w >= 0 && this.findReplace?.activateIndex(w);
4110
+ }), this.updateFindReplaceReadonlyUI(), this.findReplaceSidebarUnsub = this.findReplace?.subscribe((g) => {
4111
+ const y = e.querySelector('[data-extable-fr="replace-row"]'), w = e.querySelector('[data-extable-fr="status"]'), S = e.querySelector('[data-extable-fr="error"]');
4112
+ if (!(!y || !w || !S)) {
4113
+ i.value = g.query, n.value = g.replace, r.checked = g.options.caseInsensitive, a.checked = g.options.regex, s.checked = g.mode === "replace", y.style.display = g.mode === "replace" ? "block" : "none", u.style.display = g.mode === "replace" ? "inline-block" : "none", f.style.display = g.mode === "replace" ? "inline-block" : "none", w.textContent = `${g.matches.length} matches`, S.textContent = g.error ?? "", l.disabled = g.matches.length === 0, h.disabled = g.matches.length === 0, u.disabled = g.matches.length === 0 || g.activeIndex < 0, f.disabled = g.matches.length === 0, d.innerHTML = "";
4114
+ for (let b = 0; b < g.matches.length; b += 1) {
4115
+ const C = g.matches[b];
4116
+ if (!C) continue;
4117
+ const v = document.createElement("tr");
4118
+ v.dataset.index = String(b), v.tabIndex = 0, v.className = "extable-search-result-row", b === g.activeIndex && (v.dataset.active = "1");
4119
+ const R = document.createElement("td");
4120
+ R.textContent = `R${C.rowIndex + 1}C${C.colIndex + 1}`;
4121
+ const I = document.createElement("td"), x = m(C.text);
4122
+ I.textContent = x, I.title = C.text, v.appendChild(R), v.appendChild(I), d.appendChild(v);
4123
+ }
4124
+ }
4125
+ }) ?? null;
4126
+ }
4127
+ teardownFindReplace() {
4128
+ this.findReplaceSidebarUnsub?.(), this.findReplaceSidebarUnsub = null, this.findReplaceSidebar && (_(this.findReplaceSidebar), this.findReplaceSidebar = null), this.findReplace?.destroy(), this.findReplace = null;
4129
+ }
4130
+ // External toggle for host apps.
4131
+ stableValueKey(t) {
4132
+ if (t === null) return "null";
4133
+ if (t === void 0) return "undefined";
4134
+ if (t instanceof Date) return `date:${t.getTime()}`;
4135
+ if (typeof t == "string") return `s:${t}`;
4136
+ if (typeof t == "number") return `n:${Number.isNaN(t) ? "NaN" : String(t)}`;
4137
+ if (typeof t == "boolean") return `b:${t ? "1" : "0"}`;
4138
+ if (typeof t == "object") {
4139
+ const e = t, i = e.kind;
4140
+ if (i === "enum" && typeof e.value == "string") return `enum:${e.value}`;
4141
+ if (i === "tags" && Array.isArray(e.values))
4142
+ return `tags:${e.values.filter((n) => typeof n == "string").join("|")}`;
4143
+ }
4144
+ try {
4145
+ return `json:${JSON.stringify(t)}`;
4146
+ } catch {
4147
+ return `str:${String(t)}`;
4148
+ }
4149
+ }
4150
+ ensureFilterSort() {
4151
+ this.ensureFilterSortSidebar(), this.ensureFilterSortHeaderIntegration(), this.ensureFilterSortEscape();
4152
+ }
4153
+ ensureFilterSortEscape() {
4154
+ this.filterSortKeydown || (this.filterSortKeydown = (t) => {
4155
+ this.isFilterSortPanelVisible() && t.key === "Escape" && (t.preventDefault(), t.stopPropagation(), this.hideFilterSortPanel());
4156
+ }, document.addEventListener("keydown", this.filterSortKeydown, !0));
4157
+ }
4158
+ ensureFilterSortHeaderIntegration() {
4159
+ if (this.filterSortClickCapture) return;
4160
+ const t = this.viewportEl ?? this.root;
4161
+ this.filterSortClickCapture = (e) => {
4162
+ const n = e.target?.closest('button[data-extable-fs-open="1"]');
4163
+ if (!n) return;
4164
+ const s = n.dataset.extableColKey ?? n.closest("th[data-col-key]")?.dataset.colKey;
4165
+ s && (e.preventDefault(), e.stopPropagation(), this.showFilterSortPanel(s));
4166
+ }, t.addEventListener("click", this.filterSortClickCapture, !0), this.filterSortOpenEvent = (e) => {
4167
+ const s = e.detail?.colKey;
4168
+ s != null && this.showFilterSortPanel(String(s));
4169
+ }, t.addEventListener("extable:filter-sort-open", this.filterSortOpenEvent);
4170
+ }
4171
+ ensureFilterSortSidebar() {
4172
+ if (this.filterSortSidebar) {
4173
+ if (this.filterSortSidebar.isConnected) return;
4174
+ this.filterSortSidebar = null;
4175
+ }
4176
+ this.ensureShell();
4177
+ const t = this.shell ?? this.root, e = document.createElement("aside");
4178
+ e.className = "extable-filter-sort-sidebar", e.innerHTML = `
4179
+ <div class="extable-filter-sort-header">
4180
+ <div class="extable-filter-sort-row">
4181
+ <div class="extable-filter-sort-title" data-extable-fs="title">Sort/Filter</div>
4182
+ <button type="button" data-extable-fs="close" class="extable-filter-sort-close">×</button>
4183
+ </div>
4184
+ </div>
4185
+ <div class="extable-filter-sort-body">
4186
+ <div class="extable-filter-sort-section extable-filter-sort-section-filter">
4187
+ <div class="extable-filter-sort-section-title">Filter</div>
4188
+ <div class="extable-filter-sort-actions">
4189
+ <label><input type="checkbox" data-extable-fs="col-errors" /> Errors</label>
4190
+ <label><input type="checkbox" data-extable-fs="col-warnings" /> Warnings</label>
4191
+ </div>
4192
+ <input data-extable-fs="search" type="text" placeholder="Search values" />
4193
+ <div class="extable-filter-sort-values" data-extable-fs="values"></div>
4194
+ <div class="extable-filter-sort-actions" data-align="split">
4195
+ <button type="button" data-extable-fs="select-all">Select All</button>
4196
+ <button type="button" data-extable-fs="select-none">Select None</button>
4197
+ <button type="button" data-extable-fs="apply-filter">Apply</button>
4198
+ <button type="button" data-extable-fs="clear-filter">Clear</button>
4199
+ </div>
4200
+ </div>
4201
+ <div class="extable-filter-sort-section extable-filter-sort-section-sort">
4202
+ <div class="extable-filter-sort-section-title">Sort</div>
4203
+ <div class="extable-filter-sort-actions" data-align="right">
4204
+ <button type="button" data-extable-fs="sort-asc">Sort Asc</button>
4205
+ <button type="button" data-extable-fs="sort-desc">Sort Desc</button>
4206
+ <button type="button" data-extable-fs="clear-sort">Clear Sort</button>
4207
+ </div>
4208
+ </div>
4209
+ </div>
4210
+ `, t.appendChild(e), this.filterSortSidebar = e;
4211
+ const i = e.querySelector('button[data-extable-fs="close"]'), n = e.querySelector('input[data-extable-fs="col-errors"]'), s = e.querySelector(
4212
+ 'input[data-extable-fs="col-warnings"]'
4213
+ ), o = e.querySelector('input[data-extable-fs="search"]'), r = e.querySelector('[data-extable-fs="values"]'), a = e.querySelector('button[data-extable-fs="select-all"]'), l = e.querySelector('button[data-extable-fs="select-none"]'), h = e.querySelector(
4214
+ 'button[data-extable-fs="apply-filter"]'
4215
+ ), u = e.querySelector(
4216
+ 'button[data-extable-fs="clear-filter"]'
4217
+ ), f = e.querySelector('button[data-extable-fs="sort-asc"]'), p = e.querySelector(
4218
+ 'button[data-extable-fs="sort-desc"]'
4219
+ ), d = e.querySelector(
4220
+ 'button[data-extable-fs="clear-sort"]'
4221
+ );
4222
+ if (!i || !n || !s || !o || !r || !a || !l || !h || !u || !f || !p || !d) {
4223
+ _(e), this.filterSortSidebar = null;
4224
+ return;
4225
+ }
4226
+ i.addEventListener("click", () => this.hideFilterSortPanel()), n.addEventListener("change", () => {
4227
+ this.filterSortDraft && (this.filterSortDraft.diagErrors = n.checked);
4228
+ }), s.addEventListener("change", () => {
4229
+ this.filterSortDraft && (this.filterSortDraft.diagWarnings = s.checked);
4230
+ }), o.addEventListener("input", () => {
4231
+ this.filterSortDraft && (this.filterSortDraft.search = o.value, this.renderFilterSortValues());
4232
+ }), r.addEventListener("change", (m) => {
4233
+ const g = m.target?.closest(
4234
+ 'input[type="checkbox"][data-fs-val]'
4235
+ );
4236
+ if (!g || !this.filterSortDraft) return;
4237
+ const y = g.dataset.fsVal ?? "";
4238
+ y && (y === "__blanks__" ? this.filterSortDraft.includeBlanks = g.checked : g.checked ? this.filterSortDraft.selected.add(y) : this.filterSortDraft.selected.delete(y));
4239
+ }), a.addEventListener("click", () => {
4240
+ this.filterSortDraft && (this.filterSortDraft.selected = new Set(this.filterSortDraft.values.map((m) => m.key)), this.filterSortDraft.includeBlanks = this.filterSortDraft.hasBlanks, this.renderFilterSortValues());
4241
+ }), l.addEventListener("click", () => {
4242
+ this.filterSortDraft && (this.filterSortDraft.selected = /* @__PURE__ */ new Set(), this.filterSortDraft.includeBlanks = !1, this.renderFilterSortValues());
4243
+ }), h.addEventListener("click", () => this.applyFilterSortDraft()), u.addEventListener("click", () => this.clearFilterSortForActiveColumn()), f.addEventListener("click", () => this.setSortForActiveColumn("asc")), p.addEventListener("click", () => this.setSortForActiveColumn("desc")), d.addEventListener("click", () => this.clearSort()), this.filterSortSidebarUnsub = this.dataModel.subscribe(() => {
4244
+ this.isFilterSortPanelVisible() && this.filterSortActiveColumnKey && (this.buildFilterSortDraft(this.filterSortActiveColumnKey), this.renderFilterSortSidebar());
4245
+ });
4246
+ }
4247
+ showFilterSortPanel(t) {
4248
+ this.hideSearchPanel(), this.filterSortActiveColumnKey = t, this.buildFilterSortDraft(t), this.root.classList.toggle("extable-filter-sort-open", !0), this.renderFilterSortSidebar(), (this.filterSortSidebar?.querySelector('input[data-extable-fs="search"]') ?? null)?.focus();
4249
+ }
4250
+ hideFilterSortPanel() {
4251
+ this.root.classList.toggle("extable-filter-sort-open", !1), this.filterSortActiveColumnKey = null, this.filterSortDraft = null;
4252
+ }
4253
+ toggleFilterSortPanel(t) {
4254
+ this.isFilterSortPanelVisible() ? this.hideFilterSortPanel() : this.showFilterSortPanel(t);
4255
+ }
4256
+ buildFilterSortDraft(t) {
4257
+ const e = this.dataModel.getView(), n = this.dataModel.getSchema().columns.find((f) => String(f.key) === String(t));
4258
+ if (!n) {
4259
+ this.filterSortDraft = null;
4260
+ return;
4261
+ }
4262
+ const s = this.filterSortDraft && String(this.filterSortDraft.colKey) === String(n.key) ? this.filterSortDraft.search : "", o = this.dataModel.getDistinctValuesForColumn(n.key), r = o.values.map((f) => ({ ...f, key: this.stableValueKey(f.value) })), a = (e.filters ?? []).find(
4263
+ (f) => f.kind === "values" && String(f.key) === String(n.key)
4264
+ ), l = /* @__PURE__ */ new Set();
4265
+ if (a)
4266
+ for (const f of a.values ?? []) l.add(this.stableValueKey(f));
4267
+ else
4268
+ for (const f of r) l.add(f.key);
4269
+ const h = a ? !!a.includeBlanks : o.hasBlanks, u = e.columnDiagnostics?.[String(n.key)];
4270
+ this.filterSortDraft = {
4271
+ colKey: n.key,
4272
+ values: r,
4273
+ hasBlanks: o.hasBlanks,
4274
+ selected: l,
4275
+ includeBlanks: h,
4276
+ diagErrors: !!u?.errors,
4277
+ diagWarnings: !!u?.warnings,
4278
+ search: s
4279
+ };
4280
+ }
4281
+ renderFilterSortSidebar() {
4282
+ if (!this.filterSortSidebar) return;
4283
+ const t = this.filterSortSidebar.querySelector('[data-extable-fs="title"]'), e = this.filterSortSidebar.querySelector(
4284
+ 'input[data-extable-fs="col-errors"]'
4285
+ ), i = this.filterSortSidebar.querySelector(
4286
+ 'input[data-extable-fs="col-warnings"]'
4287
+ ), n = this.filterSortSidebar.querySelector(
4288
+ 'input[data-extable-fs="search"]'
4289
+ );
4290
+ if (!t || !e || !i || !n) return;
4291
+ const s = this.filterSortSidebar.querySelector(
4292
+ 'button[data-extable-fs="sort-asc"]'
4293
+ ), o = this.filterSortSidebar.querySelector(
4294
+ 'button[data-extable-fs="sort-desc"]'
4295
+ ), r = this.filterSortDraft;
4296
+ if (!r) {
4297
+ t.textContent = "Sort/Filter", e.checked = !1, i.checked = !1, n.value = "", s && (s.dataset.active = "0"), o && (o.dataset.active = "0"), this.renderFilterSortValues();
4298
+ return;
4299
+ }
4300
+ const h = this.dataModel.getSchema().columns.find((p) => String(p.key) === String(r.colKey))?.header ?? String(r.colKey);
4301
+ t.textContent = `Sort/Filter: ${h}`, e.checked = r.diagErrors, i.checked = r.diagWarnings, n.value = r.search;
4302
+ const f = this.dataModel.getView().sorts?.find((p) => String(p.key) === String(r.colKey));
4303
+ s && (s.dataset.active = f?.dir === "asc" ? "1" : "0"), o && (o.dataset.active = f?.dir === "desc" ? "1" : "0"), this.renderFilterSortValues();
4304
+ }
4305
+ renderFilterSortValues() {
4306
+ if (!this.filterSortSidebar) return;
4307
+ const t = this.filterSortSidebar.querySelector('[data-extable-fs="values"]');
4308
+ if (!t) return;
4309
+ t.innerHTML = "";
4310
+ const e = this.filterSortDraft;
4311
+ if (!e) return;
4312
+ const i = e.search.trim().toLowerCase();
4313
+ if (e.values.length + (e.hasBlanks ? 1 : 0) > 100 && !i) {
4314
+ const o = document.createElement("div");
4315
+ o.textContent = "Too many values (100+). Type to search.", t.appendChild(o);
4316
+ return;
4317
+ }
4318
+ if (e.hasBlanks) {
4319
+ const o = document.createElement("label"), r = document.createElement("input");
4320
+ r.type = "checkbox", r.dataset.fsVal = "__blanks__", r.checked = e.includeBlanks;
4321
+ const a = document.createElement("span");
4322
+ a.textContent = "(Blanks)", o.appendChild(r), o.appendChild(a), t.appendChild(o);
4323
+ }
4324
+ const s = i ? e.values.filter((o) => o.label.toLowerCase().includes(i)) : e.values;
4325
+ for (const o of s.slice(0, 200)) {
4326
+ const r = document.createElement("label"), a = document.createElement("input");
4327
+ a.type = "checkbox", a.dataset.fsVal = o.key, a.checked = e.selected.has(o.key);
4328
+ const l = document.createElement("span");
4329
+ l.textContent = o.label, r.appendChild(a), r.appendChild(l), t.appendChild(r);
4330
+ }
4331
+ }
4332
+ applyFilterSortDraft() {
4333
+ const t = this.filterSortDraft;
4334
+ if (!t) return;
4335
+ const e = this.dataModel.getView(), i = [...e.filters ?? []].filter(
4336
+ (a) => !(a.kind === "values" && String(a.key) === String(t.colKey))
4337
+ ), s = t.selected.size === t.values.length && t.includeBlanks === !!t.hasBlanks ? i : [
4338
+ ...i,
4339
+ {
4340
+ kind: "values",
4341
+ key: t.colKey,
4342
+ values: t.values.filter((a) => t.selected.has(a.key)).map((a) => a.value),
4343
+ includeBlanks: t.includeBlanks
4344
+ }
4345
+ ], o = { ...e.columnDiagnostics ?? {} };
4346
+ t.diagErrors || t.diagWarnings ? o[String(t.colKey)] = {
4347
+ errors: t.diagErrors,
4348
+ warnings: t.diagWarnings
4349
+ } : delete o[String(t.colKey)];
4350
+ const r = {
4351
+ ...e,
4352
+ filters: s.length ? s : void 0,
4353
+ columnDiagnostics: Object.keys(o).length ? o : void 0
4354
+ };
4355
+ this.setView(r);
4356
+ }
4357
+ clearFilterSortForActiveColumn() {
4358
+ const t = this.filterSortActiveColumnKey;
4359
+ if (t == null) return;
4360
+ const e = this.dataModel.getView(), i = [...e.filters ?? []].filter(
4361
+ (o) => !(o.kind === "values" && String(o.key) === String(t))
4362
+ ), n = { ...e.columnDiagnostics ?? {} };
4363
+ delete n[String(t)];
4364
+ const s = {
4365
+ ...e,
4366
+ filters: i.length ? i : void 0,
4367
+ columnDiagnostics: Object.keys(n).length ? n : void 0
4368
+ };
4369
+ this.setView(s), this.buildFilterSortDraft(t), this.renderFilterSortSidebar();
4370
+ }
4371
+ setSortForActiveColumn(t) {
4372
+ const e = this.filterSortActiveColumnKey;
4373
+ if (e == null) return;
4374
+ const n = { ...this.dataModel.getView(), sorts: [{ key: e, dir: t }] };
4375
+ this.setView(n), this.buildFilterSortDraft(e), this.renderFilterSortSidebar();
4376
+ }
4377
+ clearSort() {
4378
+ const t = this.dataModel.getView();
4379
+ if (!t.sorts?.length) return;
4380
+ const e = { ...t, sorts: void 0 };
4381
+ this.setView(e), this.filterSortActiveColumnKey !== null && (this.buildFilterSortDraft(this.filterSortActiveColumnKey), this.renderFilterSortSidebar());
4382
+ }
4383
+ teardownFilterSort() {
4384
+ this.filterSortSidebarUnsub?.(), this.filterSortSidebarUnsub = null, this.filterSortSidebar && (_(this.filterSortSidebar), this.filterSortSidebar = null), this.filterSortKeydown && (document.removeEventListener("keydown", this.filterSortKeydown, !0), this.filterSortKeydown = null), this.filterSortClickCapture && ((this.viewportEl ?? this.root).removeEventListener(
4385
+ "click",
4386
+ this.filterSortClickCapture,
4387
+ !0
4388
+ ), this.filterSortClickCapture = null), this.filterSortOpenEvent && ((this.viewportEl ?? this.root).removeEventListener(
4389
+ "extable:filter-sort-open",
4390
+ this.filterSortOpenEvent
4391
+ ), this.filterSortOpenEvent = null), this.root.classList.toggle("extable-filter-sort-open", !1), this.filterSortActiveColumnKey = null, this.filterSortDraft = null;
4392
+ }
4393
+ initViewportState() {
4394
+ const t = this.getScrollHost();
4395
+ this.viewportState = {
4396
+ scrollTop: t.scrollTop,
4397
+ scrollLeft: t.scrollLeft,
4398
+ clientWidth: t.clientWidth,
4399
+ clientHeight: t.clientHeight,
4400
+ deltaX: 0,
4401
+ deltaY: 0,
4402
+ timestamp: performance.now()
4403
+ };
4404
+ }
4405
+ updateViewportFromRoot() {
4406
+ this.viewportState || this.initViewportState();
4407
+ const t = this.viewportState ?? {
4408
+ scrollTop: 0,
4409
+ scrollLeft: 0,
4410
+ timestamp: performance.now()
4411
+ }, e = this.getScrollHost(), i = {
4412
+ scrollTop: e.scrollTop,
4413
+ scrollLeft: e.scrollLeft,
4414
+ clientWidth: e.clientWidth,
4415
+ clientHeight: e.clientHeight,
4416
+ deltaX: e.scrollLeft - t.scrollLeft,
4417
+ deltaY: e.scrollTop - t.scrollTop,
4418
+ timestamp: performance.now()
4419
+ };
4420
+ this.viewportState = i, this.rafId === null && (this.rafId = requestAnimationFrame(() => this.flushRender()));
4421
+ }
4422
+ flushRender() {
4423
+ this.rafId = null, this.viewportState && (this.selectionManager?.onScroll(this.viewportState.scrollTop, this.viewportState.scrollLeft), !(this.renderer instanceof nt) && this.safeRender(this.viewportState));
4424
+ }
4425
+ }
4426
+ function Ee(c, t) {
4427
+ return new xe({
4428
+ root: document.createElement("div"),
4429
+ defaultData: c.data,
4430
+ defaultView: c.view,
4431
+ schema: c.schema,
4432
+ options: t
4433
+ });
4434
+ }
4435
+ function Te(c, t) {
4436
+ return t.remount(c), t;
4437
+ }
4438
+ export {
4439
+ xe as ExtableCore,
4440
+ le as FindReplaceController,
4441
+ Ee as createTablePlaceholder,
4442
+ Ie as defineSchema,
4443
+ Te as mountTable
4444
+ };
4445
+ //# sourceMappingURL=index.js.map