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