y11n-spreadsheet 0.1.0 → 1.0.0

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