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/LICENSE +21 -0
- package/dist/components/y11n-format-toolbar.d.ts +43 -0
- package/dist/components/y11n-format-toolbar.d.ts.map +1 -0
- package/dist/components/y11n-formula-bar.d.ts +30 -0
- package/dist/components/y11n-formula-bar.d.ts.map +1 -0
- package/dist/controllers/clipboard-manager.d.ts +91 -0
- package/dist/controllers/clipboard-manager.d.ts.map +1 -0
- package/dist/controllers/selection-manager.d.ts +52 -0
- package/dist/controllers/selection-manager.d.ts.map +1 -0
- package/dist/engine/formula-engine.d.ts +75 -0
- package/dist/engine/formula-engine.d.ts.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1574 -433
- package/dist/types.d.ts +117 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/y11n-spreadsheet.d.ts +155 -0
- package/dist/y11n-spreadsheet.d.ts.map +1 -0
- package/package.json +26 -5
package/dist/index.js
CHANGED
|
@@ -1,37 +1,44 @@
|
|
|
1
|
-
import { css as
|
|
2
|
-
import { property as
|
|
3
|
-
import { repeat as
|
|
4
|
-
|
|
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
|
|
14
|
+
function ot(i) {
|
|
8
15
|
const [e, t] = i.split(":").map(Number);
|
|
9
16
|
return { row: e, col: t };
|
|
10
17
|
}
|
|
11
|
-
function
|
|
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
|
|
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
|
|
24
|
-
const
|
|
25
|
-
if (!
|
|
26
|
-
const
|
|
27
|
-
return { row: parseInt(
|
|
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
|
|
30
|
-
return `${
|
|
36
|
+
function D(i) {
|
|
37
|
+
return `${F(i.col)}${i.row + 1}`;
|
|
31
38
|
}
|
|
32
|
-
class
|
|
33
|
-
constructor(e, t,
|
|
34
|
-
this.anchor = { row: 0, col: 0 }, this.head = { row: 0, col: 0 }, this._isDragging = !1, this.host = e, this._maxRows = t, this._maxCols =
|
|
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
|
|
65
|
-
return e >=
|
|
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,
|
|
74
|
-
if (
|
|
80
|
+
startSelection(e, t, r = !1) {
|
|
81
|
+
if (r)
|
|
75
82
|
this.head = this.clamp({ row: e, col: t });
|
|
76
83
|
else {
|
|
77
|
-
const
|
|
78
|
-
this.anchor = { ...
|
|
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,
|
|
96
|
-
const
|
|
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 =
|
|
107
|
+
this.head = s, r || (this.anchor = { ...s }), this.host.requestUpdate();
|
|
101
108
|
}
|
|
102
109
|
/** Move to a specific cell */
|
|
103
|
-
moveTo(e, t,
|
|
104
|
-
const
|
|
105
|
-
this.head =
|
|
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
|
|
124
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
150
|
-
return this.
|
|
151
|
-
} catch (
|
|
152
|
-
const
|
|
153
|
-
return
|
|
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,
|
|
166
|
-
if (
|
|
167
|
-
const
|
|
168
|
-
(
|
|
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.
|
|
247
|
+
if (this._reverseDeps.size === 0 && this._data.size > 0)
|
|
178
248
|
return this.recalculate();
|
|
179
|
-
const t = /* @__PURE__ */ new Set(),
|
|
180
|
-
for (const
|
|
181
|
-
|
|
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,
|
|
184
|
-
(l.displayValue !== c.displayValue || l.type !== c.type) && (l.displayValue = c.displayValue, l.type = c.type, t.add(
|
|
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
|
|
188
|
-
for (;
|
|
189
|
-
const
|
|
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
|
-
!
|
|
264
|
+
!o.has(c) && !r.has(c) && (o.add(c), s.push(c), n.push(c));
|
|
193
265
|
}
|
|
194
|
-
for (const
|
|
195
|
-
|
|
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,
|
|
198
|
-
(l.displayValue !== c.displayValue || l.type !== c.type) && (l.displayValue = c.displayValue, l.type = c.type, t.add(
|
|
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
|
|
279
|
+
var r;
|
|
206
280
|
const t = this._deps.get(e);
|
|
207
281
|
if (t) {
|
|
208
|
-
for (const
|
|
209
|
-
(
|
|
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
|
|
218
|
-
|
|
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
|
|
228
|
-
return e.trim() !== "" && !isNaN(
|
|
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
|
|
234
|
-
for (;
|
|
235
|
-
const
|
|
236
|
-
if (/\s/.test(
|
|
237
|
-
|
|
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 (
|
|
241
|
-
let
|
|
242
|
-
for (
|
|
243
|
-
|
|
244
|
-
|
|
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(
|
|
248
|
-
let
|
|
249
|
-
for (;
|
|
250
|
-
|
|
251
|
-
t.push({ type: "NUMBER", value:
|
|
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 (
|
|
255
|
-
t.push({ type: "OPERATOR", value:
|
|
329
|
+
if (s === "+" || s === "-" || s === "*" || s === "/" || s === "&") {
|
|
330
|
+
t.push({ type: "OPERATOR", value: s }), r++;
|
|
256
331
|
continue;
|
|
257
332
|
}
|
|
258
|
-
if (
|
|
259
|
-
|
|
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 (
|
|
263
|
-
t.push({ type: "OPERATOR", value: "=" }),
|
|
337
|
+
if (s === "=") {
|
|
338
|
+
t.push({ type: "OPERATOR", value: "=" }), r++;
|
|
264
339
|
continue;
|
|
265
340
|
}
|
|
266
|
-
if (
|
|
267
|
-
t.push({ type: "LPAREN", value: "(" }),
|
|
341
|
+
if (s === "(") {
|
|
342
|
+
t.push({ type: "LPAREN", value: "(" }), r++;
|
|
268
343
|
continue;
|
|
269
344
|
}
|
|
270
|
-
if (
|
|
271
|
-
t.push({ type: "RPAREN", value: ")" }),
|
|
345
|
+
if (s === ")") {
|
|
346
|
+
t.push({ type: "RPAREN", value: ")" }), r++;
|
|
272
347
|
continue;
|
|
273
348
|
}
|
|
274
|
-
if (
|
|
275
|
-
t.push({ type: "COMMA", value: "," }),
|
|
349
|
+
if (s === ",") {
|
|
350
|
+
t.push({ type: "COMMA", value: "," }), r++;
|
|
276
351
|
continue;
|
|
277
352
|
}
|
|
278
|
-
if (/[A-Za-z_]/.test(
|
|
279
|
-
let
|
|
280
|
-
for (;
|
|
281
|
-
|
|
282
|
-
const o =
|
|
283
|
-
if (
|
|
284
|
-
t.push({ type: "BOOLEAN", value:
|
|
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 (
|
|
288
|
-
|
|
289
|
-
let
|
|
290
|
-
for (;
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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
|
-
|
|
299
|
-
|
|
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 (
|
|
305
|
-
t.push({ type: "
|
|
386
|
+
if (!a) {
|
|
387
|
+
t.push({ type: "FUNC", value: l });
|
|
306
388
|
continue;
|
|
307
389
|
}
|
|
308
|
-
|
|
309
|
-
continue;
|
|
390
|
+
throw new Error(`Unexpected identifier: ${o}`);
|
|
310
391
|
}
|
|
311
|
-
throw new Error(`Unexpected character: ${
|
|
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
|
|
325
|
-
if (t &&
|
|
326
|
-
throw new Error(`Expected ${t} but got ${
|
|
327
|
-
return e.pos++,
|
|
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 >
|
|
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
|
|
343
|
-
t = this._compareValues(t,
|
|
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
|
|
352
|
-
t = String(t) + String(
|
|
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
|
|
360
|
-
|
|
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
|
|
368
|
-
if (
|
|
448
|
+
const r = this._consume(e).value, s = this._parseUnary(e);
|
|
449
|
+
if (r === "*") t = Number(t) * Number(s);
|
|
369
450
|
else {
|
|
370
|
-
const
|
|
371
|
-
if (
|
|
372
|
-
t = Number(t) /
|
|
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
|
|
398
|
-
return this._consume(e, "RPAREN"),
|
|
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
|
|
487
|
+
const r = [];
|
|
407
488
|
if (this._peek(e).type !== "RPAREN")
|
|
408
|
-
for (
|
|
409
|
-
this._consume(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
|
|
412
|
-
if (!
|
|
413
|
-
const
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
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,
|
|
420
|
-
const
|
|
421
|
-
|
|
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
|
|
535
|
+
return n === a;
|
|
424
536
|
case "<>":
|
|
425
|
-
return
|
|
537
|
+
return n !== a;
|
|
426
538
|
case "<":
|
|
427
|
-
return
|
|
539
|
+
return n < a;
|
|
428
540
|
case ">":
|
|
429
|
-
return
|
|
541
|
+
return n > a;
|
|
430
542
|
case "<=":
|
|
431
|
-
return
|
|
543
|
+
return n <= a;
|
|
432
544
|
case ">=":
|
|
433
|
-
return
|
|
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 =
|
|
444
|
-
if (this._trackDep(
|
|
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
|
|
447
|
-
if (!
|
|
448
|
-
if (
|
|
449
|
-
|
|
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(
|
|
563
|
+
return this.parseExpression(s.rawValue.substring(1));
|
|
453
564
|
} catch {
|
|
454
565
|
throw new Error("#ERROR!");
|
|
455
566
|
} finally {
|
|
456
|
-
this.
|
|
567
|
+
this._evaluating.delete(r);
|
|
457
568
|
}
|
|
458
569
|
}
|
|
459
|
-
const
|
|
460
|
-
return !isNaN(
|
|
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,
|
|
464
|
-
for (let
|
|
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
|
|
467
|
-
this._trackDep(
|
|
468
|
-
const
|
|
469
|
-
if (
|
|
470
|
-
if (
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
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
|
-
|
|
481
|
-
const
|
|
482
|
-
!isNaN(
|
|
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.
|
|
608
|
+
const t = this._data.get(e);
|
|
492
609
|
if (!t) return 0;
|
|
493
|
-
const
|
|
494
|
-
return !isNaN(
|
|
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
|
|
500
|
-
for (let h =
|
|
501
|
-
for (let
|
|
502
|
-
const
|
|
503
|
-
if (
|
|
504
|
-
const
|
|
505
|
-
c.push(!isNaN(
|
|
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((
|
|
515
|
-
const
|
|
516
|
-
return
|
|
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
|
|
519
|
-
return
|
|
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
|
|
522
|
-
return
|
|
523
|
-
}), this.registerFunction("COUNT", (e, ...t) => t.filter((
|
|
524
|
-
const
|
|
525
|
-
return Math.round(Number(t) *
|
|
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
|
|
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
|
-
|
|
837
|
+
this._copySourceRange = t, this._copySourceData = e;
|
|
838
|
+
const r = this.serializeRange(e, t), s = this._serializeRangeAsHTML(e, t);
|
|
535
839
|
try {
|
|
536
|
-
|
|
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
|
-
|
|
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
|
|
547
|
-
for (let
|
|
548
|
-
for (let
|
|
549
|
-
|
|
550
|
-
return
|
|
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,
|
|
557
|
-
|
|
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
|
-
|
|
887
|
+
o = await navigator.clipboard.readText();
|
|
560
888
|
} catch {
|
|
561
889
|
return null;
|
|
562
890
|
}
|
|
563
|
-
return this.parseTSV(
|
|
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,
|
|
570
|
-
const
|
|
571
|
-
|
|
572
|
-
|
|
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
|
|
577
|
-
|
|
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
|
|
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
|
|
585
|
-
for (let
|
|
586
|
-
const
|
|
587
|
-
for (let
|
|
588
|
-
const
|
|
589
|
-
|
|
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
|
-
|
|
1014
|
+
r.push(o.join(" "));
|
|
592
1015
|
}
|
|
593
|
-
return
|
|
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, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
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
|
|
610
|
-
for (var
|
|
611
|
-
(
|
|
612
|
-
return
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
759
|
-
|
|
760
|
-
],
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
],
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
],
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
],
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
],
|
|
773
|
-
|
|
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
|
-
],
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
],
|
|
779
|
-
var
|
|
780
|
-
for (var
|
|
781
|
-
(
|
|
782
|
-
return
|
|
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
|
|
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
|
|
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),
|
|
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(
|
|
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
|
|
1440
|
+
return b(i, e);
|
|
835
1441
|
}
|
|
836
1442
|
_getActiveCellRef() {
|
|
837
|
-
return
|
|
1443
|
+
return D(this._selection.activeCell);
|
|
838
1444
|
}
|
|
839
1445
|
_setCellRaw(i, e) {
|
|
840
|
-
const t = this._internalData.get(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:
|
|
844
|
-
type:
|
|
845
|
-
|
|
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.
|
|
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,
|
|
865
|
-
var
|
|
866
|
-
const
|
|
867
|
-
for (const
|
|
868
|
-
const
|
|
869
|
-
|
|
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
|
|
872
|
-
deltas:
|
|
1483
|
+
return s.length === 0 ? null : {
|
|
1484
|
+
deltas: s,
|
|
873
1485
|
selectionBefore: t,
|
|
874
|
-
selectionAfter:
|
|
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((
|
|
883
|
-
updates: i.deltas.map((
|
|
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),
|
|
903
|
-
if (e ===
|
|
904
|
-
const
|
|
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
|
-
|
|
908
|
-
|
|
1524
|
+
s,
|
|
1525
|
+
s
|
|
909
1526
|
);
|
|
910
|
-
return
|
|
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,
|
|
926
|
-
this._editingCellKey =
|
|
927
|
-
const
|
|
928
|
-
this._editValue = i ?? (
|
|
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
|
|
941
|
-
const { row: i, col: e } = this._selection.activeCell, t = (
|
|
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
|
|
1018
|
-
this._isEditing && this._commitEdit(), this._selection.startSelection(
|
|
1019
|
-
const
|
|
1020
|
-
var
|
|
1021
|
-
const
|
|
1022
|
-
if (
|
|
1023
|
-
const
|
|
1024
|
-
this._selection.extendSelection(
|
|
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(),
|
|
1732
|
+
this._selection.endSelection(), a();
|
|
1028
1733
|
};
|
|
1029
|
-
document.addEventListener("pointermove", l, { signal:
|
|
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
|
|
1036
|
-
this._selection.moveTo(
|
|
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
|
|
1049
|
-
this._internalData.has(
|
|
1050
|
-
const
|
|
1051
|
-
|
|
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
|
|
1058
|
-
|
|
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
|
|
1065
|
-
for (let
|
|
1066
|
-
const
|
|
1067
|
-
this._internalData.has(
|
|
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
|
|
1070
|
-
|
|
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
|
-
|
|
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),
|
|
1115
|
-
|
|
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
|
|
1121
|
-
if (!isNaN(
|
|
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
|
|
1128
|
-
const { row: i, col: e } = this._selection.activeCell, t = this._getCSSVarPx("--ls-cell-height", 28),
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
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:
|
|
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="${
|
|
1146
|
-
display-value="${
|
|
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
|
|
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,
|
|
1177
|
-
${this._renderRows(i, e, t,
|
|
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(
|
|
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:
|
|
1191
|
-
|
|
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 ?
|
|
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 ?
|
|
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,
|
|
1212
|
-
const
|
|
1213
|
-
return
|
|
1214
|
-
${i > 0 ?
|
|
1215
|
-
${
|
|
1216
|
-
${e < this.rows ?
|
|
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
|
|
1221
|
-
for (let
|
|
1222
|
-
const o = this._selection.isCellSelected(i,
|
|
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="${
|
|
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="${
|
|
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 ?
|
|
1986
|
+
${e > 0 ? y`<div style="grid-column: span ${e};"></div>` : k}
|
|
1245
1987
|
${r}
|
|
1246
|
-
${t < this.cols ?
|
|
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 = `${
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1420
|
-
|
|
1421
|
-
],
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
],
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
],
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
],
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
],
|
|
1434
|
-
|
|
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
|
-
],
|
|
1437
|
-
|
|
2185
|
+
], w.prototype, "_editValue", 2);
|
|
2186
|
+
v([
|
|
1438
2187
|
E()
|
|
1439
|
-
],
|
|
1440
|
-
|
|
2188
|
+
], w.prototype, "_formulaBarMode", 2);
|
|
2189
|
+
v([
|
|
1441
2190
|
E()
|
|
1442
|
-
],
|
|
1443
|
-
|
|
2191
|
+
], w.prototype, "_refMode", 2);
|
|
2192
|
+
v([
|
|
1444
2193
|
E()
|
|
1445
|
-
],
|
|
1446
|
-
|
|
2194
|
+
], w.prototype, "_modeAnnouncement", 2);
|
|
2195
|
+
v([
|
|
1447
2196
|
E()
|
|
1448
|
-
],
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
],
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
],
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
],
|
|
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
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
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
|
};
|