y11n-spreadsheet 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,1179 +1,1280 @@
1
- import { css as M, LitElement as O, html as y, nothing as k } from "lit";
2
- import { property as m, state as E, customElement as T, query as I } from "lit/decorators.js";
3
- import { repeat as L } from "lit/directives/repeat.js";
4
- import { styleMap as U } from "lit/directives/style-map.js";
5
- function z(i, e) {
6
- if (i === e) return !0;
7
- if (!i || !e) return !1;
8
- const t = Object.keys(i), r = Object.keys(e);
9
- return t.length !== r.length ? !1 : t.every((s) => i[s] === e[s]);
1
+ import { LitElement as e, css as t, html as n, nothing as r } from "lit";
2
+ import { customElement as i, property as a, query as o, state as s } from "lit/decorators.js";
3
+ import { repeat as c } from "lit/directives/repeat.js";
4
+ import { styleMap as l } from "lit/directives/style-map.js";
5
+ // src/types.ts
6
+ function u(e, t) {
7
+ if (e === t) return !0;
8
+ if (!e || !t) return !1;
9
+ let n = Object.keys(e), r = Object.keys(t);
10
+ return n.length === r.length && n.every((n) => n === "numberFormat" ? d(e.numberFormat, t.numberFormat) : e[n] === t[n]);
10
11
  }
11
- function b(i, e) {
12
- return `${i}:${e}`;
12
+ function d(e, t) {
13
+ return e === t ? !0 : !e || !t ? !1 : e.type === t.type && e.decimals === t.decimals && e.currencySymbol === t.currencySymbol && e.thousandsSep === t.thousandsSep;
13
14
  }
14
- function ot(i) {
15
- const [e, t] = i.split(":").map(Number);
16
- return { row: e, col: t };
15
+ function f(e, t) {
16
+ let n = t.decimals ?? 2;
17
+ switch (t.type) {
18
+ case "percent": return p((e * 100).toFixed(n)) + "%";
19
+ case "scientific": return e.toExponential(n);
20
+ case "currency": {
21
+ let r = t.currencySymbol ?? "$", i = t.thousandsSep !== !1, a = Math.abs(e), o = i ? m(a.toFixed(n)) : a.toFixed(n);
22
+ return e < 0 && parseFloat(o) !== 0 ? `-${r}${o}` : `${r}${o}`;
23
+ }
24
+ case "number": {
25
+ let r = t.thousandsSep !== !1, i = p(e.toFixed(n));
26
+ return r ? m(i) : i;
27
+ }
28
+ }
17
29
  }
18
- function F(i) {
19
- let e = "", t = i;
20
- for (; t >= 0; )
21
- e = String.fromCharCode(t % 26 + 65) + e, t = Math.floor(t / 26) - 1;
22
- return e;
30
+ function p(e) {
31
+ return e === "-0" || parseFloat(e) === 0 && e.startsWith("-") ? e.slice(1) : e;
23
32
  }
24
- function P(i) {
25
- let e = 0;
26
- for (let t = 0; t < i.length; t++)
27
- e = e * 26 + (i.charCodeAt(t) - 64);
28
- return e - 1;
33
+ function m(e) {
34
+ let [t, n] = e.split("."), r = t.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
35
+ return n === void 0 ? r : `${r}.${n}`;
29
36
  }
30
- function R(i) {
31
- const t = i.replace(/\$/g, "").match(/^([A-Z]+)(\d+)$/i);
32
- if (!t) throw new Error(`Invalid cell reference: ${i}`);
33
- const r = P(t[1].toUpperCase());
34
- return { row: parseInt(t[2], 10) - 1, col: r };
37
+ function h(e, t) {
38
+ return `${e}:${t}`;
35
39
  }
36
- function D(i) {
37
- return `${F(i.col)}${i.row + 1}`;
40
+ function g(e) {
41
+ let [t, n] = e.split(":").map(Number);
42
+ return {
43
+ row: t,
44
+ col: n
45
+ };
38
46
  }
39
- class j {
40
- constructor(e, t, r) {
41
- this.anchor = { row: 0, col: 0 }, this.head = { row: 0, col: 0 }, this._isDragging = !1, this.host = e, this._maxRows = t, this._maxCols = r, e.addController(this);
42
- }
43
- hostConnected() {
44
- }
45
- hostDisconnected() {
46
- this._isDragging = !1;
47
- }
48
- /** Update grid bounds (e.g., when rows/cols properties change) */
49
- setBounds(e, t) {
50
- this._maxRows = e, this._maxCols = t;
51
- }
52
- /** Get the normalized selection range (top-left to bottom-right) */
53
- get range() {
54
- return {
55
- start: {
56
- row: Math.min(this.anchor.row, this.head.row),
57
- col: Math.min(this.anchor.col, this.head.col)
58
- },
59
- end: {
60
- row: Math.max(this.anchor.row, this.head.row),
61
- col: Math.max(this.anchor.col, this.head.col)
62
- }
63
- };
64
- }
65
- /** Get the currently active cell (head of selection) */
66
- get activeCell() {
67
- return { ...this.head };
68
- }
69
- /** Check if a cell is within the current selection */
70
- isCellSelected(e, t) {
71
- const r = this.range;
72
- return e >= r.start.row && e <= r.end.row && t >= r.start.col && t <= r.end.col;
73
- }
74
- /** Check if a cell is the active cell (head) */
75
- isCellActive(e, t) {
76
- return e === this.head.row && t === this.head.col;
77
- }
78
- // ─── Mouse Interaction ──────────────────────────────
79
- /** Called on mousedown/pointerdown on a cell */
80
- startSelection(e, t, r = !1) {
81
- if (r)
82
- this.head = this.clamp({ row: e, col: t });
83
- else {
84
- const s = this.clamp({ row: e, col: t });
85
- this.anchor = { ...s }, this.head = { ...s };
86
- }
87
- this._isDragging = !0, this.host.requestUpdate();
88
- }
89
- /** Called on mousemove during drag */
90
- extendSelection(e, t) {
91
- this._isDragging && (this.head = this.clamp({ row: e, col: t }), this.host.requestUpdate());
92
- }
93
- /** Called on mouseup */
94
- endSelection() {
95
- this._isDragging = !1;
96
- }
97
- get isDragging() {
98
- return this._isDragging;
99
- }
100
- // ─── Keyboard Navigation ────────────────────────────
101
- /** Move the active cell by delta. If extend=true, extends selection. */
102
- move(e, t, r = !1) {
103
- const s = this.clamp({
104
- row: this.head.row + e,
105
- col: this.head.col + t
106
- });
107
- this.head = s, r || (this.anchor = { ...s }), this.host.requestUpdate();
108
- }
109
- /** Move to a specific cell */
110
- moveTo(e, t, r = !1) {
111
- const s = this.clamp({ row: e, col: t });
112
- this.head = s, r || (this.anchor = { ...s }), this.host.requestUpdate();
113
- }
114
- /** Select all cells */
115
- selectAll() {
116
- this.anchor = { row: 0, col: 0 }, this.head = { row: this._maxRows - 1, col: this._maxCols - 1 }, this.host.requestUpdate();
117
- }
118
- /** Clear selection (collapse to active cell) */
119
- clearSelection() {
120
- this.anchor = { ...this.head }, this.host.requestUpdate();
121
- }
122
- // ─── Helpers ────────────────────────────────────────
123
- clamp(e) {
124
- return {
125
- row: Math.max(0, Math.min(e.row, this._maxRows - 1)),
126
- col: Math.max(0, Math.min(e.col, this._maxCols - 1))
127
- };
128
- }
47
+ function _(e) {
48
+ let t = "", n = e;
49
+ for (; n >= 0;) t = String.fromCharCode(n % 26 + 65) + t, n = Math.floor(n / 26) - 1;
50
+ return t;
129
51
  }
130
- const W = 64, H = /* @__PURE__ */ new Set([
131
- "SUM",
132
- "AVERAGE",
133
- "MIN",
134
- "MAX",
135
- "COUNT",
136
- "COUNTA",
137
- "CONCAT"
138
- ]), q = /* @__PURE__ */ new Set(["NOW"]);
139
- class x {
140
- constructor(e, t, r) {
141
- this.values = e, this.rows = t, this.cols = r;
142
- }
143
- get(e, t) {
144
- return this.values[e * this.cols + t];
145
- }
146
- getRow(e) {
147
- const t = e * this.cols;
148
- return this.values.slice(t, t + this.cols);
149
- }
150
- getCol(e) {
151
- const t = [];
152
- for (let r = 0; r < this.rows; r++)
153
- t.push(this.values[r * this.cols + e]);
154
- return t;
155
- }
52
+ function v(e) {
53
+ let t = 0;
54
+ for (let n = 0; n < e.length; n++) t = t * 26 + (e.charCodeAt(n) - 64);
55
+ return t - 1;
156
56
  }
157
- function V(i, e) {
158
- let t = "=", r = e;
159
- e.startsWith("<>") ? (t = "<>", r = e.substring(2)) : e.startsWith(">=") ? (t = ">=", r = e.substring(2)) : e.startsWith("<=") ? (t = "<=", r = e.substring(2)) : e.startsWith(">") ? (t = ">", r = e.substring(1)) : e.startsWith("<") ? (t = "<", r = e.substring(1)) : e.startsWith("=") && (t = "=", r = e.substring(1));
160
- const s = Number(r), o = Number(i), n = !isNaN(s) && !isNaN(o) && r.trim() !== "" && String(i).trim() !== "", a = String(i).toLowerCase(), l = r.toLowerCase();
161
- switch (t) {
162
- case "=":
163
- return n ? o === s : a === l;
164
- case "<>":
165
- return n ? o !== s : a !== l;
166
- case ">":
167
- return n ? o > s : a > l;
168
- case "<":
169
- return n ? o < s : a < l;
170
- case ">=":
171
- return n ? o >= s : a >= l;
172
- case "<=":
173
- return n ? o <= s : a <= l;
174
- default:
175
- return !1;
176
- }
57
+ function y(e) {
58
+ let t = e.replace(/\$/g, "").match(/^([A-Z]+)(\d+)$/i);
59
+ if (!t) throw Error(`Invalid cell reference: ${e}`);
60
+ let n = v(t[1].toUpperCase());
61
+ return {
62
+ row: parseInt(t[2], 10) - 1,
63
+ col: n
64
+ };
177
65
  }
178
- class K {
179
- constructor() {
180
- this._functions = /* @__PURE__ */ new Map(), this._data = /* @__PURE__ */ new Map(), this._evaluating = /* @__PURE__ */ new Set(), this._evalDepth = 0, this._deps = /* @__PURE__ */ new Map(), this._reverseDeps = /* @__PURE__ */ new Map(), this._trackingCellKey = null, this._cache = /* @__PURE__ */ new Map(), this.registerBuiltins();
181
- }
182
- /** Register a user-defined function */
183
- registerFunction(e, t) {
184
- this._functions.set(e.toUpperCase(), t);
185
- }
186
- /** Update the data reference for evaluation (full reset of dep graph and cache) */
187
- setData(e) {
188
- this._data = e, this._deps.clear(), this._reverseDeps.clear(), this._cache.clear();
189
- }
190
- /**
191
- * Update the data reference while preserving the dependency graph for
192
- * unchanged cells. Only clears deps and cache for the specified changed keys,
193
- * allowing recalculateAffected() to use the existing graph for BFS traversal.
194
- */
195
- updateData(e, t) {
196
- this._data = e;
197
- for (const r of t)
198
- this._clearDepsFor(r);
199
- }
200
- /**
201
- * Evaluate a raw value. If it starts with '=', parse and execute the formula.
202
- * Otherwise, return the value as-is (possibly coerced).
203
- *
204
- * @param forCellKey - If provided, tracks formula dependencies for this cell key.
205
- */
206
- evaluate(e, t) {
207
- if (!e || e.trim() === "")
208
- return t && (this._clearDepsFor(t), this._cache.delete(t)), { displayValue: "", type: "text" };
209
- if (!e.startsWith("="))
210
- return t && (this._clearDepsFor(t), this._cache.delete(t)), this.coerceValue(e);
211
- const r = e.substring(1).toUpperCase(), s = [...q].some((o) => r.includes(o + "("));
212
- if (t && !s) {
213
- const o = this._cache.get(t);
214
- if (o)
215
- return { displayValue: o.displayValue, type: o.type };
216
- }
217
- try {
218
- t && (this._clearDepsFor(t), this._trackingCellKey = t);
219
- const o = e.substring(1), n = this.parseExpression(o), a = this.coerceValue(String(n));
220
- return t && !s && this._cache.set(t, { displayValue: a.displayValue, type: a.type }), a;
221
- } catch (o) {
222
- const n = o instanceof Error ? o.message : "", a = n.startsWith("#") ? { displayValue: n, type: "error" } : { displayValue: "#ERROR!", type: "error" };
223
- return t && !s && this._cache.set(t, a), a;
224
- } finally {
225
- this._trackingCellKey = null;
226
- }
227
- }
228
- /**
229
- * Re-evaluate all formula cells in the grid.
230
- * Rebuilds the dependency graph. Returns a set of cell keys that changed.
231
- */
232
- recalculate() {
233
- this._deps.clear(), this._reverseDeps.clear(), this._cache.clear();
234
- const e = /* @__PURE__ */ new Set();
235
- for (const [t, r] of this._data)
236
- if (r.rawValue.startsWith("=")) {
237
- const s = this.evaluate(r.rawValue, t);
238
- (r.displayValue !== s.displayValue || r.type !== s.type) && (r.displayValue = s.displayValue, r.type = s.type, e.add(t));
239
- }
240
- return e;
241
- }
242
- /**
243
- * Recalculate only formulas affected by the given changed cell keys.
244
- * Uses the dependency graph for targeted recalculation (BFS order).
245
- */
246
- recalculateAffected(e) {
247
- if (this._reverseDeps.size === 0 && this._data.size > 0)
248
- return this.recalculate();
249
- const t = /* @__PURE__ */ new Set(), r = new Set(e);
250
- for (const a of e)
251
- this._cache.delete(a);
252
- for (const a of e) {
253
- const l = this._data.get(a);
254
- if (l != null && l.rawValue.startsWith("=")) {
255
- const c = this.evaluate(l.rawValue, a);
256
- (l.displayValue !== c.displayValue || l.type !== c.type) && (l.displayValue = c.displayValue, l.type = c.type, t.add(a));
257
- }
258
- }
259
- const s = [], o = /* @__PURE__ */ new Set(), n = [...e];
260
- for (; n.length > 0; ) {
261
- const a = n.shift(), l = this._reverseDeps.get(a);
262
- if (l)
263
- for (const c of l)
264
- !o.has(c) && !r.has(c) && (o.add(c), s.push(c), n.push(c));
265
- }
266
- for (const a of s)
267
- this._cache.delete(a);
268
- for (const a of s) {
269
- const l = this._data.get(a);
270
- if (l != null && l.rawValue.startsWith("=")) {
271
- const c = this.evaluate(l.rawValue, a);
272
- (l.displayValue !== c.displayValue || l.type !== c.type) && (l.displayValue = c.displayValue, l.type = c.type, t.add(a));
273
- }
274
- }
275
- return t;
276
- }
277
- // ─── Dependency Tracking ─────────────────────────────
278
- _clearDepsFor(e) {
279
- var r;
280
- const t = this._deps.get(e);
281
- if (t) {
282
- for (const s of t)
283
- (r = this._reverseDeps.get(s)) == null || r.delete(e);
284
- this._deps.delete(e);
285
- }
286
- this._cache.delete(e);
287
- }
288
- _trackDep(e) {
289
- if (!this._trackingCellKey) return;
290
- let t = this._deps.get(this._trackingCellKey);
291
- t || (t = /* @__PURE__ */ new Set(), this._deps.set(this._trackingCellKey, t)), t.add(e);
292
- let r = this._reverseDeps.get(e);
293
- r || (r = /* @__PURE__ */ new Set(), this._reverseDeps.set(e, r)), r.add(this._trackingCellKey);
294
- }
295
- // ─── Value Coercion ──────────────────────────────────
296
- coerceValue(e) {
297
- if (e === "#ERROR!" || e === "#REF!" || e === "#DIV/0!" || e === "#NAME?" || e === "#CIRC!" || e === "#VALUE!")
298
- return { displayValue: e, type: "error" };
299
- const t = e.toUpperCase();
300
- if (t === "TRUE" || t === "FALSE")
301
- return { displayValue: t, type: "boolean" };
302
- const r = Number(e);
303
- return e.trim() !== "" && !isNaN(r) ? { displayValue: String(r), type: "number" } : { displayValue: e, type: "text" };
304
- }
305
- // ─── Lexer ───────────────────────────────────────────
306
- tokenize(e) {
307
- const t = [];
308
- let r = 0;
309
- for (; r < e.length; ) {
310
- const s = e[r];
311
- if (/\s/.test(s)) {
312
- r++;
313
- continue;
314
- }
315
- if (s === '"') {
316
- let o = "";
317
- for (r++; r < e.length && e[r] !== '"'; )
318
- o += e[r], r++;
319
- r++, t.push({ type: "STRING", value: o });
320
- continue;
321
- }
322
- if (/\d/.test(s) || s === "." && r + 1 < e.length && /\d/.test(e[r + 1])) {
323
- let o = "";
324
- for (; r < e.length && (/\d/.test(e[r]) || e[r] === "."); )
325
- o += e[r], r++;
326
- t.push({ type: "NUMBER", value: o });
327
- continue;
328
- }
329
- if (s === "+" || s === "-" || s === "*" || s === "/" || s === "&") {
330
- t.push({ type: "OPERATOR", value: s }), r++;
331
- continue;
332
- }
333
- if (s === "<" || s === ">") {
334
- r + 1 < e.length && e[r + 1] === "=" ? (t.push({ type: "OPERATOR", value: s + "=" }), r += 2) : s === "<" && r + 1 < e.length && e[r + 1] === ">" ? (t.push({ type: "OPERATOR", value: "<>" }), r += 2) : (t.push({ type: "OPERATOR", value: s }), r++);
335
- continue;
336
- }
337
- if (s === "=") {
338
- t.push({ type: "OPERATOR", value: "=" }), r++;
339
- continue;
340
- }
341
- if (s === "(") {
342
- t.push({ type: "LPAREN", value: "(" }), r++;
343
- continue;
344
- }
345
- if (s === ")") {
346
- t.push({ type: "RPAREN", value: ")" }), r++;
347
- continue;
348
- }
349
- if (s === ",") {
350
- t.push({ type: "COMMA", value: "," }), r++;
351
- continue;
352
- }
353
- if (/[A-Za-z_$]/.test(s)) {
354
- let o = "";
355
- for (; r < e.length && /[A-Za-z0-9_$]/.test(e[r]); )
356
- o += e[r], r++;
357
- const n = o.replace(/\$/g, ""), a = n !== o, l = n.toUpperCase();
358
- if (!a && (l === "TRUE" || l === "FALSE")) {
359
- t.push({ type: "BOOLEAN", value: l });
360
- continue;
361
- }
362
- if (r < e.length && e[r] === ":" && /^[A-Z]+\d+$/i.test(n)) {
363
- r++;
364
- let c = "";
365
- for (; r < e.length && /[A-Za-z0-9$]/.test(e[r]); )
366
- c += e[r], r++;
367
- const h = c.replace(/\$/g, "");
368
- if (/^[A-Z]+\d+$/i.test(h)) {
369
- t.push({ type: "RANGE", value: `${o.toUpperCase()}:${c.toUpperCase()}` });
370
- continue;
371
- }
372
- throw new Error(`Invalid range: ${o}:${c}`);
373
- }
374
- if (!a) {
375
- let c = r;
376
- for (; c < e.length && /\s/.test(e[c]); ) c++;
377
- if (c < e.length && e[c] === "(") {
378
- t.push({ type: "FUNC", value: l });
379
- continue;
380
- }
381
- }
382
- if (/^[A-Z]+\d+$/i.test(n)) {
383
- t.push({ type: "REF", value: o.toUpperCase() });
384
- continue;
385
- }
386
- if (!a) {
387
- t.push({ type: "FUNC", value: l });
388
- continue;
389
- }
390
- throw new Error(`Unexpected identifier: ${o}`);
391
- }
392
- throw new Error(`Unexpected character: ${s}`);
393
- }
394
- return t.push({ type: "EOF", value: "" }), t;
395
- }
396
- // ─── Parser ──────────────────────────────────────────
397
- //
398
- // Parser state is passed as a mutable object through the recursive
399
- // descent chain so that nested evaluations (resolveRef / resolveRange)
400
- // each get their own independent state without save/restore.
401
- _peek(e) {
402
- return e.tokens[e.pos];
403
- }
404
- _consume(e, t) {
405
- const r = e.tokens[e.pos];
406
- if (t && r.type !== t)
407
- throw new Error(`Expected ${t} but got ${r.type} (${r.value})`);
408
- return e.pos++, r;
409
- }
410
- parseExpression(e) {
411
- if (++this._evalDepth > W)
412
- throw this._evalDepth--, new Error("#ERROR!");
413
- try {
414
- const t = { tokens: this.tokenize(e), pos: 0 };
415
- return this._parseComparison(t);
416
- } finally {
417
- this._evalDepth--;
418
- }
419
- }
420
- _parseComparison(e) {
421
- let t = this._parseConcatenation(e);
422
- for (; this._peek(e).type === "OPERATOR" && ["=", "<>", "<", ">", "<=", ">="].includes(this._peek(e).value); ) {
423
- const r = this._consume(e).value, s = this._parseConcatenation(e);
424
- t = this._compareValues(t, s, r);
425
- }
426
- return t;
427
- }
428
- _parseConcatenation(e) {
429
- let t = this._parseAddSub(e);
430
- for (; this._peek(e).type === "OPERATOR" && this._peek(e).value === "&"; ) {
431
- this._consume(e);
432
- const r = this._parseAddSub(e);
433
- t = String(t) + String(r);
434
- }
435
- return t;
436
- }
437
- _parseAddSub(e) {
438
- let t = this._parseMulDiv(e);
439
- for (; this._peek(e).type === "OPERATOR" && (this._peek(e).value === "+" || this._peek(e).value === "-"); ) {
440
- const r = this._consume(e).value, s = this._parseMulDiv(e);
441
- r === "+" ? t = Number(t) + Number(s) : t = Number(t) - Number(s);
442
- }
443
- return t;
444
- }
445
- _parseMulDiv(e) {
446
- let t = this._parseUnary(e);
447
- for (; this._peek(e).type === "OPERATOR" && (this._peek(e).value === "*" || this._peek(e).value === "/"); ) {
448
- const r = this._consume(e).value, s = this._parseUnary(e);
449
- if (r === "*") t = Number(t) * Number(s);
450
- else {
451
- const o = Number(s);
452
- if (o === 0) throw new Error("#DIV/0!");
453
- t = Number(t) / o;
454
- }
455
- }
456
- return t;
457
- }
458
- _parseUnary(e) {
459
- return this._peek(e).type === "OPERATOR" && this._peek(e).value === "-" ? (this._consume(e), -Number(this._parsePrimary(e))) : this._peek(e).type === "OPERATOR" && this._peek(e).value === "+" ? (this._consume(e), Number(this._parsePrimary(e))) : this._parsePrimary(e);
460
- }
461
- _parsePrimary(e) {
462
- const t = this._peek(e);
463
- switch (t.type) {
464
- case "NUMBER":
465
- return this._consume(e), parseFloat(t.value);
466
- case "STRING":
467
- return this._consume(e), t.value;
468
- case "BOOLEAN":
469
- return this._consume(e), t.value === "TRUE";
470
- case "REF":
471
- return this._consume(e), this._resolveRef(t.value);
472
- case "RANGE":
473
- return this._consume(e), this._resolveRange(t.value);
474
- case "FUNC":
475
- return this._parseFunction(e);
476
- case "LPAREN":
477
- this._consume(e);
478
- const r = this._parseComparison(e);
479
- return this._consume(e, "RPAREN"), r;
480
- default:
481
- throw new Error(`Unexpected token: ${t.type} (${t.value})`);
482
- }
483
- }
484
- _parseFunction(e) {
485
- const t = this._consume(e, "FUNC").value;
486
- this._consume(e, "LPAREN");
487
- const r = [];
488
- if (this._peek(e).type !== "RPAREN")
489
- for (r.push(this._parseComparison(e)); this._peek(e).type === "COMMA"; )
490
- this._consume(e), r.push(this._parseComparison(e));
491
- this._consume(e, "RPAREN");
492
- const s = this._functions.get(t);
493
- if (!s) throw new Error("#NAME?");
494
- const o = this._createContext();
495
- if (H.has(t)) {
496
- const n = [];
497
- for (const a of r)
498
- if (a instanceof x)
499
- for (const l of a.values)
500
- l !== void 0 && n.push(l);
501
- else Array.isArray(a) ? n.push(...a) : n.push(a);
502
- return s(o, ...n);
503
- }
504
- return s(o, ...r);
505
- }
506
- // ─── Comparison helper ────────────────────────────────
507
- _compareValues(e, t, r) {
508
- const s = (l) => {
509
- if (typeof l == "boolean" || typeof l == "number") return !0;
510
- const c = String(l).trim();
511
- return c !== "" && !isNaN(Number(c));
512
- };
513
- if (s(e) && s(t)) {
514
- const l = Number(e), c = Number(t);
515
- switch (r) {
516
- case "=":
517
- return l === c;
518
- case "<>":
519
- return l !== c;
520
- case "<":
521
- return l < c;
522
- case ">":
523
- return l > c;
524
- case "<=":
525
- return l <= c;
526
- case ">=":
527
- return l >= c;
528
- default:
529
- return !1;
530
- }
531
- }
532
- const n = String(e ?? ""), a = String(t ?? "");
533
- switch (r) {
534
- case "=":
535
- return n === a;
536
- case "<>":
537
- return n !== a;
538
- case "<":
539
- return n < a;
540
- case ">":
541
- return n > a;
542
- case "<=":
543
- return n <= a;
544
- case ">=":
545
- return n >= a;
546
- default:
547
- return !1;
548
- }
549
- }
550
- // ─── Reference resolution ───────────────────────────
551
- //
552
- // Each nested evaluation calls parseExpression which creates its
553
- // own ParserState, so no save/restore is needed.
554
- _resolveRef(e) {
555
- const t = R(e.replace(/\$/g, "")), r = b(t.row, t.col);
556
- if (this._trackDep(r), this._evaluating.has(r))
557
- throw new Error("#CIRC!");
558
- const s = this._data.get(r);
559
- if (!s) return 0;
560
- if (s.rawValue.startsWith("=")) {
561
- this._evaluating.add(r);
562
- try {
563
- return this.parseExpression(s.rawValue.substring(1));
564
- } catch {
565
- throw new Error("#ERROR!");
566
- } finally {
567
- this._evaluating.delete(r);
568
- }
569
- }
570
- const o = Number(s.rawValue);
571
- return !isNaN(o) && s.rawValue.trim() !== "" ? o : s.rawValue.toUpperCase() === "TRUE" ? !0 : s.rawValue.toUpperCase() === "FALSE" ? !1 : s.rawValue;
572
- }
573
- _resolveRange(e) {
574
- const [t, r] = e.split(":"), s = R(t.replace(/\$/g, "")), o = R(r.replace(/\$/g, "")), n = Math.min(s.row, o.row), a = Math.max(s.row, o.row), l = Math.min(s.col, o.col), c = Math.max(s.col, o.col), h = a - n + 1, u = c - l + 1, p = [];
575
- for (let f = n; f <= a; f++)
576
- for (let g = l; g <= c; g++) {
577
- const _ = b(f, g);
578
- this._trackDep(_);
579
- const d = this._data.get(_);
580
- if (d)
581
- if (d.rawValue.startsWith("="))
582
- if (this._evaluating.has(_))
583
- p.push("#CIRC!");
584
- else {
585
- this._evaluating.add(_);
586
- try {
587
- p.push(this.parseExpression(d.rawValue.substring(1)));
588
- } catch ($) {
589
- const B = $ instanceof Error ? $.message : "";
590
- p.push(B.startsWith("#") ? B : "#ERROR!");
591
- } finally {
592
- this._evaluating.delete(_);
593
- }
594
- }
595
- else {
596
- const $ = Number(d.rawValue);
597
- !isNaN($) && d.rawValue.trim() !== "" ? p.push($) : d.rawValue.toUpperCase() === "TRUE" ? p.push(!0) : d.rawValue.toUpperCase() === "FALSE" ? p.push(!1) : p.push(d.rawValue);
598
- }
599
- else
600
- p.push(void 0);
601
- }
602
- return new x(p, h, u);
603
- }
604
- _createContext() {
605
- return {
606
- getCellValue: (e) => {
607
- if (e.includes(":") && !/[A-Z]/i.test(e.charAt(0))) {
608
- const t = this._data.get(e);
609
- if (!t) return 0;
610
- const r = Number(t.rawValue);
611
- return !isNaN(r) && t.rawValue.trim() !== "" ? r : t.rawValue;
612
- }
613
- return this._resolveRef(e);
614
- },
615
- getRangeValues: (e, t) => {
616
- const r = R(e), s = R(t), o = Math.min(r.row, s.row), n = Math.max(r.row, s.row), a = Math.min(r.col, s.col), l = Math.max(r.col, s.col), c = [];
617
- for (let h = o; h <= n; h++)
618
- for (let u = a; u <= l; u++) {
619
- const p = b(h, u), f = this._data.get(p);
620
- if (f) {
621
- const g = Number(f.rawValue);
622
- c.push(!isNaN(g) && f.rawValue.trim() !== "" ? g : f.rawValue);
623
- }
624
- }
625
- return c;
626
- }
627
- };
628
- }
629
- // ─── Built-in Functions ─────────────────────────────
630
- registerBuiltins() {
631
- this.registerFunction("SUM", (e, ...t) => t.reduce((r, s) => r + (Number(s) || 0), 0)), this.registerFunction("AVERAGE", (e, ...t) => {
632
- const r = t.filter((o) => typeof o == "number" || !isNaN(Number(o)));
633
- return r.length === 0 ? 0 : r.reduce((o, n) => o + Number(n), 0) / r.length;
634
- }), this.registerFunction("MIN", (e, ...t) => {
635
- const r = t.map(Number).filter((s) => !isNaN(s));
636
- return r.length ? Math.min(...r) : 0;
637
- }), this.registerFunction("MAX", (e, ...t) => {
638
- const r = t.map(Number).filter((s) => !isNaN(s));
639
- return r.length ? Math.max(...r) : 0;
640
- }), this.registerFunction("COUNT", (e, ...t) => t.filter((r) => typeof r == "number" || !isNaN(Number(r)) && String(r).trim() !== "").length), this.registerFunction("COUNTA", (e, ...t) => t.filter((r) => r !== "" && r !== null && r !== void 0).length), this.registerFunction("IF", (e, t, r, s) => t ? r : s), this.registerFunction("CONCAT", (e, ...t) => t.map(String).join("")), this.registerFunction("ABS", (e, t) => Math.abs(Number(t))), this.registerFunction("ROUND", (e, t, r) => {
641
- const s = Number(r) || 0, o = Math.pow(10, s);
642
- return Math.round(Number(t) * o) / o;
643
- }), this.registerFunction("UPPER", (e, t) => String(t).toUpperCase()), this.registerFunction("LOWER", (e, t) => String(t).toLowerCase()), this.registerFunction("LEN", (e, t) => String(t).length), this.registerFunction("TRIM", (e, t) => String(t).trim()), this.registerFunction("IFERROR", (e, t, r) => {
644
- const s = String(t);
645
- return s === "#ERROR!" || s === "#REF!" || s === "#DIV/0!" || s === "#NAME?" || s === "#CIRC!" || s === "#VALUE!" ? r : t;
646
- }), this.registerFunction("AND", (e, ...t) => {
647
- for (const r of t)
648
- if (r instanceof x) {
649
- for (const s of r.values)
650
- if (s !== void 0 && !s) return !1;
651
- } else if (!r) return !1;
652
- return !0;
653
- }), this.registerFunction("OR", (e, ...t) => {
654
- for (const r of t)
655
- if (r instanceof x) {
656
- for (const s of r.values)
657
- if (s !== void 0 && s) return !0;
658
- } else if (r) return !0;
659
- return !1;
660
- }), this.registerFunction("NOT", (e, t) => !t), this.registerFunction("SUMIF", (e, t, r, s) => {
661
- const o = String(r), n = t instanceof x ? t.values : Array.isArray(t) ? t : [t], a = s instanceof x ? s.values : Array.isArray(s) ? s : void 0;
662
- let l = 0;
663
- for (let c = 0; c < n.length; c++) {
664
- const h = n[c];
665
- h !== void 0 && V(h, o) && (a ? l += Number(a[c]) || 0 : l += Number(h) || 0);
666
- }
667
- return l;
668
- }), this.registerFunction("COUNTIF", (e, t, r) => {
669
- const s = String(r), o = t instanceof x ? t.values : Array.isArray(t) ? t : [t];
670
- let n = 0;
671
- for (const a of o)
672
- a !== void 0 && V(a, s) && n++;
673
- return n;
674
- }), this.registerFunction("AVERAGEIF", (e, t, r, s) => {
675
- const o = String(r), n = t instanceof x ? t.values : Array.isArray(t) ? t : [t], a = s instanceof x ? s.values : Array.isArray(s) ? s : void 0;
676
- let l = 0, c = 0;
677
- for (let h = 0; h < n.length; h++) {
678
- const u = n[h];
679
- if (u !== void 0 && V(u, o)) {
680
- const p = Number(a ? a[h] : u);
681
- isNaN(p) || (l += p, c++);
682
- }
683
- }
684
- if (c === 0) throw new Error("#DIV/0!");
685
- return l / c;
686
- }), this.registerFunction("VLOOKUP", (e, t, r, s, o) => {
687
- if (!(r instanceof x))
688
- throw new Error("#VALUE!");
689
- const n = Number(s);
690
- if (n < 1 || n > r.cols)
691
- throw new Error("#REF!");
692
- const a = o === !1 || o === 0, l = r.getCol(0);
693
- for (let c = 0; c < l.length; c++)
694
- if (a) {
695
- const h = Number(t), u = Number(l[c]);
696
- if (!isNaN(h) && !isNaN(u) && String(t).trim() !== "" && String(l[c]).trim() !== "" ? h === u : String(l[c]).toLowerCase() === String(t).toLowerCase())
697
- return r.get(c, n - 1);
698
- } else if (Number(l[c]) > Number(t)) {
699
- if (c === 0) throw new Error("#N/A");
700
- return r.get(c - 1, n - 1);
701
- }
702
- if (!a && l.length > 0)
703
- return r.get(l.length - 1, n - 1);
704
- throw new Error("#N/A");
705
- }), this.registerFunction("HLOOKUP", (e, t, r, s, o) => {
706
- if (!(r instanceof x))
707
- throw new Error("#VALUE!");
708
- const n = Number(s);
709
- if (n < 1 || n > r.rows)
710
- throw new Error("#REF!");
711
- const a = o === !1 || o === 0, l = r.getRow(0);
712
- for (let c = 0; c < l.length; c++)
713
- if (a) {
714
- const h = Number(t), u = Number(l[c]);
715
- if (!isNaN(h) && !isNaN(u) && String(t).trim() !== "" && String(l[c]).trim() !== "" ? h === u : String(l[c]).toLowerCase() === String(t).toLowerCase())
716
- return r.get(n - 1, c);
717
- } else if (Number(l[c]) > Number(t)) {
718
- if (c === 0) throw new Error("#N/A");
719
- return r.get(n - 1, c - 1);
720
- }
721
- if (!a && l.length > 0)
722
- return r.get(n - 1, l.length - 1);
723
- throw new Error("#N/A");
724
- }), this.registerFunction("INDEX", (e, t, r, s) => {
725
- if (t instanceof x) {
726
- const o = Number(r) - 1, n = s !== void 0 ? Number(s) - 1 : 0;
727
- if (o < 0 || o >= t.rows || n < 0 || n >= t.cols)
728
- throw new Error("#REF!");
729
- const a = t.get(o, n);
730
- return a !== void 0 ? a : 0;
731
- }
732
- throw new Error("#VALUE!");
733
- }), this.registerFunction("MATCH", (e, t, r, s) => {
734
- let o;
735
- if (r instanceof x)
736
- o = r.values;
737
- else if (Array.isArray(r))
738
- o = r;
739
- else
740
- throw new Error("#VALUE!");
741
- const n = s !== void 0 ? Number(s) : 0;
742
- if (n === 0) {
743
- for (let a = 0; a < o.length; a++) {
744
- const l = Number(t), c = Number(o[a]);
745
- if (!isNaN(l) && !isNaN(c) && String(t).trim() !== "" && String(o[a]).trim() !== "" ? l === c : String(o[a]).toLowerCase() === String(t).toLowerCase())
746
- return a + 1;
747
- }
748
- throw new Error("#N/A");
749
- } else if (n === 1) {
750
- let a = -1;
751
- for (let l = 0; l < o.length; l++)
752
- Number(o[l]) <= Number(t) && (a = l);
753
- if (a === -1) throw new Error("#N/A");
754
- return a + 1;
755
- } else {
756
- let a = -1;
757
- for (let l = 0; l < o.length; l++)
758
- Number(o[l]) >= Number(t) && (a = l);
759
- if (a === -1) throw new Error("#N/A");
760
- return a + 1;
761
- }
762
- }), this.registerFunction("MOD", (e, t, r) => {
763
- const s = Number(r);
764
- if (s === 0) throw new Error("#DIV/0!");
765
- return Number(t) % s;
766
- }), this.registerFunction("POWER", (e, t, r) => Math.pow(Number(t), Number(r))), this.registerFunction("CEILING", (e, t, r) => {
767
- const s = Number(t), o = r !== void 0 ? Number(r) : 1;
768
- return o === 0 ? 0 : Math.ceil(s / o) * o;
769
- }), this.registerFunction("FLOOR", (e, t, r) => {
770
- const s = Number(t), o = r !== void 0 ? Number(r) : 1;
771
- return o === 0 ? 0 : Math.floor(s / o) * o;
772
- }), this.registerFunction("LEFT", (e, t, r) => {
773
- const s = r !== void 0 ? Number(r) : 1;
774
- return String(t).substring(0, s);
775
- }), this.registerFunction("RIGHT", (e, t, r) => {
776
- const s = String(t), o = r !== void 0 ? Number(r) : 1;
777
- return s.substring(s.length - o);
778
- }), this.registerFunction("MID", (e, t, r, s) => {
779
- const o = String(t), n = Number(r) - 1, a = Number(s);
780
- return o.substring(n, n + a);
781
- }), this.registerFunction("SUBSTITUTE", (e, t, r, s, o) => {
782
- const n = String(t), a = String(r), l = String(s);
783
- if (o !== void 0) {
784
- const c = Number(o);
785
- let h = 0, u = -1, p = 0;
786
- for (; p < n.length && (u = n.indexOf(a, p), u !== -1); ) {
787
- if (h++, h === c)
788
- return n.substring(0, u) + l + n.substring(u + a.length);
789
- p = u + 1;
790
- }
791
- return n;
792
- }
793
- return n.split(a).join(l);
794
- }), this.registerFunction("FIND", (e, t, r, s) => {
795
- const o = String(r), n = String(t), a = s !== void 0 ? Number(s) - 1 : 0, l = o.indexOf(n, a);
796
- if (l === -1) throw new Error("#VALUE!");
797
- return l + 1;
798
- }), this.registerFunction("TEXT", (e, t, r) => {
799
- const s = Number(t), o = String(r);
800
- if (isNaN(s)) return String(t);
801
- if (o === "0")
802
- return Math.round(s).toString();
803
- if (o === "0.00" || o === "0.0") {
804
- const n = (o.split(".")[1] || "").length;
805
- return s.toFixed(n);
806
- }
807
- if (o === "#,##0" || o === "#,##0.00") {
808
- const n = o.includes(".") ? (o.split(".")[1] || "").length : 0;
809
- return s.toLocaleString("en-US", {
810
- minimumFractionDigits: n,
811
- maximumFractionDigits: n
812
- });
813
- }
814
- return s.toString();
815
- }), this.registerFunction("VALUE", (e, t) => {
816
- const r = Number(String(t));
817
- if (isNaN(r)) throw new Error("#VALUE!");
818
- return r;
819
- }), this.registerFunction("DATE", (e, t, r, s) => {
820
- const o = Number(t), n = Number(r), a = Number(s), l = new Date(o, n - 1, a), c = new Date(1899, 11, 30), h = l.getTime() - c.getTime();
821
- return Math.round(h / (1e3 * 60 * 60 * 24));
822
- }), this.registerFunction("NOW", () => {
823
- const e = /* @__PURE__ */ new Date(), t = new Date(1899, 11, 30);
824
- return (e.getTime() - t.getTime()) / (1e3 * 60 * 60 * 24);
825
- });
826
- }
66
+ function b(e) {
67
+ return `${_(e.col)}${e.row + 1}`;
827
68
  }
828
- class G {
829
- constructor() {
830
- this._copySourceRange = null, this._copySourceData = null;
831
- }
832
- /**
833
- * Copy the selected range to the clipboard as both HTML table and TSV.
834
- * Falls back to text-only if ClipboardItem API is unavailable.
835
- */
836
- async copy(e, t) {
837
- this._copySourceRange = t, this._copySourceData = e;
838
- const r = this.serializeRange(e, t), s = this._serializeRangeAsHTML(e, t);
839
- try {
840
- const o = new Blob([s], { type: "text/html" }), n = new Blob([r], { type: "text/plain" }), a = new ClipboardItem({
841
- "text/html": o,
842
- "text/plain": n
843
- });
844
- await navigator.clipboard.write([a]);
845
- } catch {
846
- try {
847
- await navigator.clipboard.writeText(r);
848
- } catch {
849
- this.fallbackCopy(r);
850
- }
851
- }
852
- }
853
- /**
854
- * Cut: copy to clipboard and return the keys that should be cleared.
855
- */
856
- async cut(e, t) {
857
- await this.copy(e, t);
858
- const r = [];
859
- for (let s = t.start.row; s <= t.end.row; s++)
860
- for (let o = t.start.col; o <= t.end.col; o++)
861
- r.push(b(s, o));
862
- return r;
863
- }
864
- /**
865
- * Parse clipboard content and return the updates to apply.
866
- * If data was copied internally, uses raw values with reference adjustment.
867
- * Otherwise tries HTML table format first, then falls back to TSV text.
868
- * The target is the top-left cell where paste begins.
869
- */
870
- async paste(e, t, r, s) {
871
- if (this._copySourceRange && this._copySourceData) {
872
- const n = this._pasteInternal(e, t, r, s);
873
- if (n) return n;
874
- }
875
- try {
876
- const n = await navigator.clipboard.read();
877
- for (const a of n)
878
- if (a.types.includes("text/html")) {
879
- const c = await (await a.getType("text/html")).text(), h = this.parseHTMLTableWithFormat(c);
880
- if (h)
881
- return this._applyParsedRowsWithFormat(h, e, t, r, s);
882
- }
883
- } catch {
884
- }
885
- let o;
886
- try {
887
- o = await navigator.clipboard.readText();
888
- } catch {
889
- return null;
890
- }
891
- return this.parseTSV(o, e, t, r, s);
892
- }
893
- /**
894
- * Paste using internally stored copy data with formula reference adjustment.
895
- * Returns null if no internal data is available.
896
- */
897
- _pasteInternal(e, t, r, s) {
898
- const o = this._copySourceRange, n = this._copySourceData;
899
- if (!o || !n) return null;
900
- const a = e - o.start.row, l = t - o.start.col, c = [];
901
- for (let h = o.start.row; h <= o.end.row; h++)
902
- for (let u = o.start.col; u <= o.end.col; u++) {
903
- const p = h + a, f = u + l;
904
- if (p >= r || f >= s) continue;
905
- const g = n.get(b(h, u));
906
- let _ = (g == null ? void 0 : g.rawValue) ?? "";
907
- _.startsWith("=") && (_ = this.adjustFormulaReferences(_, a, l));
908
- const d = { id: b(p, f), value: _ };
909
- g != null && g.format && (d.format = { ...g.format }), c.push(d);
910
- }
911
- return c;
912
- }
913
- /**
914
- * Parse an HTML string and extract table data with format information.
915
- * Prefers data-format (lossless) over inline styles (best-effort).
916
- */
917
- parseHTMLTableWithFormat(e) {
918
- const s = new DOMParser().parseFromString(e, "text/html").querySelector("table");
919
- if (!s) return null;
920
- const o = [], n = s.querySelectorAll("tr");
921
- for (const a of n) {
922
- const l = [], c = a.querySelectorAll("td, th");
923
- for (const h of c) {
924
- const u = h, f = u.getAttribute("data-raw") ?? (h.textContent ?? "").trim();
925
- let g;
926
- const _ = u.getAttribute("data-format");
927
- if (_)
928
- try {
929
- g = this._sanitizeFormat(JSON.parse(_));
930
- } catch {
931
- }
932
- if (!g) {
933
- const d = u.getAttribute("style");
934
- d && (g = this._parseStyleToFormat(d));
935
- }
936
- l.push({ value: f, format: g });
937
- }
938
- l.length > 0 && o.push(l);
939
- }
940
- return o.length > 0 ? o : null;
941
- }
942
- /**
943
- * Convert format-aware parsed rows into PasteUpdate array.
944
- */
945
- _applyParsedRowsWithFormat(e, t, r, s, o) {
946
- const n = [];
947
- for (let a = 0; a < e.length; a++)
948
- for (let l = 0; l < e[a].length; l++) {
949
- const c = t + a, h = r + l;
950
- if (c < s && h < o) {
951
- const { value: u, format: p } = e[a][l], f = { id: b(c, h), value: u };
952
- p && (f.format = p), n.push(f);
953
- }
954
- }
955
- return n;
956
- }
957
- /**
958
- * Parse a TSV string and generate cell updates.
959
- * Handles RFC 4180-style quoting: fields containing tabs, newlines, or
960
- * double-quotes are wrapped in double-quotes, with internal quotes escaped
961
- * as "". This is the format Excel and Google Sheets use on the clipboard.
962
- *
963
- * Exported for testability with paste events where text is already available.
964
- */
965
- parseTSV(e, t, r, s, o) {
966
- const n = this._parseTSVRows(e), a = [];
967
- for (let l = 0; l < n.length; l++) {
968
- const c = n[l];
969
- for (let h = 0; h < c.length; h++) {
970
- const u = t + l, p = r + h;
971
- u < s && p < o && a.push({ id: b(u, p), value: c[h] });
972
- }
973
- }
974
- return a;
975
- }
976
- /**
977
- * Parse TSV text into a 2D array of strings, respecting quoted fields.
978
- * Quoted fields may contain tabs, newlines, and escaped quotes ("").
979
- */
980
- _parseTSVRows(e) {
981
- const t = [];
982
- let r = [], s = "", o = !1, n = 0;
983
- for (; n < e.length; ) {
984
- const a = e[n];
985
- o ? a === '"' ? n + 1 < e.length && e[n + 1] === '"' ? (s += '"', n += 2) : (o = !1, n++) : (s += a, n++) : a === '"' ? (o = !0, n++) : a === " " ? (r.push(s), s = "", n++) : a === "\r" ? (r.push(s), s = "", n + 1 < e.length && e[n + 1] === `
986
- ` ? n += 2 : n++, t.push(r), r = []) : a === `
987
- ` ? (r.push(s), s = "", n++, t.push(r), r = []) : (s += a, n++);
988
- }
989
- return r.push(s), (r.length > 1 || r[0] !== "") && t.push(r), t;
990
- }
991
- // ─── Reference Adjustment ─────────────────────────
992
- /**
993
- * Adjust cell references in a formula by the given row/col offset.
994
- * Absolute references ($A$1) are not adjusted.
995
- * Mixed references ($A1, A$1) only adjust the non-absolute part.
996
- */
997
- adjustFormulaReferences(e, t, r) {
998
- if (!e.startsWith("=")) return e;
999
- const s = /(\$?)([A-Z]+)(\$?)(\d+)/gi;
1000
- return e.replace(s, (n, a, l, c, h) => {
1001
- const u = P(l.toUpperCase()), p = parseInt(h, 10) - 1, f = a === "$" ? u : u + r, g = c === "$" ? p : p + t;
1002
- return f < 0 || g < 0 ? "#REF!" : `${a}${F(f)}${c}${g + 1}`;
1003
- });
1004
- }
1005
- // ─── Serialization ──────────────────────────────────
1006
- serializeRange(e, t) {
1007
- const r = [];
1008
- for (let s = t.start.row; s <= t.end.row; s++) {
1009
- const o = [];
1010
- for (let n = t.start.col; n <= t.end.col; n++) {
1011
- const a = e.get(b(s, n)), l = (a == null ? void 0 : a.displayValue) ?? "";
1012
- o.push(this._quoteTSVField(l));
1013
- }
1014
- r.push(o.join(" "));
1015
- }
1016
- return r.join(`
1017
- `);
1018
- }
1019
- /** Quote a TSV field if it contains tabs, newlines, or double-quotes */
1020
- _quoteTSVField(e) {
1021
- return e.includes(" ") || e.includes(`
1022
- `) || e.includes("\r") || e.includes('"') ? `"${e.replace(/"/g, '""')}"` : e;
1023
- }
1024
- /**
1025
- * Convert a selection range to an HTML table string.
1026
- * Includes inline styles for visual formatting and data-format for lossless round-trip.
1027
- */
1028
- _serializeRangeAsHTML(e, t) {
1029
- const r = [];
1030
- for (let s = t.start.row; s <= t.end.row; s++) {
1031
- const o = [];
1032
- for (let n = t.start.col; n <= t.end.col; n++) {
1033
- const a = e.get(b(s, n)), l = (a == null ? void 0 : a.displayValue) ?? "", c = (a == null ? void 0 : a.rawValue) ?? "", h = [];
1034
- if (c && c !== l && h.push(`data-raw="${this._escapeHTML(c)}"`), a != null && a.format) {
1035
- const p = this._formatToInlineStyle(a.format);
1036
- p && h.push(`style="${this._escapeHTML(p)}"`), h.push(`data-format="${this._escapeHTML(JSON.stringify(a.format))}"`);
1037
- }
1038
- const u = h.length > 0 ? " " + h.join(" ") : "";
1039
- o.push(`<td${u}>${this._escapeHTML(l)}</td>`);
1040
- }
1041
- r.push(`<tr>${o.join("")}</tr>`);
1042
- }
1043
- return `<table>${r.join("")}</table>`;
1044
- }
1045
- /** Convert CellFormat to a CSS inline style string */
1046
- _formatToInlineStyle(e) {
1047
- const t = [];
1048
- e.bold && t.push("font-weight: bold"), e.italic && t.push("font-style: italic");
1049
- const r = [];
1050
- return e.underline && r.push("underline"), e.strikethrough && r.push("line-through"), r.length > 0 && t.push(`text-decoration: ${r.join(" ")}`), e.textColor && t.push(`color: ${e.textColor}`), e.backgroundColor && t.push(`background-color: ${e.backgroundColor}`), e.textAlign && t.push(`text-align: ${e.textAlign}`), e.fontSize && t.push(`font-size: ${e.fontSize}px`), t.join("; ");
1051
- }
1052
- /** Parse a CSS inline style string into a CellFormat (best-effort for external sources) */
1053
- _parseStyleToFormat(e) {
1054
- if (!e) return;
1055
- const t = {}, r = this._extractStyleProp(e, "font-weight");
1056
- (r === "bold" || r && parseInt(r, 10) >= 600) && (t.bold = !0), this._extractStyleProp(e, "font-style") === "italic" && (t.italic = !0);
1057
- const o = this._extractStyleProp(e, "text-decoration");
1058
- o && (o.includes("underline") && (t.underline = !0), o.includes("line-through") && (t.strikethrough = !0));
1059
- const n = /^[a-zA-Z0-9#(),.\s%/-]+$/, a = this._extractStyleProp(e, "color");
1060
- a && a.length <= 100 && n.test(a) && (t.textColor = a);
1061
- const l = this._extractStyleProp(e, "background-color");
1062
- l && l.length <= 100 && n.test(l) && (t.backgroundColor = l);
1063
- const c = this._extractStyleProp(e, "text-align");
1064
- (c === "left" || c === "center" || c === "right") && (t.textAlign = c);
1065
- const h = this._extractStyleProp(e, "font-size");
1066
- if (h) {
1067
- const u = parseInt(h, 10);
1068
- !isNaN(u) && u > 0 && u <= 200 && (t.fontSize = u);
1069
- }
1070
- return Object.keys(t).length > 0 ? t : void 0;
1071
- }
1072
- /** Validate and sanitize a parsed CellFormat from an external source */
1073
- _sanitizeFormat(e) {
1074
- if (!e || typeof e != "object" || Array.isArray(e)) return;
1075
- const t = e, r = {}, s = /^[a-zA-Z0-9#(),.\s%/-]+$/, o = /* @__PURE__ */ new Set(["left", "center", "right"]);
1076
- return typeof t.bold == "boolean" && (r.bold = t.bold), typeof t.italic == "boolean" && (r.italic = t.italic), typeof t.underline == "boolean" && (r.underline = t.underline), typeof t.strikethrough == "boolean" && (r.strikethrough = t.strikethrough), typeof t.textColor == "string" && t.textColor.length <= 100 && s.test(t.textColor) && (r.textColor = t.textColor), typeof t.backgroundColor == "string" && t.backgroundColor.length <= 100 && s.test(t.backgroundColor) && (r.backgroundColor = t.backgroundColor), typeof t.textAlign == "string" && o.has(t.textAlign) && (r.textAlign = t.textAlign), typeof t.fontSize == "number" && t.fontSize > 0 && t.fontSize <= 200 && (r.fontSize = t.fontSize), Object.keys(r).length > 0 ? r : void 0;
1077
- }
1078
- _extractStyleProp(e, t) {
1079
- const r = new RegExp(`(?<![\\w-])${t}\\s*:\\s*([^;]+)`, "i"), s = e.match(r);
1080
- return s ? s[1].trim() : void 0;
1081
- }
1082
- /** Escape special HTML characters */
1083
- _escapeHTML(e) {
1084
- return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
1085
- }
1086
- // ─── Fallback ───────────────────────────────────────
1087
- /** @deprecated Uses document.execCommand('copy') which is deprecated. Only used as a fallback when the Clipboard API is unavailable. */
1088
- fallbackCopy(e) {
1089
- console.warn("y11n-spreadsheet: Clipboard API unavailable, using deprecated execCommand fallback.");
1090
- const t = document.createElement("textarea");
1091
- t.value = e, t.style.position = "fixed", t.style.left = "-9999px", document.body.appendChild(t), t.select();
1092
- try {
1093
- document.execCommand("copy");
1094
- } catch {
1095
- }
1096
- document.body.removeChild(t);
1097
- }
69
+ // src/controllers/selection-manager.ts
70
+ var x = class {
71
+ constructor(e, t, n) {
72
+ this.anchor = {
73
+ row: 0,
74
+ col: 0
75
+ }, this.head = {
76
+ row: 0,
77
+ col: 0
78
+ }, this._isDragging = !1, this.host = e, this._maxRows = t, this._maxCols = n, e.addController(this);
79
+ }
80
+ hostConnected() {}
81
+ hostDisconnected() {
82
+ this._isDragging = !1;
83
+ }
84
+ setBounds(e, t) {
85
+ this._maxRows = e, this._maxCols = t, this.anchor = this.clamp(this.anchor), this.head = this.clamp(this.head);
86
+ }
87
+ get range() {
88
+ return {
89
+ start: {
90
+ row: Math.min(this.anchor.row, this.head.row),
91
+ col: Math.min(this.anchor.col, this.head.col)
92
+ },
93
+ end: {
94
+ row: Math.max(this.anchor.row, this.head.row),
95
+ col: Math.max(this.anchor.col, this.head.col)
96
+ }
97
+ };
98
+ }
99
+ get activeCell() {
100
+ return { ...this.head };
101
+ }
102
+ isCellSelected(e, t) {
103
+ let n = this.range;
104
+ return e >= n.start.row && e <= n.end.row && t >= n.start.col && t <= n.end.col;
105
+ }
106
+ isCellActive(e, t) {
107
+ return e === this.head.row && t === this.head.col;
108
+ }
109
+ startSelection(e, t, n = !1) {
110
+ if (n) this.head = this.clamp({
111
+ row: e,
112
+ col: t
113
+ });
114
+ else {
115
+ let n = this.clamp({
116
+ row: e,
117
+ col: t
118
+ });
119
+ this.anchor = { ...n }, this.head = { ...n };
120
+ }
121
+ this._isDragging = !0, this.host.requestUpdate();
122
+ }
123
+ extendSelection(e, t) {
124
+ this._isDragging && (this.head = this.clamp({
125
+ row: e,
126
+ col: t
127
+ }), this.host.requestUpdate());
128
+ }
129
+ endSelection() {
130
+ this._isDragging = !1;
131
+ }
132
+ get isDragging() {
133
+ return this._isDragging;
134
+ }
135
+ move(e, t, n = !1) {
136
+ let r = this.clamp({
137
+ row: this.head.row + e,
138
+ col: this.head.col + t
139
+ });
140
+ this.head = r, n || (this.anchor = { ...r }), this.host.requestUpdate();
141
+ }
142
+ moveTo(e, t, n = !1) {
143
+ let r = this.clamp({
144
+ row: e,
145
+ col: t
146
+ });
147
+ this.head = r, n || (this.anchor = { ...r }), this.host.requestUpdate();
148
+ }
149
+ selectAll() {
150
+ this.anchor = {
151
+ row: 0,
152
+ col: 0
153
+ }, this.head = {
154
+ row: this._maxRows - 1,
155
+ col: this._maxCols - 1
156
+ }, this.host.requestUpdate();
157
+ }
158
+ clearSelection() {
159
+ this.anchor = { ...this.head }, this.host.requestUpdate();
160
+ }
161
+ clamp(e) {
162
+ return {
163
+ row: Math.max(0, Math.min(e.row, this._maxRows - 1)),
164
+ col: Math.max(0, Math.min(e.col, this._maxCols - 1))
165
+ };
166
+ }
167
+ }, S = 64, C = /* @__PURE__ */ new Set([
168
+ "SUM",
169
+ "AVERAGE",
170
+ "MIN",
171
+ "MAX",
172
+ "COUNT",
173
+ "COUNTA",
174
+ "CONCAT"
175
+ ]), w = /* @__PURE__ */ new Set(["NOW"]), T = /* @__PURE__ */ new Set([
176
+ "SUM",
177
+ "AVERAGE",
178
+ "MIN",
179
+ "MAX",
180
+ "CONCAT"
181
+ ]), E = 1e5, D = /* @__PURE__ */ new Set([
182
+ "#ERROR!",
183
+ "#REF!",
184
+ "#DIV/0!",
185
+ "#NAME?",
186
+ "#CIRC!",
187
+ "#VALUE!",
188
+ "#N/A",
189
+ "#NUM!"
190
+ ]);
191
+ function O(e) {
192
+ return typeof e == "string" && D.has(e);
1098
193
  }
1099
- var Z = Object.defineProperty, X = Object.getOwnPropertyDescriptor, N = (i, e, t, r) => {
1100
- for (var s = r > 1 ? void 0 : r ? X(e, t) : e, o = i.length - 1, n; o >= 0; o--)
1101
- (n = i[o]) && (s = (r ? n(e, t, s) : n(s)) || s);
1102
- return r && s && Z(e, t, s), s;
194
+ function k(e) {
195
+ let t = e instanceof Error ? e.message : "";
196
+ return t.startsWith("#") ? t : "#ERROR!";
197
+ }
198
+ function A(e) {
199
+ let t = e.toUpperCase();
200
+ return [...w].some((e) => t.includes(e + "("));
201
+ }
202
+ function j(e) {
203
+ if (typeof e == "number") return e;
204
+ if (typeof e == "boolean") return +!!e;
205
+ if (e == null || e === "") return 0;
206
+ if (typeof e == "string") {
207
+ if (D.has(e)) throw Error(e);
208
+ let t = Number(e);
209
+ if (e.trim() !== "" && !isNaN(t)) return t;
210
+ }
211
+ throw Error("#VALUE!");
212
+ }
213
+ function M(e, t) {
214
+ let n = (e) => typeof e == "number" || typeof e == "string" && e.trim() !== "" && !isNaN(Number(e));
215
+ if (e === void 0 || t === void 0) return NaN;
216
+ if (n(e) && n(t)) return Number(e) - Number(t);
217
+ if (n(e) || n(t) || typeof e == "boolean" || typeof t == "boolean") return NaN;
218
+ let r = String(e).toLowerCase(), i = String(t).toLowerCase();
219
+ return r < i ? -1 : +(r > i);
220
+ }
221
+ function N(e, t) {
222
+ let n = -1;
223
+ for (let r = 0; r < e.length; r++) {
224
+ let i = M(e[r], t);
225
+ if (!isNaN(i)) {
226
+ if (i > 0) break;
227
+ n = r;
228
+ }
229
+ }
230
+ return n;
231
+ }
232
+ var P = class extends Error {
233
+ constructor(e) {
234
+ super("#ERROR!"), this.key = e;
235
+ }
236
+ }, F = class {
237
+ constructor(e, t, n) {
238
+ this.values = e, this.rows = t, this.cols = n;
239
+ }
240
+ get(e, t) {
241
+ return this.values[e * this.cols + t];
242
+ }
243
+ getRow(e) {
244
+ let t = e * this.cols;
245
+ return this.values.slice(t, t + this.cols);
246
+ }
247
+ getCol(e) {
248
+ let t = [];
249
+ for (let n = 0; n < this.rows; n++) t.push(this.values[n * this.cols + e]);
250
+ return t;
251
+ }
1103
252
  };
1104
- let A = class extends O {
1105
- constructor() {
1106
- super(...arguments), this.cellRef = "A1", this.rawValue = "", this.displayValue = "", this.mode = "raw", this.readOnly = !1, this._draft = "", this._suppressNextBlur = !1;
1107
- }
1108
- willUpdate(i) {
1109
- (i.has("mode") || i.has("rawValue") || i.has("displayValue")) && (this._draft = this._sourceValue());
1110
- }
1111
- _sourceValue() {
1112
- return this.mode === "raw" ? this.rawValue : this.displayValue;
1113
- }
1114
- _setMode(i) {
1115
- this.mode !== i && this.dispatchEvent(
1116
- new CustomEvent("formula-bar-mode-change", {
1117
- detail: { mode: i },
1118
- bubbles: !0,
1119
- composed: !0
1120
- })
1121
- );
1122
- }
1123
- _onInput(i) {
1124
- this._draft = i.target.value;
1125
- }
1126
- _commit() {
1127
- this.dispatchEvent(
1128
- new CustomEvent("formula-bar-commit", {
1129
- detail: { value: this._draft },
1130
- bubbles: !0,
1131
- composed: !0
1132
- })
1133
- );
1134
- }
1135
- _onKeydown(i) {
1136
- i.key === "Enter" ? (i.preventDefault(), this._commit()) : i.key === "Escape" && (i.preventDefault(), this._draft = this._sourceValue(), this._suppressNextBlur = !0);
1137
- }
1138
- _onBlur() {
1139
- if (this._suppressNextBlur) {
1140
- this._suppressNextBlur = !1;
1141
- return;
1142
- }
1143
- this._commit();
1144
- }
1145
- render() {
1146
- return y`
1147
- <output class="cell-ref" aria-live="polite" aria-atomic="true">${this.cellRef}</output>
1148
- <div class="mode-toggle" role="group" aria-label="Formula bar mode">
1149
- <button
1150
- type="button"
1151
- aria-pressed="${this.mode === "raw"}"
1152
- @click="${() => this._setMode("raw")}"
1153
- >
1154
- Raw
1155
- </button>
1156
- <button
1157
- type="button"
1158
- aria-pressed="${this.mode === "formatted"}"
1159
- @click="${() => this._setMode("formatted")}"
1160
- >
1161
- Formatted
1162
- </button>
1163
- </div>
1164
- <input
1165
- type="text"
1166
- .value="${this._draft}"
1167
- ?disabled="${this.readOnly}"
1168
- @input="${this._onInput}"
1169
- @keydown="${this._onKeydown}"
1170
- @blur="${this._onBlur}"
1171
- aria-label="Formula bar"
1172
- />
1173
- `;
1174
- }
253
+ function I(e, t) {
254
+ let n = "=", r = t;
255
+ t.startsWith("<>") ? (n = "<>", r = t.substring(2)) : t.startsWith(">=") ? (n = ">=", r = t.substring(2)) : t.startsWith("<=") ? (n = "<=", r = t.substring(2)) : t.startsWith(">") ? (n = ">", r = t.substring(1)) : t.startsWith("<") ? (n = "<", r = t.substring(1)) : t.startsWith("=") && (n = "=", r = t.substring(1));
256
+ let i = Number(r), a = Number(e), o = !isNaN(i) && r.trim() !== "", s = !isNaN(a) && String(e).trim() !== "", c = o && s;
257
+ if (n !== "=" && n !== "<>" && o !== s) return !1;
258
+ let l = String(e).toLowerCase(), u = r.toLowerCase();
259
+ switch (n) {
260
+ case "=": return c ? a === i : l === u;
261
+ case "<>": return c ? a !== i : l !== u;
262
+ case ">": return c ? a > i : l > u;
263
+ case "<": return c ? a < i : l < u;
264
+ case ">=": return c ? a >= i : l >= u;
265
+ case "<=": return c ? a <= i : l <= u;
266
+ default: return !1;
267
+ }
268
+ }
269
+ var L = class {
270
+ constructor() {
271
+ this._functions = /* @__PURE__ */ new Map(), this._builtinFunctionNames = /* @__PURE__ */ new Set(), this._data = /* @__PURE__ */ new Map(), this._evaluating = /* @__PURE__ */ new Set(), this._evalDepth = 0, this._deps = /* @__PURE__ */ new Map(), this._reverseDeps = /* @__PURE__ */ new Map(), this._trackingCellKey = null, this._cache = /* @__PURE__ */ new Map(), this._memo = null, this.registerBuiltins(), this._builtinFunctionNames = new Set(this._functions.keys());
272
+ }
273
+ registerFunction(e, t) {
274
+ this._functions.set(e.toUpperCase(), t);
275
+ }
276
+ setFunctions(e) {
277
+ for (let e of Array.from(this._functions.keys())) this._builtinFunctionNames.has(e) || this._functions.delete(e);
278
+ for (let [t, n] of Object.entries(e)) this.registerFunction(t, n);
279
+ }
280
+ setData(e) {
281
+ this._data = e, this._deps.clear(), this._reverseDeps.clear(), this._cache.clear();
282
+ }
283
+ updateData(e, t) {
284
+ this._data = e;
285
+ for (let e of t) this._clearDepsFor(e);
286
+ }
287
+ evaluate(e, t) {
288
+ let n = !!t && e.startsWith("=") && !this._evaluating.has(t);
289
+ n && this._evaluating.add(t);
290
+ try {
291
+ return this._evaluateDeferred(e, t);
292
+ } finally {
293
+ n && this._evaluating.delete(t);
294
+ }
295
+ }
296
+ _evaluateDeferred(e, t) {
297
+ return this._withMemo(() => {
298
+ let n = [];
299
+ for (;;) {
300
+ let r = n.length > 0 ? n[n.length - 1] : null;
301
+ try {
302
+ if (r === null) return this._evaluateTopLevel(e, t);
303
+ this._evaluateFormulaCell(r, this._data.get(r)?.rawValue ?? ""), n.pop();
304
+ } catch (e) {
305
+ if (!(e instanceof P)) {
306
+ n.pop();
307
+ continue;
308
+ }
309
+ let t = n.indexOf(e.key);
310
+ if (t !== -1) {
311
+ for (let e of n.slice(t)) this._memo?.set(e, { error: "#CIRC!" });
312
+ n.length = t;
313
+ continue;
314
+ }
315
+ if (n.length >= E) return {
316
+ displayValue: "#ERROR!",
317
+ type: "error"
318
+ };
319
+ n.push(e.key);
320
+ }
321
+ }
322
+ });
323
+ }
324
+ _withMemo(e) {
325
+ if (this._memo) return e();
326
+ this._memo = /* @__PURE__ */ new Map();
327
+ try {
328
+ return e();
329
+ } finally {
330
+ this._memo = null;
331
+ }
332
+ }
333
+ _evaluateTopLevel(e, t) {
334
+ if (!e || e.trim() === "") return t && (this._clearDepsFor(t), this._cache.delete(t)), {
335
+ displayValue: "",
336
+ type: "text"
337
+ };
338
+ if (!e.startsWith("=")) return t && (this._clearDepsFor(t), this._cache.delete(t)), this.coerceValue(e);
339
+ let n = A(e);
340
+ if (t && !n) {
341
+ let n = this._cache.get(t);
342
+ if (n && n.rawValue === e) return {
343
+ displayValue: n.displayValue,
344
+ type: n.type
345
+ };
346
+ }
347
+ let r = !!t && !this._evaluating.has(t);
348
+ try {
349
+ t && (this._clearDepsFor(t), this._trackingCellKey = t, r && this._evaluating.add(t));
350
+ let i = e.substring(1), a = this.parseExpression(i), o = this._resultToDisplay(a);
351
+ return t && (this._memo?.set(t, { value: a }), n || this._cache.set(t, {
352
+ ...o,
353
+ rawValue: e
354
+ })), o;
355
+ } catch (r) {
356
+ if (r instanceof P) throw r;
357
+ let i = {
358
+ displayValue: k(r),
359
+ type: "error"
360
+ };
361
+ return t && (this._memo?.set(t, { error: i.displayValue }), n || this._cache.set(t, {
362
+ ...i,
363
+ rawValue: e
364
+ })), i;
365
+ } finally {
366
+ this._trackingCellKey = null, r && this._evaluating.delete(t);
367
+ }
368
+ }
369
+ _resultToDisplay(e) {
370
+ return typeof e == "number" ? isNaN(e) ? {
371
+ displayValue: "#VALUE!",
372
+ type: "error"
373
+ } : Number.isFinite(e) ? {
374
+ displayValue: String(parseFloat(e.toPrecision(15))),
375
+ type: "number"
376
+ } : {
377
+ displayValue: "#NUM!",
378
+ type: "error"
379
+ } : typeof e == "boolean" ? {
380
+ displayValue: e ? "TRUE" : "FALSE",
381
+ type: "boolean"
382
+ } : typeof e == "string" ? O(e) ? {
383
+ displayValue: e,
384
+ type: "error"
385
+ } : {
386
+ displayValue: e,
387
+ type: "text"
388
+ } : e instanceof F ? {
389
+ displayValue: "#VALUE!",
390
+ type: "error"
391
+ } : e == null ? {
392
+ displayValue: "0",
393
+ type: "number"
394
+ } : this.coerceValue(String(e));
395
+ }
396
+ recalculate() {
397
+ return this._deps.clear(), this._reverseDeps.clear(), this._cache.clear(), this._withMemo(() => {
398
+ let e = /* @__PURE__ */ new Set();
399
+ for (let [t, n] of this._data) if (n.rawValue.startsWith("=")) {
400
+ let r = this.evaluate(n.rawValue, t);
401
+ (n.displayValue !== r.displayValue || n.type !== r.type) && (n.displayValue = r.displayValue, n.type = r.type, e.add(t));
402
+ }
403
+ return e;
404
+ });
405
+ }
406
+ recalculateAffected(e) {
407
+ if (this._reverseDeps.size === 0 && this._data.size > 0) return this.recalculate();
408
+ let t = new Set(e), n = [...e];
409
+ for (; n.length > 0;) {
410
+ let e = n.shift(), r = this._reverseDeps.get(e);
411
+ if (r) for (let e of r) t.has(e) || (t.add(e), n.push(e));
412
+ }
413
+ for (let e of t) this._cache.delete(e);
414
+ return this._withMemo(() => {
415
+ let e = /* @__PURE__ */ new Set();
416
+ for (let n of t) {
417
+ let t = this._data.get(n);
418
+ if (t?.rawValue.startsWith("=")) {
419
+ let r = this.evaluate(t.rawValue, n);
420
+ (t.displayValue !== r.displayValue || t.type !== r.type) && (t.displayValue = r.displayValue, t.type = r.type, e.add(n));
421
+ }
422
+ }
423
+ return e;
424
+ });
425
+ }
426
+ _clearDepsFor(e) {
427
+ let t = this._deps.get(e);
428
+ if (t) {
429
+ for (let n of t) this._reverseDeps.get(n)?.delete(e);
430
+ this._deps.delete(e);
431
+ }
432
+ this._cache.delete(e);
433
+ }
434
+ _trackDep(e) {
435
+ if (!this._trackingCellKey) return;
436
+ let t = this._deps.get(this._trackingCellKey);
437
+ t || (t = /* @__PURE__ */ new Set(), this._deps.set(this._trackingCellKey, t)), t.add(e);
438
+ let n = this._reverseDeps.get(e);
439
+ n || (n = /* @__PURE__ */ new Set(), this._reverseDeps.set(e, n)), n.add(this._trackingCellKey);
440
+ }
441
+ coerceValue(e) {
442
+ if (e === "#ERROR!" || e === "#REF!" || e === "#DIV/0!" || e === "#NAME?" || e === "#CIRC!" || e === "#VALUE!" || e === "#N/A" || e === "#NUM!") return {
443
+ displayValue: e,
444
+ type: "error"
445
+ };
446
+ let t = e.toUpperCase();
447
+ if (t === "TRUE" || t === "FALSE") return {
448
+ displayValue: t,
449
+ type: "boolean"
450
+ };
451
+ let n = Number(e);
452
+ if (e.trim() !== "" && !isNaN(n)) {
453
+ let e = Number.isFinite(n) ? parseFloat(n.toPrecision(15)) : n;
454
+ return {
455
+ displayValue: String(e),
456
+ type: "number"
457
+ };
458
+ }
459
+ return {
460
+ displayValue: e,
461
+ type: "text"
462
+ };
463
+ }
464
+ tokenize(e) {
465
+ let t = [], n = 0;
466
+ for (; n < e.length;) {
467
+ let r = e[n];
468
+ if (/\s/.test(r)) n++;
469
+ else if (r === "\"") {
470
+ let r = "";
471
+ for (n++; n < e.length;) if (e[n] === "\"") {
472
+ if (n + 1 < e.length && e[n + 1] === "\"") r += "\"", n += 2;
473
+ else break;
474
+ } else r += e[n], n++;
475
+ if (n >= e.length) throw Error("Unterminated string literal");
476
+ n++, t.push({
477
+ type: "STRING",
478
+ value: r
479
+ });
480
+ } else if (/\d/.test(r) || r === "." && n + 1 < e.length && /\d/.test(e[n + 1])) {
481
+ let r = "";
482
+ for (; n < e.length && (/\d/.test(e[n]) || e[n] === ".");) r += e[n], n++;
483
+ t.push({
484
+ type: "NUMBER",
485
+ value: r
486
+ });
487
+ } else if (r === "+" || r === "-" || r === "*" || r === "/" || r === "&") t.push({
488
+ type: "OPERATOR",
489
+ value: r
490
+ }), n++;
491
+ else if (r === "<" || r === ">") n + 1 < e.length && e[n + 1] === "=" ? (t.push({
492
+ type: "OPERATOR",
493
+ value: r + "="
494
+ }), n += 2) : r === "<" && n + 1 < e.length && e[n + 1] === ">" ? (t.push({
495
+ type: "OPERATOR",
496
+ value: "<>"
497
+ }), n += 2) : (t.push({
498
+ type: "OPERATOR",
499
+ value: r
500
+ }), n++);
501
+ else if (r === "=") t.push({
502
+ type: "OPERATOR",
503
+ value: "="
504
+ }), n++;
505
+ else if (r === "(") t.push({
506
+ type: "LPAREN",
507
+ value: "("
508
+ }), n++;
509
+ else if (r === ")") t.push({
510
+ type: "RPAREN",
511
+ value: ")"
512
+ }), n++;
513
+ else if (r === ",") t.push({
514
+ type: "COMMA",
515
+ value: ","
516
+ }), n++;
517
+ else {
518
+ if (/[A-Za-z_$]/.test(r)) {
519
+ let r = "";
520
+ for (; n < e.length && /[A-Za-z0-9_$]/.test(e[n]);) r += e[n], n++;
521
+ let i = r.replace(/\$/g, ""), a = i !== r, o = i.toUpperCase();
522
+ if (!a && (o === "TRUE" || o === "FALSE")) {
523
+ t.push({
524
+ type: "BOOLEAN",
525
+ value: o
526
+ });
527
+ continue;
528
+ }
529
+ if (n < e.length && e[n] === ":" && /^[A-Z]+\d+$/i.test(i)) {
530
+ n++;
531
+ let i = "";
532
+ for (; n < e.length && /[A-Za-z0-9$]/.test(e[n]);) i += e[n], n++;
533
+ let a = i.replace(/\$/g, "");
534
+ if (/^[A-Z]+\d+$/i.test(a)) {
535
+ t.push({
536
+ type: "RANGE",
537
+ value: `${r.toUpperCase()}:${i.toUpperCase()}`
538
+ });
539
+ continue;
540
+ }
541
+ throw Error(`Invalid range: ${r}:${i}`);
542
+ }
543
+ if (!a) {
544
+ let r = n;
545
+ for (; r < e.length && /\s/.test(e[r]);) r++;
546
+ if (r < e.length && e[r] === "(") {
547
+ t.push({
548
+ type: "FUNC",
549
+ value: o
550
+ });
551
+ continue;
552
+ }
553
+ }
554
+ if (/^[A-Z]+\d+$/i.test(i)) {
555
+ t.push({
556
+ type: "REF",
557
+ value: r.toUpperCase()
558
+ });
559
+ continue;
560
+ }
561
+ if (!a) {
562
+ t.push({
563
+ type: "FUNC",
564
+ value: o
565
+ });
566
+ continue;
567
+ }
568
+ throw Error(`Unexpected identifier: ${r}`);
569
+ }
570
+ throw Error(`Unexpected character: ${r}`);
571
+ }
572
+ }
573
+ return t.push({
574
+ type: "EOF",
575
+ value: ""
576
+ }), t;
577
+ }
578
+ _peek(e) {
579
+ return e.tokens[e.pos];
580
+ }
581
+ _consume(e, t) {
582
+ let n = e.tokens[e.pos];
583
+ if (t && n.type !== t) throw Error(`Expected ${t} but got ${n.type} (${n.value})`);
584
+ return e.pos++, n;
585
+ }
586
+ parseExpression(e) {
587
+ if (++this._evalDepth > S) throw this._evalDepth--, Error("#ERROR!");
588
+ try {
589
+ let t = {
590
+ tokens: this.tokenize(e),
591
+ pos: 0
592
+ }, n = this._parseComparison(t);
593
+ if (this._peek(t).type !== "EOF") throw Error(`Unexpected token: ${this._peek(t).value}`);
594
+ return n;
595
+ } finally {
596
+ this._evalDepth--;
597
+ }
598
+ }
599
+ _parseComparison(e) {
600
+ let t = this._parseConcatenation(e);
601
+ for (; this._peek(e).type === "OPERATOR" && [
602
+ "=",
603
+ "<>",
604
+ "<",
605
+ ">",
606
+ "<=",
607
+ ">="
608
+ ].includes(this._peek(e).value);) {
609
+ let n = this._consume(e).value, r = this._parseConcatenation(e);
610
+ t = this._compareValues(t, r, n);
611
+ }
612
+ return t;
613
+ }
614
+ _parseConcatenation(e) {
615
+ let t = this._parseAddSub(e);
616
+ for (; this._peek(e).type === "OPERATOR" && this._peek(e).value === "&";) {
617
+ this._consume(e);
618
+ let n = this._parseAddSub(e);
619
+ t = String(t) + String(n);
620
+ }
621
+ return t;
622
+ }
623
+ _parseAddSub(e) {
624
+ let t = this._parseMulDiv(e);
625
+ for (; this._peek(e).type === "OPERATOR" && (this._peek(e).value === "+" || this._peek(e).value === "-");) {
626
+ let n = this._consume(e).value, r = this._parseMulDiv(e);
627
+ t = n === "+" ? j(t) + j(r) : j(t) - j(r);
628
+ }
629
+ return t;
630
+ }
631
+ _parseMulDiv(e) {
632
+ let t = this._parseUnary(e);
633
+ for (; this._peek(e).type === "OPERATOR" && (this._peek(e).value === "*" || this._peek(e).value === "/");) {
634
+ let n = this._consume(e).value, r = this._parseUnary(e);
635
+ if (n === "*") t = j(t) * j(r);
636
+ else {
637
+ let e = j(t), n = j(r);
638
+ if (n === 0) throw Error("#DIV/0!");
639
+ t = e / n;
640
+ }
641
+ }
642
+ return t;
643
+ }
644
+ _parseUnary(e) {
645
+ return this._peek(e).type === "OPERATOR" && this._peek(e).value === "-" ? (this._consume(e), -j(this._parseUnary(e))) : this._peek(e).type === "OPERATOR" && this._peek(e).value === "+" ? (this._consume(e), j(this._parseUnary(e))) : this._parsePrimary(e);
646
+ }
647
+ _parsePrimary(e) {
648
+ let t = this._peek(e);
649
+ switch (t.type) {
650
+ case "NUMBER": return this._consume(e), parseFloat(t.value);
651
+ case "STRING": return this._consume(e), t.value;
652
+ case "BOOLEAN": return this._consume(e), t.value === "TRUE";
653
+ case "REF": return this._consume(e), this._resolveRef(t.value);
654
+ case "RANGE": return this._consume(e), this._resolveRange(t.value);
655
+ case "FUNC": return this._parseFunction(e);
656
+ case "LPAREN":
657
+ this._consume(e);
658
+ let n = this._parseComparison(e);
659
+ return this._consume(e, "RPAREN"), n;
660
+ default: throw Error(`Unexpected token: ${t.type} (${t.value})`);
661
+ }
662
+ }
663
+ _parseFunction(e) {
664
+ let t = this._consume(e, "FUNC").value;
665
+ if (this._consume(e, "LPAREN"), t === "IFERROR") return this._parseIFERROR(e);
666
+ if (t === "IF") return this._parseIF(e);
667
+ let n = [];
668
+ if (this._peek(e).type !== "RPAREN") for (n.push(this._parseComparison(e)); this._peek(e).type === "COMMA";) this._consume(e), n.push(this._parseComparison(e));
669
+ this._consume(e, "RPAREN");
670
+ let r = this._functions.get(t);
671
+ if (!r) throw Error("#NAME?");
672
+ let i = this._createContext();
673
+ if (C.has(t)) {
674
+ let e = [];
675
+ for (let t of n) if (t instanceof F) for (let n of t.values) n !== void 0 && e.push(n);
676
+ else Array.isArray(t) ? e.push(...t) : e.push(t);
677
+ if (T.has(t)) {
678
+ let t = e.find(O);
679
+ if (t !== void 0) throw Error(t);
680
+ }
681
+ return r(i, ...e) ?? 0;
682
+ }
683
+ return r(i, ...n) ?? 0;
684
+ }
685
+ _parseIFERROR(e) {
686
+ let t = e.pos, n, r = !1;
687
+ try {
688
+ n = this._parseComparison(e);
689
+ } catch (n) {
690
+ if (n instanceof P) throw n;
691
+ r = !0, e.pos = t, this._skipArgument(e);
692
+ }
693
+ let i = "";
694
+ return this._peek(e).type === "COMMA" && (this._consume(e), i = this._parseComparison(e)), this._consume(e, "RPAREN"), r || O(n) ? i : n;
695
+ }
696
+ _parseIF(e) {
697
+ let t = this._parseComparison(e), n = t ? 0 : !1;
698
+ return this._peek(e).type === "COMMA" && (this._consume(e), t ? n = this._parseComparison(e) : this._skipArgument(e), this._peek(e).type === "COMMA" && (this._consume(e), t ? this._skipArgument(e) : n = this._parseComparison(e))), this._consume(e, "RPAREN"), n;
699
+ }
700
+ _skipArgument(e) {
701
+ let t = 0;
702
+ for (; this._peek(e).type !== "EOF";) {
703
+ let n = this._peek(e);
704
+ if (n.type === "LPAREN") t++, e.pos++;
705
+ else if (n.type === "RPAREN") {
706
+ if (t === 0) break;
707
+ t--, e.pos++;
708
+ } else if (n.type === "COMMA" && t === 0) break;
709
+ else e.pos++;
710
+ }
711
+ }
712
+ _compareValues(e, t, n) {
713
+ let r = (e) => {
714
+ if (typeof e == "boolean" || typeof e == "number") return !0;
715
+ let t = String(e).trim();
716
+ return t !== "" && !isNaN(Number(t));
717
+ };
718
+ if (r(e) && r(t)) {
719
+ let r = Number(e), i = Number(t);
720
+ switch (n) {
721
+ case "=": return r === i;
722
+ case "<>": return r !== i;
723
+ case "<": return r < i;
724
+ case ">": return r > i;
725
+ case "<=": return r <= i;
726
+ case ">=": return r >= i;
727
+ default: return !1;
728
+ }
729
+ }
730
+ let i = String(e ?? ""), a = String(t ?? "");
731
+ switch (n) {
732
+ case "=": return i.toLowerCase() === a.toLowerCase();
733
+ case "<>": return i.toLowerCase() !== a.toLowerCase();
734
+ case "<": return i < a;
735
+ case ">": return i > a;
736
+ case "<=": return i <= a;
737
+ case ">=": return i >= a;
738
+ default: return !1;
739
+ }
740
+ }
741
+ _resolveRef(e) {
742
+ let t = y(e.replace(/\$/g, "")), n = h(t.row, t.col);
743
+ if (this._trackDep(n), this._evaluating.has(n)) throw Error("#CIRC!");
744
+ let r = this._data.get(n);
745
+ if (!r) return 0;
746
+ if (r.rawValue.startsWith("=")) return this._evaluateFormulaCell(n, r.rawValue);
747
+ let i = Number(r.rawValue);
748
+ return !isNaN(i) && r.rawValue.trim() !== "" ? i : r.rawValue.toUpperCase() === "TRUE" || r.rawValue.toUpperCase() !== "FALSE" && r.rawValue;
749
+ }
750
+ _evaluateFormulaCell(e, t) {
751
+ if (this._evaluating.has(e)) throw Error("#CIRC!");
752
+ let n = this._memo?.get(e);
753
+ if (n) {
754
+ if (n.error !== void 0) throw Error(n.error);
755
+ return n.value;
756
+ }
757
+ if (this._evalDepth >= S) throw new P(e);
758
+ this._evaluating.add(e);
759
+ try {
760
+ let n = this.parseExpression(t.substring(1));
761
+ return this._memo?.set(e, { value: n }), n;
762
+ } catch (t) {
763
+ if (t instanceof P) throw t;
764
+ let n = k(t);
765
+ throw this._memo?.set(e, { error: n }), Error(n);
766
+ } finally {
767
+ this._evaluating.delete(e);
768
+ }
769
+ }
770
+ _resolveRange(e) {
771
+ let [t, n] = e.split(":"), r = y(t.replace(/\$/g, "")), i = y(n.replace(/\$/g, "")), a = Math.min(r.row, i.row), o = Math.max(r.row, i.row), s = Math.min(r.col, i.col), c = Math.max(r.col, i.col), l = o - a + 1, u = c - s + 1, d = [];
772
+ for (let e = a; e <= o; e++) for (let t = s; t <= c; t++) {
773
+ let n = h(e, t);
774
+ this._trackDep(n);
775
+ let r = this._data.get(n);
776
+ if (r) {
777
+ if (r.rawValue.startsWith("=")) try {
778
+ d.push(this._evaluateFormulaCell(n, r.rawValue));
779
+ } catch (e) {
780
+ if (e instanceof P) throw e;
781
+ d.push(k(e));
782
+ }
783
+ else if (r.rawValue === "") d.push(void 0);
784
+ else {
785
+ let e = Number(r.rawValue);
786
+ !isNaN(e) && r.rawValue.trim() !== "" ? d.push(e) : r.rawValue.toUpperCase() === "TRUE" ? d.push(!0) : r.rawValue.toUpperCase() === "FALSE" ? d.push(!1) : d.push(r.rawValue);
787
+ }
788
+ } else d.push(void 0);
789
+ }
790
+ return new F(d, l, u);
791
+ }
792
+ _createContext() {
793
+ return {
794
+ getCellValue: (e) => {
795
+ if (e.includes(":") && !/[A-Z]/i.test(e.charAt(0))) {
796
+ let t = this._data.get(e);
797
+ if (!t) return 0;
798
+ let n = Number(t.rawValue);
799
+ return !isNaN(n) && t.rawValue.trim() !== "" ? n : t.rawValue;
800
+ }
801
+ return this._resolveRef(e);
802
+ },
803
+ getRangeValues: (e, t) => {
804
+ let n = y(e), r = y(t), i = Math.min(n.row, r.row), a = Math.max(n.row, r.row), o = Math.min(n.col, r.col), s = Math.max(n.col, r.col), c = [];
805
+ for (let e = i; e <= a; e++) for (let t = o; t <= s; t++) {
806
+ let n = h(e, t), r = this._data.get(n);
807
+ if (r) {
808
+ let e = Number(r.rawValue);
809
+ c.push(!isNaN(e) && r.rawValue.trim() !== "" ? e : r.rawValue);
810
+ }
811
+ }
812
+ return c;
813
+ }
814
+ };
815
+ }
816
+ registerBuiltins() {
817
+ this.registerFunction("SUM", (e, ...t) => t.reduce((e, t) => e + (Number(t) || 0), 0)), this.registerFunction("AVERAGE", (e, ...t) => {
818
+ let n = t.filter((e) => typeof e == "number" || !isNaN(Number(e)));
819
+ if (n.length === 0) throw Error("#DIV/0!");
820
+ return n.reduce((e, t) => e + Number(t), 0) / n.length;
821
+ }), this.registerFunction("MIN", (e, ...t) => {
822
+ let n = t.map(Number).filter((e) => !isNaN(e));
823
+ return n.length ? Math.min(...n) : 0;
824
+ }), this.registerFunction("MAX", (e, ...t) => {
825
+ let n = t.map(Number).filter((e) => !isNaN(e));
826
+ return n.length ? Math.max(...n) : 0;
827
+ }), this.registerFunction("COUNT", (e, ...t) => t.filter((e) => typeof e == "number" || !isNaN(Number(e)) && String(e).trim() !== "").length), this.registerFunction("COUNTA", (e, ...t) => t.filter((e) => e !== "" && e != null).length), this.registerFunction("IF", (e, t, n, r) => t ? n === void 0 ? 0 : n : r !== void 0 && r), this.registerFunction("CONCAT", (e, ...t) => t.map(String).join("")), this.registerFunction("ABS", (e, t) => Math.abs(Number(t))), this.registerFunction("ROUND", (e, t, n) => {
828
+ let r = Number(n) || 0, i = Number(t), a = 10 ** r, o = parseFloat((Math.abs(i) * a).toPrecision(15));
829
+ return Math.sign(i) * Math.round(o) / a;
830
+ }), this.registerFunction("UPPER", (e, t) => String(t).toUpperCase()), this.registerFunction("LOWER", (e, t) => String(t).toLowerCase()), this.registerFunction("LEN", (e, t) => String(t).length), this.registerFunction("TRIM", (e, t) => String(t).trim().replace(/ +/g, " ")), this.registerFunction("IFERROR", (e, t, n) => {
831
+ let r = String(t);
832
+ return r === "#ERROR!" || r === "#REF!" || r === "#DIV/0!" || r === "#NAME?" || r === "#CIRC!" || r === "#VALUE!" || r === "#N/A" ? n : t;
833
+ }), this.registerFunction("AND", (e, ...t) => {
834
+ for (let e of t) if (e instanceof F) {
835
+ for (let t of e.values) if (t !== void 0 && !t) return !1;
836
+ } else if (!e) return !1;
837
+ return !0;
838
+ }), this.registerFunction("OR", (e, ...t) => {
839
+ for (let e of t) if (e instanceof F) {
840
+ for (let t of e.values) if (t !== void 0 && t) return !0;
841
+ } else if (e) return !0;
842
+ return !1;
843
+ }), this.registerFunction("NOT", (e, t) => !t), this.registerFunction("SUMIF", (e, t, n, r) => {
844
+ let i = String(n), a = t instanceof F ? t.values : Array.isArray(t) ? t : [t], o = r instanceof F ? r.values : Array.isArray(r) ? r : void 0, s = 0;
845
+ for (let e = 0; e < a.length; e++) {
846
+ let t = a[e];
847
+ (t === void 0 ? i === "" || i === "=" : I(t, i)) && (s += o ? Number(o[e]) || 0 : Number(t) || 0);
848
+ }
849
+ return s;
850
+ }), this.registerFunction("COUNTIF", (e, t, n) => {
851
+ let r = String(n), i = t instanceof F ? t.values : Array.isArray(t) ? t : [t], a = 0;
852
+ for (let e of i) e === void 0 ? (r === "" || r === "=") && a++ : I(e, r) && a++;
853
+ return a;
854
+ }), this.registerFunction("AVERAGEIF", (e, t, n, r) => {
855
+ let i = String(n), a = t instanceof F ? t.values : Array.isArray(t) ? t : [t], o = r instanceof F ? r.values : Array.isArray(r) ? r : void 0, s = 0, c = 0;
856
+ for (let e = 0; e < a.length; e++) {
857
+ let t = a[e];
858
+ if (t !== void 0 && I(t, i)) {
859
+ let n = Number(o ? o[e] : t);
860
+ isNaN(n) || (s += n, c++);
861
+ }
862
+ }
863
+ if (c === 0) throw Error("#DIV/0!");
864
+ return s / c;
865
+ }), this.registerFunction("VLOOKUP", (e, t, n, r, i) => {
866
+ if (!(n instanceof F)) throw Error("#VALUE!");
867
+ let a = Math.trunc(Number(r));
868
+ if (a < 1 || a > n.cols) throw Error("#REF!");
869
+ let o = i === !1 || i === 0, s = n.getCol(0);
870
+ for (let e = 0; e < s.length; e++) if (o) {
871
+ let r = Number(t), i = Number(s[e]);
872
+ if (!isNaN(r) && !isNaN(i) && String(t).trim() !== "" && String(s[e]).trim() !== "" ? r === i : String(s[e]).toLowerCase() === String(t).toLowerCase()) return n.get(e, a - 1);
873
+ }
874
+ if (!o) {
875
+ let e = N(s, t);
876
+ if (e !== -1) return n.get(e, a - 1);
877
+ }
878
+ throw Error("#N/A");
879
+ }), this.registerFunction("HLOOKUP", (e, t, n, r, i) => {
880
+ if (!(n instanceof F)) throw Error("#VALUE!");
881
+ let a = Math.trunc(Number(r));
882
+ if (a < 1 || a > n.rows) throw Error("#REF!");
883
+ let o = i === !1 || i === 0, s = n.getRow(0);
884
+ for (let e = 0; e < s.length; e++) if (o) {
885
+ let r = Number(t), i = Number(s[e]);
886
+ if (!isNaN(r) && !isNaN(i) && String(t).trim() !== "" && String(s[e]).trim() !== "" ? r === i : String(s[e]).toLowerCase() === String(t).toLowerCase()) return n.get(a - 1, e);
887
+ }
888
+ if (!o) {
889
+ let e = N(s, t);
890
+ if (e !== -1) return n.get(a - 1, e);
891
+ }
892
+ throw Error("#N/A");
893
+ }), this.registerFunction("INDEX", (e, t, n, r) => {
894
+ if (t instanceof F) {
895
+ let e = Math.trunc(Number(n)) - 1, i = r === void 0 ? 0 : Math.trunc(Number(r)) - 1;
896
+ if (e < 0 || e >= t.rows || i < 0 || i >= t.cols) throw Error("#REF!");
897
+ let a = t.get(e, i);
898
+ return a === void 0 ? 0 : a;
899
+ }
900
+ throw Error("#VALUE!");
901
+ }), this.registerFunction("MATCH", (e, t, n, r) => {
902
+ let i;
903
+ if (n instanceof F) i = n.values;
904
+ else if (Array.isArray(n)) i = n;
905
+ else throw Error("#VALUE!");
906
+ let a = r === void 0 ? 1 : Number(r);
907
+ if (a === 0) {
908
+ for (let e = 0; e < i.length; e++) {
909
+ let n = Number(t), r = Number(i[e]);
910
+ if (!isNaN(n) && !isNaN(r) && String(t).trim() !== "" && String(i[e]).trim() !== "" ? n === r : String(i[e]).toLowerCase() === String(t).toLowerCase()) return e + 1;
911
+ }
912
+ throw Error("#N/A");
913
+ }
914
+ if (a === 1) {
915
+ let e = N(i, t);
916
+ if (e === -1) throw Error("#N/A");
917
+ return e + 1;
918
+ }
919
+ {
920
+ let e = -1;
921
+ for (let n = 0; n < i.length; n++) M(i[n], t) >= 0 && (e = n);
922
+ if (e === -1) throw Error("#N/A");
923
+ return e + 1;
924
+ }
925
+ }), this.registerFunction("MOD", (e, t, n) => {
926
+ let r = Number(t), i = Number(n);
927
+ if (i === 0) throw Error("#DIV/0!");
928
+ return r - i * Math.floor(r / i);
929
+ }), this.registerFunction("POWER", (e, t, n) => {
930
+ let r = Number(t), i = Number(n), a = r ** +i;
931
+ if (!Number.isFinite(a) && Number.isFinite(r) && Number.isFinite(i)) throw Error(isNaN(a) ? "#NUM!" : "#DIV/0!");
932
+ return a;
933
+ }), this.registerFunction("CEILING", (e, t, n) => {
934
+ let r = Number(t), i = n === void 0 ? 1 : Number(n);
935
+ if (i === 0) return 0;
936
+ let a = parseFloat((r / i).toPrecision(10));
937
+ return Math.ceil(a) * i;
938
+ }), this.registerFunction("FLOOR", (e, t, n) => {
939
+ let r = Number(t), i = n === void 0 ? 1 : Number(n);
940
+ if (i === 0) return 0;
941
+ let a = parseFloat((r / i).toPrecision(10));
942
+ return Math.floor(a) * i;
943
+ }), this.registerFunction("LEFT", (e, t, n) => {
944
+ let r = n === void 0 ? 1 : Number(n);
945
+ if (!(r >= 0)) throw Error("#VALUE!");
946
+ return String(t).substring(0, r);
947
+ }), this.registerFunction("RIGHT", (e, t, n) => {
948
+ let r = String(t), i = n === void 0 ? 1 : Number(n);
949
+ if (!(i >= 0)) throw Error("#VALUE!");
950
+ return r.substring(r.length - i);
951
+ }), this.registerFunction("MID", (e, t, n, r) => {
952
+ let i = String(t), a = Number(n), o = Number(r);
953
+ if (!(a >= 1) || !(o >= 0)) throw Error("#VALUE!");
954
+ return i.substring(a - 1, a - 1 + o);
955
+ }), this.registerFunction("SUBSTITUTE", (e, t, n, r, i) => {
956
+ let a = String(t), o = String(n), s = String(r);
957
+ if (o === "") return a;
958
+ if (i !== void 0) {
959
+ let e = Number(i);
960
+ if (!(e >= 1)) throw Error("#VALUE!");
961
+ let t = 0, n = -1, r = 0;
962
+ for (; r < a.length && (n = a.indexOf(o, r), n !== -1);) {
963
+ if (t++, t === e) return a.substring(0, n) + s + a.substring(n + o.length);
964
+ r = n + 1;
965
+ }
966
+ return a;
967
+ }
968
+ return a.split(o).join(s);
969
+ }), this.registerFunction("FIND", (e, t, n, r) => {
970
+ let i = String(n), a = String(t), o = r === void 0 ? 1 : Number(r);
971
+ if (!(o >= 1)) throw Error("#VALUE!");
972
+ let s = i.indexOf(a, o - 1);
973
+ if (s === -1) throw Error("#VALUE!");
974
+ return s + 1;
975
+ }), this.registerFunction("TEXT", (e, t, n) => {
976
+ let r = Number(t), i = String(n);
977
+ if (isNaN(r)) return String(t);
978
+ if (i === "0") return (Math.sign(r) * Math.round(Math.abs(r))).toString();
979
+ if (i === "0.00" || i === "0.0") {
980
+ let e = (i.split(".")[1] || "").length;
981
+ return r.toFixed(e);
982
+ }
983
+ if (i === "#,##0" || i === "#,##0.00") {
984
+ let e = i.includes(".") ? (i.split(".")[1] || "").length : 0;
985
+ return r.toLocaleString("en-US", {
986
+ minimumFractionDigits: e,
987
+ maximumFractionDigits: e
988
+ });
989
+ }
990
+ return r.toString();
991
+ }), this.registerFunction("VALUE", (e, t) => {
992
+ if (typeof t == "boolean") return +!!t;
993
+ let n = Number(String(t));
994
+ if (isNaN(n)) throw Error("#VALUE!");
995
+ return n;
996
+ }), this.registerFunction("DATE", (e, t, n, r) => {
997
+ let i = Number(t), a = Number(n), o = Number(r);
998
+ i >= 0 && i <= 29 ? i += 2e3 : i >= 30 && i <= 99 && (i += 1900);
999
+ let s = /* @__PURE__ */ new Date(0);
1000
+ s.setFullYear(i, a - 1, o), s.setHours(0, 0, 0, 0);
1001
+ let c = /* @__PURE__ */ new Date(0);
1002
+ c.setFullYear(1899, 11, 30), c.setHours(0, 0, 0, 0);
1003
+ let l = s.getTime() - c.getTime();
1004
+ return Math.round(l / 864e5);
1005
+ }), this.registerFunction("NOW", () => {
1006
+ let e = /* @__PURE__ */ new Date(), t = new Date(1899, 11, 30);
1007
+ return (e.getTime() - t.getTime()) / 864e5;
1008
+ });
1009
+ }
1010
+ }, R = class {
1011
+ constructor() {
1012
+ this._copySourceRange = null, this._copySourceData = null, this._lastWrittenText = null;
1013
+ }
1014
+ async copy(e, t) {
1015
+ this._copySourceRange = t;
1016
+ let n = /* @__PURE__ */ new Map();
1017
+ for (let r = t.start.row; r <= t.end.row; r++) for (let i = t.start.col; i <= t.end.col; i++) {
1018
+ let t = h(r, i), a = e.get(t);
1019
+ a && n.set(t, { ...a });
1020
+ }
1021
+ this._copySourceData = n;
1022
+ let r = this.serializeRange(e, t);
1023
+ this._lastWrittenText = r;
1024
+ let i = this._serializeRangeAsHTML(e, t);
1025
+ try {
1026
+ let e = new Blob([i], { type: "text/html" }), t = new Blob([r], { type: "text/plain" }), n = new ClipboardItem({
1027
+ "text/html": e,
1028
+ "text/plain": t
1029
+ });
1030
+ await navigator.clipboard.write([n]);
1031
+ } catch {
1032
+ try {
1033
+ await navigator.clipboard.writeText(r);
1034
+ } catch {
1035
+ this.fallbackCopy(r);
1036
+ }
1037
+ }
1038
+ }
1039
+ async cut(e, t) {
1040
+ await this.copy(e, t);
1041
+ let n = [];
1042
+ for (let e = t.start.row; e <= t.end.row; e++) for (let r = t.start.col; r <= t.end.col; r++) n.push(h(e, r));
1043
+ return n;
1044
+ }
1045
+ async paste(e, t, n, r) {
1046
+ if (this._copySourceRange && this._copySourceData) {
1047
+ let i = !1;
1048
+ try {
1049
+ let e = await navigator.clipboard.readText(), t = (e) => e?.replace(/\r\n?/g, "\n");
1050
+ i = t(e) === t(this._lastWrittenText);
1051
+ } catch {
1052
+ i = !0;
1053
+ }
1054
+ if (i) {
1055
+ let i = this._pasteInternal(e, t, n, r);
1056
+ if (i) return i;
1057
+ }
1058
+ }
1059
+ try {
1060
+ let i = await navigator.clipboard.read();
1061
+ for (let a of i) if (a.types.includes("text/html")) {
1062
+ let i = await (await a.getType("text/html")).text(), o = this.parseHTMLTableWithFormat(i);
1063
+ if (o) return this._applyParsedRowsWithFormat(o, e, t, n, r);
1064
+ }
1065
+ } catch {}
1066
+ let i;
1067
+ try {
1068
+ i = await navigator.clipboard.readText();
1069
+ } catch {
1070
+ return null;
1071
+ }
1072
+ return this.parseTSV(i, e, t, n, r);
1073
+ }
1074
+ _pasteInternal(e, t, n, r) {
1075
+ let i = this._copySourceRange, a = this._copySourceData;
1076
+ if (!i || !a) return null;
1077
+ let o = e - i.start.row, s = t - i.start.col, c = [];
1078
+ for (let e = i.start.row; e <= i.end.row; e++) for (let t = i.start.col; t <= i.end.col; t++) {
1079
+ let i = e + o, l = t + s;
1080
+ if (i >= n || l >= r) continue;
1081
+ let u = a.get(h(e, t)), d = u?.rawValue ?? "";
1082
+ d.startsWith("=") && (d = this.adjustFormulaReferences(d, o, s));
1083
+ let f = {
1084
+ id: h(i, l),
1085
+ value: d
1086
+ };
1087
+ u?.format && (f.format = { ...u.format }), c.push(f);
1088
+ }
1089
+ return c;
1090
+ }
1091
+ parseHTMLTableWithFormat(e) {
1092
+ let t = new DOMParser().parseFromString(e, "text/html").querySelector("table");
1093
+ if (!t) return null;
1094
+ let n = [], r = t.querySelectorAll("tr");
1095
+ for (let e of r) {
1096
+ let t = [], r = e.querySelectorAll("td, th");
1097
+ for (let e of r) {
1098
+ let n = e, r = n.getAttribute("data-raw") ?? (e.textContent ?? "").trim(), i, a = n.getAttribute("data-format");
1099
+ if (a) try {
1100
+ i = this._sanitizeFormat(JSON.parse(a));
1101
+ } catch {}
1102
+ if (!i) {
1103
+ let e = n.getAttribute("style");
1104
+ e && (i = this._parseStyleToFormat(e));
1105
+ }
1106
+ t.push({
1107
+ value: r,
1108
+ format: i
1109
+ });
1110
+ }
1111
+ t.length > 0 && n.push(t);
1112
+ }
1113
+ return n.length > 0 ? n : null;
1114
+ }
1115
+ _applyParsedRowsWithFormat(e, t, n, r, i) {
1116
+ let a = [];
1117
+ for (let o = 0; o < e.length; o++) for (let s = 0; s < e[o].length; s++) {
1118
+ let c = t + o, l = n + s;
1119
+ if (c < r && l < i) {
1120
+ let { value: t, format: n } = e[o][s], r = {
1121
+ id: h(c, l),
1122
+ value: t
1123
+ };
1124
+ n && (r.format = n), a.push(r);
1125
+ }
1126
+ }
1127
+ return a;
1128
+ }
1129
+ parseTSV(e, t, n, r, i) {
1130
+ let a = this._parseTSVRows(e), o = [];
1131
+ for (let e = 0; e < a.length; e++) {
1132
+ let s = a[e];
1133
+ for (let a = 0; a < s.length; a++) {
1134
+ let c = t + e, l = n + a;
1135
+ c < r && l < i && o.push({
1136
+ id: h(c, l),
1137
+ value: s[a]
1138
+ });
1139
+ }
1140
+ }
1141
+ return o;
1142
+ }
1143
+ _parseTSVRows(e) {
1144
+ let t = [], n = [], r = "", i = !1, a = 0;
1145
+ for (; a < e.length;) {
1146
+ let o = e[a];
1147
+ i ? o === "\"" ? a + 1 < e.length && e[a + 1] === "\"" ? (r += "\"", a += 2) : (i = !1, a++) : (r += o, a++) : o === "\"" && r === "" ? (i = !0, a++) : o === " " ? (n.push(r), r = "", a++) : o === "\r" ? (n.push(r), r = "", a + 1 < e.length && e[a + 1] === "\n" ? a += 2 : a++, t.push(n), n = []) : o === "\n" ? (n.push(r), r = "", a++, t.push(n), n = []) : (r += o, a++);
1148
+ }
1149
+ return n.push(r), (n.length > 1 || n[0] !== "") && t.push(n), t;
1150
+ }
1151
+ adjustFormulaReferences(e, t, n) {
1152
+ if (!e.startsWith("=")) return e;
1153
+ let r = [], i = [], a = 0, o = "";
1154
+ for (; a < e.length;) if (e[a] === "\"") {
1155
+ o &&= (r.push(o), i.push(!1), "");
1156
+ let t = "\"";
1157
+ for (a++; a < e.length && e[a] !== "\"";) t += e[a], a++;
1158
+ a < e.length && (t += "\"", a++), r.push(t), i.push(!0);
1159
+ } else o += e[a], a++;
1160
+ o && (r.push(o), i.push(!1));
1161
+ let s = /(?<![A-Z0-9_.$])(\$?)([A-Z]+)(\$?)(\d+)(?![A-Z0-9_.]|\s*\()/gi;
1162
+ return r.map((e, r) => i[r] ? e : e.replace(s, (e, r, i, a, o) => {
1163
+ let s = v(i.toUpperCase()), c = parseInt(o, 10) - 1, l = r === "$" ? s : s + n, u = a === "$" ? c : c + t;
1164
+ return l < 0 || u < 0 ? "#REF!" : `${r}${_(l)}${a}${u + 1}`;
1165
+ })).join("");
1166
+ }
1167
+ serializeRange(e, t) {
1168
+ let n = [];
1169
+ for (let r = t.start.row; r <= t.end.row; r++) {
1170
+ let i = [];
1171
+ for (let n = t.start.col; n <= t.end.col; n++) {
1172
+ let t = e.get(h(r, n))?.displayValue ?? "";
1173
+ i.push(this._quoteTSVField(t));
1174
+ }
1175
+ n.push(i.join(" "));
1176
+ }
1177
+ return n.join("\n");
1178
+ }
1179
+ _quoteTSVField(e) {
1180
+ return e.includes(" ") || e.includes("\n") || e.includes("\r") || e.includes("\"") ? `"${e.replace(/"/g, "\"\"")}"` : e;
1181
+ }
1182
+ _serializeRangeAsHTML(e, t) {
1183
+ let n = [];
1184
+ for (let r = t.start.row; r <= t.end.row; r++) {
1185
+ let i = [];
1186
+ for (let n = t.start.col; n <= t.end.col; n++) {
1187
+ let t = e.get(h(r, n)), a = t?.displayValue ?? "", o = t?.rawValue ?? "", s = [];
1188
+ if (o && o !== a && s.push(`data-raw="${this._escapeHTML(o)}"`), t?.format) {
1189
+ let e = this._formatToInlineStyle(t.format);
1190
+ e && s.push(`style="${this._escapeHTML(e)}"`), s.push(`data-format="${this._escapeHTML(JSON.stringify(t.format))}"`);
1191
+ }
1192
+ let c = s.length > 0 ? " " + s.join(" ") : "";
1193
+ i.push(`<td${c}>${this._escapeHTML(a)}</td>`);
1194
+ }
1195
+ n.push(`<tr>${i.join("")}</tr>`);
1196
+ }
1197
+ return `<table>${n.join("")}</table>`;
1198
+ }
1199
+ _formatToInlineStyle(e) {
1200
+ let t = [];
1201
+ e.bold && t.push("font-weight: bold"), e.italic && t.push("font-style: italic");
1202
+ let n = [];
1203
+ return e.underline && n.push("underline"), e.strikethrough && n.push("line-through"), n.length > 0 && t.push(`text-decoration: ${n.join(" ")}`), e.textColor && t.push(`color: ${e.textColor}`), e.backgroundColor && t.push(`background-color: ${e.backgroundColor}`), e.textAlign && t.push(`text-align: ${e.textAlign}`), e.fontSize && t.push(`font-size: ${e.fontSize}px`), t.join("; ");
1204
+ }
1205
+ _parseStyleToFormat(e) {
1206
+ if (!e) return;
1207
+ let t = {}, n = this._extractStyleProp(e, "font-weight");
1208
+ (n === "bold" || n && parseInt(n, 10) >= 600) && (t.bold = !0), this._extractStyleProp(e, "font-style") === "italic" && (t.italic = !0);
1209
+ let r = this._extractStyleProp(e, "text-decoration");
1210
+ r && (r.includes("underline") && (t.underline = !0), r.includes("line-through") && (t.strikethrough = !0));
1211
+ let i = /^[a-zA-Z0-9#(),.\s%/-]+$/, a = this._extractStyleProp(e, "color");
1212
+ a && a.length <= 100 && i.test(a) && (t.textColor = a);
1213
+ let o = this._extractStyleProp(e, "background-color");
1214
+ o && o.length <= 100 && i.test(o) && (t.backgroundColor = o);
1215
+ let s = this._extractStyleProp(e, "text-align");
1216
+ (s === "left" || s === "center" || s === "right") && (t.textAlign = s);
1217
+ let c = this._extractStyleProp(e, "font-size");
1218
+ if (c) {
1219
+ let e = parseInt(c, 10);
1220
+ !isNaN(e) && e > 0 && e <= 200 && (t.fontSize = e);
1221
+ }
1222
+ return Object.keys(t).length > 0 ? t : void 0;
1223
+ }
1224
+ _sanitizeFormat(e) {
1225
+ if (!e || typeof e != "object" || Array.isArray(e)) return;
1226
+ let t = e, n = {}, r = /^[a-zA-Z0-9#(),.\s%/-]+$/, i = /* @__PURE__ */ new Set([
1227
+ "left",
1228
+ "center",
1229
+ "right"
1230
+ ]);
1231
+ typeof t.bold == "boolean" && (n.bold = t.bold), typeof t.italic == "boolean" && (n.italic = t.italic), typeof t.underline == "boolean" && (n.underline = t.underline), typeof t.strikethrough == "boolean" && (n.strikethrough = t.strikethrough), typeof t.textColor == "string" && t.textColor.length <= 100 && r.test(t.textColor) && (n.textColor = t.textColor), typeof t.backgroundColor == "string" && t.backgroundColor.length <= 100 && r.test(t.backgroundColor) && (n.backgroundColor = t.backgroundColor), typeof t.textAlign == "string" && i.has(t.textAlign) && (n.textAlign = t.textAlign), typeof t.fontSize == "number" && t.fontSize > 0 && t.fontSize <= 200 && (n.fontSize = t.fontSize);
1232
+ let a = this._sanitizeNumberFormat(t.numberFormat);
1233
+ return a && (n.numberFormat = a), Object.keys(n).length > 0 ? n : void 0;
1234
+ }
1235
+ _sanitizeNumberFormat(e) {
1236
+ if (!e || typeof e != "object" || Array.isArray(e)) return;
1237
+ let t = e, n = /* @__PURE__ */ new Set([
1238
+ "number",
1239
+ "currency",
1240
+ "percent",
1241
+ "scientific"
1242
+ ]);
1243
+ if (typeof t.type != "string" || !n.has(t.type)) return;
1244
+ let r = { type: t.type };
1245
+ return typeof t.decimals == "number" && Number.isInteger(t.decimals) && t.decimals >= 0 && t.decimals <= 20 && (r.decimals = t.decimals), typeof t.currencySymbol == "string" && t.currencySymbol.length <= 8 && (r.currencySymbol = t.currencySymbol), typeof t.thousandsSep == "boolean" && (r.thousandsSep = t.thousandsSep), r;
1246
+ }
1247
+ _extractStyleProp(e, t) {
1248
+ let n = RegExp(`(?<![\\w-])${t}\\s*:\\s*([^;]+)`, "i"), r = e.match(n);
1249
+ return r ? r[1].trim() : void 0;
1250
+ }
1251
+ _escapeHTML(e) {
1252
+ return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
1253
+ }
1254
+ fallbackCopy(e) {
1255
+ console.warn("y11n-spreadsheet: Clipboard API unavailable, using deprecated execCommand fallback.");
1256
+ let t = document.createElement("textarea");
1257
+ t.value = e, t.style.position = "fixed", t.style.left = "-9999px", document.body.appendChild(t), t.select();
1258
+ try {
1259
+ document.execCommand("copy");
1260
+ } catch {}
1261
+ document.body.removeChild(t);
1262
+ }
1175
1263
  };
1176
- A.styles = M`
1264
+ // \0@oxc-project+runtime@0.156.0/helpers/esm/decorate.js
1265
+ function z(e, t, n, r) {
1266
+ var i = arguments.length, a = i < 3 ? t : r === null ? r = Object.getOwnPropertyDescriptor(t, n) : r, o;
1267
+ if (typeof Reflect == "object" && typeof Reflect.decorate == "function") a = Reflect.decorate(e, t, n, r);
1268
+ else for (var s = e.length - 1; s >= 0; s--) (o = e[s]) && (a = (i < 3 ? o(a) : i > 3 ? o(t, n, a) : o(t, n)) || a);
1269
+ return i > 3 && a && Object.defineProperty(t, n, a), a;
1270
+ }
1271
+ // src/components/y11n-formula-bar.ts
1272
+ var B = class extends e {
1273
+ constructor(...e) {
1274
+ super(...e), this.cellRef = "A1", this.rawValue = "", this.displayValue = "", this.mode = "raw", this.readOnly = !1, this._draft = "";
1275
+ }
1276
+ static {
1277
+ this.styles = t`
1177
1278
  :host {
1178
1279
  display: grid;
1179
1280
  grid-template-columns: minmax(48px, auto) auto minmax(0, 1fr);
@@ -1245,761 +1346,813 @@ A.styles = M`
1245
1346
  cursor: not-allowed;
1246
1347
  }
1247
1348
  `;
1248
- N([
1249
- m({ type: String, attribute: "cell-ref" })
1250
- ], A.prototype, "cellRef", 2);
1251
- N([
1252
- m({ type: String, attribute: "raw-value" })
1253
- ], A.prototype, "rawValue", 2);
1254
- N([
1255
- m({ type: String, attribute: "display-value" })
1256
- ], A.prototype, "displayValue", 2);
1257
- N([
1258
- m({ type: String })
1259
- ], A.prototype, "mode", 2);
1260
- N([
1261
- m({ type: Boolean, attribute: "read-only", reflect: !0 })
1262
- ], A.prototype, "readOnly", 2);
1263
- N([
1264
- E()
1265
- ], A.prototype, "_draft", 2);
1266
- A = N([
1267
- T("y11n-formula-bar")
1268
- ], A);
1269
- var Y = Object.defineProperty, J = Object.getOwnPropertyDescriptor, v = (i, e, t, r) => {
1270
- for (var s = r > 1 ? void 0 : r ? J(e, t) : e, o = i.length - 1, n; o >= 0; o--)
1271
- (n = i[o]) && (s = (r ? n(e, t, s) : n(s)) || s);
1272
- return r && s && Y(e, t, s), s;
1273
- };
1274
- let w = class extends O {
1275
- constructor() {
1276
- super(...arguments), this.rows = 100, this.cols = 26, this.data = /* @__PURE__ */ new Map(), this.readOnly = !1, this.functions = {}, this._isEditing = !1, this._editValue = "", this._formulaBarMode = "raw", this._refMode = !1, this._modeAnnouncement = "", this._scrollTop = 0, this._scrollLeft = 0, this._selection = new j(this, this.rows, this.cols), this._formulaEngine = new K(), this._clipboardManager = new G(), this._internalData = /* @__PURE__ */ new Map(), this._editingCellKey = null, this._undoStack = [], this._redoStack = [], this._maxHistory = 100, this._refCursorRow = 0, this._refCursorCol = 0, this._refInsertStart = 0, this._refInsertEnd = 0;
1277
- }
1278
- // ─── Lifecycle ──────────────────────────────────────
1279
- connectedCallback() {
1280
- super.connectedCallback(), this._syncData();
1281
- }
1282
- willUpdate(i) {
1283
- if (i.has("data") && this._syncData(), (i.has("rows") || i.has("cols")) && (this.rows = Math.max(1, Math.round(this.rows)), this.cols = Math.max(1, Math.round(this.cols)), this._selection.setBounds(this.rows, this.cols)), i.has("functions")) {
1284
- for (const [e, t] of Object.entries(this.functions))
1285
- this._formulaEngine.registerFunction(e, t);
1286
- this._recalcAll();
1287
- }
1288
- }
1289
- // ─── Public API Methods ─────────────────────────────
1290
- /** Returns the current state of the grid data */
1291
- getData() {
1292
- return new Map(this._internalData);
1293
- }
1294
- /** Hard reset of the grid data */
1295
- setData(i) {
1296
- this.data = i, this._syncData(), this.requestUpdate();
1297
- }
1298
- /** Dynamically register a formula function */
1299
- registerFunction(i, e) {
1300
- this._formulaEngine.registerFunction(i, e), this._recalcAll();
1301
- }
1302
- // ─── Format API ──────────────────────────────────────
1303
- /** Returns the format for a cell, or undefined if unformatted */
1304
- getCellFormat(i) {
1305
- var e;
1306
- return (e = this._internalData.get(i)) == null ? void 0 : e.format;
1307
- }
1308
- /** Merges format into a cell (creates cell if needed). Pass undefined to unset a property. */
1309
- setCellFormat(i, e) {
1310
- this._applyFormat([i], e, "programmatic");
1311
- }
1312
- /** Applies format to all cells in a range (creates empty cells if needed) */
1313
- setRangeFormat(i, e) {
1314
- const t = [];
1315
- for (let r = i.start.row; r <= i.end.row; r++)
1316
- for (let s = i.start.col; s <= i.end.col; s++)
1317
- t.push(b(r, s));
1318
- this._applyFormat(t, e, "programmatic");
1319
- }
1320
- /** Removes all formatting from a cell */
1321
- clearCellFormat(i) {
1322
- this._clearFormat([i], "programmatic");
1323
- }
1324
- /** Removes all formatting from a range */
1325
- clearRangeFormat(i) {
1326
- const e = [];
1327
- for (let t = i.start.row; t <= i.end.row; t++)
1328
- for (let r = i.start.col; r <= i.end.col; r++)
1329
- e.push(b(t, r));
1330
- this._clearFormat(e, "programmatic");
1331
- }
1332
- /** Toggles a boolean format property on the current selection */
1333
- toggleFormat(i) {
1334
- var o;
1335
- if (this.readOnly) return;
1336
- const e = this._selection.range, t = [];
1337
- let r = !0;
1338
- for (let n = e.start.row; n <= e.end.row; n++)
1339
- for (let a = e.start.col; a <= e.end.col; a++) {
1340
- const l = b(n, a);
1341
- t.push(l);
1342
- const c = this._internalData.get(l);
1343
- (o = c == null ? void 0 : c.format) != null && o[i] || (r = !1);
1344
- }
1345
- const s = { [i]: !r };
1346
- this._applyFormat(t, s, "user");
1347
- }
1348
- // ─── Format Internals ────────────────────────────────
1349
- _applyFormat(i, e, t) {
1350
- const r = this._snapshotSelection(), s = this._buildFormatBatch(i, e, r);
1351
- s && (t === "user" ? this._executeUserBatch(s) : (this._applyBatch(s, t), this._pushHistory(s)), this._dispatchFormatChange({ cellIds: i, format: e, source: t }));
1352
- }
1353
- _clearFormat(i, e) {
1354
- const t = this._snapshotSelection(), r = [];
1355
- for (const o of i) {
1356
- const n = this._internalData.get(o);
1357
- n != null && n.format && r.push({
1358
- id: o,
1359
- before: n.rawValue,
1360
- after: n.rawValue,
1361
- formatBefore: { ...n.format },
1362
- formatAfter: void 0
1363
- });
1364
- }
1365
- if (r.length === 0) return;
1366
- const s = {
1367
- deltas: r,
1368
- selectionBefore: t,
1369
- selectionAfter: t,
1370
- operation: "format"
1371
- };
1372
- e === "user" ? this._executeUserBatch(s) : (this._applyBatch(s, e), this._pushHistory(s)), this._dispatchFormatChange({ cellIds: i, format: {}, source: e });
1373
- }
1374
- _buildFormatBatch(i, e, t) {
1375
- const r = [];
1376
- for (const s of i) {
1377
- const o = this._internalData.get(s), n = o == null ? void 0 : o.format, a = { ...n, ...e };
1378
- for (const c of Object.keys(a))
1379
- a[c] === void 0 && delete a[c];
1380
- !z(n, a) && r.push({
1381
- id: s,
1382
- before: (o == null ? void 0 : o.rawValue) ?? "",
1383
- after: (o == null ? void 0 : o.rawValue) ?? "",
1384
- formatBefore: n ? { ...n } : void 0,
1385
- formatAfter: Object.keys(a).length > 0 ? a : void 0
1386
- });
1387
- }
1388
- return r.length === 0 ? null : {
1389
- deltas: r,
1390
- selectionBefore: t,
1391
- selectionAfter: t,
1392
- operation: "format"
1393
- };
1394
- }
1395
- /** Convert CellFormat to inline styles for styleMap */
1396
- _getCellStyles(i, e) {
1397
- const t = this._getCell(i, e);
1398
- if (!(t != null && t.format)) return {};
1399
- const r = {}, s = t.format;
1400
- s.bold && (r["font-weight"] = "bold"), s.italic && (r["font-style"] = "italic");
1401
- const o = [];
1402
- if (s.underline && o.push("underline"), s.strikethrough && o.push("line-through"), o.length > 0 && (r["text-decoration"] = o.join(" ")), s.textColor && (r.color = s.textColor), s.backgroundColor && (r["background-color"] = s.backgroundColor), s.fontSize && (r["font-size"] = `${s.fontSize}px`), s.textAlign) {
1403
- r["text-align"] = s.textAlign;
1404
- const n = { left: "flex-start", center: "center", right: "flex-end" };
1405
- r["justify-content"] = n[s.textAlign];
1406
- }
1407
- return r;
1408
- }
1409
- // ─── Data Sync ──────────────────────────────────────
1410
- _syncData() {
1411
- const i = this._internalData, e = new Map(this.data), t = [];
1412
- for (const [r, s] of e) {
1413
- const o = i.get(r);
1414
- (!o || o.rawValue !== s.rawValue) && t.push(r);
1415
- }
1416
- for (const r of i.keys())
1417
- e.has(r) || t.push(r);
1418
- this._internalData = e, this._undoStack = [], this._redoStack = [], t.length > 0 && i.size > 0 ? (this._formulaEngine.updateData(this._internalData, t), this._recalcAffected(t)) : (this._formulaEngine.setData(this._internalData), this._recalcAll());
1419
- }
1420
- _recalcAll() {
1421
- this._formulaEngine.recalculate();
1422
- }
1423
- _recalcAffected(i) {
1424
- this._formulaEngine.recalculateAffected(i);
1425
- }
1426
- // ─── Cell Access ────────────────────────────────────
1427
- _getCell(i, e) {
1428
- return this._internalData.get(b(i, e));
1429
- }
1430
- _getCellDisplay(i, e) {
1431
- const t = this._getCell(i, e);
1432
- return (t == null ? void 0 : t.displayValue) ?? "";
1433
- }
1434
- _getCellRaw(i, e) {
1435
- const t = this._getCell(i, e);
1436
- return (t == null ? void 0 : t.rawValue) ?? "";
1437
- }
1438
- _getActiveCellKey() {
1439
- const { row: i, col: e } = this._selection.activeCell;
1440
- return b(i, e);
1441
- }
1442
- _getActiveCellRef() {
1443
- return D(this._selection.activeCell);
1444
- }
1445
- _setCellRaw(i, e) {
1446
- const t = this._internalData.get(i), r = this._formulaEngine.evaluate(e, i);
1447
- this._internalData.set(i, {
1448
- rawValue: e,
1449
- displayValue: r.displayValue,
1450
- type: r.type,
1451
- format: t == null ? void 0 : t.format
1452
- });
1453
- }
1454
- _applyRawValueByString(i, e) {
1455
- if (e === "") {
1456
- const t = this._internalData.get(i);
1457
- t != null && t.format ? this._internalData.set(i, {
1458
- rawValue: "",
1459
- displayValue: "",
1460
- type: "text",
1461
- format: t.format
1462
- }) : this._internalData.delete(i);
1463
- return;
1464
- }
1465
- this._setCellRaw(i, e);
1466
- }
1467
- _snapshotSelection() {
1468
- return {
1469
- anchor: { ...this._selection.anchor },
1470
- head: { ...this._selection.head }
1471
- };
1472
- }
1473
- _restoreSelection(i) {
1474
- this._selection.moveTo(i.anchor.row, i.anchor.col), this._selection.moveTo(i.head.row, i.head.col, !0), this._dispatchSelectionChange(), this.updateComplete.then(() => this._focusActiveCell());
1475
- }
1476
- _buildCommandBatch(i, e, t, r) {
1477
- var o;
1478
- const s = [];
1479
- for (const n of i) {
1480
- const a = ((o = this._internalData.get(n.id)) == null ? void 0 : o.rawValue) ?? "", l = n.value;
1481
- a !== l && s.push({ id: n.id, before: a, after: l });
1482
- }
1483
- return s.length === 0 ? null : {
1484
- deltas: s,
1485
- selectionBefore: t,
1486
- selectionAfter: r,
1487
- operation: e
1488
- };
1489
- }
1490
- _pushHistory(i) {
1491
- this._undoStack.push(i), this._redoStack = [], this._undoStack.length > this._maxHistory && this._undoStack.shift();
1492
- }
1493
- _finalizeBatch(i, e, t) {
1494
- this._recalcAffected(i.deltas.map((s) => s.id)), (e !== "user" || i.operation !== "edit") && this._dispatchDataChange({
1495
- updates: i.deltas.map((s) => ({ id: s.id, value: s[t] })),
1496
- source: e,
1497
- operation: i.operation
1498
- }), this.requestUpdate();
1499
- }
1500
- _applyBatch(i, e) {
1501
- for (const t of i.deltas)
1502
- this._applyRawValueByString(t.id, t.after), "formatAfter" in t && this._applyCellFormat(t.id, t.formatAfter);
1503
- this._restoreSelection(i.selectionAfter), this._finalizeBatch(i, e, "after");
1504
- }
1505
- _revertBatch(i) {
1506
- for (const e of i.deltas)
1507
- this._applyRawValueByString(e.id, e.before), "formatBefore" in e && this._applyCellFormat(e.id, e.formatBefore);
1508
- this._restoreSelection(i.selectionBefore), this._finalizeBatch(i, "undo", "before");
1509
- }
1510
- /** Low-level: set or remove format on a cell */
1511
- _applyCellFormat(i, e) {
1512
- let t = this._internalData.get(i);
1513
- t || (t = { rawValue: "", displayValue: "", type: "text" }, this._internalData.set(i, t)), e && Object.keys(e).length > 0 ? t.format = e : (delete t.format, t.rawValue === "" && this._internalData.delete(i));
1514
- }
1515
- _executeUserBatch(i) {
1516
- this._applyBatch(i, "user"), this._pushHistory(i);
1517
- }
1518
- _commitRawValue(i, e) {
1519
- const t = this._internalData.get(i), r = (t == null ? void 0 : t.rawValue) ?? "";
1520
- if (e === r) return !1;
1521
- const s = this._snapshotSelection(), o = this._buildCommandBatch(
1522
- [{ id: i, value: e }],
1523
- "edit",
1524
- s,
1525
- s
1526
- );
1527
- return o ? (this._executeUserBatch(o), this._dispatchCellChange({ cellId: i, value: e, oldValue: r }), !0) : !1;
1528
- }
1529
- _undo() {
1530
- if (this.readOnly) return;
1531
- const i = this._undoStack.pop();
1532
- i && (this._revertBatch(i), this._redoStack.push(i), this._dispatchFormatChangeForBatch(i, "undo"));
1533
- }
1534
- _redo() {
1535
- if (this.readOnly) return;
1536
- const i = this._redoStack.pop();
1537
- i && (this._applyBatch(i, "redo"), this._undoStack.push(i), this._undoStack.length > this._maxHistory && this._undoStack.shift(), this._dispatchFormatChangeForBatch(i, "redo"));
1538
- }
1539
- /** Dispatch format-change for any batch that contains format deltas.
1540
- * NOTE: The event carries the format from the first delta only. For heterogeneous
1541
- * batches (e.g., paste with varied per-cell formatting), this is an approximation —
1542
- * the cellIds are accurate but the format may not represent every cell. Consumers
1543
- * that need per-cell accuracy should read cell data directly. */
1544
- _dispatchFormatChangeForBatch(i, e) {
1545
- const t = i.deltas.filter(
1546
- (o) => o.formatBefore !== void 0 || o.formatAfter !== void 0
1547
- );
1548
- if (t.length === 0) return;
1549
- const r = t.map((o) => o.id), s = e === "undo" ? t[0].formatBefore ?? {} : t[0].formatAfter ?? {};
1550
- this._dispatchFormatChange({ cellIds: r, format: s, source: e });
1551
- }
1552
- // ─── Editing ────────────────────────────────────────
1553
- _startEditing(i) {
1554
- if (this.readOnly || this._isEditing) return;
1555
- const { row: e, col: t } = this._selection.activeCell, r = b(e, t);
1556
- this._editingCellKey = r;
1557
- const s = this._internalData.get(r);
1558
- this._editValue = i ?? (s == null ? void 0 : s.rawValue) ?? "", this._isEditing = !0;
1559
- }
1560
- _commitEdit() {
1561
- if (!this._isEditing || !this._editingCellKey) return;
1562
- const i = this._editingCellKey, e = this._editValue;
1563
- this._isEditing = !1, this._editingCellKey = null, this._commitRawValue(i, e), this.updateComplete.then(() => this._focusActiveCell());
1564
- }
1565
- _cancelEdit() {
1566
- this._isEditing = !1, this._editingCellKey = null, this.updateComplete.then(() => this._focusActiveCell());
1567
- }
1568
- // ─── Reference Selection ────────────────────────────
1569
- /**
1570
- * Check if the cursor is at a position in the formula where a cell reference
1571
- * can be inserted (after =, +, -, *, /, &, (, ,, or comparison operators).
1572
- */
1573
- _isRefPositionInFormula() {
1574
- var r;
1575
- if (!this._editValue.startsWith("=")) return !1;
1576
- const i = ((r = this._editor) == null ? void 0 : r.selectionStart) ?? this._editValue.length, e = this._editValue.substring(0, i).trimEnd();
1577
- if (e === "=") return !0;
1578
- const t = e[e.length - 1];
1579
- return ["+", "-", "*", "/", "&", "(", ",", "<", ">", "="].includes(t);
1580
- }
1581
- /**
1582
- * Handle an arrow key press during formula editing to insert or move
1583
- * a cell reference.
1584
- */
1585
- _handleRefArrow(i) {
1586
- var r;
1587
- const e = i === "ArrowDown" ? 1 : i === "ArrowUp" ? -1 : 0, t = i === "ArrowRight" ? 1 : i === "ArrowLeft" ? -1 : 0;
1588
- if (this._refMode) {
1589
- this._refCursorRow = Math.max(0, Math.min(this._refCursorRow + e, this.rows - 1)), this._refCursorCol = Math.max(0, Math.min(this._refCursorCol + t, this.cols - 1));
1590
- const s = D({ row: this._refCursorRow, col: this._refCursorCol });
1591
- this._modeAnnouncement = `Selecting ${s}`, this._editValue = this._editValue.substring(0, this._refInsertStart) + s + this._editValue.substring(this._refInsertEnd), this._refInsertEnd = this._refInsertStart + s.length;
1592
- } else {
1593
- const { row: s, col: o } = this._selection.activeCell;
1594
- this._refCursorRow = Math.max(0, Math.min(s + e, this.rows - 1)), this._refCursorCol = Math.max(0, Math.min(o + t, this.cols - 1)), this._refMode = !0, this._modeAnnouncement = `Reference selection mode. Selecting ${D({ row: this._refCursorRow, col: this._refCursorCol })}`;
1595
- const n = ((r = this._editor) == null ? void 0 : r.selectionStart) ?? this._editValue.length;
1596
- this._refInsertStart = n;
1597
- const a = D({ row: this._refCursorRow, col: this._refCursorCol });
1598
- this._editValue = this._editValue.substring(0, n) + a + this._editValue.substring(n), this._refInsertEnd = n + a.length;
1599
- }
1600
- }
1601
- _exitRefMode() {
1602
- this._refMode && (this._modeAnnouncement = "Reference selection ended"), this._refMode = !1;
1603
- }
1604
- /**
1605
- * Cycle the cell reference at/before the cursor through reference modes:
1606
- * A1 → $A$1 → A$1 → $A1 → A1
1607
- */
1608
- _cycleReferenceMode() {
1609
- var _;
1610
- const i = ((_ = this._editor) == null ? void 0 : _.selectionStart) ?? this._editValue.length, e = this._editValue, t = /(\$?)([A-Z]+)(\$?)(\d+)/gi;
1611
- let r, s = null;
1612
- for (; (r = t.exec(e)) !== null; ) {
1613
- const d = r.index, $ = d + r[0].length;
1614
- d <= i && $ >= i - 1 && (s = r);
1615
- }
1616
- if (!s) return;
1617
- const [o, n, a, l, c] = s, h = s.index, u = n === "$", p = l === "$";
1618
- let f;
1619
- !u && !p ? f = `$${a}$${c}` : u && p ? f = `${a}$${c}` : !u && p ? f = `$${a}${c}` : f = `${a}${c}`, this._editValue = e.substring(0, h) + f + e.substring(h + o.length);
1620
- const g = h + f.length;
1621
- this.updateComplete.then(() => {
1622
- this._editor && this._editor.setSelectionRange(g, g);
1623
- });
1624
- }
1625
- // ─── Focus Management ───────────────────────────────
1626
- _focusActiveCell() {
1627
- var r;
1628
- const { row: i, col: e } = this._selection.activeCell, t = (r = this.shadowRoot) == null ? void 0 : r.querySelector(
1629
- `[data-row="${i}"][data-col="${e}"]`
1630
- );
1631
- t && t.focus();
1632
- }
1633
- // ─── Keyboard Handling ──────────────────────────────
1634
- _handleGridKeydown(i) {
1635
- if (this._isEditing) {
1636
- this._handleEditorKeydown(i);
1637
- return;
1638
- }
1639
- const e = i.shiftKey, t = i.ctrlKey || i.metaKey;
1640
- switch (i.stopPropagation(), i.key) {
1641
- case "ArrowUp":
1642
- i.preventDefault(), this._selection.move(-1, 0, e), this._dispatchSelectionChange(), this.updateComplete.then(() => this._focusActiveCell());
1643
- break;
1644
- case "ArrowDown":
1645
- i.preventDefault(), this._selection.move(1, 0, e), this._dispatchSelectionChange(), this.updateComplete.then(() => this._focusActiveCell());
1646
- break;
1647
- case "ArrowLeft":
1648
- i.preventDefault(), this._selection.move(0, -1, e), this._dispatchSelectionChange(), this.updateComplete.then(() => this._focusActiveCell());
1649
- break;
1650
- case "ArrowRight":
1651
- i.preventDefault(), this._selection.move(0, 1, e), this._dispatchSelectionChange(), this.updateComplete.then(() => this._focusActiveCell());
1652
- break;
1653
- case "Tab":
1654
- i.preventDefault(), e ? this._selection.move(0, -1) : this._selection.move(0, 1), this._dispatchSelectionChange(), this.updateComplete.then(() => this._focusActiveCell());
1655
- break;
1656
- case "Enter":
1657
- i.preventDefault(), this.readOnly || this._startEditing();
1658
- break;
1659
- case "Escape":
1660
- i.preventDefault(), this._selection.clearSelection(), this._dispatchSelectionChange();
1661
- break;
1662
- case "Delete":
1663
- case "Backspace":
1664
- i.preventDefault(), this.readOnly || this._clearSelectedCells();
1665
- break;
1666
- case "a":
1667
- t && (i.preventDefault(), this._selection.selectAll(), this._dispatchSelectionChange());
1668
- break;
1669
- case "c":
1670
- t && (i.preventDefault(), this._handleCopy());
1671
- break;
1672
- case "x":
1673
- t && (i.preventDefault(), this._handleCut());
1674
- break;
1675
- case "v":
1676
- t && (i.preventDefault(), this._handlePaste());
1677
- break;
1678
- case "b":
1679
- t && (i.preventDefault(), this.toggleFormat("bold"));
1680
- break;
1681
- case "i":
1682
- t && (i.preventDefault(), this.toggleFormat("italic"));
1683
- break;
1684
- case "u":
1685
- t && (i.preventDefault(), this.toggleFormat("underline"));
1686
- break;
1687
- case "z":
1688
- case "Z":
1689
- t && (i.preventDefault(), e ? this._redo() : this._undo());
1690
- break;
1691
- default:
1692
- !t && !i.altKey && i.key.length === 1 && !this.readOnly && (i.preventDefault(), this._startEditing(i.key));
1693
- break;
1694
- }
1695
- }
1696
- _handleEditorKeydown(i) {
1697
- switch (i.stopPropagation(), i.key) {
1698
- case "Enter":
1699
- i.preventDefault(), this._exitRefMode(), this._commitEdit(), this._selection.move(1, 0), this._dispatchSelectionChange(), this.updateComplete.then(() => this._focusActiveCell());
1700
- break;
1701
- case "Tab":
1702
- i.preventDefault(), this._exitRefMode(), this._commitEdit(), i.shiftKey ? this._selection.move(0, -1) : this._selection.move(0, 1), this._dispatchSelectionChange(), this.updateComplete.then(() => this._focusActiveCell());
1703
- break;
1704
- case "Escape":
1705
- i.preventDefault(), this._exitRefMode(), this._cancelEdit();
1706
- break;
1707
- case "ArrowUp":
1708
- case "ArrowDown":
1709
- case "ArrowLeft":
1710
- case "ArrowRight":
1711
- (this._refMode || this._isRefPositionInFormula()) && (i.preventDefault(), this._handleRefArrow(i.key));
1712
- break;
1713
- case "F4":
1714
- this._editValue.startsWith("=") && (i.preventDefault(), this._cycleReferenceMode());
1715
- break;
1716
- }
1717
- }
1718
- // ─── Mouse Handling ─────────────────────────────────
1719
- _handleCellPointerDown(i) {
1720
- const t = i.target.closest("[data-row]");
1721
- if (!t) return;
1722
- const r = parseInt(t.dataset.row, 10), s = parseInt(t.dataset.col, 10);
1723
- this._isEditing && this._commitEdit(), this._selection.startSelection(r, s, i.shiftKey), this._dispatchSelectionChange(), t.focus();
1724
- const o = new AbortController(), { signal: n } = o, a = () => o.abort(), l = (h) => {
1725
- var f, g;
1726
- const u = (f = this.shadowRoot) == null ? void 0 : f.elementFromPoint(h.clientX, h.clientY), p = (g = u == null ? void 0 : u.closest) == null ? void 0 : g.call(u, "[data-row]");
1727
- if (p) {
1728
- const _ = parseInt(p.dataset.row, 10), d = parseInt(p.dataset.col, 10);
1729
- this._selection.extendSelection(_, d), this._dispatchSelectionChange();
1730
- }
1731
- }, c = () => {
1732
- this._selection.endSelection(), a();
1733
- };
1734
- document.addEventListener("pointermove", l, { signal: n }), document.addEventListener("pointerup", c, { signal: n }), document.addEventListener("pointercancel", c, { signal: n }), window.addEventListener("blur", c, { signal: n });
1735
- }
1736
- _handleCellDblClick(i) {
1737
- if (this.readOnly) return;
1738
- const t = i.target.closest("[data-row]");
1739
- if (!t) return;
1740
- const r = parseInt(t.dataset.row, 10), s = parseInt(t.dataset.col, 10);
1741
- this._selection.moveTo(r, s), this._startEditing();
1742
- }
1743
- // ─── Clipboard ──────────────────────────────────────
1744
- async _handleCopy() {
1745
- await this._clipboardManager.copy(this._internalData, this._selection.range);
1746
- }
1747
- async _handleCut() {
1748
- if (this.readOnly) return;
1749
- const i = await this._clipboardManager.cut(
1750
- this._internalData,
1751
- this._selection.range
1752
- ), e = [], t = this._snapshotSelection();
1753
- for (const s of i)
1754
- this._internalData.has(s) && e.push({ id: s, value: "" });
1755
- const r = this._buildCommandBatch(e, "cut", t, t);
1756
- r && this._executeUserBatch(r);
1757
- }
1758
- async _handlePaste() {
1759
- if (this.readOnly) return;
1760
- const { row: i, col: e } = this._selection.activeCell, t = await this._clipboardManager.paste(i, e, this.rows, this.cols);
1761
- if (t && t.length > 0) {
1762
- const r = this._snapshotSelection(), s = t.map((n) => ({ id: n.id, value: n.value })), o = this._buildCommandBatch(s, "paste", r, r);
1763
- if (o) {
1764
- const n = new Map(t.map((l) => [l.id, l]));
1765
- for (const l of o.deltas) {
1766
- const c = n.get(l.id);
1767
- if (c != null && c.format) {
1768
- const h = this._internalData.get(l.id);
1769
- l.formatBefore = h != null && h.format ? { ...h.format } : void 0, l.formatAfter = c.format;
1770
- }
1771
- }
1772
- const a = new Set(o.deltas.map((l) => l.id));
1773
- for (const l of t)
1774
- if (l.format && !a.has(l.id)) {
1775
- const c = this._internalData.get(l.id);
1776
- o.deltas.push({
1777
- id: l.id,
1778
- before: (c == null ? void 0 : c.rawValue) ?? "",
1779
- after: l.value,
1780
- formatBefore: c != null && c.format ? { ...c.format } : void 0,
1781
- formatAfter: l.format
1782
- });
1783
- }
1784
- this._executeUserBatch(o);
1785
- }
1786
- }
1787
- }
1788
- // ─── Cell Clearing ──────────────────────────────────
1789
- _clearSelectedCells() {
1790
- const i = this._selection.range, e = [], t = this._snapshotSelection();
1791
- for (let s = i.start.row; s <= i.end.row; s++)
1792
- for (let o = i.start.col; o <= i.end.col; o++) {
1793
- const n = b(s, o);
1794
- this._internalData.has(n) && e.push({ id: n, value: "" });
1795
- }
1796
- const r = this._buildCommandBatch(e, "clear", t, t);
1797
- r && this._executeUserBatch(r);
1798
- }
1799
- // ─── Scroll Handling ────────────────────────────────
1800
- _handleScroll(i) {
1801
- const e = i.target;
1802
- this._scrollTop = e.scrollTop, this._scrollLeft = e.scrollLeft;
1803
- }
1804
- // ─── Editor Input ───────────────────────────────────
1805
- _handleEditorInput(i) {
1806
- this._editValue = i.target.value, this._exitRefMode();
1807
- }
1808
- _handleEditorBlur() {
1809
- this._isEditing && this._commitEdit();
1810
- }
1811
- _handleFormulaBarModeChange(i) {
1812
- this._formulaBarMode = i.detail.mode;
1813
- }
1814
- _handleFormulaBarCommit(i) {
1815
- if (this.readOnly) return;
1816
- const e = this._editingCellKey ?? this._getActiveCellKey();
1817
- this._isEditing && (this._isEditing = !1, this._editingCellKey = null), this._commitRawValue(e, i.detail.value);
1818
- }
1819
- // ─── Event Dispatching ──────────────────────────────
1820
- _dispatchCellChange(i) {
1821
- this.dispatchEvent(new CustomEvent("cell-change", { detail: i, bubbles: !0, composed: !0 }));
1822
- }
1823
- _dispatchSelectionChange() {
1824
- const i = { range: this._selection.range };
1825
- this.dispatchEvent(
1826
- new CustomEvent("selection-change", { detail: i, bubbles: !0, composed: !0 })
1827
- );
1828
- }
1829
- _dispatchDataChange(i) {
1830
- this.dispatchEvent(
1831
- new CustomEvent("data-change", { detail: i, bubbles: !0, composed: !0 })
1832
- );
1833
- }
1834
- _dispatchFormatChange(i) {
1835
- this.dispatchEvent(
1836
- new CustomEvent("format-change", { detail: i, bubbles: !0, composed: !0 })
1837
- );
1838
- }
1839
- // ─── Virtual Rendering ──────────────────────────────
1840
- /**
1841
- * Compute which rows and columns are visible based on scroll position.
1842
- * We add a buffer of extra rows/cols for smooth scrolling.
1843
- */
1844
- _getVisibleRange() {
1845
- const i = this._grid;
1846
- if (!i)
1847
- return { startRow: 0, endRow: Math.min(this.rows, 40), startCol: 0, endCol: this.cols };
1848
- const e = this._getCSSVarPx("--ls-cell-height", 28), t = this._getCSSVarPx("--ls-cell-width", 100), r = i.clientHeight, s = i.clientWidth, o = 5;
1849
- let n = Math.max(0, Math.floor(this._scrollTop / e) - o), a = Math.min(this.rows, Math.ceil((this._scrollTop + r) / e) + o), l = Math.max(0, Math.floor(this._scrollLeft / t) - o), c = Math.min(this.cols, Math.ceil((this._scrollLeft + s) / t) + o);
1850
- const { row: h, col: u } = this._selection.activeCell;
1851
- return h < n && (n = h), h >= a && (a = h + 1), u < l && (l = u), u >= c && (c = u + 1), { startRow: n, endRow: a, startCol: l, endCol: c };
1852
- }
1853
- _getCSSVarPx(i, e) {
1854
- const t = getComputedStyle(this).getPropertyValue(i);
1855
- if (t) {
1856
- const r = parseInt(t, 10);
1857
- if (!isNaN(r)) return r;
1858
- }
1859
- return e;
1860
- }
1861
- // ─── Editor Positioning ─────────────────────────────
1862
- _getEditorStyle() {
1863
- if (!this._isEditing) return { display: "none" };
1864
- const { row: i, col: e } = this._selection.activeCell, t = this._getCSSVarPx("--ls-cell-height", 28), r = this._getCSSVarPx("--ls-cell-width", 100), s = 50, o = (i + 1) * t - this._scrollTop, n = s + e * r - this._scrollLeft, a = {
1865
- display: "block",
1866
- position: "absolute",
1867
- top: `${o}px`,
1868
- left: `${n}px`,
1869
- width: `${r}px`,
1870
- height: `${t}px`,
1871
- "z-index": "10"
1872
- }, l = this._getCellStyles(i, e);
1873
- for (const [c, h] of Object.entries(l))
1874
- c !== "justify-content" && c !== "background-color" && (a[c] = h);
1875
- return a;
1876
- }
1877
- render() {
1878
- const { startRow: i, endRow: e, startCol: t, endCol: r } = this._getVisibleRange(), s = this._selection.activeCell, o = this._getCellRaw(s.row, s.col), n = this._getCellDisplay(s.row, s.col);
1879
- return y`
1880
- <div class="ls-wrapper">
1881
- <y11n-formula-bar
1882
- cell-ref="${this._getActiveCellRef()}"
1883
- raw-value="${o}"
1884
- display-value="${n}"
1885
- .mode="${this._formulaBarMode}"
1886
- .readOnly="${this.readOnly}"
1887
- @formula-bar-mode-change="${this._handleFormulaBarModeChange}"
1888
- @formula-bar-commit="${this._handleFormulaBarCommit}"
1889
- ></y11n-formula-bar>
1890
-
1891
- <div class="ls-grid-shell">
1892
- <input
1893
- id="editor"
1894
- part="editor"
1895
- style=${U(this._getEditorStyle())}
1896
- .value="${this._editValue}"
1897
- @input="${this._handleEditorInput}"
1898
- @keydown="${this._handleEditorKeydown}"
1899
- @blur="${this._handleEditorBlur}"
1900
- aria-label="Cell editor"
1901
- />
1902
-
1903
- <div
1904
- class="ls-grid"
1905
- role="grid"
1906
- aria-rowcount="${this.rows + 1}"
1907
- aria-colcount="${this.cols + 1}"
1908
- aria-readonly="${this.readOnly}"
1909
- style="--cols: ${this.cols};"
1910
- tabindex="-1"
1911
- @keydown="${this._handleGridKeydown}"
1912
- @scroll="${this._handleScroll}"
1913
- >
1914
- ${this._renderHeaderRow(t, r)}
1915
- ${this._renderRows(i, e, t, r)}
1916
- </div>
1917
- </div>
1918
-
1919
- <div class="sr-only" role="status" aria-live="polite" aria-atomic="true">
1920
- ${this._getAnnouncement(s)}
1921
- </div>
1922
- <div class="sr-only" role="log" aria-live="assertive" aria-atomic="true">
1923
- ${this._modeAnnouncement}
1924
- </div>
1925
- </div>
1926
- `;
1927
- }
1928
- _renderHeaderRow(i, e) {
1929
- const t = [];
1930
- for (let r = i; r < e; r++)
1931
- t.push({ letter: F(r), index: r });
1932
- return y`
1933
- <div class="ls-header-row" role="row" aria-rowindex="1">
1934
- <div class="ls-corner-header" role="columnheader"></div>
1935
- ${i > 0 ? y`<div style="grid-column: span ${i};"></div>` : k}
1936
- ${t.map(
1937
- (r) => y`
1938
- <div
1939
- class="ls-col-header"
1940
- role="columnheader"
1941
- aria-colindex="${r.index + 2}"
1942
- >
1943
- ${r.letter}
1944
- </div>
1945
- `
1946
- )}
1947
- ${e < this.cols ? y`<div style="grid-column: span ${this.cols - e};"></div>` : k}
1948
- </div>
1949
- `;
1950
- }
1951
- _renderRows(i, e, t, r) {
1952
- const s = this._getCSSVarPx("--ls-cell-height", 28), o = Array.from({ length: e - i }, (n, a) => i + a);
1953
- return y`
1954
- ${i > 0 ? y`<div style="grid-column: 1 / -1; height: ${i * s}px;"></div>` : k}
1955
- ${L(o, (n) => n, (n) => this._renderRow(n, t, r))}
1956
- ${e < this.rows ? y`<div style="grid-column: 1 / -1; height: ${(this.rows - e) * s}px;"></div>` : k}
1957
- `;
1958
- }
1959
- _renderRow(i, e, t) {
1960
- const r = [];
1961
- for (let s = e; s < t; s++) {
1962
- const o = this._selection.isCellSelected(i, s), n = this._selection.isCellActive(i, s), a = this._refMode && i === this._refCursorRow && s === this._refCursorCol, l = this._getCellDisplay(i, s), c = b(i, s), h = this._getCellStyles(i, s);
1963
- r.push(y`
1964
- <div
1965
- class="ls-cell ${n ? "active-cell" : ""} ${a ? "ref-highlight" : ""}"
1966
- role="gridcell"
1967
- aria-colindex="${s + 2}"
1968
- aria-selected="${o}"
1969
- aria-readonly="${this.readOnly}"
1970
- aria-current="${n ? "true" : "false"}"
1971
- data-row="${i}"
1972
- data-col="${s}"
1973
- data-key="${c}"
1974
- tabindex="${n ? 0 : -1}"
1975
- style=${U(h)}
1976
- @pointerdown="${this._handleCellPointerDown}"
1977
- @dblclick="${this._handleCellDblClick}"
1349
+ }
1350
+ willUpdate(e) {
1351
+ (e.has("mode") || e.has("rawValue") || e.has("displayValue")) && (this._draft = this._sourceValue());
1352
+ }
1353
+ _sourceValue() {
1354
+ return this.mode === "raw" ? this.rawValue : this.displayValue;
1355
+ }
1356
+ _setMode(e) {
1357
+ this.mode !== e && this.dispatchEvent(new CustomEvent("formula-bar-mode-change", {
1358
+ detail: { mode: e },
1359
+ bubbles: !0,
1360
+ composed: !0
1361
+ }));
1362
+ }
1363
+ _onInput(e) {
1364
+ this._draft = e.target.value;
1365
+ }
1366
+ _commit() {
1367
+ this.dispatchEvent(new CustomEvent("formula-bar-commit", {
1368
+ detail: {
1369
+ value: this._draft,
1370
+ cellRef: this.cellRef
1371
+ },
1372
+ bubbles: !0,
1373
+ composed: !0
1374
+ }));
1375
+ }
1376
+ _onKeydown(e) {
1377
+ e.key === "Enter" ? (e.preventDefault(), this._commit()) : e.key === "Escape" && (e.preventDefault(), this._draft = this._sourceValue());
1378
+ }
1379
+ _onBlur() {
1380
+ this._draft !== this._sourceValue() && this._commit();
1381
+ }
1382
+ render() {
1383
+ return n`
1384
+ <output class="cell-ref" aria-live="polite" aria-atomic="true">${this.cellRef}</output>
1385
+ <div class="mode-toggle" role="group" aria-label="Formula bar mode">
1386
+ <button
1387
+ type="button"
1388
+ aria-pressed="${this.mode === "raw"}"
1389
+ @click="${() => this._setMode("raw")}"
1978
1390
  >
1979
- <span class="cell-text">${l}</span>
1980
- </div>
1981
- `);
1982
- }
1983
- return y`
1984
- <div class="ls-row" role="row" aria-rowindex="${i + 2}">
1985
- <div class="ls-row-header" role="rowheader">${i + 1}</div>
1986
- ${e > 0 ? y`<div style="grid-column: span ${e};"></div>` : k}
1987
- ${r}
1988
- ${t < this.cols ? y`<div style="grid-column: span ${this.cols - t};"></div>` : k}
1391
+ Raw
1392
+ </button>
1393
+ <button
1394
+ type="button"
1395
+ aria-pressed="${this.mode === "formatted"}"
1396
+ @click="${() => this._setMode("formatted")}"
1397
+ >
1398
+ Formatted
1399
+ </button>
1989
1400
  </div>
1401
+ <input
1402
+ type="text"
1403
+ .value="${this._draft}"
1404
+ ?disabled="${this.readOnly}"
1405
+ @input="${this._onInput}"
1406
+ @keydown="${this._onKeydown}"
1407
+ @blur="${this._onBlur}"
1408
+ aria-label="Formula bar"
1409
+ />
1990
1410
  `;
1991
- }
1992
- _getAnnouncement(i) {
1993
- const e = `${F(i.col)}${i.row + 1}`, t = this._getCellDisplay(i.row, i.col);
1994
- return t ? `${e}: ${t}` : e;
1995
- }
1996
- updated() {
1997
- this._isEditing && this._editor && (this._editor.focus(), this._refMode ? this._editor.setSelectionRange(this._refInsertEnd, this._refInsertEnd) : this._editor.setSelectionRange(this._editValue.length, this._editValue.length));
1998
- }
1411
+ }
1999
1412
  };
2000
- w.styles = M`
1413
+ z([a({
1414
+ type: String,
1415
+ attribute: "cell-ref"
1416
+ })], B.prototype, "cellRef", void 0), z([a({
1417
+ type: String,
1418
+ attribute: "raw-value"
1419
+ })], B.prototype, "rawValue", void 0), z([a({
1420
+ type: String,
1421
+ attribute: "display-value"
1422
+ })], B.prototype, "displayValue", void 0), z([a({ type: String })], B.prototype, "mode", void 0), z([a({
1423
+ type: Boolean,
1424
+ attribute: "read-only",
1425
+ reflect: !0
1426
+ })], B.prototype, "readOnly", void 0), z([s()], B.prototype, "_draft", void 0), B = z([i("y11n-formula-bar")], B);
1427
+ // src/y11n-spreadsheet.ts
1428
+ var V;
1429
+ function H(e) {
1430
+ let t = { ...e };
1431
+ return e.format && (t.format = { ...e.format }, e.format.numberFormat && (t.format.numberFormat = { ...e.format.numberFormat })), t;
1432
+ }
1433
+ function U(e, t, n, r) {
1434
+ let i = [], a = 0;
1435
+ for (let t of e) t > a && i.push(r(a, t - a)), i.push(n(t)), a = t + 1;
1436
+ return t > a && i.push(r(a, t - a)), i;
1437
+ }
1438
+ function W(e, t, n) {
1439
+ let r = [];
1440
+ for (let n = e; n < t; n++) r.push(n);
1441
+ for (let i of n) (i < e || i >= t) && !r.includes(i) && r.push(i);
1442
+ return r.sort((e, t) => e - t);
1443
+ }
1444
+ var G = class extends e {
1445
+ static {
1446
+ V = this;
1447
+ }
1448
+ constructor(...e) {
1449
+ super(...e), this.rows = 100, this.cols = 26, this.data = /* @__PURE__ */ new Map(), this.readOnly = !1, this.gridLabel = "Spreadsheet", this.functions = {}, this._isEditing = !1, this._editValue = "", this._formulaBarMode = "raw", this._editorNeedsCursorReset = !1, this._refMode = !1, this._modeAnnouncement = "", this._scrollTop = 0, this._scrollLeft = 0, this._selection = new x(this, this.rows, this.cols), this._formulaEngine = new L(), this._clipboardManager = new R(), this._internalData = /* @__PURE__ */ new Map(), this._lastSyncedDataRef = null, this._editingCellKey = null, this._undoStack = [], this._redoStack = [], this._maxHistory = 100, this._refCursorRow = 0, this._refCursorCol = 0, this._refInsertStart = 0, this._refInsertEnd = 0, this._pasteInProgress = !1;
1450
+ }
1451
+ static {
1452
+ this.PASTE_CHUNK_SIZE = 500;
1453
+ }
1454
+ static {
1455
+ this.INITIAL_VIEW_ROWS = 15;
1456
+ }
1457
+ static {
1458
+ this.INITIAL_VIEW_COLS = 8;
1459
+ }
1460
+ static {
1461
+ this.ROW_HEADER_WIDTH = 50;
1462
+ }
1463
+ willUpdate(e) {
1464
+ e.has("data") && this.data !== this._lastSyncedDataRef && this._syncData(), (e.has("rows") || e.has("cols")) && (this.rows = Math.max(1, Math.round(this.rows)), this.cols = Math.max(1, Math.round(this.cols)), this._selection.setBounds(this.rows, this.cols)), e.has("functions") && (e.get("functions") !== void 0 || Object.keys(this.functions).length > 0) && (this._formulaEngine.setFunctions(this.functions), this._recalcAll());
1465
+ }
1466
+ getData() {
1467
+ let e = /* @__PURE__ */ new Map();
1468
+ for (let [t, n] of this._internalData) e.set(t, H(n));
1469
+ return e;
1470
+ }
1471
+ setData(e) {
1472
+ this.data = e, this._syncData(), this.requestUpdate();
1473
+ }
1474
+ registerFunction(e, t) {
1475
+ this._formulaEngine.registerFunction(e, t), this._recalcAll();
1476
+ }
1477
+ getCellFormat(e) {
1478
+ return this._internalData.get(e)?.format;
1479
+ }
1480
+ setCellFormat(e, t) {
1481
+ this._applyFormat([e], t, "programmatic");
1482
+ }
1483
+ setRangeFormat(e, t) {
1484
+ let n = [];
1485
+ for (let t = e.start.row; t <= e.end.row; t++) for (let r = e.start.col; r <= e.end.col; r++) n.push(h(t, r));
1486
+ this._applyFormat(n, t, "programmatic");
1487
+ }
1488
+ clearCellFormat(e) {
1489
+ this._clearFormat([e], "programmatic");
1490
+ }
1491
+ clearRangeFormat(e) {
1492
+ let t = [];
1493
+ for (let n = e.start.row; n <= e.end.row; n++) for (let r = e.start.col; r <= e.end.col; r++) t.push(h(n, r));
1494
+ this._clearFormat(t, "programmatic");
1495
+ }
1496
+ toggleFormat(e) {
1497
+ if (this.readOnly || this._pasteInProgress) return;
1498
+ let t = this._selection.range, n = [], r = !0;
1499
+ for (let i = t.start.row; i <= t.end.row; i++) for (let a = t.start.col; a <= t.end.col; a++) {
1500
+ let t = h(i, a);
1501
+ n.push(t), this._internalData.get(t)?.format?.[e] || (r = !1);
1502
+ }
1503
+ let i = { [e]: !r };
1504
+ this._applyFormat(n, i, "user");
1505
+ }
1506
+ _applyFormat(e, t, n) {
1507
+ let r = this._snapshotSelection(), i = this._buildFormatBatch(e, t, r);
1508
+ i && (n === "user" ? this._executeUserBatch(i) : (this._applyBatch(i, n), this._pushHistory(i)), this._dispatchFormatChange({
1509
+ cellIds: e,
1510
+ format: t,
1511
+ source: n
1512
+ }));
1513
+ }
1514
+ _clearFormat(e, t) {
1515
+ let n = this._snapshotSelection(), r = [];
1516
+ for (let t of e) {
1517
+ let e = this._internalData.get(t);
1518
+ e?.format && r.push({
1519
+ id: t,
1520
+ before: e.rawValue,
1521
+ after: e.rawValue,
1522
+ formatBefore: { ...e.format },
1523
+ formatAfter: void 0
1524
+ });
1525
+ }
1526
+ if (r.length === 0) return;
1527
+ let i = {
1528
+ deltas: r,
1529
+ selectionBefore: n,
1530
+ selectionAfter: n,
1531
+ operation: "format"
1532
+ };
1533
+ t === "user" ? this._executeUserBatch(i) : (this._applyBatch(i, t), this._pushHistory(i)), this._dispatchFormatChange({
1534
+ cellIds: e,
1535
+ format: {},
1536
+ source: t
1537
+ });
1538
+ }
1539
+ _buildFormatBatch(e, t, n) {
1540
+ let r = [];
1541
+ for (let n of e) {
1542
+ let e = this._internalData.get(n), i = e?.format, a = {
1543
+ ...i,
1544
+ ...t
1545
+ };
1546
+ for (let e of Object.keys(a)) a[e] === void 0 && delete a[e];
1547
+ u(i, a) || r.push({
1548
+ id: n,
1549
+ before: e?.rawValue ?? "",
1550
+ after: e?.rawValue ?? "",
1551
+ formatBefore: i ? { ...i } : void 0,
1552
+ formatAfter: Object.keys(a).length > 0 ? a : void 0
1553
+ });
1554
+ }
1555
+ return r.length === 0 ? null : {
1556
+ deltas: r,
1557
+ selectionBefore: n,
1558
+ selectionAfter: n,
1559
+ operation: "format"
1560
+ };
1561
+ }
1562
+ _getCellStyles(e, t) {
1563
+ let n = this._getCell(e, t);
1564
+ if (!n?.format) return {};
1565
+ let r = {}, i = n.format;
1566
+ i.bold && (r["font-weight"] = "bold"), i.italic && (r["font-style"] = "italic");
1567
+ let a = [];
1568
+ return i.underline && a.push("underline"), i.strikethrough && a.push("line-through"), a.length > 0 && (r["text-decoration"] = a.join(" ")), i.textColor && (r.color = i.textColor), i.backgroundColor && (r["background-color"] = i.backgroundColor), i.fontSize && (r["font-size"] = `${i.fontSize}px`), i.textAlign && (r["text-align"] = i.textAlign, r["justify-content"] = {
1569
+ left: "flex-start",
1570
+ center: "center",
1571
+ right: "flex-end"
1572
+ }[i.textAlign]), r;
1573
+ }
1574
+ _syncData() {
1575
+ let e = this._internalData, t = /* @__PURE__ */ new Map();
1576
+ for (let [e, n] of this.data) t.set(e, H(n));
1577
+ let n = [];
1578
+ for (let [r, i] of t) {
1579
+ let t = e.get(r);
1580
+ !t || t.rawValue !== i.rawValue || !u(t.format, i.format) ? n.push(r) : (i.displayValue = t.displayValue, i.type = t.type);
1581
+ }
1582
+ for (let r of e.keys()) t.has(r) || n.push(r);
1583
+ if (this._internalData = t, this._lastSyncedDataRef = this.data, this._undoStack = [], this._redoStack = [], n.length > 0 && e.size > 0) {
1584
+ this._formulaEngine.updateData(this._internalData, n);
1585
+ for (let e of n) {
1586
+ let t = this._internalData.get(e);
1587
+ if (t && !t.rawValue.startsWith("=")) {
1588
+ let e = this._formulaEngine.evaluate(t.rawValue);
1589
+ t.displayValue = e.displayValue, t.type = e.type;
1590
+ }
1591
+ }
1592
+ this._recalcAffected(n), this._applyNumberFormatsToKeys(new Set(n));
1593
+ } else this._formulaEngine.setData(this._internalData), this._recalcAll();
1594
+ }
1595
+ _recalcAll() {
1596
+ this._formulaEngine.recalculate(), this._reapplyNumberFormats();
1597
+ }
1598
+ _recalcAffected(e) {
1599
+ let t = this._formulaEngine.recalculateAffected(e);
1600
+ this._applyNumberFormatsToKeys(t);
1601
+ }
1602
+ _reapplyNumberFormats() {
1603
+ for (let [e, t] of this._internalData) this._applyNumberFormatToCell(e, t);
1604
+ }
1605
+ _applyNumberFormatsToKeys(e) {
1606
+ for (let t of e) {
1607
+ let e = this._internalData.get(t);
1608
+ e && this._applyNumberFormatToCell(t, e);
1609
+ }
1610
+ }
1611
+ _applyNumberFormatToCell(e, t) {
1612
+ t.type === "number" && t.format?.numberFormat && (t.displayValue = f(t.rawValue.startsWith("=") ? Number(this._formulaEngine.evaluate(t.rawValue).displayValue) : Number(t.rawValue), t.format.numberFormat));
1613
+ }
1614
+ _getCell(e, t) {
1615
+ return this._internalData.get(h(e, t));
1616
+ }
1617
+ _getCellDisplay(e, t) {
1618
+ return this._getCell(e, t)?.displayValue ?? "";
1619
+ }
1620
+ _getCellRaw(e, t) {
1621
+ return this._getCell(e, t)?.rawValue ?? "";
1622
+ }
1623
+ _getActiveCellKey() {
1624
+ let { row: e, col: t } = this._selection.activeCell;
1625
+ return h(e, t);
1626
+ }
1627
+ _getActiveCellRef() {
1628
+ return b(this._selection.activeCell);
1629
+ }
1630
+ _setCellRaw(e, t) {
1631
+ let n = this._internalData.get(e), r = this._formulaEngine.evaluate(t, e), i = r.displayValue, a = n?.format?.numberFormat;
1632
+ a && r.type === "number" && (i = f(Number(r.displayValue), a)), this._internalData.set(e, {
1633
+ rawValue: t,
1634
+ displayValue: i,
1635
+ type: r.type,
1636
+ format: n?.format
1637
+ });
1638
+ }
1639
+ _applyRawValueByString(e, t) {
1640
+ if (t === "") {
1641
+ let t = this._internalData.get(e);
1642
+ t?.format ? this._internalData.set(e, {
1643
+ rawValue: "",
1644
+ displayValue: "",
1645
+ type: "text",
1646
+ format: t.format
1647
+ }) : this._internalData.delete(e);
1648
+ } else this._setCellRaw(e, t);
1649
+ }
1650
+ _snapshotSelection() {
1651
+ return {
1652
+ anchor: { ...this._selection.anchor },
1653
+ head: { ...this._selection.head }
1654
+ };
1655
+ }
1656
+ _restoreSelection(e) {
1657
+ this._selection.moveTo(e.anchor.row, e.anchor.col), this._selection.moveTo(e.head.row, e.head.col, !0), this._dispatchSelectionChange(), this.updateComplete.then(() => this._focusActiveCell());
1658
+ }
1659
+ _buildCommandBatch(e, t, n, r) {
1660
+ let i = [];
1661
+ for (let t of e) {
1662
+ let e = this._internalData.get(t.id)?.rawValue ?? "", n = t.value;
1663
+ e !== n && i.push({
1664
+ id: t.id,
1665
+ before: e,
1666
+ after: n
1667
+ });
1668
+ }
1669
+ return i.length === 0 ? null : {
1670
+ deltas: i,
1671
+ selectionBefore: n,
1672
+ selectionAfter: r,
1673
+ operation: t
1674
+ };
1675
+ }
1676
+ _pushHistory(e) {
1677
+ this._undoStack.push(e), this._redoStack = [], this._undoStack.length > this._maxHistory && this._undoStack.shift();
1678
+ }
1679
+ _finalizeBatch(e, t, n) {
1680
+ this._recalcAffected(e.deltas.map((e) => e.id)), (t !== "user" || e.operation !== "edit") && this._dispatchDataChange({
1681
+ updates: e.deltas.map((e) => ({
1682
+ id: e.id,
1683
+ value: e[n]
1684
+ })),
1685
+ source: t,
1686
+ operation: e.operation
1687
+ }), this.requestUpdate();
1688
+ }
1689
+ _applyBatch(e, t) {
1690
+ for (let t of e.deltas) this._applyRawValueByString(t.id, t.after), "formatAfter" in t && this._applyCellFormat(t.id, t.formatAfter);
1691
+ this._lastSyncedDataRef = this.data, this._restoreSelection(e.selectionAfter), this._finalizeBatch(e, t, "after");
1692
+ }
1693
+ _revertBatch(e) {
1694
+ for (let t of e.deltas) this._applyRawValueByString(t.id, t.before), "formatBefore" in t && this._applyCellFormat(t.id, t.formatBefore);
1695
+ this._lastSyncedDataRef = this.data, this._restoreSelection(e.selectionBefore), this._finalizeBatch(e, "undo", "before");
1696
+ }
1697
+ _applyCellFormat(e, t) {
1698
+ let n = this._internalData.get(e);
1699
+ if (n || (n = {
1700
+ rawValue: "",
1701
+ displayValue: "",
1702
+ type: "text"
1703
+ }, this._internalData.set(e, n)), t && Object.keys(t).length > 0) n.format = t;
1704
+ else if (delete n.format, n.rawValue === "") {
1705
+ this._internalData.delete(e);
1706
+ return;
1707
+ }
1708
+ if (n.type === "number" && n.format?.numberFormat) {
1709
+ if (n.rawValue.startsWith("=")) {
1710
+ let e = this._formulaEngine.evaluate(n.rawValue);
1711
+ n.displayValue = f(Number(e.displayValue), n.format.numberFormat);
1712
+ } else n.displayValue = f(Number(n.rawValue), n.format.numberFormat);
1713
+ } else if (n.type === "number" && !n.format?.numberFormat) {
1714
+ let e = this._formulaEngine.evaluate(n.rawValue);
1715
+ n.displayValue = e.displayValue;
1716
+ }
1717
+ }
1718
+ _executeUserBatch(e) {
1719
+ this._applyBatch(e, "user"), this._pushHistory(e);
1720
+ }
1721
+ _commitRawValue(e, t) {
1722
+ let n = this._internalData.get(e)?.rawValue ?? "";
1723
+ if (t === n) return !1;
1724
+ let r = this._snapshotSelection(), i = this._buildCommandBatch([{
1725
+ id: e,
1726
+ value: t
1727
+ }], "edit", r, r);
1728
+ return i ? (this._executeUserBatch(i), this._dispatchCellChange({
1729
+ cellId: e,
1730
+ value: t,
1731
+ oldValue: n
1732
+ }), !0) : !1;
1733
+ }
1734
+ _undo() {
1735
+ if (this.readOnly || this._pasteInProgress) return;
1736
+ let e = this._undoStack.pop();
1737
+ e && (this._revertBatch(e), this._redoStack.push(e), this._dispatchFormatChangeForBatch(e, "undo"));
1738
+ }
1739
+ _redo() {
1740
+ if (this.readOnly || this._pasteInProgress) return;
1741
+ let e = this._redoStack.pop();
1742
+ e && (this._applyBatch(e, "redo"), this._undoStack.push(e), this._undoStack.length > this._maxHistory && this._undoStack.shift(), this._dispatchFormatChangeForBatch(e, "redo"));
1743
+ }
1744
+ _dispatchFormatChangeForBatch(e, t) {
1745
+ let n = e.deltas.filter((e) => e.formatBefore !== void 0 || e.formatAfter !== void 0);
1746
+ if (n.length === 0) return;
1747
+ let r = n.map((e) => e.id), i = t === "undo" ? n[0].formatBefore ?? {} : n[0].formatAfter ?? {};
1748
+ this._dispatchFormatChange({
1749
+ cellIds: r,
1750
+ format: i,
1751
+ source: t
1752
+ });
1753
+ }
1754
+ _startEditing(e) {
1755
+ if (this.readOnly || this._isEditing || this._pasteInProgress) return;
1756
+ let { row: t, col: n } = this._selection.activeCell, r = h(t, n);
1757
+ this._editingCellKey = r;
1758
+ let i = this._internalData.get(r);
1759
+ this._editValue = e ?? i?.rawValue ?? "", this._isEditing = !0, this._editorNeedsCursorReset = !0, this._modeAnnouncement = `Editing ${b(this._selection.activeCell)}`;
1760
+ }
1761
+ _commitEdit() {
1762
+ if (!this._isEditing || !this._editingCellKey) return;
1763
+ let e = this._editingCellKey, t = this._editValue;
1764
+ this._isEditing = !1, this._editingCellKey = null, this._modeAnnouncement = "Edit committed", this._commitRawValue(e, t), this.updateComplete.then(() => this._focusActiveCell());
1765
+ }
1766
+ _cancelEdit() {
1767
+ this._isEditing = !1, this._editingCellKey = null, this._modeAnnouncement = "Edit cancelled", this.updateComplete.then(() => this._focusActiveCell());
1768
+ }
1769
+ _isRefPositionInFormula() {
1770
+ if (!this._editValue.startsWith("=")) return !1;
1771
+ let e = this._editor?.selectionStart ?? this._editValue.length, t = this._editValue.substring(0, e).trimEnd();
1772
+ if (t === "=") return !0;
1773
+ let n = t[t.length - 1];
1774
+ return [
1775
+ "+",
1776
+ "-",
1777
+ "*",
1778
+ "/",
1779
+ "&",
1780
+ "(",
1781
+ ",",
1782
+ ":",
1783
+ "<",
1784
+ ">",
1785
+ "="
1786
+ ].includes(n);
1787
+ }
1788
+ _handleRefArrow(e) {
1789
+ let t = e === "ArrowDown" ? 1 : e === "ArrowUp" ? -1 : 0, n = e === "ArrowRight" ? 1 : e === "ArrowLeft" ? -1 : 0;
1790
+ if (this._refMode) {
1791
+ let e = this._selection.clamp({
1792
+ row: this._refCursorRow + t,
1793
+ col: this._refCursorCol + n
1794
+ });
1795
+ this._refCursorRow = e.row, this._refCursorCol = e.col;
1796
+ let r = b({
1797
+ row: this._refCursorRow,
1798
+ col: this._refCursorCol
1799
+ });
1800
+ this._modeAnnouncement = `Selecting ${r}`, this._editValue = this._editValue.substring(0, this._refInsertStart) + r + this._editValue.substring(this._refInsertEnd), this._refInsertEnd = this._refInsertStart + r.length;
1801
+ } else {
1802
+ let { row: e, col: r } = this._selection.activeCell, i = this._selection.clamp({
1803
+ row: e + t,
1804
+ col: r + n
1805
+ });
1806
+ this._refCursorRow = i.row, this._refCursorCol = i.col, this._refMode = !0, this._modeAnnouncement = `Reference selection mode. Selecting ${b({
1807
+ row: this._refCursorRow,
1808
+ col: this._refCursorCol
1809
+ })}`;
1810
+ let a = this._editor?.selectionStart ?? this._editValue.length;
1811
+ this._refInsertStart = a;
1812
+ let o = b({
1813
+ row: this._refCursorRow,
1814
+ col: this._refCursorCol
1815
+ });
1816
+ this._editValue = this._editValue.substring(0, a) + o + this._editValue.substring(a), this._refInsertEnd = a + o.length;
1817
+ }
1818
+ this._ensureCellVisible(this._refCursorRow, this._refCursorCol);
1819
+ }
1820
+ _exitRefMode() {
1821
+ this._refMode && (this._modeAnnouncement = "Reference selection ended"), this._refMode = !1;
1822
+ }
1823
+ _cycleReferenceMode() {
1824
+ let e = this._editor?.selectionStart ?? this._editValue.length, t = this._editValue, n = /(?<![A-Z0-9_.$])(\$?)([A-Z]+)(\$?)(\d+)(?![A-Z0-9_.(])/gi, r, i = null;
1825
+ for (; (r = n.exec(t)) !== null;) {
1826
+ let t = r.index, n = t + r[0].length;
1827
+ t <= e && n >= e - 1 && (i = r);
1828
+ }
1829
+ if (!i) return;
1830
+ let [a, o, s, c, l] = i, u = i.index, d = o === "$", f = c === "$", p;
1831
+ p = !d && !f ? `$${s}$${l}` : d && f ? `${s}$${l}` : !d && f ? `$${s}${l}` : `${s}${l}`, this._editValue = t.substring(0, u) + p + t.substring(u + a.length);
1832
+ let m = u + p.length;
1833
+ this.updateComplete.then(() => {
1834
+ this._editor && this._editor.setSelectionRange(m, m);
1835
+ });
1836
+ }
1837
+ _focusActiveCell() {
1838
+ let { row: e, col: t } = this._selection.activeCell;
1839
+ this._ensureCellVisible(e, t);
1840
+ let n = this.shadowRoot?.querySelector(`[data-row="${e}"][data-col="${t}"]`);
1841
+ n && n.focus();
1842
+ }
1843
+ _handleGridKeydown(e) {
1844
+ if (this._isEditing) {
1845
+ this._handleEditorKeydown(e);
1846
+ return;
1847
+ }
1848
+ let t = e.shiftKey, n = e.ctrlKey || e.metaKey;
1849
+ if (!n && !e.altKey && [...e.key].length === 1) {
1850
+ this.readOnly || (e.preventDefault(), e.stopPropagation(), this._startEditing(e.key));
1851
+ return;
1852
+ }
1853
+ let r = !0;
1854
+ switch (n && e.key.length === 1 ? e.key.toLowerCase() : e.key) {
1855
+ case "ArrowUp":
1856
+ e.preventDefault(), this._selection.move(-1, 0, t), this._dispatchSelectionChange(), this.updateComplete.then(() => this._focusActiveCell());
1857
+ break;
1858
+ case "ArrowDown":
1859
+ e.preventDefault(), this._selection.move(1, 0, t), this._dispatchSelectionChange(), this.updateComplete.then(() => this._focusActiveCell());
1860
+ break;
1861
+ case "ArrowLeft":
1862
+ e.preventDefault(), this._selection.move(0, -1, t), this._dispatchSelectionChange(), this.updateComplete.then(() => this._focusActiveCell());
1863
+ break;
1864
+ case "ArrowRight":
1865
+ e.preventDefault(), this._selection.move(0, 1, t), this._dispatchSelectionChange(), this.updateComplete.then(() => this._focusActiveCell());
1866
+ break;
1867
+ case "Enter":
1868
+ e.preventDefault(), this.readOnly || this._startEditing();
1869
+ break;
1870
+ case "F2":
1871
+ e.preventDefault(), this.readOnly || this._startEditing();
1872
+ break;
1873
+ case "Escape":
1874
+ e.preventDefault(), this._selection.clearSelection(), this._dispatchSelectionChange();
1875
+ break;
1876
+ case "Delete":
1877
+ case "Backspace":
1878
+ e.preventDefault(), this.readOnly || this._clearSelectedCells();
1879
+ break;
1880
+ case "Home":
1881
+ e.preventDefault(), n ? this._selection.moveTo(0, 0, t) : this._selection.moveTo(this._selection.activeCell.row, 0, t), this._dispatchSelectionChange(), this.updateComplete.then(() => this._focusActiveCell());
1882
+ break;
1883
+ case "End":
1884
+ e.preventDefault(), n ? this._selection.moveTo(this.rows - 1, this.cols - 1, t) : this._selection.moveTo(this._selection.activeCell.row, this.cols - 1, t), this._dispatchSelectionChange(), this.updateComplete.then(() => this._focusActiveCell());
1885
+ break;
1886
+ case "PageDown": {
1887
+ e.preventDefault();
1888
+ let n = this._getCSSVarPx("--ls-cell-height", 28), r = this._grid?.clientHeight || n * V.INITIAL_VIEW_ROWS, i = Math.max(1, Math.floor(r / n) - 1);
1889
+ this._selection.move(i, 0, t), this._dispatchSelectionChange(), this.updateComplete.then(() => this._focusActiveCell());
1890
+ break;
1891
+ }
1892
+ case "PageUp": {
1893
+ e.preventDefault();
1894
+ let n = this._getCSSVarPx("--ls-cell-height", 28), r = this._grid?.clientHeight || n * V.INITIAL_VIEW_ROWS, i = Math.max(1, Math.floor(r / n) - 1);
1895
+ this._selection.move(-i, 0, t), this._dispatchSelectionChange(), this.updateComplete.then(() => this._focusActiveCell());
1896
+ break;
1897
+ }
1898
+ case "a":
1899
+ n ? (e.preventDefault(), this._selection.selectAll(), this._dispatchSelectionChange()) : r = !1;
1900
+ break;
1901
+ case "c":
1902
+ n ? (e.preventDefault(), this._handleCopy()) : r = !1;
1903
+ break;
1904
+ case "x":
1905
+ n ? (e.preventDefault(), this._handleCut()) : r = !1;
1906
+ break;
1907
+ case "v":
1908
+ n ? (e.preventDefault(), this._handlePaste()) : r = !1;
1909
+ break;
1910
+ case "b":
1911
+ n ? (e.preventDefault(), this.toggleFormat("bold")) : r = !1;
1912
+ break;
1913
+ case "i":
1914
+ n ? (e.preventDefault(), this.toggleFormat("italic")) : r = !1;
1915
+ break;
1916
+ case "u":
1917
+ n ? (e.preventDefault(), this.toggleFormat("underline")) : r = !1;
1918
+ break;
1919
+ case "y":
1920
+ n ? (e.preventDefault(), this._redo()) : r = !1;
1921
+ break;
1922
+ case "z":
1923
+ n ? (e.preventDefault(), t ? this._redo() : this._undo()) : r = !1;
1924
+ break;
1925
+ default: r = !1;
1926
+ }
1927
+ r && e.stopPropagation();
1928
+ }
1929
+ _handleEditorKeydown(e) {
1930
+ switch (e.stopPropagation(), e.key) {
1931
+ case "Enter":
1932
+ e.preventDefault(), this._exitRefMode(), this._commitEdit(), this._selection.move(1, 0), this._dispatchSelectionChange(), this.updateComplete.then(() => this._focusActiveCell());
1933
+ break;
1934
+ case "Tab":
1935
+ e.preventDefault(), this._exitRefMode(), this._commitEdit(), e.shiftKey ? this._selection.move(0, -1) : this._selection.move(0, 1), this._dispatchSelectionChange(), this.updateComplete.then(() => this._focusActiveCell());
1936
+ break;
1937
+ case "Escape":
1938
+ e.preventDefault(), this._exitRefMode(), this._cancelEdit();
1939
+ break;
1940
+ case "ArrowUp":
1941
+ case "ArrowDown":
1942
+ case "ArrowLeft":
1943
+ case "ArrowRight":
1944
+ (this._refMode || this._isRefPositionInFormula()) && (e.preventDefault(), this._handleRefArrow(e.key));
1945
+ break;
1946
+ case "F4": this._editValue.startsWith("=") && (e.preventDefault(), this._cycleReferenceMode());
1947
+ }
1948
+ }
1949
+ _handleSelectAll() {
1950
+ this._selection.selectAll(), this._dispatchSelectionChange(), this.updateComplete.then(() => this._focusActiveCell());
1951
+ }
1952
+ _handleCornerKeydown(e) {
1953
+ (e.key === "Enter" || e.key === " ") && (e.preventDefault(), e.stopPropagation(), this._handleSelectAll());
1954
+ }
1955
+ _handleCellPointerDown(e) {
1956
+ let t = e.target.closest("[data-row]");
1957
+ if (!t) return;
1958
+ let n = parseInt(t.dataset.row, 10), r = parseInt(t.dataset.col, 10);
1959
+ this._isEditing && this._commitEdit(), this._selection.startSelection(n, r, e.shiftKey), this._dispatchSelectionChange(), t.focus();
1960
+ let i = new AbortController(), { signal: a } = i, o = () => i.abort(), s = (e) => {
1961
+ let t = (this.shadowRoot?.elementFromPoint(e.clientX, e.clientY))?.closest?.("[data-row]");
1962
+ if (t) {
1963
+ let e = parseInt(t.dataset.row, 10), n = parseInt(t.dataset.col, 10);
1964
+ this._selection.extendSelection(e, n), this._dispatchSelectionChange();
1965
+ }
1966
+ }, c = () => {
1967
+ this._selection.endSelection(), o();
1968
+ };
1969
+ document.addEventListener("pointermove", s, { signal: a }), document.addEventListener("pointerup", c, { signal: a }), document.addEventListener("pointercancel", c, { signal: a }), window.addEventListener("blur", c, { signal: a });
1970
+ }
1971
+ _handleCellDblClick(e) {
1972
+ if (this.readOnly) return;
1973
+ let t = e.target.closest("[data-row]");
1974
+ if (!t) return;
1975
+ let n = parseInt(t.dataset.row, 10), r = parseInt(t.dataset.col, 10);
1976
+ this._selection.moveTo(n, r), this._startEditing();
1977
+ }
1978
+ async _handleCopy() {
1979
+ await this._clipboardManager.copy(this._internalData, this._selection.range);
1980
+ }
1981
+ async _handleCut() {
1982
+ if (this.readOnly || this._pasteInProgress) return;
1983
+ let e = await this._clipboardManager.cut(this._internalData, this._selection.range), t = [], n = this._snapshotSelection();
1984
+ for (let n of e) this._internalData.has(n) && t.push({
1985
+ id: n,
1986
+ value: ""
1987
+ });
1988
+ let r = this._buildCommandBatch(t, "cut", n, n);
1989
+ r && this._executeUserBatch(r);
1990
+ }
1991
+ async _handlePaste() {
1992
+ if (this.readOnly || this._pasteInProgress) return;
1993
+ let { row: e, col: t } = this._selection.range.start, n = await this._clipboardManager.paste(e, t, this.rows, this.cols);
1994
+ if (!n || n.length === 0) return;
1995
+ let r = this._snapshotSelection(), i = n.map((e) => ({
1996
+ id: e.id,
1997
+ value: e.value
1998
+ })), a = this._buildCommandBatch(i, "paste", r, r);
1999
+ a ||= {
2000
+ deltas: [],
2001
+ selectionBefore: r,
2002
+ selectionAfter: r,
2003
+ operation: "paste"
2004
+ };
2005
+ let o = new Map(n.map((e) => [e.id, e]));
2006
+ for (let e of a.deltas) {
2007
+ let t = o.get(e.id);
2008
+ if (t?.format) {
2009
+ let n = this._internalData.get(e.id);
2010
+ e.formatBefore = n?.format ? { ...n.format } : void 0, e.formatAfter = t.format;
2011
+ }
2012
+ }
2013
+ let s = new Set(a.deltas.map((e) => e.id));
2014
+ for (let e of n) if (e.format && !s.has(e.id)) {
2015
+ let t = this._internalData.get(e.id);
2016
+ a.deltas.push({
2017
+ id: e.id,
2018
+ before: t?.rawValue ?? "",
2019
+ after: e.value,
2020
+ formatBefore: t?.format ? { ...t.format } : void 0,
2021
+ formatAfter: e.format
2022
+ });
2023
+ }
2024
+ if (a.deltas.length !== 0) {
2025
+ if (a.deltas.length <= V.PASTE_CHUNK_SIZE) this._executeUserBatch(a);
2026
+ else {
2027
+ this._pasteInProgress = !0;
2028
+ try {
2029
+ await this._applyBatchProgressive(a), this._pushHistory(a);
2030
+ } finally {
2031
+ this._pasteInProgress = !1;
2032
+ }
2033
+ }
2034
+ }
2035
+ }
2036
+ _applyBatchProgressive(e) {
2037
+ return new Promise((t) => {
2038
+ let n = e.deltas, r = 0, i = () => {
2039
+ let a = Math.min(r + V.PASTE_CHUNK_SIZE, n.length);
2040
+ for (let e = r; e < a; e++) {
2041
+ let t = n[e];
2042
+ this._applyRawValueByString(t.id, t.after), "formatAfter" in t && this._applyCellFormat(t.id, t.formatAfter);
2043
+ }
2044
+ r = a, r < n.length ? (this.requestUpdate(), requestAnimationFrame(i)) : (this._restoreSelection(e.selectionAfter), this._finalizeBatch(e, "user", "after"), t());
2045
+ };
2046
+ i();
2047
+ });
2048
+ }
2049
+ _clearSelectedCells() {
2050
+ if (this._pasteInProgress) return;
2051
+ let e = this._selection.range, t = [], n = this._snapshotSelection();
2052
+ for (let n = e.start.row; n <= e.end.row; n++) for (let r = e.start.col; r <= e.end.col; r++) {
2053
+ let e = h(n, r);
2054
+ this._internalData.has(e) && t.push({
2055
+ id: e,
2056
+ value: ""
2057
+ });
2058
+ }
2059
+ let r = this._buildCommandBatch(t, "clear", n, n);
2060
+ r && this._executeUserBatch(r);
2061
+ }
2062
+ _handleScroll(e) {
2063
+ let t = e.target;
2064
+ this._scrollTop = t.scrollTop, this._scrollLeft = t.scrollLeft;
2065
+ }
2066
+ _handleEditorInput(e) {
2067
+ this._editValue = e.target.value, this._exitRefMode();
2068
+ }
2069
+ _handleEditorBlur() {
2070
+ this._isEditing && this._commitEdit();
2071
+ }
2072
+ _handleFormulaBarModeChange(e) {
2073
+ this._formulaBarMode = e.detail.mode;
2074
+ }
2075
+ _handleFormulaBarCommit(e) {
2076
+ if (this.readOnly) return;
2077
+ let t;
2078
+ if (this._editingCellKey) t = this._editingCellKey;
2079
+ else {
2080
+ let n = y(e.detail.cellRef);
2081
+ t = n ? h(n.row, n.col) : this._getActiveCellKey();
2082
+ }
2083
+ this._isEditing && (this._isEditing = !1, this._editingCellKey = null), this._commitRawValue(t, e.detail.value);
2084
+ }
2085
+ _dispatchCellChange(e) {
2086
+ this.dispatchEvent(new CustomEvent("cell-change", {
2087
+ detail: e,
2088
+ bubbles: !0,
2089
+ composed: !0
2090
+ }));
2091
+ }
2092
+ _dispatchSelectionChange() {
2093
+ let e = { range: this._selection.range };
2094
+ this.dispatchEvent(new CustomEvent("selection-change", {
2095
+ detail: e,
2096
+ bubbles: !0,
2097
+ composed: !0
2098
+ }));
2099
+ }
2100
+ _dispatchDataChange(e) {
2101
+ this.dispatchEvent(new CustomEvent("data-change", {
2102
+ detail: e,
2103
+ bubbles: !0,
2104
+ composed: !0
2105
+ }));
2106
+ }
2107
+ _dispatchFormatChange(e) {
2108
+ this.dispatchEvent(new CustomEvent("format-change", {
2109
+ detail: e,
2110
+ bubbles: !0,
2111
+ composed: !0
2112
+ }));
2113
+ }
2114
+ _getVisibleRange() {
2115
+ let e = this._getCSSVarPx("--ls-cell-height", 28), t = this._getCSSVarPx("--ls-cell-width", 100), n = this._grid, r = n?.clientHeight || e * V.INITIAL_VIEW_ROWS, i = n?.clientWidth || t * V.INITIAL_VIEW_COLS, a = Math.max(0, Math.floor(this._scrollTop / e) - 5), o = Math.min(this.rows, Math.ceil((this._scrollTop + r) / e) + 5), s = Math.max(0, Math.floor(this._scrollLeft / t) - 5), c = Math.min(this.cols, Math.ceil((this._scrollLeft + i) / t) + 5), { row: l, col: u } = this._selection.activeCell, d = [l], f = [u];
2116
+ return this._refMode && (d.push(this._refCursorRow), f.push(this._refCursorCol)), {
2117
+ rows: W(a, o, d),
2118
+ cols: W(s, c, f)
2119
+ };
2120
+ }
2121
+ _ensureCellVisible(e, t) {
2122
+ let n = this._grid;
2123
+ if (!n) return;
2124
+ let r = this._getCSSVarPx("--ls-cell-height", 28), i = this._getCSSVarPx("--ls-cell-width", 100), a = n.clientHeight || r * V.INITIAL_VIEW_ROWS, o = n.clientWidth || i * V.INITIAL_VIEW_COLS, s = Math.max(i, o - V.ROW_HEADER_WIDTH), c = Math.max(r, a - r), l = e * r, u = l + r, d = t * i, f = d + i, p = this._scrollTop;
2125
+ l < p ? p = l : u > p + c && (p = u - c);
2126
+ let m = this._scrollLeft;
2127
+ d < m ? m = d : f > m + s && (m = f - s), (p !== this._scrollTop || m !== this._scrollLeft) && (this._scrollTop = p, this._scrollLeft = m, n.scrollTop = p, n.scrollLeft = m);
2128
+ }
2129
+ _getCSSVarPx(e, t) {
2130
+ let n = getComputedStyle(this).getPropertyValue(e);
2131
+ if (n) {
2132
+ let e = parseInt(n, 10);
2133
+ if (!isNaN(e)) return e;
2134
+ }
2135
+ return t;
2136
+ }
2137
+ _getEditorStyle() {
2138
+ if (!this._isEditing) return { display: "none" };
2139
+ let { row: e, col: t } = this._selection.activeCell, n = this._getCSSVarPx("--ls-cell-height", 28), r = this._getCSSVarPx("--ls-cell-width", 100), i = V.ROW_HEADER_WIDTH, a = (e + 1) * n - this._scrollTop, o = i + t * r - this._scrollLeft, s = {
2140
+ display: "block",
2141
+ position: "absolute",
2142
+ top: `${a}px`,
2143
+ left: `${o}px`,
2144
+ width: `${r}px`,
2145
+ height: `${n}px`,
2146
+ "z-index": "10"
2147
+ }, c = this._getCellStyles(e, t);
2148
+ for (let [e, t] of Object.entries(c)) e !== "justify-content" && e !== "background-color" && (s[e] = t);
2149
+ return s;
2150
+ }
2151
+ static {
2152
+ this.styles = t`
2001
2153
  :host {
2002
2154
  display: block;
2155
+ height: 100%;
2003
2156
  position: relative;
2004
2157
  font-family: var(--ls-font-family, system-ui, -apple-system, sans-serif);
2005
2158
  font-size: var(--ls-font-size, 13px);
@@ -2009,7 +2162,7 @@ w.styles = M`
2009
2162
  --_header-bg: var(--ls-header-bg, #f3f3f3);
2010
2163
  --_border-color: var(--ls-border-color, #e0e0e0);
2011
2164
  --_selection-border: var(--ls-selection-border, 2px solid #1a73e8);
2012
- --_selection-bg: var(--ls-selection-bg, rgba(26, 115, 232, 0.1));
2165
+ --_selection-bg: var(--ls-selection-bg, rgba(26, 115, 232, 0.2));
2013
2166
  --_focus-ring: var(--ls-focus-ring, 2px solid #1a73e8);
2014
2167
  --_editor-bg: var(--ls-editor-bg, #fff);
2015
2168
  --_editor-shadow: var(--ls-editor-shadow, 0 2px 6px rgba(0,0,0,0.2));
@@ -2041,10 +2194,12 @@ w.styles = M`
2041
2194
  .ls-grid {
2042
2195
  display: grid;
2043
2196
  grid-template-columns: var(--_row-header-width) repeat(var(--cols), var(--_cell-width));
2197
+ align-content: start;
2044
2198
  overflow: auto;
2045
2199
  position: relative;
2046
2200
  width: 100%;
2047
- height: 100%;
2201
+ height: round(down, 100%, var(--_cell-height));
2202
+ scroll-snap-type: y mandatory;
2048
2203
  outline: none;
2049
2204
  }
2050
2205
 
@@ -2076,6 +2231,7 @@ w.styles = M`
2076
2231
  position: sticky;
2077
2232
  left: 0;
2078
2233
  z-index: 4;
2234
+ scroll-snap-align: start;
2079
2235
  }
2080
2236
 
2081
2237
  .ls-row-header {
@@ -2091,6 +2247,7 @@ w.styles = M`
2091
2247
  border-bottom: 1px solid var(--_border-color);
2092
2248
  font-weight: 600;
2093
2249
  user-select: none;
2250
+ scroll-snap-align: start;
2094
2251
  }
2095
2252
 
2096
2253
  .ls-cell {
@@ -2162,235 +2319,165 @@ w.styles = M`
2162
2319
  border: 0;
2163
2320
  }
2164
2321
  `;
2165
- v([
2166
- m({ type: Number })
2167
- ], w.prototype, "rows", 2);
2168
- v([
2169
- m({ type: Number })
2170
- ], w.prototype, "cols", 2);
2171
- v([
2172
- m({ attribute: !1 })
2173
- ], w.prototype, "data", 2);
2174
- v([
2175
- m({ type: Boolean, attribute: "read-only", reflect: !0 })
2176
- ], w.prototype, "readOnly", 2);
2177
- v([
2178
- m({ attribute: !1 })
2179
- ], w.prototype, "functions", 2);
2180
- v([
2181
- E()
2182
- ], w.prototype, "_isEditing", 2);
2183
- v([
2184
- E()
2185
- ], w.prototype, "_editValue", 2);
2186
- v([
2187
- E()
2188
- ], w.prototype, "_formulaBarMode", 2);
2189
- v([
2190
- E()
2191
- ], w.prototype, "_refMode", 2);
2192
- v([
2193
- E()
2194
- ], w.prototype, "_modeAnnouncement", 2);
2195
- v([
2196
- E()
2197
- ], w.prototype, "_scrollTop", 2);
2198
- v([
2199
- E()
2200
- ], w.prototype, "_scrollLeft", 2);
2201
- v([
2202
- I("#editor")
2203
- ], w.prototype, "_editor", 2);
2204
- v([
2205
- I(".ls-grid")
2206
- ], w.prototype, "_grid", 2);
2207
- w = v([
2208
- T("y11n-spreadsheet")
2209
- ], w);
2210
- var Q = Object.defineProperty, tt = Object.getOwnPropertyDescriptor, S = (i, e, t, r) => {
2211
- for (var s = r > 1 ? void 0 : r ? tt(e, t) : e, o = i.length - 1, n; o >= 0; o--)
2212
- (n = i[o]) && (s = (r ? n(e, t, s) : n(s)) || s);
2213
- return r && s && Q(e, t, s), s;
2214
- };
2215
- let C = class extends O {
2216
- constructor() {
2217
- super(...arguments), this.bold = !1, this.italic = !1, this.underline = !1, this.strikethrough = !1, this.textColor = "#000000", this.backgroundColor = "#ffffff", this.textAlign = "left", this.fontSize = 13, this.readOnly = !1;
2218
- }
2219
- // ─── Event Dispatching ─────────────────────────────────
2220
- _dispatch(i, e) {
2221
- this.dispatchEvent(
2222
- new CustomEvent("format-action", {
2223
- detail: { action: i, value: e },
2224
- bubbles: !0,
2225
- composed: !0
2226
- })
2227
- );
2228
- }
2229
- // ─── Keyboard Navigation ───────────────────────────────
2230
- _handleToolbarKeydown(i) {
2231
- const e = i.currentTarget, t = Array.from(
2232
- e.querySelectorAll("button:not(:disabled), select:not(:disabled), .color-wrapper")
2233
- ), r = t.findIndex(
2234
- (o) => o === i.target || o.contains(i.target)
2235
- );
2236
- if (r === -1) return;
2237
- let s = -1;
2238
- if (i.key === "ArrowRight" ? s = (r + 1) % t.length : i.key === "ArrowLeft" ? s = (r - 1 + t.length) % t.length : i.key === "Home" ? s = 0 : i.key === "End" && (s = t.length - 1), s >= 0) {
2239
- i.preventDefault();
2240
- const o = t[s];
2241
- (o.querySelector("input, select, button") ?? o).focus();
2242
- }
2243
- }
2244
- // ─── Render ────────────────────────────────────────────
2245
- render() {
2246
- const i = this.readOnly;
2247
- return y`
2248
- <div
2249
- class="toolbar"
2250
- role="toolbar"
2251
- aria-label="Cell formatting"
2252
- @keydown=${this._handleToolbarKeydown}
2253
- >
2254
- <!-- Text style toggles -->
2255
- <button
2256
- class="bold-btn"
2257
- type="button"
2258
- aria-pressed="${this.bold}"
2259
- aria-label="Bold"
2260
- title="Bold (Ctrl+B)"
2261
- ?disabled=${i}
2262
- tabindex="0"
2263
- @click=${() => this._dispatch("bold", !this.bold)}
2264
- >B</button>
2265
- <button
2266
- class="italic-btn"
2267
- type="button"
2268
- aria-pressed="${this.italic}"
2269
- aria-label="Italic"
2270
- title="Italic (Ctrl+I)"
2271
- ?disabled=${i}
2272
- tabindex="-1"
2273
- @click=${() => this._dispatch("italic", !this.italic)}
2274
- >I</button>
2275
- <button
2276
- class="underline-btn"
2277
- type="button"
2278
- aria-pressed="${this.underline}"
2279
- aria-label="Underline"
2280
- title="Underline (Ctrl+U)"
2281
- ?disabled=${i}
2282
- tabindex="-1"
2283
- @click=${() => this._dispatch("underline", !this.underline)}
2284
- >U</button>
2285
- <button
2286
- class="strikethrough-btn"
2287
- type="button"
2288
- aria-pressed="${this.strikethrough}"
2289
- aria-label="Strikethrough"
2290
- title="Strikethrough"
2291
- ?disabled=${i}
2292
- tabindex="-1"
2293
- @click=${() => this._dispatch("strikethrough", !this.strikethrough)}
2294
- >S</button>
2295
-
2296
- <div class="separator" role="separator"></div>
2322
+ }
2323
+ render() {
2324
+ let { rows: e, cols: t } = this._getVisibleRange(), r = this._selection.activeCell, i = this._getCellRaw(r.row, r.col), a = this._getCellDisplay(r.row, r.col);
2325
+ return n`
2326
+ <div class="ls-wrapper">
2327
+ <y11n-formula-bar
2328
+ cell-ref="${this._getActiveCellRef()}"
2329
+ raw-value="${i}"
2330
+ display-value="${a}"
2331
+ .mode="${this._formulaBarMode}"
2332
+ .readOnly="${this.readOnly}"
2333
+ @formula-bar-mode-change="${this._handleFormulaBarModeChange}"
2334
+ @formula-bar-commit="${this._handleFormulaBarCommit}"
2335
+ ></y11n-formula-bar>
2297
2336
 
2298
- <!-- Text color -->
2299
- <div class="color-wrapper" tabindex="-1">
2300
- <div class="color-swatch" aria-hidden="true">
2301
- A<span class="indicator" style="background-color: ${this.textColor}"></span>
2302
- </div>
2337
+ <div class="ls-grid-shell">
2303
2338
  <input
2304
- type="color"
2305
- .value=${this.textColor}
2306
- ?disabled=${i}
2307
- aria-label="Text color"
2308
- title="Text color"
2309
- @input=${(e) => this._dispatch("textColor", e.target.value)}
2339
+ id="editor"
2340
+ part="editor"
2341
+ style=${l(this._getEditorStyle())}
2342
+ .value="${this._editValue}"
2343
+ @input="${this._handleEditorInput}"
2344
+ @keydown="${this._handleEditorKeydown}"
2345
+ @blur="${this._handleEditorBlur}"
2346
+ aria-label="Cell editor"
2310
2347
  />
2311
- </div>
2312
2348
 
2313
- <!-- Background color -->
2314
- <div class="color-wrapper" tabindex="-1">
2315
- <div class="color-swatch" aria-hidden="true">
2316
- <span class="indicator" style="background-color: ${this.backgroundColor}; width: 16px; height: 16px; border-radius: 2px; border: 1px solid #ccc;"></span>
2349
+ <div
2350
+ class="ls-grid"
2351
+ role="grid"
2352
+ aria-label="${this.gridLabel}"
2353
+ aria-rowcount="${this.rows + 1}"
2354
+ aria-colcount="${this.cols + 1}"
2355
+ aria-readonly="${this.readOnly}"
2356
+ style="--cols: ${this.cols};"
2357
+ tabindex="-1"
2358
+ @keydown="${this._handleGridKeydown}"
2359
+ @scroll="${this._handleScroll}"
2360
+ >
2361
+ ${this._renderHeaderRow(t)}
2362
+ ${this._renderRows(e, t)}
2317
2363
  </div>
2318
- <input
2319
- type="color"
2320
- .value=${this.backgroundColor}
2321
- ?disabled=${i}
2322
- aria-label="Background color"
2323
- title="Background color"
2324
- @input=${(e) => this._dispatch("backgroundColor", e.target.value)}
2325
- />
2326
2364
  </div>
2327
2365
 
2328
- <div class="separator" role="separator"></div>
2329
-
2330
- <!-- Alignment group -->
2331
- <div class="align-group" role="group" aria-label="Text alignment">
2332
- <button
2333
- type="button"
2334
- aria-pressed="${this.textAlign === "left"}"
2335
- aria-label="Align left"
2336
- title="Align left"
2337
- ?disabled=${i}
2338
- tabindex="-1"
2339
- @click=${() => this._dispatch("textAlign", "left")}
2340
- ><span class="align-icon align-left" aria-hidden="true"><span></span><span></span><span></span></span></button>
2341
- <button
2342
- type="button"
2343
- aria-pressed="${this.textAlign === "center"}"
2344
- aria-label="Align center"
2345
- title="Align center"
2346
- ?disabled=${i}
2347
- tabindex="-1"
2348
- @click=${() => this._dispatch("textAlign", "center")}
2349
- ><span class="align-icon align-center" aria-hidden="true"><span></span><span></span><span></span></span></button>
2350
- <button
2351
- type="button"
2352
- aria-pressed="${this.textAlign === "right"}"
2353
- aria-label="Align right"
2354
- title="Align right"
2355
- ?disabled=${i}
2356
- tabindex="-1"
2357
- @click=${() => this._dispatch("textAlign", "right")}
2358
- ><span class="align-icon align-right" aria-hidden="true"><span></span><span></span><span></span></span></button>
2366
+ <div class="sr-only" role="status" aria-live="polite" aria-atomic="true">
2367
+ ${this._getAnnouncement(r)}
2359
2368
  </div>
2360
-
2361
- <div class="separator" role="separator"></div>
2362
-
2363
- <!-- Font size -->
2364
- <select
2365
- class="font-size-select"
2366
- .value=${String(this.fontSize)}
2367
- ?disabled=${i}
2368
- aria-label="Font size"
2369
- title="Font size"
2370
- tabindex="-1"
2371
- @change=${(e) => this._dispatch("fontSize", Number(e.target.value))}
2372
- >
2373
- ${[8, 9, 10, 11, 12, 13, 14, 16, 18, 20, 24, 28, 32, 36].map(
2374
- (e) => y`<option value=${e} ?selected=${this.fontSize === e}>${e}</option>`
2375
- )}
2376
- </select>
2377
-
2378
- <div class="separator" role="separator"></div>
2379
-
2380
- <!-- Clear formatting -->
2381
- <button
2382
- type="button"
2383
- aria-label="Clear formatting"
2384
- title="Clear formatting"
2385
- ?disabled=${i}
2369
+ <div class="sr-only" role="log" aria-live="assertive" aria-atomic="true">
2370
+ ${this._modeAnnouncement}
2371
+ </div>
2372
+ </div>
2373
+ `;
2374
+ }
2375
+ _renderColumnSpacer(e) {
2376
+ return n`<div style="grid-column: span ${e};"></div>`;
2377
+ }
2378
+ _renderHeaderRow(e) {
2379
+ return n`
2380
+ <div class="ls-header-row" role="row" aria-rowindex="1">
2381
+ <div
2382
+ class="ls-corner-header"
2383
+ role="columnheader"
2384
+ aria-colindex="1"
2385
+ aria-label="Select all"
2386
2386
  tabindex="-1"
2387
- @click=${() => this._dispatch("clearFormat")}
2388
- >${"✘"}</button>
2387
+ @click="${this._handleSelectAll}"
2388
+ @keydown="${this._handleCornerKeydown}"
2389
+ ><span class="sr-only">Select all</span></div>
2390
+ ${U(e, this.cols, (e) => n`
2391
+ <div
2392
+ class="ls-col-header"
2393
+ role="columnheader"
2394
+ aria-colindex="${e + 2}"
2395
+ aria-label="Column ${_(e)}"
2396
+ >
2397
+ ${_(e)}
2398
+ </div>
2399
+ `, (e, t) => this._renderColumnSpacer(t))}
2400
+ </div>
2401
+ `;
2402
+ }
2403
+ _renderRows(e, t) {
2404
+ let r = this._getCSSVarPx("--ls-cell-height", 28), i = U(e, this.rows, (e) => ({
2405
+ key: `r${e}`,
2406
+ row: e,
2407
+ gap: 0
2408
+ }), (e, t) => ({
2409
+ key: `g${e}`,
2410
+ row: e,
2411
+ gap: t
2412
+ }));
2413
+ return n`
2414
+ ${c(i, (e) => e.key, (e) => e.gap > 0 ? n`<div style="grid-column: 1 / -1; height: ${e.gap * r}px;"></div>` : this._renderRow(e.row, t))}
2415
+ `;
2416
+ }
2417
+ _renderCell(e, t) {
2418
+ let i = this._selection.isCellSelected(e, t), a = this._selection.isCellActive(e, t), o = this._refMode && e === this._refCursorRow && t === this._refCursorCol, s = this._getCellDisplay(e, t), c = h(e, t), u = this._getCellStyles(e, t);
2419
+ return n`
2420
+ <div
2421
+ class="ls-cell ${a ? "active-cell" : ""} ${o ? "ref-highlight" : ""}"
2422
+ role="gridcell"
2423
+ aria-colindex="${t + 2}"
2424
+ aria-selected="${i}"
2425
+ aria-readonly="${this.readOnly}"
2426
+ aria-current="${a ? "true" : r}"
2427
+ data-row="${e}"
2428
+ data-col="${t}"
2429
+ data-key="${c}"
2430
+ tabindex="${a ? 0 : -1}"
2431
+ style=${l(u)}
2432
+ @pointerdown="${this._handleCellPointerDown}"
2433
+ @dblclick="${this._handleCellDblClick}"
2434
+ >
2435
+ <span class="cell-text">${s}</span>
2436
+ </div>
2437
+ `;
2438
+ }
2439
+ _renderRow(e, t) {
2440
+ return n`
2441
+ <div class="ls-row" role="row" aria-rowindex="${e + 2}">
2442
+ <div
2443
+ class="ls-row-header"
2444
+ role="rowheader"
2445
+ aria-colindex="1"
2446
+ aria-label="Row ${e + 1}"
2447
+ >${e + 1}</div>
2448
+ ${c(U(t, this.cols, (t) => ({
2449
+ key: `c${t}`,
2450
+ tpl: this._renderCell(e, t)
2451
+ }), (e, t) => ({
2452
+ key: `g${e}`,
2453
+ tpl: this._renderColumnSpacer(t)
2454
+ })), (e) => e.key, (e) => e.tpl)}
2389
2455
  </div>
2390
2456
  `;
2391
- }
2457
+ }
2458
+ _getAnnouncement(e) {
2459
+ let t = `${_(e.col)}${e.row + 1}`, n = this._getCellDisplay(e.row, e.col), r = n ? `${t}: ${n}` : t, i = this._selection.range, a = i.end.row - i.start.row + 1, o = i.end.col - i.start.col + 1;
2460
+ return a > 1 || o > 1 ? `${r}, ${a} row${a > 1 ? "s" : ""}, ${o} column${o > 1 ? "s" : ""} selected` : r;
2461
+ }
2462
+ updated() {
2463
+ this._isEditing && this._editor && (this._editor.focus(), this._refMode ? this._editor.setSelectionRange(this._refInsertEnd, this._refInsertEnd) : this._editorNeedsCursorReset && (this._editorNeedsCursorReset = !1, this._editor.setSelectionRange(this._editValue.length, this._editValue.length)));
2464
+ }
2392
2465
  };
2393
- C.styles = M`
2466
+ z([a({ type: Number })], G.prototype, "rows", void 0), z([a({ type: Number })], G.prototype, "cols", void 0), z([a({ attribute: !1 })], G.prototype, "data", void 0), z([a({
2467
+ type: Boolean,
2468
+ attribute: "read-only",
2469
+ reflect: !0
2470
+ })], G.prototype, "readOnly", void 0), z([a({
2471
+ type: String,
2472
+ attribute: "grid-label"
2473
+ })], G.prototype, "gridLabel", void 0), z([a({ attribute: !1 })], G.prototype, "functions", void 0), z([s()], G.prototype, "_isEditing", void 0), z([s()], G.prototype, "_editValue", void 0), z([s()], G.prototype, "_formulaBarMode", void 0), z([s()], G.prototype, "_refMode", void 0), z([s()], G.prototype, "_modeAnnouncement", void 0), z([s()], G.prototype, "_scrollTop", void 0), z([s()], G.prototype, "_scrollLeft", void 0), z([o("#editor")], G.prototype, "_editor", void 0), z([o(".ls-grid")], G.prototype, "_grid", void 0), G = V = z([i("y11n-spreadsheet")], G);
2474
+ // src/components/y11n-format-toolbar.ts
2475
+ var K = class extends e {
2476
+ constructor(...e) {
2477
+ super(...e), this.bold = !1, this.italic = !1, this.underline = !1, this.strikethrough = !1, this.textColor = "#000000", this.backgroundColor = "#ffffff", this.textAlign = "left", this.fontSize = 13, this.readOnly = !1;
2478
+ }
2479
+ static {
2480
+ this.styles = t`
2394
2481
  :host {
2395
2482
  display: block;
2396
2483
  font-family: var(--ls-font-family, system-ui, -apple-system, sans-serif);
@@ -2552,60 +2639,200 @@ C.styles = M`
2552
2639
  cursor: not-allowed;
2553
2640
  }
2554
2641
  `;
2555
- S([
2556
- m({ type: Boolean })
2557
- ], C.prototype, "bold", 2);
2558
- S([
2559
- m({ type: Boolean })
2560
- ], C.prototype, "italic", 2);
2561
- S([
2562
- m({ type: Boolean })
2563
- ], C.prototype, "underline", 2);
2564
- S([
2565
- m({ type: Boolean })
2566
- ], C.prototype, "strikethrough", 2);
2567
- S([
2568
- m({ type: String })
2569
- ], C.prototype, "textColor", 2);
2570
- S([
2571
- m({ type: String })
2572
- ], C.prototype, "backgroundColor", 2);
2573
- S([
2574
- m({ type: String })
2575
- ], C.prototype, "textAlign", 2);
2576
- S([
2577
- m({ type: Number })
2578
- ], C.prototype, "fontSize", 2);
2579
- S([
2580
- m({ type: Boolean, attribute: "read-only", reflect: !0 })
2581
- ], C.prototype, "readOnly", 2);
2582
- C = S([
2583
- T("y11n-format-toolbar")
2584
- ], C);
2585
- function nt(i, e) {
2586
- let t, r, s, o, n, a, l, c, h = !0;
2587
- for (let p = e.start.row; p <= e.end.row; p++)
2588
- for (let f = e.start.col; f <= e.end.col; f++) {
2589
- const g = `${p}:${f}`, _ = i(g), d = _ == null ? void 0 : _.format;
2590
- h ? (t = (d == null ? void 0 : d.bold) ?? !1, r = (d == null ? void 0 : d.italic) ?? !1, s = (d == null ? void 0 : d.underline) ?? !1, o = (d == null ? void 0 : d.strikethrough) ?? !1, n = d == null ? void 0 : d.textColor, a = d == null ? void 0 : d.backgroundColor, l = d == null ? void 0 : d.textAlign, c = d == null ? void 0 : d.fontSize, h = !1) : (t !== ((d == null ? void 0 : d.bold) ?? !1) && (t = void 0), r !== ((d == null ? void 0 : d.italic) ?? !1) && (r = void 0), s !== ((d == null ? void 0 : d.underline) ?? !1) && (s = void 0), o !== ((d == null ? void 0 : d.strikethrough) ?? !1) && (o = void 0), n !== (d == null ? void 0 : d.textColor) && (n = void 0), a !== (d == null ? void 0 : d.backgroundColor) && (a = void 0), l !== (d == null ? void 0 : d.textAlign) && (l = void 0), c !== (d == null ? void 0 : d.fontSize) && (c = void 0));
2591
- }
2592
- const u = {};
2593
- return t && (u.bold = !0), r && (u.italic = !0), s && (u.underline = !0), o && (u.strikethrough = !0), n !== void 0 && (u.textColor = n), a !== void 0 && (u.backgroundColor = a), l !== void 0 && (u.textAlign = l), c !== void 0 && (u.fontSize = c), u;
2594
- }
2595
- export {
2596
- G as ClipboardManager,
2597
- K as FormulaEngine,
2598
- x as RangeValue,
2599
- j as SelectionManager,
2600
- C as Y11nFormatToolbar,
2601
- A as Y11nFormulaBar,
2602
- w as Y11nSpreadsheet,
2603
- b as cellKey,
2604
- F as colToLetter,
2605
- nt as computeSelectionFormat,
2606
- D as coordToRef,
2607
- z as formatsEqual,
2608
- P as letterToCol,
2609
- ot as parseKey,
2610
- R as refToCoord
2642
+ }
2643
+ _dispatch(e, t) {
2644
+ this.dispatchEvent(new CustomEvent("format-action", {
2645
+ detail: {
2646
+ action: e,
2647
+ value: t
2648
+ },
2649
+ bubbles: !0,
2650
+ composed: !0
2651
+ }));
2652
+ }
2653
+ _handleToolbarKeydown(e) {
2654
+ let t = e.currentTarget, n = Array.from(t.querySelectorAll("button:not(:disabled), select:not(:disabled), .color-wrapper")), r = n.findIndex((t) => t === e.target || t.contains(e.target));
2655
+ if (r === -1) return;
2656
+ let i = -1;
2657
+ if (e.key === "ArrowRight" ? i = (r + 1) % n.length : e.key === "ArrowLeft" ? i = (r - 1 + n.length) % n.length : e.key === "Home" ? i = 0 : e.key === "End" && (i = n.length - 1), i >= 0) {
2658
+ e.preventDefault();
2659
+ let t = n[i];
2660
+ (t.querySelector("input, select, button") ?? t).focus();
2661
+ }
2662
+ }
2663
+ render() {
2664
+ let e = this.readOnly;
2665
+ return n`
2666
+ <div
2667
+ class="toolbar"
2668
+ role="toolbar"
2669
+ aria-label="Cell formatting"
2670
+ @keydown=${this._handleToolbarKeydown}
2671
+ >
2672
+ <!-- Text style toggles -->
2673
+ <button
2674
+ class="bold-btn"
2675
+ type="button"
2676
+ aria-pressed="${this.bold}"
2677
+ aria-label="Bold"
2678
+ title="Bold (Ctrl+B)"
2679
+ ?disabled=${e}
2680
+ tabindex="0"
2681
+ @click=${() => this._dispatch("bold", !this.bold)}
2682
+ >B</button>
2683
+ <button
2684
+ class="italic-btn"
2685
+ type="button"
2686
+ aria-pressed="${this.italic}"
2687
+ aria-label="Italic"
2688
+ title="Italic (Ctrl+I)"
2689
+ ?disabled=${e}
2690
+ tabindex="-1"
2691
+ @click=${() => this._dispatch("italic", !this.italic)}
2692
+ >I</button>
2693
+ <button
2694
+ class="underline-btn"
2695
+ type="button"
2696
+ aria-pressed="${this.underline}"
2697
+ aria-label="Underline"
2698
+ title="Underline (Ctrl+U)"
2699
+ ?disabled=${e}
2700
+ tabindex="-1"
2701
+ @click=${() => this._dispatch("underline", !this.underline)}
2702
+ >U</button>
2703
+ <button
2704
+ class="strikethrough-btn"
2705
+ type="button"
2706
+ aria-pressed="${this.strikethrough}"
2707
+ aria-label="Strikethrough"
2708
+ title="Strikethrough"
2709
+ ?disabled=${e}
2710
+ tabindex="-1"
2711
+ @click=${() => this._dispatch("strikethrough", !this.strikethrough)}
2712
+ >S</button>
2713
+
2714
+ <div class="separator" role="separator"></div>
2715
+
2716
+ <!-- Text color -->
2717
+ <div class="color-wrapper" tabindex="-1">
2718
+ <div class="color-swatch" aria-hidden="true">
2719
+ A<span class="indicator" style="background-color: ${this.textColor}"></span>
2720
+ </div>
2721
+ <input
2722
+ type="color"
2723
+ .value=${this.textColor}
2724
+ ?disabled=${e}
2725
+ aria-label="Text color"
2726
+ title="Text color"
2727
+ @input=${(e) => this._dispatch("textColor", e.target.value)}
2728
+ />
2729
+ </div>
2730
+
2731
+ <!-- Background color -->
2732
+ <div class="color-wrapper" tabindex="-1">
2733
+ <div class="color-swatch" aria-hidden="true">
2734
+ <span class="indicator" style="background-color: ${this.backgroundColor}; width: 16px; height: 16px; border-radius: 2px; border: 1px solid #ccc;"></span>
2735
+ </div>
2736
+ <input
2737
+ type="color"
2738
+ .value=${this.backgroundColor}
2739
+ ?disabled=${e}
2740
+ aria-label="Background color"
2741
+ title="Background color"
2742
+ @input=${(e) => this._dispatch("backgroundColor", e.target.value)}
2743
+ />
2744
+ </div>
2745
+
2746
+ <div class="separator" role="separator"></div>
2747
+
2748
+ <!-- Alignment group -->
2749
+ <div class="align-group" role="group" aria-label="Text alignment">
2750
+ <button
2751
+ type="button"
2752
+ aria-pressed="${this.textAlign === "left"}"
2753
+ aria-label="Align left"
2754
+ title="Align left"
2755
+ ?disabled=${e}
2756
+ tabindex="-1"
2757
+ @click=${() => this._dispatch("textAlign", "left")}
2758
+ ><span class="align-icon align-left" aria-hidden="true"><span></span><span></span><span></span></span></button>
2759
+ <button
2760
+ type="button"
2761
+ aria-pressed="${this.textAlign === "center"}"
2762
+ aria-label="Align center"
2763
+ title="Align center"
2764
+ ?disabled=${e}
2765
+ tabindex="-1"
2766
+ @click=${() => this._dispatch("textAlign", "center")}
2767
+ ><span class="align-icon align-center" aria-hidden="true"><span></span><span></span><span></span></span></button>
2768
+ <button
2769
+ type="button"
2770
+ aria-pressed="${this.textAlign === "right"}"
2771
+ aria-label="Align right"
2772
+ title="Align right"
2773
+ ?disabled=${e}
2774
+ tabindex="-1"
2775
+ @click=${() => this._dispatch("textAlign", "right")}
2776
+ ><span class="align-icon align-right" aria-hidden="true"><span></span><span></span><span></span></span></button>
2777
+ </div>
2778
+
2779
+ <div class="separator" role="separator"></div>
2780
+
2781
+ <!-- Font size -->
2782
+ <select
2783
+ class="font-size-select"
2784
+ .value=${String(this.fontSize)}
2785
+ ?disabled=${e}
2786
+ aria-label="Font size"
2787
+ title="Font size"
2788
+ tabindex="-1"
2789
+ @change=${(e) => this._dispatch("fontSize", Number(e.target.value))}
2790
+ >
2791
+ ${[
2792
+ 8,
2793
+ 9,
2794
+ 10,
2795
+ 11,
2796
+ 12,
2797
+ 13,
2798
+ 14,
2799
+ 16,
2800
+ 18,
2801
+ 20,
2802
+ 24,
2803
+ 28,
2804
+ 32,
2805
+ 36
2806
+ ].map((e) => n`<option value=${e} ?selected=${this.fontSize === e}>${e}</option>`)}
2807
+ </select>
2808
+
2809
+ <div class="separator" role="separator"></div>
2810
+
2811
+ <!-- Clear formatting -->
2812
+ <button
2813
+ type="button"
2814
+ aria-label="Clear formatting"
2815
+ title="Clear formatting"
2816
+ ?disabled=${e}
2817
+ tabindex="-1"
2818
+ @click=${() => this._dispatch("clearFormat")}
2819
+ >${"✘"}</button>
2820
+ </div>
2821
+ `;
2822
+ }
2611
2823
  };
2824
+ z([a({ type: Boolean })], K.prototype, "bold", void 0), z([a({ type: Boolean })], K.prototype, "italic", void 0), z([a({ type: Boolean })], K.prototype, "underline", void 0), z([a({ type: Boolean })], K.prototype, "strikethrough", void 0), z([a({ type: String })], K.prototype, "textColor", void 0), z([a({ type: String })], K.prototype, "backgroundColor", void 0), z([a({ type: String })], K.prototype, "textAlign", void 0), z([a({ type: Number })], K.prototype, "fontSize", void 0), z([a({
2825
+ type: Boolean,
2826
+ attribute: "read-only",
2827
+ reflect: !0
2828
+ })], K.prototype, "readOnly", void 0), K = z([i("y11n-format-toolbar")], K);
2829
+ function q(e, t) {
2830
+ let n, r, i, a, o, s, c, l, u = !0;
2831
+ for (let d = t.start.row; d <= t.end.row; d++) for (let f = t.start.col; f <= t.end.col; f++) {
2832
+ let t = e(`${d}:${f}`)?.format;
2833
+ u ? (n = t?.bold ?? !1, r = t?.italic ?? !1, i = t?.underline ?? !1, a = t?.strikethrough ?? !1, o = t?.textColor, s = t?.backgroundColor, c = t?.textAlign, l = t?.fontSize, u = !1) : (n !== (t?.bold ?? !1) && (n = void 0), r !== (t?.italic ?? !1) && (r = void 0), i !== (t?.underline ?? !1) && (i = void 0), a !== (t?.strikethrough ?? !1) && (a = void 0), o !== t?.textColor && (o = void 0), s !== t?.backgroundColor && (s = void 0), c !== t?.textAlign && (c = void 0), l !== t?.fontSize && (l = void 0));
2834
+ }
2835
+ let d = {};
2836
+ return n && (d.bold = !0), r && (d.italic = !0), i && (d.underline = !0), a && (d.strikethrough = !0), o !== void 0 && (d.textColor = o), s !== void 0 && (d.backgroundColor = s), c !== void 0 && (d.textAlign = c), l !== void 0 && (d.fontSize = l), d;
2837
+ }
2838
+ export { R as ClipboardManager, L as FormulaEngine, F as RangeValue, x as SelectionManager, K as Y11nFormatToolbar, B as Y11nFormulaBar, G as Y11nSpreadsheet, h as cellKey, _ as colToLetter, q as computeSelectionFormat, b as coordToRef, f as formatNumber, u as formatsEqual, v as letterToCol, g as parseKey, y as refToCoord };