@casualoffice/sheets 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/api-BI2VLYQ6.d.cts +62 -0
  2. package/dist/api-BI2VLYQ6.d.ts +62 -0
  3. package/dist/chrome.cjs +2569 -0
  4. package/dist/chrome.cjs.map +1 -0
  5. package/dist/chrome.d.cts +96 -0
  6. package/dist/chrome.d.ts +96 -0
  7. package/dist/chrome.js +2556 -0
  8. package/dist/chrome.js.map +1 -0
  9. package/dist/collab.cjs +770 -0
  10. package/dist/collab.cjs.map +1 -0
  11. package/dist/collab.d.cts +248 -0
  12. package/dist/collab.d.ts +248 -0
  13. package/dist/collab.js +737 -0
  14. package/dist/collab.js.map +1 -0
  15. package/dist/embed/embed-runtime.js +128 -128
  16. package/dist/embed.cjs +166 -0
  17. package/dist/embed.cjs.map +1 -1
  18. package/dist/embed.d.cts +78 -3
  19. package/dist/embed.d.ts +78 -3
  20. package/dist/embed.js +166 -0
  21. package/dist/embed.js.map +1 -1
  22. package/dist/index.cjs +262 -165
  23. package/dist/index.cjs.map +1 -1
  24. package/dist/index.d.cts +4 -3
  25. package/dist/index.d.ts +4 -3
  26. package/dist/index.js +271 -168
  27. package/dist/index.js.map +1 -1
  28. package/dist/{protocol--KyBQUjU.d.cts → protocol-Cq4Cdoyi.d.cts} +19 -2
  29. package/dist/{protocol-cEzy7S0i.d.ts → protocol-DqDaG2yG.d.ts} +19 -2
  30. package/dist/sheets.cjs +102 -16
  31. package/dist/sheets.cjs.map +1 -1
  32. package/dist/sheets.d.cts +49 -63
  33. package/dist/sheets.d.ts +49 -63
  34. package/dist/sheets.js +111 -19
  35. package/dist/sheets.js.map +1 -1
  36. package/package.json +28 -3
  37. package/src/chrome/AutoSumPicker.tsx +176 -0
  38. package/src/chrome/BordersPicker.tsx +171 -0
  39. package/src/chrome/ChromeBottom.tsx +18 -0
  40. package/src/chrome/ChromeTop.tsx +21 -0
  41. package/src/chrome/ColorPicker.tsx +220 -0
  42. package/src/chrome/FindReplace.tsx +370 -0
  43. package/src/chrome/FormulaBar.tsx +378 -0
  44. package/src/chrome/Icon.tsx +43 -0
  45. package/src/chrome/MenuBar.tsx +336 -0
  46. package/src/chrome/NameBox.tsx +347 -0
  47. package/src/chrome/SheetTabs.tsx +346 -0
  48. package/src/chrome/StatusBar.tsx +232 -0
  49. package/src/chrome/Toolbar.tsx +401 -0
  50. package/src/chrome/fonts.ts +42 -0
  51. package/src/chrome/index.ts +24 -0
  52. package/src/collab/attachCollab.ts +151 -0
  53. package/src/collab/bridge-helpers.ts +97 -0
  54. package/src/collab/bridge.ts +885 -0
  55. package/src/collab/bridge.unit.test.ts +160 -0
  56. package/src/collab/index.ts +38 -0
  57. package/src/collab/replay-retry.ts +137 -0
  58. package/src/collab/replay-retry.unit.test.ts +223 -0
  59. package/src/collab/ws-url.ts +20 -0
  60. package/src/collab/ws-url.unit.test.ts +35 -0
  61. package/src/embed/EmbedHostTransport.ts +16 -1
  62. package/src/embed/EmbedTransport.ts +16 -0
  63. package/src/embed/EmbedTransport.unit.test.ts +88 -2
  64. package/src/embed/index.ts +7 -0
  65. package/src/embed/protocol.ts +34 -0
  66. package/src/embed-runtime/index.tsx +20 -0
  67. package/src/sheets/CasualSheets.tsx +204 -33
@@ -0,0 +1,2569 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/chrome/index.ts
21
+ var chrome_exports = {};
22
+ __export(chrome_exports, {
23
+ AutoSumPicker: () => AutoSumPicker,
24
+ BordersPicker: () => BordersPicker,
25
+ ChromeBottom: () => ChromeBottom,
26
+ ChromeTop: () => ChromeTop,
27
+ ColorPicker: () => ColorPicker,
28
+ FindReplace: () => FindReplace,
29
+ FormulaBar: () => FormulaBar,
30
+ Icon: () => Icon,
31
+ MenuBar: () => MenuBar,
32
+ NameBox: () => NameBox,
33
+ SheetTabs: () => SheetTabs,
34
+ StatusBar: () => StatusBar,
35
+ Toolbar: () => Toolbar,
36
+ ensureChromeFonts: () => ensureChromeFonts
37
+ });
38
+ module.exports = __toCommonJS(chrome_exports);
39
+
40
+ // src/chrome/Toolbar.tsx
41
+ var import_react4 = require("react");
42
+ var import_core = require("@univerjs/core");
43
+
44
+ // src/chrome/Icon.tsx
45
+ var import_jsx_runtime = require("react/jsx-runtime");
46
+ function Icon({ name, size = 20, style }) {
47
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
48
+ "span",
49
+ {
50
+ className: "material-symbols-outlined",
51
+ "aria-hidden": true,
52
+ style: {
53
+ fontFamily: "'Material Symbols Outlined'",
54
+ fontWeight: "normal",
55
+ fontStyle: "normal",
56
+ fontSize: size,
57
+ lineHeight: 1,
58
+ letterSpacing: "normal",
59
+ textTransform: "none",
60
+ display: "inline-block",
61
+ whiteSpace: "nowrap",
62
+ wordWrap: "normal",
63
+ direction: "ltr",
64
+ // Hint the variable-font axes so weight/optical-size are consistent.
65
+ fontVariationSettings: "'opsz' 20, 'wght' 400, 'FILL' 0, 'GRAD' 0",
66
+ ...style
67
+ },
68
+ children: name
69
+ }
70
+ );
71
+ }
72
+
73
+ // src/chrome/fonts.ts
74
+ var LINKS = [
75
+ {
76
+ id: "cs-font-inter",
77
+ href: "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
78
+ },
79
+ {
80
+ id: "cs-font-material-symbols",
81
+ href: "https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,300..700,0..1,-50..200&display=block"
82
+ }
83
+ ];
84
+ var done = false;
85
+ function ensureChromeFonts() {
86
+ if (done || typeof document === "undefined") return;
87
+ done = true;
88
+ const head = document.head;
89
+ for (const { id, href } of LINKS) {
90
+ if (document.getElementById(id)) continue;
91
+ if (document.querySelector(`link[href*="${href.split("?")[0]}"]`)) continue;
92
+ const link = document.createElement("link");
93
+ link.id = id;
94
+ link.rel = "stylesheet";
95
+ link.href = href;
96
+ head.appendChild(link);
97
+ }
98
+ }
99
+
100
+ // src/chrome/ColorPicker.tsx
101
+ var import_react = require("react");
102
+ var import_jsx_runtime2 = require("react/jsx-runtime");
103
+ var SWATCHES = [
104
+ { hex: "#000000", label: "Black" },
105
+ { hex: "#5f6368", label: "Dark gray" },
106
+ { hex: "#9aa0a6", label: "Gray" },
107
+ { hex: "#d9dce0", label: "Light gray" },
108
+ { hex: "#ffffff", label: "White" },
109
+ { hex: "#d13438", label: "Red" },
110
+ { hex: "#e8710a", label: "Orange" },
111
+ { hex: "#f2c811", label: "Yellow" },
112
+ { hex: "#107c10", label: "Green" },
113
+ { hex: "#0e7490", label: "Teal" },
114
+ { hex: "#1a73e8", label: "Blue" },
115
+ { hex: "#5b2a86", label: "Purple" },
116
+ { hex: "#c2185b", label: "Magenta" },
117
+ { hex: "#795548", label: "Brown" }
118
+ ];
119
+ var ROW_STYLE = {
120
+ display: "inline-flex",
121
+ alignItems: "center",
122
+ position: "relative"
123
+ };
124
+ var BTN_STYLE = {
125
+ width: 30,
126
+ height: 30,
127
+ display: "inline-flex",
128
+ alignItems: "center",
129
+ justifyContent: "center",
130
+ border: "none",
131
+ borderRadius: 6,
132
+ background: "transparent",
133
+ cursor: "pointer",
134
+ color: "var(--cs-chrome-fg, #201f1e)",
135
+ padding: 0
136
+ };
137
+ var POPOVER_STYLE = {
138
+ position: "absolute",
139
+ top: "100%",
140
+ left: 0,
141
+ marginTop: 4,
142
+ zIndex: 1e3,
143
+ padding: 8,
144
+ borderRadius: 8,
145
+ border: "1px solid var(--cs-chrome-border, #e6e9ee)",
146
+ background: "var(--cs-chrome-input-bg, #fff)",
147
+ boxShadow: "0 6px 20px rgba(0,0,0,0.18)"
148
+ };
149
+ var GRID_STYLE = {
150
+ display: "grid",
151
+ gridTemplateColumns: "repeat(7, 22px)",
152
+ gap: 6
153
+ };
154
+ var SWATCH_STYLE = {
155
+ width: 22,
156
+ height: 22,
157
+ borderRadius: 4,
158
+ border: "1px solid var(--cs-chrome-border, rgba(0,0,0,0.18))",
159
+ cursor: "pointer",
160
+ padding: 0
161
+ };
162
+ var NONE_STYLE = {
163
+ marginTop: 8,
164
+ width: "100%",
165
+ height: 26,
166
+ display: "inline-flex",
167
+ alignItems: "center",
168
+ justifyContent: "center",
169
+ gap: 4,
170
+ border: "1px solid var(--cs-chrome-border, rgba(0,0,0,0.18))",
171
+ borderRadius: 6,
172
+ background: "var(--cs-chrome-input-bg, #fff)",
173
+ color: "var(--cs-chrome-fg, #201f1e)",
174
+ font: "inherit",
175
+ fontSize: 12,
176
+ cursor: "pointer"
177
+ };
178
+ function ColorPicker({ api }) {
179
+ const [open, setOpen] = (0, import_react.useState)(null);
180
+ const rootRef = (0, import_react.useRef)(null);
181
+ (0, import_react.useEffect)(() => {
182
+ if (!open) return;
183
+ const onPointerDown = (e) => {
184
+ if (rootRef.current && !rootRef.current.contains(e.target)) setOpen(null);
185
+ };
186
+ const onKeyDown = (e) => {
187
+ if (e.key === "Escape") setOpen(null);
188
+ };
189
+ document.addEventListener("pointerdown", onPointerDown, true);
190
+ document.addEventListener("keydown", onKeyDown);
191
+ return () => {
192
+ document.removeEventListener("pointerdown", onPointerDown, true);
193
+ document.removeEventListener("keydown", onKeyDown);
194
+ };
195
+ }, [open]);
196
+ const pick = (kind, hex) => {
197
+ if (api) {
198
+ if (kind === "text") {
199
+ void api.executeCommand("sheet.command.set-range-text-color", { value: hex });
200
+ } else if (hex === null) {
201
+ void api.executeCommand("sheet.command.reset-background-color");
202
+ } else {
203
+ void api.executeCommand("sheet.command.set-background-color", { value: hex });
204
+ }
205
+ }
206
+ setOpen(null);
207
+ };
208
+ const toggle = (kind) => setOpen((cur) => cur === kind ? null : kind);
209
+ const renderButton = (kind, icon, label, testid) => {
210
+ const on = open === kind;
211
+ const baseBg = on ? "var(--cs-chrome-active, #e6f3f7)" : "transparent";
212
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
213
+ "button",
214
+ {
215
+ type: "button",
216
+ title: label,
217
+ "aria-label": label,
218
+ "aria-haspopup": "true",
219
+ "aria-expanded": on,
220
+ "data-testid": testid,
221
+ style: {
222
+ ...BTN_STYLE,
223
+ background: baseBg,
224
+ color: on ? "var(--cs-chrome-active-fg, #0e7490)" : BTN_STYLE.color
225
+ },
226
+ onMouseDown: (e) => {
227
+ e.preventDefault();
228
+ toggle(kind);
229
+ },
230
+ onMouseEnter: (e) => {
231
+ if (!on) e.currentTarget.style.background = "var(--cs-chrome-hover, rgba(0,0,0,0.06))";
232
+ },
233
+ onMouseLeave: (e) => {
234
+ e.currentTarget.style.background = baseBg;
235
+ },
236
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Icon, { name: icon, size: 20 })
237
+ }
238
+ );
239
+ };
240
+ const renderPopover = (kind) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: POPOVER_STYLE, "data-testid": "cs-color-popover", role: "menu", children: [
241
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: GRID_STYLE, children: SWATCHES.map((s) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
242
+ "button",
243
+ {
244
+ type: "button",
245
+ role: "menuitem",
246
+ title: s.label,
247
+ "aria-label": s.label,
248
+ "data-color": s.hex,
249
+ style: { ...SWATCH_STYLE, background: s.hex },
250
+ onMouseDown: (e) => {
251
+ e.preventDefault();
252
+ pick(kind, s.hex);
253
+ }
254
+ },
255
+ s.hex
256
+ )) }),
257
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
258
+ "button",
259
+ {
260
+ type: "button",
261
+ role: "menuitem",
262
+ "data-color": "none",
263
+ style: NONE_STYLE,
264
+ onMouseDown: (e) => {
265
+ e.preventDefault();
266
+ pick(kind, null);
267
+ },
268
+ children: [
269
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Icon, { name: "format_color_reset", size: 16 }),
270
+ "None"
271
+ ]
272
+ }
273
+ )
274
+ ] });
275
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { ref: rootRef, style: ROW_STYLE, "data-testid": "cs-color-picker", children: [
276
+ renderButton("text", "format_color_text", "Text color", "cs-color-text"),
277
+ renderButton("fill", "format_color_fill", "Fill color", "cs-color-fill"),
278
+ open && renderPopover(open)
279
+ ] });
280
+ }
281
+
282
+ // src/chrome/BordersPicker.tsx
283
+ var import_react2 = require("react");
284
+ var import_jsx_runtime3 = require("react/jsx-runtime");
285
+ var OPTIONS = [
286
+ { value: "all", icon: "border_all", label: "All borders" },
287
+ { value: "outside", icon: "border_outer", label: "Outside borders" },
288
+ { value: "inside", icon: "border_inner", label: "Inside borders" },
289
+ { value: "top", icon: "border_top", label: "Top border" },
290
+ { value: "bottom", icon: "border_bottom", label: "Bottom border" },
291
+ { value: "left", icon: "border_left", label: "Left border" },
292
+ { value: "right", icon: "border_right", label: "Right border" },
293
+ { value: "none", icon: "border_clear", label: "No border" }
294
+ ];
295
+ var ROW_STYLE2 = {
296
+ display: "inline-flex",
297
+ alignItems: "center",
298
+ position: "relative"
299
+ };
300
+ var BTN_STYLE2 = {
301
+ width: 30,
302
+ height: 30,
303
+ display: "inline-flex",
304
+ alignItems: "center",
305
+ justifyContent: "center",
306
+ border: "none",
307
+ borderRadius: 6,
308
+ background: "transparent",
309
+ cursor: "pointer",
310
+ color: "var(--cs-chrome-fg, #201f1e)",
311
+ padding: 0
312
+ };
313
+ var POPOVER_STYLE2 = {
314
+ position: "absolute",
315
+ top: "100%",
316
+ left: 0,
317
+ marginTop: 4,
318
+ zIndex: 1e3,
319
+ padding: 4,
320
+ minWidth: 168,
321
+ borderRadius: 8,
322
+ border: "1px solid var(--cs-chrome-border, #e6e9ee)",
323
+ background: "var(--cs-chrome-input-bg, #fff)",
324
+ boxShadow: "0 6px 20px rgba(0,0,0,0.18)"
325
+ };
326
+ var ITEM_STYLE = {
327
+ display: "flex",
328
+ alignItems: "center",
329
+ gap: 8,
330
+ width: "100%",
331
+ height: 28,
332
+ padding: "0 8px",
333
+ border: "none",
334
+ borderRadius: 6,
335
+ background: "transparent",
336
+ color: "var(--cs-chrome-fg, #201f1e)",
337
+ font: "inherit",
338
+ fontSize: 13,
339
+ cursor: "pointer",
340
+ textAlign: "left"
341
+ };
342
+ function BordersPicker({ api }) {
343
+ const [open, setOpen] = (0, import_react2.useState)(false);
344
+ const rootRef = (0, import_react2.useRef)(null);
345
+ (0, import_react2.useEffect)(() => {
346
+ if (!open) return;
347
+ const onPointerDown = (e) => {
348
+ if (rootRef.current && !rootRef.current.contains(e.target)) setOpen(false);
349
+ };
350
+ const onKeyDown = (e) => {
351
+ if (e.key === "Escape") setOpen(false);
352
+ };
353
+ document.addEventListener("pointerdown", onPointerDown, true);
354
+ document.addEventListener("keydown", onKeyDown);
355
+ return () => {
356
+ document.removeEventListener("pointerdown", onPointerDown, true);
357
+ document.removeEventListener("keydown", onKeyDown);
358
+ };
359
+ }, [open]);
360
+ const pick = (value) => {
361
+ void api?.executeCommand("sheet.command.set-border-position", { value });
362
+ setOpen(false);
363
+ };
364
+ const on = open;
365
+ const baseBg = on ? "var(--cs-chrome-active, #e6f3f7)" : "transparent";
366
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { ref: rootRef, style: ROW_STYLE2, "data-testid": "cs-borders", children: [
367
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
368
+ "button",
369
+ {
370
+ type: "button",
371
+ title: "Borders",
372
+ "aria-label": "Borders",
373
+ "aria-haspopup": "true",
374
+ "aria-expanded": on,
375
+ "data-testid": "cs-borders-button",
376
+ disabled: !api,
377
+ style: {
378
+ ...BTN_STYLE2,
379
+ background: baseBg,
380
+ color: on ? "var(--cs-chrome-active-fg, #0e7490)" : BTN_STYLE2.color
381
+ },
382
+ onMouseDown: (e) => {
383
+ e.preventDefault();
384
+ setOpen((v) => !v);
385
+ },
386
+ onMouseEnter: (e) => {
387
+ if (!on) e.currentTarget.style.background = "var(--cs-chrome-hover, rgba(0,0,0,0.06))";
388
+ },
389
+ onMouseLeave: (e) => {
390
+ e.currentTarget.style.background = baseBg;
391
+ },
392
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Icon, { name: "border_all", size: 20 })
393
+ }
394
+ ),
395
+ open && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: POPOVER_STYLE2, "data-testid": "cs-borders-popover", role: "menu", children: OPTIONS.map((o) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
396
+ "button",
397
+ {
398
+ type: "button",
399
+ role: "menuitem",
400
+ "data-border": o.value,
401
+ "data-testid": `cs-border-${o.value}`,
402
+ style: ITEM_STYLE,
403
+ onMouseEnter: (e) => {
404
+ e.currentTarget.style.background = "var(--cs-chrome-hover, rgba(0,0,0,0.06))";
405
+ },
406
+ onMouseLeave: (e) => {
407
+ e.currentTarget.style.background = "transparent";
408
+ },
409
+ onMouseDown: (e) => {
410
+ e.preventDefault();
411
+ pick(o.value);
412
+ },
413
+ children: [
414
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Icon, { name: o.icon, size: 18 }),
415
+ o.label
416
+ ]
417
+ },
418
+ o.value
419
+ )) })
420
+ ] });
421
+ }
422
+
423
+ // src/chrome/AutoSumPicker.tsx
424
+ var import_react3 = require("react");
425
+ var import_jsx_runtime4 = require("react/jsx-runtime");
426
+ var FUNCS = [
427
+ { fn: "SUM", label: "Sum" },
428
+ { fn: "AVERAGE", label: "Average" },
429
+ { fn: "COUNT", label: "Count numbers" },
430
+ { fn: "MAX", label: "Max" },
431
+ { fn: "MIN", label: "Min" }
432
+ ];
433
+ var ROW_STYLE3 = {
434
+ display: "inline-flex",
435
+ alignItems: "center",
436
+ position: "relative"
437
+ };
438
+ var BTN_STYLE3 = {
439
+ width: 30,
440
+ height: 30,
441
+ display: "inline-flex",
442
+ alignItems: "center",
443
+ justifyContent: "center",
444
+ border: "none",
445
+ borderRadius: 6,
446
+ background: "transparent",
447
+ cursor: "pointer",
448
+ color: "var(--cs-chrome-fg, #201f1e)",
449
+ padding: 0
450
+ };
451
+ var POPOVER_STYLE3 = {
452
+ position: "absolute",
453
+ top: "100%",
454
+ left: 0,
455
+ marginTop: 4,
456
+ zIndex: 1e3,
457
+ padding: 4,
458
+ minWidth: 160,
459
+ borderRadius: 8,
460
+ border: "1px solid var(--cs-chrome-border, #e6e9ee)",
461
+ background: "var(--cs-chrome-input-bg, #fff)",
462
+ boxShadow: "0 6px 20px rgba(0,0,0,0.18)"
463
+ };
464
+ var ITEM_STYLE2 = {
465
+ display: "block",
466
+ width: "100%",
467
+ textAlign: "left",
468
+ height: 28,
469
+ padding: "0 10px",
470
+ border: "none",
471
+ borderRadius: 6,
472
+ background: "transparent",
473
+ color: "var(--cs-chrome-fg, #201f1e)",
474
+ font: "inherit",
475
+ fontSize: 13,
476
+ cursor: "pointer"
477
+ };
478
+ function AutoSumPicker({ api }) {
479
+ const [open, setOpen] = (0, import_react3.useState)(false);
480
+ const rootRef = (0, import_react3.useRef)(null);
481
+ (0, import_react3.useEffect)(() => {
482
+ if (!open) return;
483
+ const onPointerDown = (e) => {
484
+ if (rootRef.current && !rootRef.current.contains(e.target)) setOpen(false);
485
+ };
486
+ const onKeyDown = (e) => {
487
+ if (e.key === "Escape") setOpen(false);
488
+ };
489
+ document.addEventListener("pointerdown", onPointerDown, true);
490
+ document.addEventListener("keydown", onKeyDown);
491
+ return () => {
492
+ document.removeEventListener("pointerdown", onPointerDown, true);
493
+ document.removeEventListener("keydown", onKeyDown);
494
+ };
495
+ }, [open]);
496
+ const apply = (fn) => {
497
+ setOpen(false);
498
+ const sheet = api?.univer.getActiveWorkbook()?.getActiveSheet();
499
+ const range = sheet?.getActiveRange?.();
500
+ if (!sheet || !range) return;
501
+ const multi = range.getWidth() * range.getHeight() > 1;
502
+ if (multi) {
503
+ const target = sheet.getRange(range.getRow() + range.getHeight(), range.getColumn());
504
+ target.setValue({ f: `=${fn}(${range.getA1Notation()})` });
505
+ target.activate?.();
506
+ } else {
507
+ sheet.getRange(range.getRow(), range.getColumn()).setValue({ f: `=${fn}()` });
508
+ }
509
+ };
510
+ const on = open;
511
+ const baseBg = on ? "var(--cs-chrome-active, #e6f3f7)" : "transparent";
512
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { ref: rootRef, style: ROW_STYLE3, "data-testid": "cs-autosum", children: [
513
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
514
+ "button",
515
+ {
516
+ type: "button",
517
+ title: "AutoSum",
518
+ "aria-label": "AutoSum",
519
+ "aria-haspopup": "true",
520
+ "aria-expanded": on,
521
+ "data-testid": "cs-autosum-button",
522
+ disabled: !api,
523
+ style: {
524
+ ...BTN_STYLE3,
525
+ background: baseBg,
526
+ color: on ? "var(--cs-chrome-active-fg, #0e7490)" : BTN_STYLE3.color
527
+ },
528
+ onMouseDown: (e) => {
529
+ e.preventDefault();
530
+ setOpen((v) => !v);
531
+ },
532
+ onMouseEnter: (e) => {
533
+ if (!on) e.currentTarget.style.background = "var(--cs-chrome-hover, rgba(0,0,0,0.06))";
534
+ },
535
+ onMouseLeave: (e) => {
536
+ e.currentTarget.style.background = baseBg;
537
+ },
538
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Icon, { name: "functions", size: 20 })
539
+ }
540
+ ),
541
+ open && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: POPOVER_STYLE3, "data-testid": "cs-autosum-popover", role: "menu", children: FUNCS.map((f) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
542
+ "button",
543
+ {
544
+ type: "button",
545
+ role: "menuitem",
546
+ "data-fn": f.fn,
547
+ "data-testid": `cs-autosum-${f.fn}`,
548
+ style: ITEM_STYLE2,
549
+ onMouseEnter: (e) => {
550
+ e.currentTarget.style.background = "var(--cs-chrome-hover, rgba(0,0,0,0.06))";
551
+ },
552
+ onMouseLeave: (e) => {
553
+ e.currentTarget.style.background = "transparent";
554
+ },
555
+ onMouseDown: (e) => {
556
+ e.preventDefault();
557
+ apply(f.fn);
558
+ },
559
+ children: f.label
560
+ },
561
+ f.fn
562
+ )) })
563
+ ] });
564
+ }
565
+
566
+ // src/chrome/Toolbar.tsx
567
+ var import_jsx_runtime5 = require("react/jsx-runtime");
568
+ var NO_STYLE = {
569
+ bold: false,
570
+ italic: false,
571
+ underline: false,
572
+ strike: false,
573
+ ht: 0,
574
+ vt: 0,
575
+ tb: 0,
576
+ ff: "",
577
+ fs: 0
578
+ };
579
+ function readActiveStyle(api) {
580
+ const sel = api.getSelection();
581
+ const sheet = api.univer.getActiveWorkbook()?.getActiveSheet();
582
+ if (!sel || !sheet) return NO_STYLE;
583
+ const range = sheet.getRange(sel.range.startRow, sel.range.startColumn);
584
+ const s = range.getCellStyleData?.() ?? null;
585
+ return {
586
+ bold: s?.bl === 1,
587
+ italic: s?.it === 1,
588
+ underline: s?.ul?.s === 1,
589
+ strike: s?.st?.s === 1,
590
+ ht: typeof s?.ht === "number" ? s.ht : 0,
591
+ vt: typeof s?.vt === "number" ? s.vt : 0,
592
+ tb: typeof s?.tb === "number" ? s.tb : 0,
593
+ ff: typeof s?.ff === "string" ? s.ff : "",
594
+ fs: typeof s?.fs === "number" ? s.fs : 0
595
+ };
596
+ }
597
+ var FONT_FAMILIES = ["Arial", "Calibri", "Times New Roman", "Courier New", "Georgia", "Verdana"];
598
+ var FONT_SIZES = [8, 9, 10, 11, 12, 14, 16, 18, 20, 24, 28, 36, 48, 72];
599
+ var GROUPS = [
600
+ [
601
+ { id: "undo", label: "Undo", command: "univer.command.undo", icon: "undo" },
602
+ { id: "redo", label: "Redo", command: "univer.command.redo", icon: "redo" }
603
+ ],
604
+ [
605
+ { id: "bold", label: "Bold", command: "sheet.command.set-range-bold", icon: "format_bold" },
606
+ {
607
+ id: "italic",
608
+ label: "Italic",
609
+ command: "sheet.command.set-range-italic",
610
+ icon: "format_italic"
611
+ },
612
+ {
613
+ id: "underline",
614
+ label: "Underline",
615
+ command: "sheet.command.set-range-underline",
616
+ icon: "format_underlined"
617
+ },
618
+ {
619
+ id: "strikethrough",
620
+ label: "Strikethrough",
621
+ command: "sheet.command.set-range-stroke",
622
+ icon: "format_strikethrough"
623
+ }
624
+ ],
625
+ [
626
+ {
627
+ id: "align-left",
628
+ label: "Align left",
629
+ command: "sheet.command.set-horizontal-text-align",
630
+ icon: "format_align_left",
631
+ params: { value: 1 }
632
+ },
633
+ {
634
+ id: "align-center",
635
+ label: "Align center",
636
+ command: "sheet.command.set-horizontal-text-align",
637
+ icon: "format_align_center",
638
+ params: { value: 2 }
639
+ },
640
+ {
641
+ id: "align-right",
642
+ label: "Align right",
643
+ command: "sheet.command.set-horizontal-text-align",
644
+ icon: "format_align_right",
645
+ params: { value: 3 }
646
+ }
647
+ ],
648
+ [
649
+ {
650
+ id: "align-top",
651
+ label: "Align top",
652
+ command: "sheet.command.set-vertical-text-align",
653
+ icon: "vertical_align_top",
654
+ params: { value: 1 }
655
+ },
656
+ {
657
+ id: "align-middle",
658
+ label: "Align middle",
659
+ command: "sheet.command.set-vertical-text-align",
660
+ icon: "vertical_align_center",
661
+ params: { value: 2 }
662
+ },
663
+ {
664
+ id: "align-bottom",
665
+ label: "Align bottom",
666
+ command: "sheet.command.set-vertical-text-align",
667
+ icon: "vertical_align_bottom",
668
+ params: { value: 3 }
669
+ },
670
+ {
671
+ id: "wrap-text",
672
+ label: "Wrap text",
673
+ command: "sheet.command.set-text-wrap",
674
+ icon: "wrap_text",
675
+ params: { value: 3 }
676
+ }
677
+ ],
678
+ [
679
+ {
680
+ id: "merge",
681
+ label: "Merge cells",
682
+ command: "sheet.command.add-worksheet-merge-all",
683
+ icon: "cell_merge"
684
+ },
685
+ {
686
+ id: "unmerge",
687
+ label: "Unmerge cells",
688
+ command: "sheet.command.remove-worksheet-merge",
689
+ icon: "splitscreen_vertical_add"
690
+ }
691
+ ],
692
+ [
693
+ {
694
+ id: "currency",
695
+ label: "Currency format",
696
+ command: "sheet.command.numfmt.set.currency",
697
+ icon: "attach_money"
698
+ },
699
+ {
700
+ id: "percent",
701
+ label: "Percent format",
702
+ command: "sheet.command.numfmt.set.percent",
703
+ icon: "percent"
704
+ },
705
+ {
706
+ id: "decimal-increase",
707
+ label: "Increase decimals",
708
+ command: "sheet.command.numfmt.add.decimal.command",
709
+ icon: "decimal_increase"
710
+ },
711
+ {
712
+ id: "decimal-decrease",
713
+ label: "Decrease decimals",
714
+ command: "sheet.command.numfmt.subtract.decimal.command",
715
+ icon: "decimal_decrease"
716
+ }
717
+ ],
718
+ [
719
+ {
720
+ id: "clear-format",
721
+ label: "Clear formatting",
722
+ command: "sheet.command.clear-selection-format",
723
+ icon: "format_clear"
724
+ }
725
+ ]
726
+ ];
727
+ var BAR_STYLE = {
728
+ display: "flex",
729
+ alignItems: "center",
730
+ gap: 2,
731
+ padding: "4px 8px",
732
+ borderBottom: "1px solid var(--cs-chrome-border, #e6e9ee)",
733
+ background: "var(--cs-chrome-bg, #eef1f5)",
734
+ flex: "0 0 auto",
735
+ userSelect: "none"
736
+ };
737
+ var BTN_STYLE4 = {
738
+ width: 30,
739
+ height: 30,
740
+ display: "inline-flex",
741
+ alignItems: "center",
742
+ justifyContent: "center",
743
+ border: "none",
744
+ borderRadius: 6,
745
+ background: "transparent",
746
+ cursor: "pointer",
747
+ color: "var(--cs-chrome-fg, #201f1e)",
748
+ padding: 0
749
+ };
750
+ var DIVIDER_STYLE = {
751
+ width: 1,
752
+ height: 18,
753
+ margin: "0 4px",
754
+ background: "var(--cs-chrome-border, #cdd3db)",
755
+ flex: "0 0 auto"
756
+ };
757
+ var SELECT_STYLE = {
758
+ height: 26,
759
+ border: "1px solid var(--cs-chrome-border, #cdd3db)",
760
+ borderRadius: 6,
761
+ background: "var(--cs-chrome-input-bg, #fff)",
762
+ color: "var(--cs-chrome-fg, #201f1e)",
763
+ font: "inherit",
764
+ fontSize: 13,
765
+ padding: "0 4px",
766
+ cursor: "pointer"
767
+ };
768
+ function isActive(id, s) {
769
+ switch (id) {
770
+ case "bold":
771
+ return s.bold;
772
+ case "italic":
773
+ return s.italic;
774
+ case "underline":
775
+ return s.underline;
776
+ case "strikethrough":
777
+ return s.strike;
778
+ case "align-left":
779
+ return s.ht === 1;
780
+ case "align-center":
781
+ return s.ht === 2;
782
+ case "align-right":
783
+ return s.ht === 3;
784
+ case "align-top":
785
+ return s.vt === 1;
786
+ case "align-middle":
787
+ return s.vt === 2;
788
+ case "align-bottom":
789
+ return s.vt === 3;
790
+ case "wrap-text":
791
+ return s.tb === 3;
792
+ default:
793
+ return false;
794
+ }
795
+ }
796
+ function Toolbar({ api }) {
797
+ const [active, setActive] = (0, import_react4.useState)(NO_STYLE);
798
+ (0, import_react4.useEffect)(() => {
799
+ ensureChromeFonts();
800
+ }, []);
801
+ (0, import_react4.useEffect)(() => {
802
+ if (!api) return;
803
+ const refresh = () => setActive(readActiveStyle(api));
804
+ refresh();
805
+ const injector = api.univer._injector;
806
+ const cmd = injector?.get(import_core.ICommandService);
807
+ const sub = cmd?.onCommandExecuted(() => refresh());
808
+ return () => sub?.dispose();
809
+ }, [api]);
810
+ const dispatch = (command, params) => void api?.executeCommand(command, params);
811
+ const familyValue = active.ff || "Arial";
812
+ const familyOptions = active.ff && !FONT_FAMILIES.includes(active.ff) ? [active.ff, ...FONT_FAMILIES] : FONT_FAMILIES;
813
+ const sizeValue = active.fs || 11;
814
+ const sizeOptions = active.fs && !FONT_SIZES.includes(active.fs) ? [active.fs, ...FONT_SIZES] : FONT_SIZES;
815
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: BAR_STYLE, "data-testid": "casual-sheets-toolbar", role: "toolbar", "aria-label": "Editor", children: [
816
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
817
+ "select",
818
+ {
819
+ "aria-label": "Font family",
820
+ "data-testid": "cs-font-family",
821
+ style: { ...SELECT_STYLE, width: 116 },
822
+ value: familyValue,
823
+ onChange: (e) => dispatch("sheet.command.set-range-font-family", { value: e.target.value }),
824
+ children: familyOptions.map((f) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: f, children: f }, f))
825
+ }
826
+ ),
827
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
828
+ "select",
829
+ {
830
+ "aria-label": "Font size",
831
+ "data-testid": "cs-font-size",
832
+ style: { ...SELECT_STYLE, width: 56, marginLeft: 4 },
833
+ value: sizeValue,
834
+ onChange: (e) => dispatch("sheet.command.set-range-fontsize", { value: Number(e.target.value) }),
835
+ children: sizeOptions.map((s) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: s, children: s }, s))
836
+ }
837
+ ),
838
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: DIVIDER_STYLE, "aria-hidden": true }),
839
+ GROUPS.map((group, gi) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { style: { display: "inline-flex", alignItems: "center" }, children: [
840
+ gi > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: DIVIDER_STYLE, "aria-hidden": true }),
841
+ group.map((a) => {
842
+ const on = isActive(a.id, active);
843
+ const baseBg = on ? "var(--cs-chrome-active, #e6f3f7)" : "transparent";
844
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
845
+ "button",
846
+ {
847
+ type: "button",
848
+ title: a.label,
849
+ "aria-label": a.label,
850
+ "aria-pressed": on,
851
+ "data-action": a.id,
852
+ "data-active": on ? "true" : void 0,
853
+ style: {
854
+ ...BTN_STYLE4,
855
+ background: baseBg,
856
+ color: on ? "var(--cs-chrome-active-fg, #0e7490)" : BTN_STYLE4.color
857
+ },
858
+ onMouseDown: (e) => {
859
+ e.preventDefault();
860
+ dispatch(a.command, a.params);
861
+ },
862
+ onMouseEnter: (e) => {
863
+ if (!on)
864
+ e.currentTarget.style.background = "var(--cs-chrome-hover, rgba(0,0,0,0.06))";
865
+ },
866
+ onMouseLeave: (e) => {
867
+ e.currentTarget.style.background = baseBg;
868
+ },
869
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Icon, { name: a.icon, size: 20 })
870
+ },
871
+ a.id
872
+ );
873
+ })
874
+ ] }, gi)),
875
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: DIVIDER_STYLE, "aria-hidden": true }),
876
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ColorPicker, { api }),
877
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(BordersPicker, { api }),
878
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: DIVIDER_STYLE, "aria-hidden": true }),
879
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(AutoSumPicker, { api })
880
+ ] });
881
+ }
882
+
883
+ // src/chrome/FormulaBar.tsx
884
+ var import_react6 = require("react");
885
+ var import_core3 = require("@univerjs/core");
886
+
887
+ // src/chrome/NameBox.tsx
888
+ var import_react5 = require("react");
889
+ var import_core2 = require("@univerjs/core");
890
+ var import_jsx_runtime6 = require("react/jsx-runtime");
891
+ function readDefinedNames(api) {
892
+ if (!api) return [];
893
+ try {
894
+ const wb = api.univer.getActiveWorkbook();
895
+ const names = wb?.getDefinedNames?.();
896
+ if (!names) return [];
897
+ return names.map((dn) => ({
898
+ name: dn.getName?.() ?? "",
899
+ ref: dn.getFormulaOrRefString?.() ?? ""
900
+ })).filter((e) => e.name !== "");
901
+ } catch {
902
+ return [];
903
+ }
904
+ }
905
+ function colToLetters(col) {
906
+ let s = "";
907
+ let n = col;
908
+ do {
909
+ s = String.fromCharCode(65 + n % 26) + s;
910
+ n = Math.floor(n / 26) - 1;
911
+ } while (n >= 0);
912
+ return s;
913
+ }
914
+ function readActiveRef(api) {
915
+ const sel = api.getSelection();
916
+ if (!sel) return "";
917
+ const { startRow, startColumn } = sel.range;
918
+ return colToLetters(startColumn) + (startRow + 1);
919
+ }
920
+ var BOX_STYLE = {
921
+ flex: "0 0 auto",
922
+ width: 80,
923
+ height: 24,
924
+ padding: "0 8px",
925
+ border: "1px solid var(--cs-chrome-border, #e6e9ee)",
926
+ borderRadius: 4,
927
+ background: "var(--cs-chrome-input-bg, #fff)",
928
+ color: "var(--cs-chrome-fg, #201f1e)",
929
+ font: "inherit",
930
+ fontSize: 13,
931
+ textAlign: "center",
932
+ boxSizing: "border-box"
933
+ };
934
+ var ROOT_STYLE = {
935
+ flex: "0 0 auto",
936
+ display: "inline-flex",
937
+ alignItems: "center",
938
+ position: "relative"
939
+ };
940
+ var DROPDOWN_BTN_STYLE = {
941
+ flex: "0 0 auto",
942
+ width: 20,
943
+ height: 24,
944
+ marginLeft: 2,
945
+ display: "inline-flex",
946
+ alignItems: "center",
947
+ justifyContent: "center",
948
+ border: "none",
949
+ borderRadius: 4,
950
+ background: "transparent",
951
+ color: "var(--cs-chrome-fg, #201f1e)",
952
+ cursor: "pointer",
953
+ padding: 0
954
+ };
955
+ var MENU_STYLE = {
956
+ position: "absolute",
957
+ top: "100%",
958
+ left: 0,
959
+ marginTop: 4,
960
+ zIndex: 1e3,
961
+ minWidth: 160,
962
+ maxHeight: 280,
963
+ overflowY: "auto",
964
+ padding: 4,
965
+ borderRadius: 8,
966
+ border: "1px solid var(--cs-chrome-border, #e6e9ee)",
967
+ background: "var(--cs-chrome-input-bg, #fff)",
968
+ boxShadow: "0 6px 20px rgba(0,0,0,0.18)"
969
+ };
970
+ var MENU_ITEM_STYLE = {
971
+ display: "flex",
972
+ alignItems: "baseline",
973
+ justifyContent: "space-between",
974
+ gap: 12,
975
+ width: "100%",
976
+ height: 28,
977
+ padding: "0 8px",
978
+ border: "none",
979
+ borderRadius: 6,
980
+ background: "transparent",
981
+ color: "var(--cs-chrome-fg, #201f1e)",
982
+ font: "inherit",
983
+ fontSize: 13,
984
+ textAlign: "left",
985
+ cursor: "pointer",
986
+ boxSizing: "border-box"
987
+ };
988
+ var MENU_EMPTY_STYLE = {
989
+ padding: "6px 8px",
990
+ color: "var(--cs-chrome-muted, #8a8886)",
991
+ fontSize: 12,
992
+ whiteSpace: "nowrap"
993
+ };
994
+ var ITEM_REF_STYLE = {
995
+ color: "var(--cs-chrome-muted, #8a8886)",
996
+ fontSize: 11,
997
+ flex: "0 0 auto"
998
+ };
999
+ function NameBox({ api }) {
1000
+ const [ref, setRef] = (0, import_react5.useState)("");
1001
+ const [draft, setDraft] = (0, import_react5.useState)(null);
1002
+ const draftRef = (0, import_react5.useRef)(null);
1003
+ draftRef.current = draft;
1004
+ const inputRef = (0, import_react5.useRef)(null);
1005
+ const [open, setOpen] = (0, import_react5.useState)(false);
1006
+ const [names, setNames] = (0, import_react5.useState)([]);
1007
+ const rootRef = (0, import_react5.useRef)(null);
1008
+ (0, import_react5.useEffect)(() => {
1009
+ if (!api) return;
1010
+ const refresh = () => {
1011
+ if (draftRef.current !== null) return;
1012
+ setRef(readActiveRef(api));
1013
+ };
1014
+ refresh();
1015
+ const injector = api.univer._injector;
1016
+ const cmd = injector?.get(import_core2.ICommandService);
1017
+ const sub = cmd?.onCommandExecuted(() => refresh());
1018
+ return () => sub?.dispose();
1019
+ }, [api]);
1020
+ (0, import_react5.useEffect)(() => {
1021
+ if (!open) return;
1022
+ const onPointerDown = (e) => {
1023
+ if (rootRef.current && !rootRef.current.contains(e.target)) setOpen(false);
1024
+ };
1025
+ const onKeyDown2 = (e) => {
1026
+ if (e.key === "Escape") setOpen(false);
1027
+ };
1028
+ document.addEventListener("pointerdown", onPointerDown, true);
1029
+ document.addEventListener("keydown", onKeyDown2);
1030
+ return () => {
1031
+ document.removeEventListener("pointerdown", onPointerDown, true);
1032
+ document.removeEventListener("keydown", onKeyDown2);
1033
+ };
1034
+ }, [open]);
1035
+ const navigate = (text) => {
1036
+ const t = text.trim();
1037
+ setDraft(null);
1038
+ if (!api || t === "") {
1039
+ setRef(readActiveRef(api ?? {}));
1040
+ return;
1041
+ }
1042
+ const sheet = api.univer.getActiveWorkbook()?.getActiveSheet();
1043
+ if (!sheet) return;
1044
+ try {
1045
+ const range = sheet.getRange(
1046
+ t
1047
+ );
1048
+ range.activate();
1049
+ } catch {
1050
+ setRef(readActiveRef(api));
1051
+ }
1052
+ };
1053
+ const toggleDropdown = () => {
1054
+ setOpen((cur) => {
1055
+ const next = !cur;
1056
+ if (next) setNames(readDefinedNames(api));
1057
+ return next;
1058
+ });
1059
+ };
1060
+ const navigateName = (entry) => {
1061
+ setOpen(false);
1062
+ const raw = entry.ref.trim();
1063
+ if (raw === "") return;
1064
+ const afterSheet = raw.includes("!") ? raw.slice(raw.lastIndexOf("!") + 1) : raw;
1065
+ const a1 = afterSheet.replace(/\$/g, "");
1066
+ navigate(a1);
1067
+ };
1068
+ const onKeyDown = (e) => {
1069
+ if (e.key === "Enter") {
1070
+ e.preventDefault();
1071
+ navigate(e.target.value);
1072
+ e.target.blur();
1073
+ } else if (e.key === "Escape") {
1074
+ e.preventDefault();
1075
+ setDraft(null);
1076
+ setRef(api ? readActiveRef(api) : "");
1077
+ e.target.blur();
1078
+ }
1079
+ };
1080
+ const shown = draft ?? ref;
1081
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { ref: rootRef, style: ROOT_STYLE, children: [
1082
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1083
+ "input",
1084
+ {
1085
+ ref: inputRef,
1086
+ type: "text",
1087
+ "aria-label": "Name box",
1088
+ title: "Name box",
1089
+ "data-testid": "cs-namebox-input",
1090
+ style: BOX_STYLE,
1091
+ value: shown,
1092
+ disabled: !api,
1093
+ onChange: (e) => setDraft(e.target.value),
1094
+ onKeyDown,
1095
+ onFocus: (e) => e.currentTarget.select(),
1096
+ onBlur: () => {
1097
+ if (draftRef.current !== null) {
1098
+ setDraft(null);
1099
+ setRef(api ? readActiveRef(api) : "");
1100
+ }
1101
+ }
1102
+ }
1103
+ ),
1104
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1105
+ "button",
1106
+ {
1107
+ type: "button",
1108
+ title: "Defined names",
1109
+ "aria-label": "Defined names",
1110
+ "aria-haspopup": "true",
1111
+ "aria-expanded": open,
1112
+ "data-testid": "cs-namebox-dropdown",
1113
+ style: {
1114
+ ...DROPDOWN_BTN_STYLE,
1115
+ background: open ? "var(--cs-chrome-active, #e6f3f7)" : "transparent",
1116
+ color: open ? "var(--cs-chrome-active-fg, #0e7490)" : DROPDOWN_BTN_STYLE.color
1117
+ },
1118
+ disabled: !api,
1119
+ onMouseDown: (e) => {
1120
+ e.preventDefault();
1121
+ toggleDropdown();
1122
+ },
1123
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Icon, { name: "expand_more", size: 18 })
1124
+ }
1125
+ ),
1126
+ open && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: MENU_STYLE, role: "menu", "data-testid": "cs-namebox-menu", children: names.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: MENU_EMPTY_STYLE, children: "No names" }) : names.map((entry, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1127
+ "button",
1128
+ {
1129
+ type: "button",
1130
+ role: "menuitem",
1131
+ title: entry.ref || entry.name,
1132
+ "data-testid": `cs-namebox-name-${i}`,
1133
+ style: MENU_ITEM_STYLE,
1134
+ onMouseDown: (e) => {
1135
+ e.preventDefault();
1136
+ navigateName(entry);
1137
+ },
1138
+ onMouseEnter: (e) => {
1139
+ e.currentTarget.style.background = "var(--cs-chrome-hover, rgba(0,0,0,0.06))";
1140
+ },
1141
+ onMouseLeave: (e) => {
1142
+ e.currentTarget.style.background = "transparent";
1143
+ },
1144
+ children: [
1145
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: entry.name }),
1146
+ entry.ref && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: ITEM_REF_STYLE, children: entry.ref })
1147
+ ]
1148
+ },
1149
+ entry.name
1150
+ )) })
1151
+ ] });
1152
+ }
1153
+
1154
+ // src/chrome/FormulaBar.tsx
1155
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1156
+ var FUNCTIONS = [
1157
+ "ABS",
1158
+ "AND",
1159
+ "AVERAGE",
1160
+ "AVERAGEIF",
1161
+ "AVERAGEIFS",
1162
+ "CEILING",
1163
+ "CHOOSE",
1164
+ "COLUMN",
1165
+ "COLUMNS",
1166
+ "CONCAT",
1167
+ "CONCATENATE",
1168
+ "COUNT",
1169
+ "COUNTA",
1170
+ "COUNTBLANK",
1171
+ "COUNTIF",
1172
+ "COUNTIFS",
1173
+ "DATE",
1174
+ "DATEDIF",
1175
+ "DAY",
1176
+ "EDATE",
1177
+ "EOMONTH",
1178
+ "EXACT",
1179
+ "FILTER",
1180
+ "FIND",
1181
+ "FLOOR",
1182
+ "HLOOKUP",
1183
+ "HOUR",
1184
+ "IF",
1185
+ "IFERROR",
1186
+ "IFNA",
1187
+ "IFS",
1188
+ "INDEX",
1189
+ "INDIRECT",
1190
+ "INT",
1191
+ "ISBLANK",
1192
+ "ISERROR",
1193
+ "ISNA",
1194
+ "ISNUMBER",
1195
+ "ISTEXT",
1196
+ "LEFT",
1197
+ "LEN",
1198
+ "LOOKUP",
1199
+ "LOWER",
1200
+ "MATCH",
1201
+ "MAX",
1202
+ "MAXIFS",
1203
+ "MEDIAN",
1204
+ "MID",
1205
+ "MIN",
1206
+ "MINIFS",
1207
+ "MINUTE",
1208
+ "MOD",
1209
+ "MONTH",
1210
+ "NETWORKDAYS",
1211
+ "NOT",
1212
+ "NOW",
1213
+ "OFFSET",
1214
+ "OR",
1215
+ "POWER",
1216
+ "PRODUCT",
1217
+ "PROPER",
1218
+ "RAND",
1219
+ "RANDBETWEEN",
1220
+ "RANK",
1221
+ "REPLACE",
1222
+ "REPT",
1223
+ "RIGHT",
1224
+ "ROUND",
1225
+ "ROUNDDOWN",
1226
+ "ROUNDUP",
1227
+ "ROW",
1228
+ "ROWS",
1229
+ "SEARCH",
1230
+ "SECOND",
1231
+ "SORT",
1232
+ "SQRT",
1233
+ "SUBSTITUTE",
1234
+ "SUM",
1235
+ "SUMIF",
1236
+ "SUMIFS",
1237
+ "SUMPRODUCT",
1238
+ "SWITCH",
1239
+ "TEXT",
1240
+ "TEXTJOIN",
1241
+ "TODAY",
1242
+ "TRANSPOSE",
1243
+ "TRIM",
1244
+ "UNIQUE",
1245
+ "UPPER",
1246
+ "VALUE",
1247
+ "VLOOKUP",
1248
+ "WEEKDAY",
1249
+ "WORKDAY",
1250
+ "XLOOKUP",
1251
+ "XMATCH",
1252
+ "YEAR"
1253
+ ];
1254
+ var TOKEN_RE = /([A-Za-z][A-Za-z0-9._]*)$/;
1255
+ function suggestFor(draft) {
1256
+ if (!draft || !draft.startsWith("=")) return [];
1257
+ const m = TOKEN_RE.exec(draft);
1258
+ if (!m) return [];
1259
+ const prefix = m[1].toUpperCase();
1260
+ return FUNCTIONS.filter((f) => f.startsWith(prefix) && f !== prefix).slice(0, 8);
1261
+ }
1262
+ function colToLetters2(col) {
1263
+ let s = "";
1264
+ let n = col;
1265
+ do {
1266
+ s = String.fromCharCode(65 + n % 26) + s;
1267
+ n = Math.floor(n / 26) - 1;
1268
+ } while (n >= 0);
1269
+ return s;
1270
+ }
1271
+ function readActiveCell(api) {
1272
+ const sel = api.getSelection();
1273
+ const sheet = api.univer.getActiveWorkbook()?.getActiveSheet();
1274
+ if (!sel || !sheet) return null;
1275
+ const { startRow, startColumn } = sel.range;
1276
+ const range = sheet.getRange(startRow, startColumn);
1277
+ const formula = range.getFormula?.() || "";
1278
+ const raw = range.getValue?.();
1279
+ return {
1280
+ a1: colToLetters2(startColumn) + (startRow + 1),
1281
+ text: formula || (raw == null ? "" : String(raw))
1282
+ };
1283
+ }
1284
+ var BAR_STYLE2 = {
1285
+ display: "flex",
1286
+ alignItems: "center",
1287
+ gap: 6,
1288
+ padding: "3px 6px",
1289
+ borderBottom: "1px solid var(--cs-chrome-border, rgba(0,0,0,0.12))",
1290
+ background: "var(--cs-chrome-bg, #f8f9fa)",
1291
+ flex: "0 0 auto",
1292
+ font: "inherit",
1293
+ fontSize: 13
1294
+ };
1295
+ var FX_STYLE = {
1296
+ flex: "0 0 auto",
1297
+ fontStyle: "italic",
1298
+ color: "var(--cs-chrome-muted, #6b7280)"
1299
+ };
1300
+ var INPUT_STYLE = {
1301
+ width: "100%",
1302
+ height: 24,
1303
+ padding: "0 8px",
1304
+ border: "1px solid var(--cs-chrome-border, rgba(0,0,0,0.18))",
1305
+ borderRadius: 4,
1306
+ background: "var(--cs-chrome-input-bg, #fff)",
1307
+ color: "var(--cs-chrome-fg, #1f2329)",
1308
+ font: "inherit",
1309
+ fontSize: 13,
1310
+ boxSizing: "border-box"
1311
+ };
1312
+ var INPUT_WRAP_STYLE = { position: "relative", flex: "1 1 auto", minWidth: 0 };
1313
+ var AC_STYLE = {
1314
+ position: "absolute",
1315
+ top: "100%",
1316
+ left: 0,
1317
+ marginTop: 2,
1318
+ minWidth: 200,
1319
+ maxHeight: 240,
1320
+ overflowY: "auto",
1321
+ zIndex: 1e3,
1322
+ padding: 4,
1323
+ border: "1px solid var(--cs-chrome-border, #e6e9ee)",
1324
+ borderRadius: 8,
1325
+ background: "var(--cs-chrome-input-bg, #fff)",
1326
+ boxShadow: "0 6px 20px rgba(0,0,0,0.16)"
1327
+ };
1328
+ var AC_ITEM_STYLE = {
1329
+ display: "block",
1330
+ width: "100%",
1331
+ textAlign: "left",
1332
+ height: 26,
1333
+ padding: "0 8px",
1334
+ border: "none",
1335
+ borderRadius: 6,
1336
+ background: "transparent",
1337
+ color: "var(--cs-chrome-fg, #201f1e)",
1338
+ font: "inherit",
1339
+ fontSize: 13,
1340
+ cursor: "pointer"
1341
+ };
1342
+ function FormulaBar({ api }) {
1343
+ const [cell, setCell] = (0, import_react6.useState)(null);
1344
+ const [draft, setDraft] = (0, import_react6.useState)(null);
1345
+ const draftRef = (0, import_react6.useRef)(null);
1346
+ draftRef.current = draft;
1347
+ const [acIdx, setAcIdx] = (0, import_react6.useState)(0);
1348
+ const [acDismissed, setAcDismissed] = (0, import_react6.useState)(false);
1349
+ (0, import_react6.useEffect)(() => {
1350
+ if (!api) return;
1351
+ const refresh = () => {
1352
+ if (draftRef.current !== null) return;
1353
+ setCell(readActiveCell(api));
1354
+ };
1355
+ refresh();
1356
+ const injector = api.univer._injector;
1357
+ const cmd = injector?.get(import_core3.ICommandService);
1358
+ const sub = cmd?.onCommandExecuted(() => refresh());
1359
+ return () => sub?.dispose();
1360
+ }, [api]);
1361
+ const suggestions = (0, import_react6.useMemo)(() => suggestFor(draft), [draft]);
1362
+ const acOpen = !acDismissed && suggestions.length > 0;
1363
+ const commit = (text) => {
1364
+ setDraft(null);
1365
+ setAcDismissed(false);
1366
+ if (!api) return;
1367
+ const sel = api.getSelection();
1368
+ const sheet = api.univer.getActiveWorkbook()?.getActiveSheet();
1369
+ if (!sel || !sheet) return;
1370
+ const range = sheet.getRange(sel.range.startRow, sel.range.startColumn);
1371
+ const t = text.trim();
1372
+ if (t.startsWith("=")) range.setValue({ f: t });
1373
+ else if (t !== "" && !Number.isNaN(Number(t))) range.setValue(Number(t));
1374
+ else range.setValue(t);
1375
+ };
1376
+ const onType = (v) => {
1377
+ setDraft(v);
1378
+ setAcIdx(0);
1379
+ setAcDismissed(false);
1380
+ };
1381
+ const complete = (fn) => {
1382
+ const base = draft ?? "";
1383
+ setDraft(base.replace(TOKEN_RE, `${fn}(`));
1384
+ setAcIdx(0);
1385
+ };
1386
+ const onKeyDown = (e) => {
1387
+ if (acOpen) {
1388
+ if (e.key === "ArrowDown") {
1389
+ e.preventDefault();
1390
+ setAcIdx((i) => Math.min(i + 1, suggestions.length - 1));
1391
+ return;
1392
+ }
1393
+ if (e.key === "ArrowUp") {
1394
+ e.preventDefault();
1395
+ setAcIdx((i) => Math.max(i - 1, 0));
1396
+ return;
1397
+ }
1398
+ if (e.key === "Enter" || e.key === "Tab") {
1399
+ e.preventDefault();
1400
+ complete(suggestions[acIdx] ?? suggestions[0]);
1401
+ return;
1402
+ }
1403
+ if (e.key === "Escape") {
1404
+ e.preventDefault();
1405
+ setAcDismissed(true);
1406
+ return;
1407
+ }
1408
+ }
1409
+ if (e.key === "Enter") {
1410
+ e.preventDefault();
1411
+ commit(e.target.value);
1412
+ } else if (e.key === "Escape") {
1413
+ e.preventDefault();
1414
+ setDraft(null);
1415
+ e.target.blur();
1416
+ }
1417
+ };
1418
+ const shown = draft ?? cell?.text ?? "";
1419
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: BAR_STYLE2, "data-testid": "casual-sheets-formula-bar", children: [
1420
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(NameBox, { api }),
1421
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: FX_STYLE, "aria-hidden": true, children: "fx" }),
1422
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: INPUT_WRAP_STYLE, children: [
1423
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1424
+ "input",
1425
+ {
1426
+ type: "text",
1427
+ "aria-label": "Formula bar",
1428
+ "data-testid": "casual-sheets-formula-input",
1429
+ style: INPUT_STYLE,
1430
+ value: shown,
1431
+ onChange: (e) => onType(e.target.value),
1432
+ onKeyDown,
1433
+ onBlur: (e) => {
1434
+ if (draftRef.current !== null) commit(e.target.value);
1435
+ },
1436
+ disabled: !api
1437
+ }
1438
+ ),
1439
+ acOpen && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: AC_STYLE, "data-testid": "cs-formula-suggestions", role: "listbox", children: suggestions.map((fn, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1440
+ "button",
1441
+ {
1442
+ type: "button",
1443
+ role: "option",
1444
+ "aria-selected": i === acIdx,
1445
+ "data-testid": `cs-formula-suggestion-${fn}`,
1446
+ style: {
1447
+ ...AC_ITEM_STYLE,
1448
+ background: i === acIdx ? "var(--cs-chrome-active, #e6f3f7)" : "transparent",
1449
+ color: i === acIdx ? "var(--cs-chrome-active-fg, #0e7490)" : AC_ITEM_STYLE.color
1450
+ },
1451
+ onMouseDown: (e) => {
1452
+ e.preventDefault();
1453
+ complete(fn);
1454
+ },
1455
+ onMouseEnter: () => setAcIdx(i),
1456
+ children: fn
1457
+ },
1458
+ fn
1459
+ )) })
1460
+ ] })
1461
+ ] });
1462
+ }
1463
+
1464
+ // src/chrome/StatusBar.tsx
1465
+ var import_react7 = require("react");
1466
+ var import_core4 = require("@univerjs/core");
1467
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1468
+ function readStats(api) {
1469
+ const sel = api.getSelection();
1470
+ const sheet = api.univer.getActiveWorkbook()?.getActiveSheet();
1471
+ if (!sel || !sheet) return null;
1472
+ const { startRow, startColumn, endRow, endColumn } = sel.range;
1473
+ if (startRow === endRow && startColumn === endColumn) return null;
1474
+ const values = sheet.getRange(sel.range).getValues?.();
1475
+ if (!values) return null;
1476
+ let count = 0;
1477
+ let numCount = 0;
1478
+ let sum = 0;
1479
+ let min = Infinity;
1480
+ let max = -Infinity;
1481
+ for (const row of values) {
1482
+ for (const v of row) {
1483
+ if (v == null || v === "") continue;
1484
+ count += 1;
1485
+ if (typeof v === "number" && Number.isFinite(v)) {
1486
+ numCount += 1;
1487
+ sum += v;
1488
+ if (v < min) min = v;
1489
+ if (v > max) max = v;
1490
+ }
1491
+ }
1492
+ }
1493
+ if (count === 0) return null;
1494
+ return { count, numCount, sum, avg: numCount ? sum / numCount : 0, min, max };
1495
+ }
1496
+ function readZoom(api) {
1497
+ const sheet = api.univer.getActiveWorkbook()?.getActiveSheet();
1498
+ const ws = sheet?.getSheet?.();
1499
+ const z = ws?.getConfig?.().zoomRatio;
1500
+ return typeof z === "number" && z > 0 ? z : 1;
1501
+ }
1502
+ function fmt(n) {
1503
+ return Number(n.toFixed(10)).toLocaleString();
1504
+ }
1505
+ var BAR_STYLE3 = {
1506
+ display: "flex",
1507
+ alignItems: "center",
1508
+ justifyContent: "flex-end",
1509
+ gap: 16,
1510
+ height: 24,
1511
+ padding: "0 12px",
1512
+ borderTop: "1px solid var(--cs-chrome-border, rgba(0,0,0,0.12))",
1513
+ background: "var(--cs-chrome-bg, #f8f9fa)",
1514
+ color: "var(--cs-chrome-muted, #4b5563)",
1515
+ flex: "0 0 auto",
1516
+ font: "inherit",
1517
+ fontSize: 12,
1518
+ userSelect: "none"
1519
+ };
1520
+ var ZOOM_GROUP_STYLE = {
1521
+ display: "flex",
1522
+ alignItems: "center",
1523
+ gap: 4
1524
+ };
1525
+ var ZOOM_BTN_STYLE = {
1526
+ width: 20,
1527
+ height: 18,
1528
+ border: "none",
1529
+ borderRadius: 4,
1530
+ background: "transparent",
1531
+ color: "var(--cs-chrome-fg, #201f1e)",
1532
+ cursor: "pointer",
1533
+ fontSize: 14,
1534
+ lineHeight: 1,
1535
+ padding: 0
1536
+ };
1537
+ var ZOOM_LEVEL_STYLE = {
1538
+ minWidth: 40,
1539
+ height: 18,
1540
+ border: "none",
1541
+ borderRadius: 4,
1542
+ background: "transparent",
1543
+ color: "var(--cs-chrome-muted, #4b5563)",
1544
+ cursor: "pointer",
1545
+ font: "inherit",
1546
+ fontSize: 12,
1547
+ textAlign: "center"
1548
+ };
1549
+ function StatusBar({ api }) {
1550
+ const [stats, setStats] = (0, import_react7.useState)(null);
1551
+ const [zoom, setZoom] = (0, import_react7.useState)(1);
1552
+ (0, import_react7.useEffect)(() => {
1553
+ if (!api) return;
1554
+ const refresh = () => {
1555
+ setStats(readStats(api));
1556
+ setZoom(readZoom(api));
1557
+ };
1558
+ refresh();
1559
+ const injector = api.univer._injector;
1560
+ const cmd = injector?.get(import_core4.ICommandService);
1561
+ const sub = cmd?.onCommandExecuted(() => refresh());
1562
+ return () => sub?.dispose();
1563
+ }, [api]);
1564
+ const setZoomRatio = (ratio) => {
1565
+ const wb = api?.univer.getActiveWorkbook();
1566
+ const sheet = wb?.getActiveSheet();
1567
+ if (!wb || !sheet) return;
1568
+ const clamped = Math.min(4, Math.max(0.1, ratio));
1569
+ void api?.executeCommand("sheet.operation.set-zoom-ratio", {
1570
+ unitId: wb.getId(),
1571
+ subUnitId: sheet.getSheetId(),
1572
+ zoomRatio: clamped
1573
+ });
1574
+ setZoom(clamped);
1575
+ };
1576
+ const zoomBy = (delta) => setZoomRatio(Math.round((zoom + delta) * 100) / 100);
1577
+ const resetZoom = () => setZoomRatio(1);
1578
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: BAR_STYLE3, "data-testid": "casual-sheets-status-bar", children: [
1579
+ stats && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1580
+ stats.numCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { "data-stat": "average", children: [
1581
+ "Average: ",
1582
+ fmt(stats.avg)
1583
+ ] }),
1584
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { "data-stat": "count", children: [
1585
+ "Count: ",
1586
+ stats.count
1587
+ ] }),
1588
+ stats.numCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1589
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { "data-stat": "num-count", children: [
1590
+ "Numerical Count: ",
1591
+ stats.numCount
1592
+ ] }),
1593
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { "data-stat": "min", children: [
1594
+ "Min: ",
1595
+ fmt(stats.min)
1596
+ ] }),
1597
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { "data-stat": "max", children: [
1598
+ "Max: ",
1599
+ fmt(stats.max)
1600
+ ] }),
1601
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { "data-stat": "sum", children: [
1602
+ "Sum: ",
1603
+ fmt(stats.sum)
1604
+ ] })
1605
+ ] })
1606
+ ] }),
1607
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { style: ZOOM_GROUP_STYLE, "data-testid": "cs-zoom", children: [
1608
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1609
+ "button",
1610
+ {
1611
+ type: "button",
1612
+ style: ZOOM_BTN_STYLE,
1613
+ "aria-label": "Zoom out",
1614
+ title: "Zoom out",
1615
+ "data-testid": "cs-zoom-out",
1616
+ disabled: !api,
1617
+ onMouseDown: (e) => {
1618
+ e.preventDefault();
1619
+ zoomBy(-0.1);
1620
+ },
1621
+ children: "\u2212"
1622
+ }
1623
+ ),
1624
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1625
+ "button",
1626
+ {
1627
+ type: "button",
1628
+ style: ZOOM_LEVEL_STYLE,
1629
+ "aria-label": "Reset zoom to 100%",
1630
+ title: "Reset zoom to 100%",
1631
+ "data-testid": "cs-zoom-level",
1632
+ disabled: !api,
1633
+ onMouseDown: (e) => {
1634
+ e.preventDefault();
1635
+ resetZoom();
1636
+ },
1637
+ children: [
1638
+ Math.round(zoom * 100),
1639
+ "%"
1640
+ ]
1641
+ }
1642
+ ),
1643
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1644
+ "button",
1645
+ {
1646
+ type: "button",
1647
+ style: ZOOM_BTN_STYLE,
1648
+ "aria-label": "Zoom in",
1649
+ title: "Zoom in",
1650
+ "data-testid": "cs-zoom-in",
1651
+ disabled: !api,
1652
+ onMouseDown: (e) => {
1653
+ e.preventDefault();
1654
+ zoomBy(0.1);
1655
+ },
1656
+ children: "+"
1657
+ }
1658
+ )
1659
+ ] })
1660
+ ] });
1661
+ }
1662
+
1663
+ // src/chrome/MenuBar.tsx
1664
+ var import_react8 = require("react");
1665
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1666
+ var MENUS = [
1667
+ {
1668
+ id: "edit",
1669
+ label: "Edit",
1670
+ items: [
1671
+ { id: "undo", label: "Undo", command: "univer.command.undo", icon: "undo" },
1672
+ { id: "redo", label: "Redo", command: "univer.command.redo", icon: "redo" },
1673
+ { id: "cut", label: "Cut", command: "univer.command.cut", icon: "content_cut" },
1674
+ { id: "copy", label: "Copy", command: "univer.command.copy", icon: "content_copy" },
1675
+ { id: "paste", label: "Paste", command: "univer.command.paste", icon: "content_paste" }
1676
+ ]
1677
+ },
1678
+ {
1679
+ id: "insert",
1680
+ label: "Insert",
1681
+ items: [
1682
+ {
1683
+ id: "insert-row",
1684
+ label: "Insert row above",
1685
+ command: "sheet.command.insert-row-before",
1686
+ icon: "add_row_above"
1687
+ },
1688
+ {
1689
+ id: "insert-col",
1690
+ label: "Insert column left",
1691
+ command: "sheet.command.insert-col-before",
1692
+ icon: "add_column_left"
1693
+ },
1694
+ {
1695
+ id: "delete-row",
1696
+ label: "Delete row",
1697
+ command: "sheet.command.remove-row",
1698
+ icon: "delete"
1699
+ },
1700
+ {
1701
+ id: "delete-col",
1702
+ label: "Delete column",
1703
+ command: "sheet.command.remove-col",
1704
+ icon: "delete"
1705
+ }
1706
+ ]
1707
+ },
1708
+ {
1709
+ id: "format",
1710
+ label: "Format",
1711
+ items: [
1712
+ { id: "bold", label: "Bold", command: "sheet.command.set-range-bold", icon: "format_bold" },
1713
+ {
1714
+ id: "italic",
1715
+ label: "Italic",
1716
+ command: "sheet.command.set-range-italic",
1717
+ icon: "format_italic"
1718
+ },
1719
+ {
1720
+ id: "underline",
1721
+ label: "Underline",
1722
+ command: "sheet.command.set-range-underline",
1723
+ icon: "format_underlined"
1724
+ },
1725
+ {
1726
+ id: "wrap-text",
1727
+ label: "Wrap text",
1728
+ command: "sheet.command.set-text-wrap",
1729
+ icon: "wrap_text"
1730
+ },
1731
+ {
1732
+ id: "clear-format",
1733
+ label: "Clear formatting",
1734
+ command: "sheet.command.clear-selection-format",
1735
+ icon: "format_clear"
1736
+ }
1737
+ ]
1738
+ },
1739
+ {
1740
+ id: "data",
1741
+ label: "Data",
1742
+ items: [
1743
+ {
1744
+ id: "sort-asc",
1745
+ label: "Sort ascending",
1746
+ command: "sheet.command.sort-range-asc",
1747
+ icon: "arrow_upward"
1748
+ },
1749
+ {
1750
+ id: "sort-desc",
1751
+ label: "Sort descending",
1752
+ command: "sheet.command.sort-range-desc",
1753
+ icon: "arrow_downward"
1754
+ },
1755
+ {
1756
+ id: "toggle-filter",
1757
+ label: "Toggle filter",
1758
+ command: "sheet.command.smart-toggle-filter",
1759
+ icon: "filter_alt"
1760
+ }
1761
+ ]
1762
+ },
1763
+ {
1764
+ id: "view",
1765
+ label: "View",
1766
+ items: [
1767
+ {
1768
+ id: "freeze",
1769
+ label: "Freeze panes",
1770
+ command: "sheet.command.set-selection-frozen",
1771
+ icon: "table_view"
1772
+ },
1773
+ {
1774
+ id: "unfreeze",
1775
+ label: "Unfreeze panes",
1776
+ command: "sheet.command.cancel-frozen",
1777
+ icon: "grid_off"
1778
+ },
1779
+ {
1780
+ id: "toggle-gridlines",
1781
+ label: "Toggle gridlines",
1782
+ command: "sheet.command.toggle-gridlines",
1783
+ icon: "grid_on"
1784
+ }
1785
+ ]
1786
+ }
1787
+ ];
1788
+ var BAR_STYLE4 = {
1789
+ position: "relative",
1790
+ display: "flex",
1791
+ alignItems: "center",
1792
+ gap: 2,
1793
+ padding: "2px 6px",
1794
+ borderBottom: "1px solid var(--cs-chrome-border, #e6e9ee)",
1795
+ background: "var(--cs-chrome-bg, #eef1f5)",
1796
+ flex: "0 0 auto",
1797
+ userSelect: "none",
1798
+ font: "inherit",
1799
+ fontSize: 13
1800
+ };
1801
+ var MENU_BTN_STYLE = {
1802
+ height: 26,
1803
+ display: "inline-flex",
1804
+ alignItems: "center",
1805
+ padding: "0 10px",
1806
+ border: "none",
1807
+ borderRadius: 6,
1808
+ background: "transparent",
1809
+ cursor: "pointer",
1810
+ color: "var(--cs-chrome-fg, #201f1e)",
1811
+ font: "inherit",
1812
+ fontSize: 13
1813
+ };
1814
+ var DROPDOWN_STYLE = {
1815
+ position: "absolute",
1816
+ top: "100%",
1817
+ minWidth: 200,
1818
+ marginTop: 2,
1819
+ padding: 4,
1820
+ display: "flex",
1821
+ flexDirection: "column",
1822
+ border: "1px solid var(--cs-chrome-border, #e6e9ee)",
1823
+ borderRadius: 8,
1824
+ background: "var(--cs-chrome-input-bg, #fff)",
1825
+ boxShadow: "0 6px 20px rgba(0,0,0,0.16)",
1826
+ zIndex: 1e3
1827
+ };
1828
+ var ITEM_STYLE3 = {
1829
+ display: "flex",
1830
+ alignItems: "center",
1831
+ gap: 10,
1832
+ width: "100%",
1833
+ height: 30,
1834
+ padding: "0 10px",
1835
+ border: "none",
1836
+ borderRadius: 6,
1837
+ background: "transparent",
1838
+ cursor: "pointer",
1839
+ color: "var(--cs-chrome-fg, #201f1e)",
1840
+ font: "inherit",
1841
+ fontSize: 13,
1842
+ textAlign: "left"
1843
+ };
1844
+ function MenuBar({ api }) {
1845
+ const [open, setOpen] = (0, import_react8.useState)(null);
1846
+ const rootRef = (0, import_react8.useRef)(null);
1847
+ (0, import_react8.useEffect)(() => {
1848
+ ensureChromeFonts();
1849
+ }, []);
1850
+ (0, import_react8.useEffect)(() => {
1851
+ if (open === null) return;
1852
+ const onKey = (e) => {
1853
+ if (e.key === "Escape") setOpen(null);
1854
+ };
1855
+ const onDown = (e) => {
1856
+ if (!rootRef.current?.contains(e.target)) setOpen(null);
1857
+ };
1858
+ document.addEventListener("keydown", onKey);
1859
+ document.addEventListener("pointerdown", onDown, true);
1860
+ return () => {
1861
+ document.removeEventListener("keydown", onKey);
1862
+ document.removeEventListener("pointerdown", onDown, true);
1863
+ };
1864
+ }, [open]);
1865
+ const run = (item) => {
1866
+ setOpen(null);
1867
+ void api?.executeCommand(item.command, item.params);
1868
+ };
1869
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1870
+ "div",
1871
+ {
1872
+ ref: rootRef,
1873
+ style: BAR_STYLE4,
1874
+ "data-testid": "cs-menubar",
1875
+ role: "menubar",
1876
+ "aria-label": "Menu bar",
1877
+ children: MENUS.map((menu) => {
1878
+ const isOpen = open === menu.id;
1879
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { position: "relative" }, children: [
1880
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1881
+ "button",
1882
+ {
1883
+ type: "button",
1884
+ "data-menu": menu.id,
1885
+ "aria-haspopup": "menu",
1886
+ "aria-expanded": isOpen,
1887
+ style: {
1888
+ ...MENU_BTN_STYLE,
1889
+ background: isOpen ? "var(--cs-chrome-active, #e6f3f7)" : "transparent",
1890
+ color: isOpen ? "var(--cs-chrome-active-fg, #0e7490)" : MENU_BTN_STYLE.color
1891
+ },
1892
+ onMouseDown: (e) => {
1893
+ e.preventDefault();
1894
+ setOpen((cur) => cur === menu.id ? null : menu.id);
1895
+ },
1896
+ onMouseEnter: (e) => {
1897
+ if (open !== null && open !== menu.id) setOpen(menu.id);
1898
+ else if (!isOpen)
1899
+ e.currentTarget.style.background = "var(--cs-chrome-hover, rgba(0,0,0,0.06))";
1900
+ },
1901
+ onMouseLeave: (e) => {
1902
+ e.currentTarget.style.background = isOpen ? "var(--cs-chrome-active, #e6f3f7)" : "transparent";
1903
+ },
1904
+ children: menu.label
1905
+ }
1906
+ ),
1907
+ isOpen && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: DROPDOWN_STYLE, role: "menu", "aria-label": menu.label, children: menu.items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
1908
+ "button",
1909
+ {
1910
+ type: "button",
1911
+ role: "menuitem",
1912
+ "data-testid": `cs-menuitem-${item.id}`,
1913
+ disabled: !api,
1914
+ style: { ...ITEM_STYLE3, opacity: api ? 1 : 0.5 },
1915
+ onMouseDown: (e) => {
1916
+ e.preventDefault();
1917
+ run(item);
1918
+ },
1919
+ onMouseEnter: (e) => {
1920
+ e.currentTarget.style.background = "var(--cs-chrome-hover, rgba(0,0,0,0.06))";
1921
+ },
1922
+ onMouseLeave: (e) => {
1923
+ e.currentTarget.style.background = "transparent";
1924
+ },
1925
+ children: [
1926
+ item.icon ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Icon, { name: item.icon, size: 18 }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: { width: 18 }, "aria-hidden": true }),
1927
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: item.label })
1928
+ ]
1929
+ },
1930
+ item.id
1931
+ )) })
1932
+ ] }, menu.id);
1933
+ })
1934
+ }
1935
+ );
1936
+ }
1937
+
1938
+ // src/chrome/SheetTabs.tsx
1939
+ var import_react9 = require("react");
1940
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1941
+ var EMPTY = { tabs: [], activeId: null };
1942
+ var SHEET_LIST_MUTATIONS = /* @__PURE__ */ new Set([
1943
+ "sheet.mutation.insert-sheet",
1944
+ "sheet.mutation.remove-sheet",
1945
+ "sheet.mutation.set-worksheet-name",
1946
+ "sheet.mutation.set-worksheet-order",
1947
+ "sheet.mutation.set-worksheet-hidden"
1948
+ ]);
1949
+ function readSheets(api) {
1950
+ const wb = api.univer.getActiveWorkbook();
1951
+ if (!wb) return EMPTY;
1952
+ const tabs = wb.getSheets().map((s) => ({
1953
+ id: s.getSheetId(),
1954
+ name: s.getSheetName(),
1955
+ hidden: s.isSheetHidden?.() === true
1956
+ }));
1957
+ return { tabs, activeId: wb.getActiveSheet()?.getSheetId() ?? null };
1958
+ }
1959
+ var BAR_STYLE5 = {
1960
+ display: "flex",
1961
+ alignItems: "stretch",
1962
+ gap: 2,
1963
+ height: 30,
1964
+ flex: "0 0 auto",
1965
+ padding: "0 6px",
1966
+ borderTop: "1px solid var(--cs-chrome-border, rgba(0,0,0,0.12))",
1967
+ background: "var(--cs-chrome-bg, #f8f9fa)",
1968
+ font: "inherit",
1969
+ fontSize: 13,
1970
+ overflowX: "auto",
1971
+ overflowY: "hidden"
1972
+ };
1973
+ var ADD_BTN_STYLE = {
1974
+ flex: "0 0 auto",
1975
+ width: 28,
1976
+ border: "none",
1977
+ background: "transparent",
1978
+ color: "var(--cs-chrome-fg, #201f1e)",
1979
+ cursor: "pointer",
1980
+ fontSize: 18,
1981
+ lineHeight: 1,
1982
+ borderRadius: 4
1983
+ };
1984
+ var RENAME_INPUT_STYLE = {
1985
+ height: 22,
1986
+ alignSelf: "center",
1987
+ padding: "0 6px",
1988
+ border: "1px solid var(--cs-chrome-active-fg, #0e7490)",
1989
+ borderRadius: 4,
1990
+ background: "var(--cs-chrome-input-bg, #fff)",
1991
+ color: "var(--cs-chrome-fg, #201f1e)",
1992
+ font: "inherit",
1993
+ fontSize: 13,
1994
+ width: 120
1995
+ };
1996
+ var MENU_STYLE2 = {
1997
+ position: "fixed",
1998
+ zIndex: 1e3,
1999
+ minWidth: 140,
2000
+ padding: 4,
2001
+ border: "1px solid var(--cs-chrome-border, #e6e9ee)",
2002
+ borderRadius: 8,
2003
+ background: "var(--cs-chrome-input-bg, #fff)",
2004
+ boxShadow: "0 6px 20px rgba(0,0,0,0.16)"
2005
+ };
2006
+ var MENU_ITEM_STYLE2 = {
2007
+ display: "block",
2008
+ width: "100%",
2009
+ textAlign: "left",
2010
+ height: 28,
2011
+ padding: "0 10px",
2012
+ border: "none",
2013
+ borderRadius: 6,
2014
+ background: "transparent",
2015
+ color: "var(--cs-chrome-fg, #201f1e)",
2016
+ font: "inherit",
2017
+ fontSize: 13,
2018
+ cursor: "pointer"
2019
+ };
2020
+ function SheetTabs({ api }) {
2021
+ const [{ tabs, activeId }, setSnapshot] = (0, import_react9.useState)(EMPTY);
2022
+ const [renaming, setRenaming] = (0, import_react9.useState)(null);
2023
+ const [draft, setDraft] = (0, import_react9.useState)("");
2024
+ const [menu, setMenu] = (0, import_react9.useState)(null);
2025
+ (0, import_react9.useEffect)(() => {
2026
+ if (!api) return;
2027
+ const refresh = () => setSnapshot(readSheets(api));
2028
+ refresh();
2029
+ const f = api.univer;
2030
+ const subs = [];
2031
+ try {
2032
+ subs.push(
2033
+ f.addEvent(f.Event.SheetCreated, refresh),
2034
+ f.addEvent(f.Event.SheetDeleted, refresh),
2035
+ f.addEvent(f.Event.SheetNameChanged, refresh),
2036
+ f.addEvent(f.Event.ActiveSheetChanged, refresh),
2037
+ f.addEvent(f.Event.SheetMoved, refresh),
2038
+ // Mutation-level fallback — catches collab/replay-driven changes that
2039
+ // don't emit the higher-level facade events.
2040
+ f.addEvent(f.Event.CommandExecuted, (e) => {
2041
+ if (e?.id && SHEET_LIST_MUTATIONS.has(e.id)) refresh();
2042
+ })
2043
+ );
2044
+ if (typeof f.onUniverSheetCreated === "function") {
2045
+ subs.push(f.onUniverSheetCreated(() => queueMicrotask(refresh)));
2046
+ }
2047
+ } catch {
2048
+ }
2049
+ return () => {
2050
+ for (const s of subs) s?.dispose?.();
2051
+ };
2052
+ }, [api]);
2053
+ (0, import_react9.useEffect)(() => {
2054
+ if (!menu) return;
2055
+ const close = () => setMenu(null);
2056
+ window.addEventListener("pointerdown", close);
2057
+ window.addEventListener("keydown", close);
2058
+ return () => {
2059
+ window.removeEventListener("pointerdown", close);
2060
+ window.removeEventListener("keydown", close);
2061
+ };
2062
+ }, [menu]);
2063
+ if (!api) {
2064
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: BAR_STYLE5, "data-testid": "casual-sheets-tabs" });
2065
+ }
2066
+ const visible = tabs.filter((t) => !t.hidden);
2067
+ const wb = () => api.univer.getActiveWorkbook();
2068
+ const switchTo = (id) => {
2069
+ if (id === activeId) return;
2070
+ wb()?.setActiveSheet(id);
2071
+ };
2072
+ const addSheet = () => {
2073
+ const created = wb()?.insertSheet?.();
2074
+ created?.activate?.();
2075
+ };
2076
+ const startRename = (tab) => {
2077
+ setMenu(null);
2078
+ setRenaming(tab.id);
2079
+ setDraft(tab.name);
2080
+ };
2081
+ const commitRename = () => {
2082
+ const id = renaming;
2083
+ setRenaming(null);
2084
+ if (!id) return;
2085
+ const name = draft.trim();
2086
+ if (!name) return;
2087
+ const sheet = wb()?.getSheetBySheetId?.(id);
2088
+ if (sheet && sheet.getSheetName?.() !== name) sheet.setName?.(name);
2089
+ };
2090
+ const deleteSheet = (id) => {
2091
+ setMenu(null);
2092
+ if (visible.length <= 1) return;
2093
+ wb()?.deleteSheet?.(id);
2094
+ };
2095
+ const onRenameKey = (e) => {
2096
+ if (e.key === "Enter") {
2097
+ e.preventDefault();
2098
+ commitRename();
2099
+ } else if (e.key === "Escape") {
2100
+ e.preventDefault();
2101
+ setRenaming(null);
2102
+ }
2103
+ };
2104
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: BAR_STYLE5, "data-testid": "casual-sheets-tabs", role: "tablist", children: [
2105
+ visible.map((tab) => {
2106
+ const active = tab.id === activeId;
2107
+ if (renaming === tab.id) {
2108
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2109
+ "input",
2110
+ {
2111
+ autoFocus: true,
2112
+ style: RENAME_INPUT_STYLE,
2113
+ value: draft,
2114
+ "data-testid": "cs-tab-rename-input",
2115
+ onChange: (e) => setDraft(e.target.value),
2116
+ onKeyDown: onRenameKey,
2117
+ onBlur: commitRename
2118
+ },
2119
+ tab.id
2120
+ );
2121
+ }
2122
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2123
+ "button",
2124
+ {
2125
+ type: "button",
2126
+ role: "tab",
2127
+ "aria-selected": active,
2128
+ "data-testid": `cs-tab-${tab.id}`,
2129
+ "data-active": active || void 0,
2130
+ title: tab.name,
2131
+ style: {
2132
+ flex: "0 0 auto",
2133
+ maxWidth: 160,
2134
+ padding: "0 12px",
2135
+ border: "none",
2136
+ borderTop: active ? "2px solid var(--cs-chrome-active-fg, #0e7490)" : "2px solid transparent",
2137
+ background: active ? "var(--cs-chrome-input-bg, #fff)" : "transparent",
2138
+ color: active ? "var(--cs-chrome-active-fg, #0e7490)" : "var(--cs-chrome-fg, #201f1e)",
2139
+ fontWeight: active ? 600 : 400,
2140
+ font: "inherit",
2141
+ fontSize: 13,
2142
+ cursor: "pointer",
2143
+ whiteSpace: "nowrap",
2144
+ overflow: "hidden",
2145
+ textOverflow: "ellipsis"
2146
+ },
2147
+ onMouseDown: (e) => {
2148
+ e.preventDefault();
2149
+ switchTo(tab.id);
2150
+ },
2151
+ onDoubleClick: () => startRename(tab),
2152
+ onContextMenu: (e) => {
2153
+ e.preventDefault();
2154
+ setMenu({ x: e.clientX, y: e.clientY, sheetId: tab.id });
2155
+ },
2156
+ children: tab.name
2157
+ },
2158
+ tab.id
2159
+ );
2160
+ }),
2161
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2162
+ "button",
2163
+ {
2164
+ type: "button",
2165
+ style: ADD_BTN_STYLE,
2166
+ "aria-label": "Add sheet",
2167
+ title: "Add sheet",
2168
+ "data-testid": "cs-tab-add",
2169
+ onMouseDown: (e) => {
2170
+ e.preventDefault();
2171
+ addSheet();
2172
+ },
2173
+ children: "+"
2174
+ }
2175
+ ),
2176
+ menu && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
2177
+ "div",
2178
+ {
2179
+ style: { ...MENU_STYLE2, left: menu.x, top: menu.y },
2180
+ "data-testid": "cs-tab-menu",
2181
+ role: "menu",
2182
+ onPointerDown: (e) => e.stopPropagation(),
2183
+ children: [
2184
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2185
+ "button",
2186
+ {
2187
+ type: "button",
2188
+ role: "menuitem",
2189
+ style: MENU_ITEM_STYLE2,
2190
+ "data-testid": "cs-tab-menu-rename",
2191
+ onMouseDown: (e) => {
2192
+ e.preventDefault();
2193
+ const tab = tabs.find((t) => t.id === menu.sheetId);
2194
+ if (tab) startRename(tab);
2195
+ },
2196
+ children: "Rename"
2197
+ }
2198
+ ),
2199
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2200
+ "button",
2201
+ {
2202
+ type: "button",
2203
+ role: "menuitem",
2204
+ style: {
2205
+ ...MENU_ITEM_STYLE2,
2206
+ opacity: visible.length <= 1 ? 0.5 : 1,
2207
+ cursor: visible.length <= 1 ? "not-allowed" : "pointer"
2208
+ },
2209
+ "data-testid": "cs-tab-menu-delete",
2210
+ onMouseDown: (e) => {
2211
+ e.preventDefault();
2212
+ deleteSheet(menu.sheetId);
2213
+ },
2214
+ children: "Delete"
2215
+ }
2216
+ )
2217
+ ]
2218
+ }
2219
+ )
2220
+ ] });
2221
+ }
2222
+
2223
+ // src/chrome/FindReplace.tsx
2224
+ var import_react10 = require("react");
2225
+ var import_jsx_runtime11 = require("react/jsx-runtime");
2226
+ function findMatches(api, query, matchCase) {
2227
+ if (!query) return [];
2228
+ const snap = api.getSnapshot();
2229
+ const sheetId = api.getSelection()?.sheetId ?? api.univer.getActiveWorkbook()?.getActiveSheet()?.getSheetId();
2230
+ const sheet = sheetId ? snap?.sheets?.[sheetId] : void 0;
2231
+ const cellData = sheet?.cellData;
2232
+ if (!cellData) return [];
2233
+ const needle = matchCase ? query : query.toLowerCase();
2234
+ const out = [];
2235
+ for (const rk of Object.keys(cellData).sort((a, b) => Number(a) - Number(b))) {
2236
+ const rowObj = cellData[rk];
2237
+ if (!rowObj) continue;
2238
+ for (const ck of Object.keys(rowObj).sort((a, b) => Number(a) - Number(b))) {
2239
+ const cell = rowObj[ck];
2240
+ if (!cell) continue;
2241
+ const hay = `${cell.v ?? ""}${cell.f ? ` ${cell.f}` : ""}`;
2242
+ const cmp = matchCase ? hay : hay.toLowerCase();
2243
+ if (cmp.includes(needle)) out.push({ row: Number(rk), col: Number(ck) });
2244
+ }
2245
+ }
2246
+ return out;
2247
+ }
2248
+ var WRAP_STYLE = {
2249
+ position: "absolute",
2250
+ top: 8,
2251
+ right: 12,
2252
+ zIndex: 1100,
2253
+ width: 320,
2254
+ padding: 10,
2255
+ borderRadius: 8,
2256
+ border: "1px solid var(--cs-chrome-border, #e6e9ee)",
2257
+ background: "var(--cs-chrome-input-bg, #fff)",
2258
+ boxShadow: "0 8px 28px rgba(0,0,0,0.18)",
2259
+ font: "inherit",
2260
+ fontSize: 13,
2261
+ color: "var(--cs-chrome-fg, #201f1e)"
2262
+ };
2263
+ var ROW_STYLE4 = { display: "flex", alignItems: "center", gap: 6, marginBottom: 6 };
2264
+ var INPUT_STYLE2 = {
2265
+ flex: "1 1 auto",
2266
+ height: 26,
2267
+ padding: "0 8px",
2268
+ border: "1px solid var(--cs-chrome-border, #cdd3db)",
2269
+ borderRadius: 4,
2270
+ background: "var(--cs-chrome-input-bg, #fff)",
2271
+ color: "var(--cs-chrome-fg, #201f1e)",
2272
+ font: "inherit",
2273
+ fontSize: 13,
2274
+ boxSizing: "border-box"
2275
+ };
2276
+ var BTN_STYLE5 = {
2277
+ height: 26,
2278
+ padding: "0 10px",
2279
+ border: "1px solid var(--cs-chrome-border, #cdd3db)",
2280
+ borderRadius: 4,
2281
+ background: "var(--cs-chrome-bg, #f3f5f8)",
2282
+ color: "var(--cs-chrome-fg, #201f1e)",
2283
+ font: "inherit",
2284
+ fontSize: 12,
2285
+ cursor: "pointer"
2286
+ };
2287
+ var ICON_BTN_STYLE = {
2288
+ width: 26,
2289
+ height: 26,
2290
+ display: "inline-flex",
2291
+ alignItems: "center",
2292
+ justifyContent: "center",
2293
+ border: "none",
2294
+ borderRadius: 4,
2295
+ background: "transparent",
2296
+ color: "var(--cs-chrome-fg, #201f1e)",
2297
+ cursor: "pointer",
2298
+ padding: 0
2299
+ };
2300
+ function FindReplace({ api }) {
2301
+ const [open, setOpen] = (0, import_react10.useState)(false);
2302
+ const [showReplace, setShowReplace] = (0, import_react10.useState)(false);
2303
+ const [query, setQuery] = (0, import_react10.useState)("");
2304
+ const [replaceText, setReplaceText] = (0, import_react10.useState)("");
2305
+ const [matchCase, setMatchCase] = (0, import_react10.useState)(false);
2306
+ const [idx, setIdx] = (0, import_react10.useState)(0);
2307
+ const inputRef = (0, import_react10.useRef)(null);
2308
+ (0, import_react10.useEffect)(() => {
2309
+ const onKey = (e) => {
2310
+ const mod = e.metaKey || e.ctrlKey;
2311
+ if (mod && (e.key === "f" || e.key === "F")) {
2312
+ e.preventDefault();
2313
+ setShowReplace(false);
2314
+ setOpen(true);
2315
+ requestAnimationFrame(() => inputRef.current?.focus());
2316
+ } else if (mod && (e.key === "h" || e.key === "H")) {
2317
+ e.preventDefault();
2318
+ setShowReplace(true);
2319
+ setOpen(true);
2320
+ requestAnimationFrame(() => inputRef.current?.focus());
2321
+ }
2322
+ };
2323
+ document.addEventListener("keydown", onKey);
2324
+ return () => document.removeEventListener("keydown", onKey);
2325
+ }, []);
2326
+ const matches = (0, import_react10.useMemo)(
2327
+ () => api && open && query ? findMatches(api, query, matchCase) : [],
2328
+ [api, open, query, matchCase]
2329
+ );
2330
+ (0, import_react10.useEffect)(() => {
2331
+ setIdx((i) => matches.length === 0 ? 0 : Math.min(i, matches.length - 1));
2332
+ }, [matches.length]);
2333
+ if (!open || !api) return null;
2334
+ const go = (delta) => {
2335
+ if (matches.length === 0) return;
2336
+ const next = (idx + delta + matches.length) % matches.length;
2337
+ setIdx(next);
2338
+ activate(matches[next]);
2339
+ };
2340
+ const activate = (m) => {
2341
+ const sheet = api.univer.getActiveWorkbook()?.getActiveSheet();
2342
+ sheet?.getRange(m.row, m.col)?.activate?.();
2343
+ };
2344
+ const replaceOne = () => {
2345
+ if (matches.length === 0) return;
2346
+ const m = matches[Math.min(idx, matches.length - 1)];
2347
+ replaceAt(m);
2348
+ };
2349
+ const replaceAt = (m) => {
2350
+ const sheet = api.univer.getActiveWorkbook()?.getActiveSheet();
2351
+ if (!sheet) return;
2352
+ const cur = String(sheet.getRange(m.row, m.col).getValue?.() ?? "");
2353
+ const next = replaceInString(cur, query, replaceText, matchCase);
2354
+ if (next !== cur) sheet.getRange(m.row, m.col).setValue(next);
2355
+ };
2356
+ const replaceAll = () => {
2357
+ const sheet = api.univer.getActiveWorkbook()?.getActiveSheet();
2358
+ if (!sheet) return;
2359
+ for (const m of matches) {
2360
+ const cur = String(sheet.getRange(m.row, m.col).getValue?.() ?? "");
2361
+ const next = replaceInString(cur, query, replaceText, matchCase);
2362
+ if (next !== cur) sheet.getRange(m.row, m.col).setValue(next);
2363
+ }
2364
+ };
2365
+ const onInputKey = (e) => {
2366
+ if (e.key === "Enter") {
2367
+ e.preventDefault();
2368
+ go(e.shiftKey ? -1 : 1);
2369
+ } else if (e.key === "Escape") {
2370
+ e.preventDefault();
2371
+ setOpen(false);
2372
+ }
2373
+ };
2374
+ const count = matches.length;
2375
+ const position = count === 0 ? 0 : Math.min(idx, count - 1) + 1;
2376
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
2377
+ "div",
2378
+ {
2379
+ style: WRAP_STYLE,
2380
+ "data-testid": "cs-find-replace",
2381
+ role: "dialog",
2382
+ "aria-label": "Find and replace",
2383
+ children: [
2384
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: ROW_STYLE4, children: [
2385
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2386
+ "input",
2387
+ {
2388
+ ref: inputRef,
2389
+ type: "text",
2390
+ "aria-label": "Find",
2391
+ placeholder: "Find",
2392
+ "data-testid": "cs-find-input",
2393
+ style: INPUT_STYLE2,
2394
+ value: query,
2395
+ onChange: (e) => setQuery(e.target.value),
2396
+ onKeyDown: onInputKey
2397
+ }
2398
+ ),
2399
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
2400
+ "span",
2401
+ {
2402
+ style: { minWidth: 54, textAlign: "center", color: "var(--cs-chrome-muted, #6b7280)" },
2403
+ "data-testid": "cs-find-count",
2404
+ children: [
2405
+ position,
2406
+ "/",
2407
+ count
2408
+ ]
2409
+ }
2410
+ ),
2411
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2412
+ "button",
2413
+ {
2414
+ type: "button",
2415
+ style: ICON_BTN_STYLE,
2416
+ "aria-label": "Previous match",
2417
+ "data-testid": "cs-find-prev",
2418
+ onMouseDown: (e) => {
2419
+ e.preventDefault();
2420
+ go(-1);
2421
+ },
2422
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Icon, { name: "keyboard_arrow_up", size: 18 })
2423
+ }
2424
+ ),
2425
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2426
+ "button",
2427
+ {
2428
+ type: "button",
2429
+ style: ICON_BTN_STYLE,
2430
+ "aria-label": "Next match",
2431
+ "data-testid": "cs-find-next",
2432
+ onMouseDown: (e) => {
2433
+ e.preventDefault();
2434
+ go(1);
2435
+ },
2436
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Icon, { name: "keyboard_arrow_down", size: 18 })
2437
+ }
2438
+ ),
2439
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2440
+ "button",
2441
+ {
2442
+ type: "button",
2443
+ style: ICON_BTN_STYLE,
2444
+ "aria-label": "Close",
2445
+ "data-testid": "cs-find-close",
2446
+ onMouseDown: (e) => {
2447
+ e.preventDefault();
2448
+ setOpen(false);
2449
+ },
2450
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Icon, { name: "close", size: 18 })
2451
+ }
2452
+ )
2453
+ ] }),
2454
+ showReplace && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: ROW_STYLE4, children: [
2455
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2456
+ "input",
2457
+ {
2458
+ type: "text",
2459
+ "aria-label": "Replace with",
2460
+ placeholder: "Replace with",
2461
+ "data-testid": "cs-replace-input",
2462
+ style: INPUT_STYLE2,
2463
+ value: replaceText,
2464
+ onChange: (e) => setReplaceText(e.target.value)
2465
+ }
2466
+ ),
2467
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2468
+ "button",
2469
+ {
2470
+ type: "button",
2471
+ style: BTN_STYLE5,
2472
+ "data-testid": "cs-replace-one",
2473
+ onMouseDown: (e) => {
2474
+ e.preventDefault();
2475
+ replaceOne();
2476
+ },
2477
+ children: "Replace"
2478
+ }
2479
+ ),
2480
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2481
+ "button",
2482
+ {
2483
+ type: "button",
2484
+ style: BTN_STYLE5,
2485
+ "data-testid": "cs-replace-all",
2486
+ onMouseDown: (e) => {
2487
+ e.preventDefault();
2488
+ replaceAll();
2489
+ },
2490
+ children: "All"
2491
+ }
2492
+ )
2493
+ ] }),
2494
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 12 }, children: [
2495
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("label", { style: { display: "inline-flex", alignItems: "center", gap: 4, cursor: "pointer" }, children: [
2496
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2497
+ "input",
2498
+ {
2499
+ type: "checkbox",
2500
+ "data-testid": "cs-find-match-case",
2501
+ checked: matchCase,
2502
+ onChange: (e) => setMatchCase(e.target.checked)
2503
+ }
2504
+ ),
2505
+ "Match case"
2506
+ ] }),
2507
+ !showReplace && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2508
+ "button",
2509
+ {
2510
+ type: "button",
2511
+ style: {
2512
+ ...BTN_STYLE5,
2513
+ marginLeft: "auto",
2514
+ border: "none",
2515
+ background: "transparent",
2516
+ color: "var(--cs-chrome-active-fg, #0e7490)"
2517
+ },
2518
+ "data-testid": "cs-find-toggle-replace",
2519
+ onMouseDown: (e) => {
2520
+ e.preventDefault();
2521
+ setShowReplace(true);
2522
+ },
2523
+ children: "Replace\u2026"
2524
+ }
2525
+ )
2526
+ ] })
2527
+ ]
2528
+ }
2529
+ );
2530
+ }
2531
+ function replaceInString(text, query, replacement, matchCase) {
2532
+ if (!query) return text;
2533
+ if (matchCase) return text.split(query).join(replacement);
2534
+ const lower = text.toLowerCase();
2535
+ const needle = query.toLowerCase();
2536
+ let out = "";
2537
+ let i = 0;
2538
+ while (i < text.length) {
2539
+ if (lower.startsWith(needle, i)) {
2540
+ out += replacement;
2541
+ i += needle.length;
2542
+ } else {
2543
+ out += text[i];
2544
+ i += 1;
2545
+ }
2546
+ }
2547
+ return out;
2548
+ }
2549
+
2550
+ // src/chrome/ChromeTop.tsx
2551
+ var import_jsx_runtime12 = require("react/jsx-runtime");
2552
+ function ChromeTop({ api }) {
2553
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
2554
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(MenuBar, { api }),
2555
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Toolbar, { api }),
2556
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(FormulaBar, { api })
2557
+ ] });
2558
+ }
2559
+
2560
+ // src/chrome/ChromeBottom.tsx
2561
+ var import_jsx_runtime13 = require("react/jsx-runtime");
2562
+ function ChromeBottom({ api }) {
2563
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
2564
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(SheetTabs, { api }),
2565
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(StatusBar, { api }),
2566
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(FindReplace, { api })
2567
+ ] });
2568
+ }
2569
+ //# sourceMappingURL=chrome.cjs.map