y11n-spreadsheet 0.1.0 → 1.0.1

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