@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/README.md +77 -0
- package/dist/address.d.ts +9 -0
- package/dist/cellValueCodec.d.ts +4 -0
- package/dist/commandQueue.d.ts +20 -0
- package/dist/dataModel.d.ts +127 -0
- package/dist/dateUtils.d.ts +5 -0
- package/dist/fillHandle.d.ts +17 -0
- package/dist/findReplace.d.ts +53 -0
- package/dist/geometry.d.ts +5 -0
- package/dist/index.css +1 -0
- package/dist/index.d.ts +177 -0
- package/dist/index.js +4445 -0
- package/dist/index.js.map +1 -0
- package/dist/lockManager.d.ts +15 -0
- package/dist/renderers.d.ts +129 -0
- package/dist/selectionManager.d.ts +135 -0
- package/dist/styleResolver.d.ts +10 -0
- package/dist/types.d.ts +300 -0
- package/dist/utils.d.ts +3 -0
- package/dist/validation.d.ts +2 -0
- package/package.json +44 -0
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("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
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
|