@casualoffice/sheets 0.17.0 → 0.19.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 (52) hide show
  1. package/dist/chrome.cjs +3339 -244
  2. package/dist/chrome.cjs.map +1 -1
  3. package/dist/chrome.js +3298 -203
  4. package/dist/chrome.js.map +1 -1
  5. package/dist/embed/embed-runtime.js +203 -157
  6. package/dist/index.cjs +5971 -1572
  7. package/dist/index.cjs.map +1 -1
  8. package/dist/index.js +5961 -1548
  9. package/dist/index.js.map +1 -1
  10. package/dist/sheets.cjs +4937 -538
  11. package/dist/sheets.cjs.map +1 -1
  12. package/dist/sheets.js +4945 -532
  13. package/dist/sheets.js.map +1 -1
  14. package/package.json +8 -7
  15. package/src/charts/ChartContextMenu.tsx +264 -0
  16. package/src/charts/ChartLayer.tsx +333 -0
  17. package/src/charts/ChartOverlay.tsx +293 -0
  18. package/src/charts/ChartsPanel.tsx +211 -0
  19. package/src/charts/FormatChartDialog.tsx +419 -0
  20. package/src/charts/InsertChartDialog.tsx +478 -0
  21. package/src/charts/build-option.ts +476 -0
  22. package/src/charts/charts-context.tsx +205 -0
  23. package/src/charts/echarts-init.ts +58 -0
  24. package/src/charts/hit-test.ts +130 -0
  25. package/src/charts/insert-chart.ts +106 -0
  26. package/src/charts/naming.ts +38 -0
  27. package/src/charts/render-to-png.ts +117 -0
  28. package/src/charts/resources.ts +108 -0
  29. package/src/charts/types.ts +239 -0
  30. package/src/charts/univer-dom.ts +102 -0
  31. package/src/chrome/CommentsPanel.tsx +427 -0
  32. package/src/chrome/ConditionalFormattingDialog.tsx +534 -0
  33. package/src/chrome/CustomSortDialog.tsx +357 -0
  34. package/src/chrome/DataValidationDialog.tsx +536 -0
  35. package/src/chrome/DeleteCellsDialog.tsx +183 -0
  36. package/src/chrome/GoalSeekDialog.tsx +370 -0
  37. package/src/chrome/HistoryPanel.tsx +319 -0
  38. package/src/chrome/InsertCellsDialog.tsx +185 -0
  39. package/src/chrome/InsertChartDialog.tsx +490 -0
  40. package/src/chrome/InsertFunctionDialog.tsx +493 -0
  41. package/src/chrome/InsertPivotDialog.tsx +488 -0
  42. package/src/chrome/InsertSparklineDialog.tsx +344 -0
  43. package/src/chrome/NameManagerDialog.tsx +378 -0
  44. package/src/chrome/PanelHost.tsx +55 -0
  45. package/src/chrome/PanelRail.tsx +90 -0
  46. package/src/chrome/PasteSpecialDialog.tsx +286 -0
  47. package/src/chrome/PivotFieldsPanel.tsx +1052 -0
  48. package/src/chrome/TablesPanel.tsx +301 -0
  49. package/src/chrome/dialog-context.tsx +24 -0
  50. package/src/chrome/panel-context.tsx +55 -0
  51. package/src/chrome/panel-registry.ts +48 -0
  52. package/src/sheets/CasualSheets.tsx +60 -34
package/dist/chrome.cjs CHANGED
@@ -53,8 +53,8 @@ __export(chrome_exports, {
53
53
  module.exports = __toCommonJS(chrome_exports);
54
54
 
55
55
  // src/chrome/Toolbar.tsx
56
- var import_react7 = require("react");
57
- var import_core2 = require("@univerjs/core");
56
+ var import_react19 = require("react");
57
+ var import_core4 = require("@univerjs/core");
58
58
 
59
59
  // src/chrome/Icon.tsx
60
60
  var import_jsx_runtime = require("react/jsx-runtime");
@@ -580,7 +580,7 @@ function AutoSumPicker({ api }) {
580
580
  }
581
581
 
582
582
  // src/chrome/dialog-context.tsx
583
- var import_react6 = require("react");
583
+ var import_react18 = require("react");
584
584
 
585
585
  // src/chrome/FormatCellsDialog.tsx
586
586
  var import_react5 = require("react");
@@ -1163,36 +1163,3131 @@ function FormatCellsDialog({ api, onClose }) {
1163
1163
  )
1164
1164
  ] })
1165
1165
  ] }),
1166
- tab === "fill" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { style: DIALOG_FIELD_STYLE, children: [
1167
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: DIALOG_LABEL_STYLE, children: "Fill color" }),
1168
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1166
+ tab === "fill" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { style: DIALOG_FIELD_STYLE, children: [
1167
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: DIALOG_LABEL_STYLE, children: "Fill color" }),
1168
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1169
+ "input",
1170
+ {
1171
+ style: COLOR_INPUT_STYLE,
1172
+ "data-testid": "cs-format-cells-fill-color",
1173
+ type: "color",
1174
+ value: state.fillColor,
1175
+ onChange: (e) => update("fillColor", e.target.value)
1176
+ }
1177
+ )
1178
+ ] })
1179
+ ] })
1180
+ ]
1181
+ }
1182
+ );
1183
+ }
1184
+
1185
+ // src/chrome/DataValidationDialog.tsx
1186
+ var import_react6 = require("react");
1187
+ var import_facade = require("@univerjs/sheets-data-validation/facade");
1188
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1189
+ var RULE_TYPE_OPTIONS = [
1190
+ { value: "list", label: "List of items" },
1191
+ { value: "number", label: "Whole number" },
1192
+ { value: "decimal", label: "Decimal" },
1193
+ { value: "date", label: "Date" },
1194
+ { value: "textLength", label: "Text length" },
1195
+ { value: "checkbox", label: "Checkbox" }
1196
+ ];
1197
+ var OPERATOR_OPTIONS = [
1198
+ { value: "between", label: "Between" },
1199
+ { value: "notBetween", label: "Not between" },
1200
+ { value: "equal", label: "Equal to" },
1201
+ { value: "notEqual", label: "Not equal to" },
1202
+ { value: "greater", label: "Greater than" },
1203
+ { value: "greaterEqual", label: "Greater than or equal to" },
1204
+ { value: "less", label: "Less than" },
1205
+ { value: "lessEqual", label: "Less than or equal to" }
1206
+ ];
1207
+ function isRangeOperator(op) {
1208
+ return op === "between" || op === "notBetween";
1209
+ }
1210
+ function usesOperator(type) {
1211
+ return type === "number" || type === "decimal" || type === "date" || type === "textLength";
1212
+ }
1213
+ var INITIAL_STATE = {
1214
+ ruleType: "list",
1215
+ operator: "between",
1216
+ operand1: "",
1217
+ operand2: "",
1218
+ listItems: "",
1219
+ ignoreBlank: true,
1220
+ showErrorMessage: false,
1221
+ errorMessage: "",
1222
+ showInputMessage: false,
1223
+ inputMessage: ""
1224
+ };
1225
+ function activeRange2(api) {
1226
+ return api.univer.getActiveWorkbook()?.getActiveSheet()?.getActiveRange() ?? null;
1227
+ }
1228
+ function applyValidation(api, s, remove) {
1229
+ const range = activeRange2(api);
1230
+ if (!range) return false;
1231
+ const dvRange = range;
1232
+ if (remove) {
1233
+ dvRange.setDataValidation(null);
1234
+ return true;
1235
+ }
1236
+ const builder = api.univer.newDataValidation();
1237
+ const n1 = Number(s.operand1);
1238
+ const n2 = Number(s.operand2);
1239
+ const wholeNumber = s.ruleType === "number";
1240
+ switch (s.ruleType) {
1241
+ case "list": {
1242
+ const values = s.listItems.split(/[\n,]/).map((v) => v.trim()).filter((v) => v.length > 0);
1243
+ if (values.length === 0) return false;
1244
+ builder.requireValueInList(values, false, true);
1245
+ break;
1246
+ }
1247
+ case "number":
1248
+ case "decimal": {
1249
+ if (!Number.isFinite(n1)) return false;
1250
+ switch (s.operator) {
1251
+ case "between":
1252
+ if (!Number.isFinite(n2)) return false;
1253
+ builder.requireNumberBetween(n1, n2, wholeNumber);
1254
+ break;
1255
+ case "notBetween":
1256
+ if (!Number.isFinite(n2)) return false;
1257
+ builder.requireNumberNotBetween(n1, n2, wholeNumber);
1258
+ break;
1259
+ case "equal":
1260
+ builder.requireNumberEqualTo(n1, wholeNumber);
1261
+ break;
1262
+ case "notEqual":
1263
+ builder.requireNumberNotEqualTo(n1, wholeNumber);
1264
+ break;
1265
+ case "greater":
1266
+ builder.requireNumberGreaterThan(n1, wholeNumber);
1267
+ break;
1268
+ case "greaterEqual":
1269
+ builder.requireNumberGreaterThanOrEqualTo(n1, wholeNumber);
1270
+ break;
1271
+ case "less":
1272
+ builder.requireNumberLessThan(n1, wholeNumber);
1273
+ break;
1274
+ case "lessEqual":
1275
+ builder.requireNumberLessThanOrEqualTo(n1, wholeNumber);
1276
+ break;
1277
+ }
1278
+ break;
1279
+ }
1280
+ case "date": {
1281
+ const d1 = new Date(s.operand1);
1282
+ const d2 = new Date(s.operand2);
1283
+ if (Number.isNaN(d1.getTime())) return false;
1284
+ switch (s.operator) {
1285
+ case "between":
1286
+ if (Number.isNaN(d2.getTime())) return false;
1287
+ builder.requireDateBetween(d1, d2);
1288
+ break;
1289
+ case "notBetween":
1290
+ if (Number.isNaN(d2.getTime())) return false;
1291
+ builder.requireDateNotBetween(d1, d2);
1292
+ break;
1293
+ case "equal":
1294
+ builder.requireDateEqualTo(d1);
1295
+ break;
1296
+ // The builder has no requireDateNotEqualTo; map the remaining operators
1297
+ // to the closest available on/after / on/before methods.
1298
+ case "notEqual":
1299
+ case "greater":
1300
+ builder.requireDateAfter(d1);
1301
+ break;
1302
+ case "greaterEqual":
1303
+ builder.requireDateOnOrAfter(d1);
1304
+ break;
1305
+ case "less":
1306
+ builder.requireDateBefore(d1);
1307
+ break;
1308
+ case "lessEqual":
1309
+ builder.requireDateOnOrBefore(d1);
1310
+ break;
1311
+ }
1312
+ break;
1313
+ }
1314
+ case "textLength": {
1315
+ if (!Number.isFinite(n1)) return false;
1316
+ const anchor = dvRange.getA1Notation?.() ?? "A1";
1317
+ const cell = anchor.split(":")[0];
1318
+ const len = `LEN(${cell})`;
1319
+ let formula;
1320
+ switch (s.operator) {
1321
+ case "between":
1322
+ if (!Number.isFinite(n2)) return false;
1323
+ formula = `=AND(${len}>=${n1}, ${len}<=${n2})`;
1324
+ break;
1325
+ case "notBetween":
1326
+ if (!Number.isFinite(n2)) return false;
1327
+ formula = `=OR(${len}<${n1}, ${len}>${n2})`;
1328
+ break;
1329
+ case "equal":
1330
+ formula = `=${len}=${n1}`;
1331
+ break;
1332
+ case "notEqual":
1333
+ formula = `=${len}<>${n1}`;
1334
+ break;
1335
+ case "greater":
1336
+ formula = `=${len}>${n1}`;
1337
+ break;
1338
+ case "greaterEqual":
1339
+ formula = `=${len}>=${n1}`;
1340
+ break;
1341
+ case "less":
1342
+ formula = `=${len}<${n1}`;
1343
+ break;
1344
+ case "lessEqual":
1345
+ formula = `=${len}<=${n1}`;
1346
+ break;
1347
+ }
1348
+ builder.requireFormulaSatisfied(formula);
1349
+ break;
1350
+ }
1351
+ case "checkbox":
1352
+ builder.requireCheckbox();
1353
+ break;
1354
+ }
1355
+ builder.setAllowBlank(s.ignoreBlank);
1356
+ builder.setOptions({
1357
+ showErrorMessage: s.showErrorMessage,
1358
+ error: s.showErrorMessage ? s.errorMessage : void 0,
1359
+ showInputMessage: s.showInputMessage,
1360
+ prompt: s.showInputMessage ? s.inputMessage : void 0
1361
+ });
1362
+ dvRange.setDataValidation(builder.build());
1363
+ return true;
1364
+ }
1365
+ var CHECK_STYLE2 = {
1366
+ display: "flex",
1367
+ alignItems: "center",
1368
+ gap: 6,
1369
+ marginBottom: 8,
1370
+ cursor: "pointer"
1371
+ };
1372
+ var TEXTAREA_STYLE = {
1373
+ ...DIALOG_INPUT_STYLE,
1374
+ height: "auto",
1375
+ minHeight: 64,
1376
+ padding: "6px 8px",
1377
+ resize: "vertical",
1378
+ lineHeight: 1.4
1379
+ };
1380
+ var RANGE_NOTE_STYLE = {
1381
+ fontSize: 12,
1382
+ color: "var(--cs-chrome-muted, #605e5c)",
1383
+ marginBottom: 12
1384
+ };
1385
+ var TWO_COL_STYLE = {
1386
+ display: "grid",
1387
+ gridTemplateColumns: "1fr 1fr",
1388
+ gap: 8
1389
+ };
1390
+ function DataValidationDialog({ api, onClose }) {
1391
+ const [state, setState] = (0, import_react6.useState)(INITIAL_STATE);
1392
+ const rangeLabel = (0, import_react6.useMemo)(() => {
1393
+ const fRange = activeRange2(api);
1394
+ return fRange?.getA1Notation?.() ?? null;
1395
+ }, [api]);
1396
+ const hasSelection = activeRange2(api) !== null;
1397
+ const update = (key, value) => setState((prev) => ({ ...prev, [key]: value }));
1398
+ const apply = () => {
1399
+ if (applyValidation(api, state, false)) onClose();
1400
+ };
1401
+ const removeRule = () => {
1402
+ applyValidation(api, state, true);
1403
+ onClose();
1404
+ };
1405
+ const showOperator = usesOperator(state.ruleType);
1406
+ const showSecondOperand = showOperator && isRangeOperator(state.operator);
1407
+ const operandType = state.ruleType === "date" ? "date" : "number";
1408
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1409
+ Dialog,
1410
+ {
1411
+ title: "Data validation",
1412
+ onClose,
1413
+ width: 440,
1414
+ "data-testid": "cs-data-validation-dialog",
1415
+ footer: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1416
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1417
+ "button",
1418
+ {
1419
+ type: "button",
1420
+ style: DIALOG_BTN_SECONDARY_STYLE,
1421
+ "data-testid": "cs-data-validation-remove",
1422
+ onClick: removeRule,
1423
+ children: "Remove rule"
1424
+ }
1425
+ ),
1426
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { flex: 1 } }),
1427
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", style: DIALOG_BTN_SECONDARY_STYLE, onClick: onClose, children: "Cancel" }),
1428
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1429
+ "button",
1430
+ {
1431
+ type: "button",
1432
+ style: DIALOG_BTN_PRIMARY_STYLE,
1433
+ "data-testid": "cs-data-validation-apply",
1434
+ disabled: !hasSelection,
1435
+ onClick: apply,
1436
+ children: "Apply"
1437
+ }
1438
+ )
1439
+ ] }),
1440
+ children: [
1441
+ hasSelection ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: RANGE_NOTE_STYLE, "data-testid": "cs-data-validation-range", children: [
1442
+ "Applies to ",
1443
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("strong", { children: rangeLabel ?? "the current selection" })
1444
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: RANGE_NOTE_STYLE, "data-testid": "cs-data-validation-no-selection", children: "Select one or more cells first, then reopen this dialog." }),
1445
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { style: DIALOG_FIELD_STYLE, children: [
1446
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: DIALOG_LABEL_STYLE, children: "Criteria" }),
1447
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1448
+ "select",
1449
+ {
1450
+ style: DIALOG_INPUT_STYLE,
1451
+ "data-testid": "cs-data-validation-type",
1452
+ value: state.ruleType,
1453
+ onChange: (e) => update("ruleType", e.target.value),
1454
+ children: RULE_TYPE_OPTIONS.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: opt.value, children: opt.label }, opt.value))
1455
+ }
1456
+ )
1457
+ ] }),
1458
+ state.ruleType === "list" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { style: DIALOG_FIELD_STYLE, children: [
1459
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: DIALOG_LABEL_STYLE, children: "Items (one per line, or comma-separated)" }),
1460
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1461
+ "textarea",
1462
+ {
1463
+ style: TEXTAREA_STYLE,
1464
+ "data-testid": "cs-data-validation-list-items",
1465
+ value: state.listItems,
1466
+ placeholder: "Yes\nNo\nMaybe",
1467
+ onChange: (e) => update("listItems", e.target.value)
1468
+ }
1469
+ )
1470
+ ] }),
1471
+ showOperator && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1472
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { style: DIALOG_FIELD_STYLE, children: [
1473
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: DIALOG_LABEL_STYLE, children: "Condition" }),
1474
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1475
+ "select",
1476
+ {
1477
+ style: DIALOG_INPUT_STYLE,
1478
+ "data-testid": "cs-data-validation-operator",
1479
+ value: state.operator,
1480
+ onChange: (e) => update("operator", e.target.value),
1481
+ children: OPERATOR_OPTIONS.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: opt.value, children: opt.label }, opt.value))
1482
+ }
1483
+ )
1484
+ ] }),
1485
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: showSecondOperand ? TWO_COL_STYLE : void 0, children: [
1486
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { style: DIALOG_FIELD_STYLE, children: [
1487
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: DIALOG_LABEL_STYLE, children: showSecondOperand ? "Minimum" : "Value" }),
1488
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1489
+ "input",
1490
+ {
1491
+ style: DIALOG_INPUT_STYLE,
1492
+ "data-testid": "cs-data-validation-operand1",
1493
+ type: operandType,
1494
+ value: state.operand1,
1495
+ onChange: (e) => update("operand1", e.target.value)
1496
+ }
1497
+ )
1498
+ ] }),
1499
+ showSecondOperand && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { style: DIALOG_FIELD_STYLE, children: [
1500
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: DIALOG_LABEL_STYLE, children: "Maximum" }),
1501
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1502
+ "input",
1503
+ {
1504
+ style: DIALOG_INPUT_STYLE,
1505
+ "data-testid": "cs-data-validation-operand2",
1506
+ type: operandType,
1507
+ value: state.operand2,
1508
+ onChange: (e) => update("operand2", e.target.value)
1509
+ }
1510
+ )
1511
+ ] })
1512
+ ] })
1513
+ ] }),
1514
+ state.ruleType === "checkbox" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: RANGE_NOTE_STYLE, children: "Each cell in the selection becomes a checkbox (checked = TRUE)." }),
1515
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { style: CHECK_STYLE2, "data-testid": "cs-data-validation-ignore-blank-label", children: [
1516
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1517
+ "input",
1518
+ {
1519
+ type: "checkbox",
1520
+ "data-testid": "cs-data-validation-ignore-blank",
1521
+ checked: state.ignoreBlank,
1522
+ onChange: (e) => update("ignoreBlank", e.target.checked)
1523
+ }
1524
+ ),
1525
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "Ignore blank cells" })
1526
+ ] }),
1527
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { style: CHECK_STYLE2, children: [
1528
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1529
+ "input",
1530
+ {
1531
+ type: "checkbox",
1532
+ "data-testid": "cs-data-validation-show-input",
1533
+ checked: state.showInputMessage,
1534
+ onChange: (e) => update("showInputMessage", e.target.checked)
1535
+ }
1536
+ ),
1537
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "Show input message" })
1538
+ ] }),
1539
+ state.showInputMessage && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { style: DIALOG_FIELD_STYLE, children: [
1540
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: DIALOG_LABEL_STYLE, children: "Input message" }),
1541
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1542
+ "input",
1543
+ {
1544
+ style: DIALOG_INPUT_STYLE,
1545
+ "data-testid": "cs-data-validation-input-message",
1546
+ value: state.inputMessage,
1547
+ onChange: (e) => update("inputMessage", e.target.value)
1548
+ }
1549
+ )
1550
+ ] }),
1551
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { style: CHECK_STYLE2, children: [
1552
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1553
+ "input",
1554
+ {
1555
+ type: "checkbox",
1556
+ "data-testid": "cs-data-validation-show-error",
1557
+ checked: state.showErrorMessage,
1558
+ onChange: (e) => update("showErrorMessage", e.target.checked)
1559
+ }
1560
+ ),
1561
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "Show error message on invalid input" })
1562
+ ] }),
1563
+ state.showErrorMessage && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { style: DIALOG_FIELD_STYLE, children: [
1564
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: DIALOG_LABEL_STYLE, children: "Error message" }),
1565
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1566
+ "input",
1567
+ {
1568
+ style: DIALOG_INPUT_STYLE,
1569
+ "data-testid": "cs-data-validation-error-message",
1570
+ value: state.errorMessage,
1571
+ onChange: (e) => update("errorMessage", e.target.value)
1572
+ }
1573
+ )
1574
+ ] })
1575
+ ]
1576
+ }
1577
+ );
1578
+ }
1579
+
1580
+ // src/chrome/ConditionalFormattingDialog.tsx
1581
+ var import_react7 = require("react");
1582
+ var import_facade2 = require("@univerjs/sheets-conditional-formatting/facade");
1583
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1584
+ var RULE_TYPE_OPTIONS2 = [
1585
+ { value: "cellValue", label: "Cell value" },
1586
+ { value: "textContains", label: "Text contains" },
1587
+ { value: "topN", label: "Top N" },
1588
+ { value: "bottomN", label: "Bottom N" },
1589
+ { value: "colorScale", label: "Color scale" }
1590
+ ];
1591
+ var OPERATOR_OPTIONS2 = [
1592
+ { value: "greater", label: "Greater than" },
1593
+ { value: "greaterEqual", label: "Greater than or equal to" },
1594
+ { value: "less", label: "Less than" },
1595
+ { value: "lessEqual", label: "Less than or equal to" },
1596
+ { value: "equal", label: "Equal to" },
1597
+ { value: "notEqual", label: "Not equal to" },
1598
+ { value: "between", label: "Between" },
1599
+ { value: "notBetween", label: "Not between" }
1600
+ ];
1601
+ function isRangeOperator2(op) {
1602
+ return op === "between" || op === "notBetween";
1603
+ }
1604
+ var INITIAL_STATE2 = {
1605
+ ruleType: "cellValue",
1606
+ operator: "greater",
1607
+ operand1: "",
1608
+ operand2: "",
1609
+ text: "",
1610
+ rankN: "10",
1611
+ rankPercent: false,
1612
+ fillColor: "#fce8b2",
1613
+ textColor: "#7f6000",
1614
+ scaleMinColor: "#ffffff",
1615
+ scaleMaxColor: "#57bb8a"
1616
+ };
1617
+ function activeRange3(api) {
1618
+ return api.univer.getActiveWorkbook()?.getActiveSheet()?.getActiveRange() ?? null;
1619
+ }
1620
+ function activeSheet(api) {
1621
+ return api.univer.getActiveWorkbook()?.getActiveSheet() ?? null;
1622
+ }
1623
+ function usesHighlight(type) {
1624
+ return type !== "colorScale";
1625
+ }
1626
+ function applyRule(api, s) {
1627
+ const range = activeRange3(api);
1628
+ const sheet = activeSheet(api);
1629
+ if (!range || !sheet) return false;
1630
+ const cfSheet = sheet;
1631
+ const iRange = range.getRange();
1632
+ const n1 = Number(s.operand1);
1633
+ const n2 = Number(s.operand2);
1634
+ const rankN = Number(s.rankN);
1635
+ let builder = cfSheet.newConditionalFormattingRule();
1636
+ switch (s.ruleType) {
1637
+ case "cellValue": {
1638
+ if (!Number.isFinite(n1)) return false;
1639
+ switch (s.operator) {
1640
+ case "greater":
1641
+ builder = builder.whenNumberGreaterThan(n1);
1642
+ break;
1643
+ case "greaterEqual":
1644
+ builder = builder.whenNumberGreaterThanOrEqualTo(n1);
1645
+ break;
1646
+ case "less":
1647
+ builder = builder.whenNumberLessThan(n1);
1648
+ break;
1649
+ case "lessEqual":
1650
+ builder = builder.whenNumberLessThanOrEqualTo(n1);
1651
+ break;
1652
+ case "equal":
1653
+ builder = builder.whenNumberEqualTo(n1);
1654
+ break;
1655
+ case "notEqual":
1656
+ builder = builder.whenNumberNotEqualTo(n1);
1657
+ break;
1658
+ case "between":
1659
+ if (!Number.isFinite(n2)) return false;
1660
+ builder = builder.whenNumberBetween(n1, n2);
1661
+ break;
1662
+ case "notBetween":
1663
+ if (!Number.isFinite(n2)) return false;
1664
+ builder = builder.whenNumberNotBetween(n1, n2);
1665
+ break;
1666
+ }
1667
+ break;
1668
+ }
1669
+ case "textContains": {
1670
+ const text = s.text.trim();
1671
+ if (text.length === 0) return false;
1672
+ builder = builder.whenTextContains(text);
1673
+ break;
1674
+ }
1675
+ case "topN":
1676
+ case "bottomN": {
1677
+ if (!Number.isFinite(rankN) || rankN <= 0) return false;
1678
+ builder = builder.setRank({
1679
+ isBottom: s.ruleType === "bottomN",
1680
+ isPercent: s.rankPercent,
1681
+ value: rankN
1682
+ });
1683
+ break;
1684
+ }
1685
+ case "colorScale": {
1686
+ builder = builder.setColorScale([
1687
+ { index: 0, color: s.scaleMinColor, value: { type: "min" } },
1688
+ { index: 1, color: s.scaleMaxColor, value: { type: "max" } }
1689
+ ]);
1690
+ break;
1691
+ }
1692
+ }
1693
+ if (usesHighlight(s.ruleType)) {
1694
+ builder = builder.setBackground(s.fillColor);
1695
+ builder = builder.setFontColor(s.textColor);
1696
+ }
1697
+ const rule = builder.setRanges([iRange]).build();
1698
+ cfSheet.addConditionalFormattingRule(rule);
1699
+ return true;
1700
+ }
1701
+ var RANGE_NOTE_STYLE2 = {
1702
+ fontSize: 12,
1703
+ color: "var(--cs-chrome-muted, #605e5c)",
1704
+ marginBottom: 12
1705
+ };
1706
+ var CHECK_STYLE3 = {
1707
+ display: "flex",
1708
+ alignItems: "center",
1709
+ gap: 6,
1710
+ marginBottom: 8,
1711
+ cursor: "pointer"
1712
+ };
1713
+ var COLOR_INPUT_STYLE2 = {
1714
+ width: 48,
1715
+ height: 30,
1716
+ padding: 2,
1717
+ border: "1px solid var(--cs-chrome-border, #cdd3db)",
1718
+ borderRadius: 6,
1719
+ background: "var(--cs-chrome-input-bg, #fff)",
1720
+ cursor: "pointer"
1721
+ };
1722
+ var TWO_COL_STYLE2 = {
1723
+ display: "grid",
1724
+ gridTemplateColumns: "1fr 1fr",
1725
+ gap: 8
1726
+ };
1727
+ function ConditionalFormattingDialog({ api, onClose }) {
1728
+ const [state, setState] = (0, import_react7.useState)(INITIAL_STATE2);
1729
+ const rangeLabel = (0, import_react7.useMemo)(() => {
1730
+ const fRange = activeRange3(api);
1731
+ return fRange?.getA1Notation?.() ?? null;
1732
+ }, [api]);
1733
+ const hasSelection = activeRange3(api) !== null;
1734
+ const update = (key, value) => setState((prev) => ({ ...prev, [key]: value }));
1735
+ const apply = () => {
1736
+ if (applyRule(api, state)) onClose();
1737
+ };
1738
+ const clearRules = () => {
1739
+ const sheet = activeSheet(api);
1740
+ sheet?.clearConditionalFormatRules?.();
1741
+ onClose();
1742
+ };
1743
+ const showOperator = state.ruleType === "cellValue";
1744
+ const showSecondOperand = showOperator && isRangeOperator2(state.operator);
1745
+ const isRank = state.ruleType === "topN" || state.ruleType === "bottomN";
1746
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1747
+ Dialog,
1748
+ {
1749
+ title: "Conditional formatting",
1750
+ onClose,
1751
+ width: 440,
1752
+ "data-testid": "cs-conditional-formatting-dialog",
1753
+ footer: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1754
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1755
+ "button",
1756
+ {
1757
+ type: "button",
1758
+ style: DIALOG_BTN_SECONDARY_STYLE,
1759
+ "data-testid": "cs-conditional-formatting-clear",
1760
+ onClick: clearRules,
1761
+ children: "Clear rules"
1762
+ }
1763
+ ),
1764
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: { flex: 1 } }),
1765
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", style: DIALOG_BTN_SECONDARY_STYLE, onClick: onClose, children: "Cancel" }),
1766
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1767
+ "button",
1768
+ {
1769
+ type: "button",
1770
+ style: DIALOG_BTN_PRIMARY_STYLE,
1771
+ "data-testid": "cs-conditional-formatting-apply",
1772
+ disabled: !hasSelection,
1773
+ onClick: apply,
1774
+ children: "Apply"
1775
+ }
1776
+ )
1777
+ ] }),
1778
+ children: [
1779
+ hasSelection ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: RANGE_NOTE_STYLE2, "data-testid": "cs-conditional-formatting-range", children: [
1780
+ "Applies to ",
1781
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("strong", { children: rangeLabel ?? "the current selection" })
1782
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: RANGE_NOTE_STYLE2, "data-testid": "cs-conditional-formatting-no-selection", children: "Select one or more cells first, then reopen this dialog." }),
1783
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { style: DIALOG_FIELD_STYLE, children: [
1784
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: DIALOG_LABEL_STYLE, children: "Condition type" }),
1785
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1786
+ "select",
1787
+ {
1788
+ style: DIALOG_INPUT_STYLE,
1789
+ "data-testid": "cs-conditional-formatting-type",
1790
+ value: state.ruleType,
1791
+ onChange: (e) => update("ruleType", e.target.value),
1792
+ children: RULE_TYPE_OPTIONS2.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: opt.value, children: opt.label }, opt.value))
1793
+ }
1794
+ )
1795
+ ] }),
1796
+ showOperator && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1797
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { style: DIALOG_FIELD_STYLE, children: [
1798
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: DIALOG_LABEL_STYLE, children: "Condition" }),
1799
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1800
+ "select",
1801
+ {
1802
+ style: DIALOG_INPUT_STYLE,
1803
+ "data-testid": "cs-conditional-formatting-operator",
1804
+ value: state.operator,
1805
+ onChange: (e) => update("operator", e.target.value),
1806
+ children: OPERATOR_OPTIONS2.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: opt.value, children: opt.label }, opt.value))
1807
+ }
1808
+ )
1809
+ ] }),
1810
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: showSecondOperand ? TWO_COL_STYLE2 : void 0, children: [
1811
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { style: DIALOG_FIELD_STYLE, children: [
1812
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: DIALOG_LABEL_STYLE, children: showSecondOperand ? "Minimum" : "Value" }),
1813
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1814
+ "input",
1815
+ {
1816
+ style: DIALOG_INPUT_STYLE,
1817
+ "data-testid": "cs-conditional-formatting-operand1",
1818
+ type: "number",
1819
+ value: state.operand1,
1820
+ onChange: (e) => update("operand1", e.target.value)
1821
+ }
1822
+ )
1823
+ ] }),
1824
+ showSecondOperand && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { style: DIALOG_FIELD_STYLE, children: [
1825
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: DIALOG_LABEL_STYLE, children: "Maximum" }),
1826
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1827
+ "input",
1828
+ {
1829
+ style: DIALOG_INPUT_STYLE,
1830
+ "data-testid": "cs-conditional-formatting-operand2",
1831
+ type: "number",
1832
+ value: state.operand2,
1833
+ onChange: (e) => update("operand2", e.target.value)
1834
+ }
1835
+ )
1836
+ ] })
1837
+ ] })
1838
+ ] }),
1839
+ state.ruleType === "textContains" && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { style: DIALOG_FIELD_STYLE, children: [
1840
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: DIALOG_LABEL_STYLE, children: "Text contains" }),
1841
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1842
+ "input",
1843
+ {
1844
+ style: DIALOG_INPUT_STYLE,
1845
+ "data-testid": "cs-conditional-formatting-text",
1846
+ value: state.text,
1847
+ placeholder: "e.g. urgent",
1848
+ onChange: (e) => update("text", e.target.value)
1849
+ }
1850
+ )
1851
+ ] }),
1852
+ isRank && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1853
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { style: DIALOG_FIELD_STYLE, children: [
1854
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { style: DIALOG_LABEL_STYLE, children: [
1855
+ state.ruleType === "topN" ? "Top" : "Bottom",
1856
+ " ",
1857
+ state.rankPercent ? "percent" : "count"
1858
+ ] }),
1859
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1860
+ "input",
1861
+ {
1862
+ style: DIALOG_INPUT_STYLE,
1863
+ "data-testid": "cs-conditional-formatting-rank-n",
1864
+ type: "number",
1865
+ min: 1,
1866
+ value: state.rankN,
1867
+ onChange: (e) => update("rankN", e.target.value)
1868
+ }
1869
+ )
1870
+ ] }),
1871
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { style: CHECK_STYLE3, children: [
1872
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1873
+ "input",
1874
+ {
1875
+ type: "checkbox",
1876
+ "data-testid": "cs-conditional-formatting-rank-percent",
1877
+ checked: state.rankPercent,
1878
+ onChange: (e) => update("rankPercent", e.target.checked)
1879
+ }
1880
+ ),
1881
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: "Interpret as percent" })
1882
+ ] })
1883
+ ] }),
1884
+ state.ruleType === "colorScale" ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: TWO_COL_STYLE2, children: [
1885
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { style: DIALOG_FIELD_STYLE, children: [
1886
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: DIALOG_LABEL_STYLE, children: "Min color" }),
1887
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1888
+ "input",
1889
+ {
1890
+ style: COLOR_INPUT_STYLE2,
1891
+ "data-testid": "cs-conditional-formatting-scale-min",
1892
+ type: "color",
1893
+ value: state.scaleMinColor,
1894
+ onChange: (e) => update("scaleMinColor", e.target.value)
1895
+ }
1896
+ )
1897
+ ] }),
1898
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { style: DIALOG_FIELD_STYLE, children: [
1899
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: DIALOG_LABEL_STYLE, children: "Max color" }),
1900
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1901
+ "input",
1902
+ {
1903
+ style: COLOR_INPUT_STYLE2,
1904
+ "data-testid": "cs-conditional-formatting-scale-max",
1905
+ type: "color",
1906
+ value: state.scaleMaxColor,
1907
+ onChange: (e) => update("scaleMaxColor", e.target.value)
1908
+ }
1909
+ )
1910
+ ] })
1911
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: TWO_COL_STYLE2, children: [
1912
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { style: DIALOG_FIELD_STYLE, children: [
1913
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: DIALOG_LABEL_STYLE, children: "Fill color" }),
1914
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1915
+ "input",
1916
+ {
1917
+ style: COLOR_INPUT_STYLE2,
1918
+ "data-testid": "cs-conditional-formatting-fill-color",
1919
+ type: "color",
1920
+ value: state.fillColor,
1921
+ onChange: (e) => update("fillColor", e.target.value)
1922
+ }
1923
+ )
1924
+ ] }),
1925
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { style: DIALOG_FIELD_STYLE, children: [
1926
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: DIALOG_LABEL_STYLE, children: "Text color" }),
1927
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1928
+ "input",
1929
+ {
1930
+ style: COLOR_INPUT_STYLE2,
1931
+ "data-testid": "cs-conditional-formatting-text-color",
1932
+ type: "color",
1933
+ value: state.textColor,
1934
+ onChange: (e) => update("textColor", e.target.value)
1935
+ }
1936
+ )
1937
+ ] })
1938
+ ] })
1939
+ ]
1940
+ }
1941
+ );
1942
+ }
1943
+
1944
+ // src/chrome/CustomSortDialog.tsx
1945
+ var import_react8 = require("react");
1946
+ var import_facade3 = require("@univerjs/sheets-sort/facade");
1947
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1948
+ function activeRange4(api) {
1949
+ return api.univer.getActiveWorkbook()?.getActiveSheet()?.getActiveRange() ?? null;
1950
+ }
1951
+ function columnLetter(index) {
1952
+ let n = index;
1953
+ let label = "";
1954
+ do {
1955
+ label = String.fromCharCode(65 + n % 26) + label;
1956
+ n = Math.floor(n / 26) - 1;
1957
+ } while (n >= 0);
1958
+ return label;
1959
+ }
1960
+ function readRangeInfo(api) {
1961
+ const range = activeRange4(api);
1962
+ if (!range) return null;
1963
+ const values = range.getValues?.();
1964
+ return {
1965
+ startRow: range.getRow(),
1966
+ startColumn: range.getColumn(),
1967
+ width: range.getWidth(),
1968
+ height: range.getHeight(),
1969
+ a1: range.getA1Notation?.() ?? null,
1970
+ firstRow: values && values.length > 0 ? values[0] : null
1971
+ };
1972
+ }
1973
+ function applySort(api, info, levels, hasHeader) {
1974
+ const worksheet = api.univer.getActiveWorkbook()?.getActiveSheet();
1975
+ if (!worksheet) return false;
1976
+ const bandStartRow = hasHeader ? info.startRow + 1 : info.startRow;
1977
+ const bandHeight = hasHeader ? info.height - 1 : info.height;
1978
+ if (bandHeight <= 0) return false;
1979
+ const band = worksheet.getRange(bandStartRow, info.startColumn, bandHeight, info.width);
1980
+ const specs = levels.filter((l) => l.column >= 0 && l.column < info.width).map((l) => ({ column: l.column, ascending: l.ascending }));
1981
+ if (specs.length === 0) return false;
1982
+ band.sort(specs);
1983
+ return true;
1984
+ }
1985
+ var RANGE_NOTE_STYLE3 = {
1986
+ fontSize: 12,
1987
+ color: "var(--cs-chrome-muted, #605e5c)",
1988
+ marginBottom: 12
1989
+ };
1990
+ var CHECK_STYLE4 = {
1991
+ display: "flex",
1992
+ alignItems: "center",
1993
+ gap: 6,
1994
+ marginBottom: 12,
1995
+ cursor: "pointer"
1996
+ };
1997
+ var LEVEL_ROW_STYLE = {
1998
+ display: "grid",
1999
+ gridTemplateColumns: "auto 1fr 130px 28px",
2000
+ alignItems: "center",
2001
+ gap: 8,
2002
+ marginBottom: 8
2003
+ };
2004
+ var LEVEL_PREFIX_STYLE = {
2005
+ fontSize: 12,
2006
+ color: "var(--cs-chrome-muted, #605e5c)",
2007
+ whiteSpace: "nowrap"
2008
+ };
2009
+ var REMOVE_BTN_STYLE = {
2010
+ width: 28,
2011
+ height: 30,
2012
+ display: "inline-flex",
2013
+ alignItems: "center",
2014
+ justifyContent: "center",
2015
+ border: "1px solid var(--cs-chrome-border, #cdd3db)",
2016
+ borderRadius: 6,
2017
+ background: "var(--cs-chrome-input-bg, #fff)",
2018
+ color: "var(--cs-chrome-muted, #605e5c)",
2019
+ cursor: "pointer",
2020
+ padding: 0,
2021
+ fontSize: 16,
2022
+ lineHeight: 1
2023
+ };
2024
+ var ADD_BTN_STYLE = {
2025
+ ...DIALOG_BTN_SECONDARY_STYLE,
2026
+ marginTop: 4
2027
+ };
2028
+ function CustomSortDialog({ api, onClose }) {
2029
+ const info = (0, import_react8.useMemo)(() => readRangeInfo(api), [api]);
2030
+ const hasSelection = info !== null;
2031
+ const width = info?.width ?? 1;
2032
+ const [hasHeader, setHasHeader] = (0, import_react8.useState)(false);
2033
+ const [levels, setLevels] = (0, import_react8.useState)([{ column: 0, ascending: true }]);
2034
+ const columnOptions = (0, import_react8.useMemo)(() => {
2035
+ const startCol = info?.startColumn ?? 0;
2036
+ return Array.from({ length: width }, (_, offset) => {
2037
+ const headerCell = hasHeader ? info?.firstRow?.[offset] : void 0;
2038
+ const headerText = headerCell !== void 0 && headerCell !== null && String(headerCell).trim() !== "" ? String(headerCell) : null;
2039
+ const letter = columnLetter(startCol + offset);
2040
+ return {
2041
+ value: offset,
2042
+ label: headerText ? `${headerText} (${letter})` : `Column ${letter}`
2043
+ };
2044
+ });
2045
+ }, [width, hasHeader, info]);
2046
+ const updateLevel = (index, patch) => setLevels((prev) => prev.map((l, i) => i === index ? { ...l, ...patch } : l));
2047
+ const addLevel = () => {
2048
+ const used = new Set(levels.map((l) => l.column));
2049
+ let next = 0;
2050
+ for (let c = 0; c < width; c += 1) {
2051
+ if (!used.has(c)) {
2052
+ next = c;
2053
+ break;
2054
+ }
2055
+ }
2056
+ setLevels((prev) => [...prev, { column: next, ascending: true }]);
2057
+ };
2058
+ const removeLevel = (index) => setLevels((prev) => prev.length <= 1 ? prev : prev.filter((_, i) => i !== index));
2059
+ const apply = () => {
2060
+ if (info && applySort(api, info, levels, hasHeader)) onClose();
2061
+ };
2062
+ const canApply = hasSelection && levels.length > 0;
2063
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2064
+ Dialog,
2065
+ {
2066
+ title: "Sort range",
2067
+ onClose,
2068
+ width: 480,
2069
+ "data-testid": "cs-custom-sort-dialog",
2070
+ footer: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
2071
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", style: DIALOG_BTN_SECONDARY_STYLE, onClick: onClose, children: "Cancel" }),
2072
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2073
+ "button",
2074
+ {
2075
+ type: "button",
2076
+ style: DIALOG_BTN_PRIMARY_STYLE,
2077
+ "data-testid": "cs-custom-sort-apply",
2078
+ disabled: !canApply,
2079
+ onClick: apply,
2080
+ children: "Sort"
2081
+ }
2082
+ )
2083
+ ] }),
2084
+ children: [
2085
+ hasSelection ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: RANGE_NOTE_STYLE3, "data-testid": "cs-custom-sort-range", children: [
2086
+ "Sort ",
2087
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("strong", { children: info?.a1 ?? "the current selection" })
2088
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: RANGE_NOTE_STYLE3, "data-testid": "cs-custom-sort-no-selection", children: "Select the range you want to sort first, then reopen this dialog." }),
2089
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("label", { style: CHECK_STYLE4, "data-testid": "cs-custom-sort-has-header-label", children: [
2090
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2091
+ "input",
2092
+ {
2093
+ type: "checkbox",
2094
+ "data-testid": "cs-custom-sort-has-header",
2095
+ checked: hasHeader,
2096
+ onChange: (e) => setHasHeader(e.target.checked)
2097
+ }
2098
+ ),
2099
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: "Data has header row" })
2100
+ ] }),
2101
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: DIALOG_FIELD_STYLE, children: [
2102
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: DIALOG_LABEL_STYLE, children: "Sort by" }),
2103
+ levels.map((level, index) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: LEVEL_ROW_STYLE, "data-testid": `cs-custom-sort-level-${index}`, children: [
2104
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: LEVEL_PREFIX_STYLE, children: index === 0 ? "Sort by" : "then by" }),
2105
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2106
+ "select",
2107
+ {
2108
+ style: DIALOG_INPUT_STYLE,
2109
+ "data-testid": `cs-custom-sort-column-${index}`,
2110
+ value: level.column,
2111
+ onChange: (e) => updateLevel(index, { column: Number(e.target.value) }),
2112
+ children: columnOptions.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: opt.value, children: opt.label }, opt.value))
2113
+ }
2114
+ ),
2115
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2116
+ "select",
2117
+ {
2118
+ style: DIALOG_INPUT_STYLE,
2119
+ "data-testid": `cs-custom-sort-order-${index}`,
2120
+ value: level.ascending ? "asc" : "desc",
2121
+ onChange: (e) => updateLevel(index, { ascending: e.target.value === "asc" }),
2122
+ children: [
2123
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "asc", children: "A \u2192 Z (ascending)" }),
2124
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("option", { value: "desc", children: "Z \u2192 A (descending)" })
2125
+ ]
2126
+ }
2127
+ ),
2128
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2129
+ "button",
2130
+ {
2131
+ type: "button",
2132
+ style: REMOVE_BTN_STYLE,
2133
+ "data-testid": `cs-custom-sort-remove-${index}`,
2134
+ "aria-label": "Remove sort level",
2135
+ disabled: levels.length <= 1,
2136
+ onClick: () => removeLevel(index),
2137
+ children: "\u2212"
2138
+ }
2139
+ )
2140
+ ] }, index)),
2141
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2142
+ "button",
2143
+ {
2144
+ type: "button",
2145
+ style: ADD_BTN_STYLE,
2146
+ "data-testid": "cs-custom-sort-add-level",
2147
+ disabled: levels.length >= width,
2148
+ onClick: addLevel,
2149
+ children: "Add another sort level"
2150
+ }
2151
+ )
2152
+ ] })
2153
+ ]
2154
+ }
2155
+ );
2156
+ }
2157
+
2158
+ // src/chrome/PasteSpecialDialog.tsx
2159
+ var import_react9 = require("react");
2160
+ var import_facade4 = require("@univerjs/sheets-ui/facade");
2161
+ var import_jsx_runtime10 = require("react/jsx-runtime");
2162
+ var MODE_OPTIONS = [
2163
+ {
2164
+ value: "values",
2165
+ label: "Values only",
2166
+ hint: "Paste cell values, dropping formatting and formulas."
2167
+ },
2168
+ {
2169
+ value: "formats",
2170
+ label: "Formats only",
2171
+ hint: "Paste number formats, fonts, borders and fills \u2014 no values."
2172
+ },
2173
+ {
2174
+ value: "formulas",
2175
+ label: "Formulas only",
2176
+ hint: "Paste formulas, adjusting relative references."
2177
+ },
2178
+ {
2179
+ value: "transpose",
2180
+ label: "Transpose",
2181
+ hint: "Flip the current selection \u2014 rows become columns and columns become rows, in place."
2182
+ }
2183
+ ];
2184
+ var PASTE_HOOK = {
2185
+ values: "special-paste-value",
2186
+ formats: "special-paste-format",
2187
+ formulas: "special-paste-formula"
2188
+ };
2189
+ var SHEET_PASTE_COMMAND = "sheet.command.paste";
2190
+ function activeRange5(api) {
2191
+ return api.univer.getActiveWorkbook()?.getActiveSheet()?.getActiveRange() ?? null;
2192
+ }
2193
+ function transposeSelection(api) {
2194
+ const sheet = api.univer.getActiveWorkbook()?.getActiveSheet();
2195
+ const range = sheet?.getActiveRange();
2196
+ if (!sheet || !range) return false;
2197
+ const rows = range.getHeight();
2198
+ const cols = range.getWidth();
2199
+ if (rows <= 0 || cols <= 0) return false;
2200
+ const values = range.getValues();
2201
+ const transposed = [];
2202
+ for (let c = 0; c < cols; c++) {
2203
+ const newRow = [];
2204
+ for (let r = 0; r < rows; r++) {
2205
+ newRow.push(values[r]?.[c] ?? null);
2206
+ }
2207
+ transposed.push(newRow);
2208
+ }
2209
+ const target = sheet.getRange(range.getRow(), range.getColumn(), cols, rows);
2210
+ target.setValues(transposed);
2211
+ return true;
2212
+ }
2213
+ async function applyPasteSpecial(api, mode) {
2214
+ if (activeRange5(api) === null) return false;
2215
+ if (mode === "transpose") {
2216
+ return transposeSelection(api);
2217
+ }
2218
+ return api.executeCommand(SHEET_PASTE_COMMAND, { value: PASTE_HOOK[mode] });
2219
+ }
2220
+ var MODE_RADIO_ROW_STYLE = {
2221
+ display: "flex",
2222
+ alignItems: "flex-start",
2223
+ gap: 8,
2224
+ padding: "8px 10px",
2225
+ border: "1px solid var(--cs-chrome-border, #cdd3db)",
2226
+ borderRadius: 6,
2227
+ marginBottom: 8,
2228
+ cursor: "pointer"
2229
+ };
2230
+ var MODE_RADIO_ROW_ACTIVE_STYLE = {
2231
+ ...MODE_RADIO_ROW_STYLE,
2232
+ borderColor: "var(--cs-chrome-active-fg, #0e7490)",
2233
+ background: "var(--cs-chrome-active-bg, rgba(14, 116, 144, 0.06))"
2234
+ };
2235
+ var MODE_LABEL_STYLE = {
2236
+ fontSize: 13,
2237
+ fontWeight: 500,
2238
+ color: "var(--cs-chrome-fg, #201f1e)"
2239
+ };
2240
+ var MODE_HINT_STYLE = {
2241
+ fontSize: 12,
2242
+ color: "var(--cs-chrome-muted, #605e5c)",
2243
+ marginTop: 2,
2244
+ lineHeight: 1.35
2245
+ };
2246
+ var RANGE_NOTE_STYLE4 = {
2247
+ fontSize: 12,
2248
+ color: "var(--cs-chrome-muted, #605e5c)",
2249
+ marginBottom: 12
2250
+ };
2251
+ function PasteSpecialDialog({ api, onClose }) {
2252
+ const [mode, setMode] = (0, import_react9.useState)("values");
2253
+ const rangeLabel = (0, import_react9.useMemo)(() => {
2254
+ const fRange = activeRange5(api);
2255
+ return fRange?.getA1Notation?.() ?? null;
2256
+ }, [api]);
2257
+ const hasSelection = activeRange5(api) !== null;
2258
+ const apply = () => {
2259
+ void applyPasteSpecial(api, mode).then((ok) => {
2260
+ if (ok) onClose();
2261
+ });
2262
+ };
2263
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
2264
+ Dialog,
2265
+ {
2266
+ title: "Paste special",
2267
+ onClose,
2268
+ width: 440,
2269
+ "data-testid": "cs-paste-special-dialog",
2270
+ footer: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
2271
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", style: DIALOG_BTN_SECONDARY_STYLE, onClick: onClose, children: "Cancel" }),
2272
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2273
+ "button",
2274
+ {
2275
+ type: "button",
2276
+ style: DIALOG_BTN_PRIMARY_STYLE,
2277
+ "data-testid": "cs-paste-special-apply",
2278
+ disabled: !hasSelection,
2279
+ onClick: apply,
2280
+ children: "Paste"
2281
+ }
2282
+ )
2283
+ ] }),
2284
+ children: [
2285
+ hasSelection ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: RANGE_NOTE_STYLE4, "data-testid": "cs-paste-special-range", children: [
2286
+ mode === "transpose" ? "Transposes" : "Pastes into",
2287
+ " ",
2288
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("strong", { children: rangeLabel ?? "the current selection" })
2289
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: RANGE_NOTE_STYLE4, "data-testid": "cs-paste-special-no-selection", children: "Select the destination cell(s) first, then reopen this dialog." }),
2290
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: DIALOG_FIELD_STYLE, children: [
2291
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: DIALOG_LABEL_STYLE, children: "Paste" }),
2292
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { role: "radiogroup", "aria-label": "Paste special mode", children: MODE_OPTIONS.map((opt) => {
2293
+ const active = mode === opt.value;
2294
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
2295
+ "label",
2296
+ {
2297
+ style: active ? MODE_RADIO_ROW_ACTIVE_STYLE : MODE_RADIO_ROW_STYLE,
2298
+ "data-testid": `cs-paste-special-mode-${opt.value}`,
2299
+ children: [
2300
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2301
+ "input",
2302
+ {
2303
+ type: "radio",
2304
+ name: "cs-paste-special-mode",
2305
+ value: opt.value,
2306
+ checked: active,
2307
+ onChange: () => setMode(opt.value),
2308
+ style: { marginTop: 2 }
2309
+ }
2310
+ ),
2311
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { children: [
2312
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: MODE_LABEL_STYLE, children: opt.label }),
2313
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: MODE_HINT_STYLE, children: opt.hint })
2314
+ ] })
2315
+ ]
2316
+ },
2317
+ opt.value
2318
+ );
2319
+ }) })
2320
+ ] })
2321
+ ]
2322
+ }
2323
+ );
2324
+ }
2325
+
2326
+ // src/chrome/InsertFunctionDialog.tsx
2327
+ var import_react10 = require("react");
2328
+ var import_jsx_runtime11 = require("react/jsx-runtime");
2329
+ var FUNCTIONS = [
2330
+ // Math
2331
+ {
2332
+ name: "SUM",
2333
+ category: "Math",
2334
+ description: "Sum of a set of numbers.",
2335
+ syntax: "SUM(value1, [value2, \u2026])"
2336
+ },
2337
+ {
2338
+ name: "PRODUCT",
2339
+ category: "Math",
2340
+ description: "Product of a set of numbers.",
2341
+ syntax: "PRODUCT(factor1, [factor2, \u2026])"
2342
+ },
2343
+ {
2344
+ name: "ROUND",
2345
+ category: "Math",
2346
+ description: "Round a number to a given number of places.",
2347
+ syntax: "ROUND(value, [places])"
2348
+ },
2349
+ {
2350
+ name: "ABS",
2351
+ category: "Math",
2352
+ description: "Absolute value of a number.",
2353
+ syntax: "ABS(value)"
2354
+ },
2355
+ {
2356
+ name: "MOD",
2357
+ category: "Math",
2358
+ description: "Remainder after division.",
2359
+ syntax: "MOD(dividend, divisor)"
2360
+ },
2361
+ {
2362
+ name: "POWER",
2363
+ category: "Math",
2364
+ description: "A number raised to a power.",
2365
+ syntax: "POWER(base, exponent)"
2366
+ },
2367
+ // Statistical
2368
+ {
2369
+ name: "AVERAGE",
2370
+ category: "Statistical",
2371
+ description: "Arithmetic mean of a set of numbers.",
2372
+ syntax: "AVERAGE(value1, [value2, \u2026])"
2373
+ },
2374
+ {
2375
+ name: "COUNT",
2376
+ category: "Statistical",
2377
+ description: "Count of numeric values in a range.",
2378
+ syntax: "COUNT(value1, [value2, \u2026])"
2379
+ },
2380
+ {
2381
+ name: "COUNTA",
2382
+ category: "Statistical",
2383
+ description: "Count of non-empty values in a range.",
2384
+ syntax: "COUNTA(value1, [value2, \u2026])"
2385
+ },
2386
+ {
2387
+ name: "MAX",
2388
+ category: "Statistical",
2389
+ description: "Largest value in a set.",
2390
+ syntax: "MAX(value1, [value2, \u2026])"
2391
+ },
2392
+ {
2393
+ name: "MIN",
2394
+ category: "Statistical",
2395
+ description: "Smallest value in a set.",
2396
+ syntax: "MIN(value1, [value2, \u2026])"
2397
+ },
2398
+ {
2399
+ name: "MEDIAN",
2400
+ category: "Statistical",
2401
+ description: "Median of a set of numbers.",
2402
+ syntax: "MEDIAN(value1, [value2, \u2026])"
2403
+ },
2404
+ // Logical
2405
+ {
2406
+ name: "IF",
2407
+ category: "Logical",
2408
+ description: "Return one value if a condition is true, another if false.",
2409
+ syntax: "IF(condition, value_if_true, value_if_false)"
2410
+ },
2411
+ {
2412
+ name: "IFS",
2413
+ category: "Logical",
2414
+ description: "Test multiple conditions, return the first match.",
2415
+ syntax: "IFS(condition1, value1, [condition2, value2, \u2026])"
2416
+ },
2417
+ {
2418
+ name: "AND",
2419
+ category: "Logical",
2420
+ description: "True when all arguments are true.",
2421
+ syntax: "AND(logical1, [logical2, \u2026])"
2422
+ },
2423
+ {
2424
+ name: "OR",
2425
+ category: "Logical",
2426
+ description: "True when any argument is true.",
2427
+ syntax: "OR(logical1, [logical2, \u2026])"
2428
+ },
2429
+ {
2430
+ name: "IFERROR",
2431
+ category: "Logical",
2432
+ description: "Return a fallback when a formula errors.",
2433
+ syntax: "IFERROR(value, value_if_error)"
2434
+ },
2435
+ // Lookup
2436
+ {
2437
+ name: "VLOOKUP",
2438
+ category: "Lookup",
2439
+ description: "Search a column for a key, return a value from the same row.",
2440
+ syntax: "VLOOKUP(search_key, range, index, [is_sorted])"
2441
+ },
2442
+ {
2443
+ name: "HLOOKUP",
2444
+ category: "Lookup",
2445
+ description: "Search a row for a key, return a value from the same column.",
2446
+ syntax: "HLOOKUP(search_key, range, index, [is_sorted])"
2447
+ },
2448
+ {
2449
+ name: "INDEX",
2450
+ category: "Lookup",
2451
+ description: "Value at a given row/column offset in a range.",
2452
+ syntax: "INDEX(reference, [row], [column])"
2453
+ },
2454
+ {
2455
+ name: "MATCH",
2456
+ category: "Lookup",
2457
+ description: "Position of a value within a range.",
2458
+ syntax: "MATCH(search_key, range, [search_type])"
2459
+ },
2460
+ {
2461
+ name: "XLOOKUP",
2462
+ category: "Lookup",
2463
+ description: "Search a range and return a corresponding value.",
2464
+ syntax: "XLOOKUP(search_key, lookup_range, result_range)"
2465
+ },
2466
+ // Text
2467
+ {
2468
+ name: "CONCATENATE",
2469
+ category: "Text",
2470
+ description: "Join strings end to end.",
2471
+ syntax: "CONCATENATE(string1, [string2, \u2026])"
2472
+ },
2473
+ {
2474
+ name: "LEFT",
2475
+ category: "Text",
2476
+ description: "Leftmost characters of a string.",
2477
+ syntax: "LEFT(string, [num_chars])"
2478
+ },
2479
+ {
2480
+ name: "RIGHT",
2481
+ category: "Text",
2482
+ description: "Rightmost characters of a string.",
2483
+ syntax: "RIGHT(string, [num_chars])"
2484
+ },
2485
+ {
2486
+ name: "MID",
2487
+ category: "Text",
2488
+ description: "Characters from the middle of a string.",
2489
+ syntax: "MID(string, start, length)"
2490
+ },
2491
+ { name: "LEN", category: "Text", description: "Length of a string.", syntax: "LEN(string)" },
2492
+ {
2493
+ name: "TRIM",
2494
+ category: "Text",
2495
+ description: "Remove leading/trailing/duplicate spaces.",
2496
+ syntax: "TRIM(string)"
2497
+ },
2498
+ // Date
2499
+ { name: "TODAY", category: "Date", description: "Today's date.", syntax: "TODAY()" },
2500
+ { name: "NOW", category: "Date", description: "Current date and time.", syntax: "NOW()" },
2501
+ {
2502
+ name: "DATE",
2503
+ category: "Date",
2504
+ description: "Build a date from year, month, day.",
2505
+ syntax: "DATE(year, month, day)"
2506
+ },
2507
+ {
2508
+ name: "DATEDIF",
2509
+ category: "Date",
2510
+ description: "Difference between two dates in a chosen unit.",
2511
+ syntax: "DATEDIF(start_date, end_date, unit)"
2512
+ }
2513
+ ];
2514
+ var CATEGORY_ORDER = ["Math", "Statistical", "Logical", "Lookup", "Text", "Date"];
2515
+ function activeRange6(api) {
2516
+ return api.univer.getActiveWorkbook()?.getActiveSheet()?.getActiveRange() ?? null;
2517
+ }
2518
+ function insertFunction(api, fn) {
2519
+ const range = activeRange6(api);
2520
+ if (!range) return false;
2521
+ const zeroArg = fn.syntax.endsWith("()");
2522
+ const formula = zeroArg ? `=${fn.name}()` : `=${fn.name}(`;
2523
+ range.setValue(formula);
2524
+ api.focus();
2525
+ return true;
2526
+ }
2527
+ var SEARCH_STYLE = {
2528
+ ...DIALOG_INPUT_STYLE,
2529
+ width: "100%",
2530
+ marginBottom: 12
2531
+ };
2532
+ var RANGE_NOTE_STYLE5 = {
2533
+ fontSize: 12,
2534
+ color: "var(--cs-chrome-muted, #605e5c)",
2535
+ marginBottom: 12
2536
+ };
2537
+ var LIST_STYLE = {
2538
+ maxHeight: 280,
2539
+ overflow: "auto",
2540
+ border: "1px solid var(--cs-chrome-border, #edeff3)",
2541
+ borderRadius: 8
2542
+ };
2543
+ var GROUP_HEADER_STYLE = {
2544
+ position: "sticky",
2545
+ top: 0,
2546
+ padding: "6px 12px",
2547
+ fontSize: 11,
2548
+ fontWeight: 600,
2549
+ letterSpacing: 0.4,
2550
+ textTransform: "uppercase",
2551
+ color: "var(--cs-chrome-muted, #605e5c)",
2552
+ background: "var(--cs-chrome-input-bg, #fff)",
2553
+ borderBottom: "1px solid var(--cs-chrome-border, #edeff3)"
2554
+ };
2555
+ function itemStyle(selected) {
2556
+ return {
2557
+ display: "block",
2558
+ width: "100%",
2559
+ textAlign: "left",
2560
+ padding: "8px 12px",
2561
+ border: "none",
2562
+ borderBottom: "1px solid var(--cs-chrome-border, #f3f4f6)",
2563
+ background: selected ? "var(--cs-chrome-hover-bg, #eef6f8)" : "transparent",
2564
+ color: "var(--cs-chrome-fg, #201f1e)",
2565
+ font: "inherit",
2566
+ fontSize: 13,
2567
+ cursor: "pointer"
2568
+ };
2569
+ }
2570
+ var ITEM_NAME_STYLE = {
2571
+ fontWeight: 600,
2572
+ fontFamily: "var(--cs-chrome-mono, ui-monospace, SFMono-Regular, Menlo, monospace)"
2573
+ };
2574
+ var ITEM_DESC_STYLE = {
2575
+ fontSize: 12,
2576
+ color: "var(--cs-chrome-muted, #605e5c)",
2577
+ marginTop: 2
2578
+ };
2579
+ var PREVIEW_STYLE = {
2580
+ marginTop: 12,
2581
+ padding: "10px 12px",
2582
+ borderRadius: 8,
2583
+ background: "var(--cs-chrome-subtle-bg, #f6f8fa)",
2584
+ fontSize: 12,
2585
+ color: "var(--cs-chrome-fg, #201f1e)"
2586
+ };
2587
+ var PREVIEW_SYNTAX_STYLE = {
2588
+ fontFamily: "var(--cs-chrome-mono, ui-monospace, SFMono-Regular, Menlo, monospace)",
2589
+ fontWeight: 600,
2590
+ marginBottom: 4
2591
+ };
2592
+ function InsertFunctionDialog({ api, onClose }) {
2593
+ const [query, setQuery] = (0, import_react10.useState)("");
2594
+ const [selectedName, setSelectedName] = (0, import_react10.useState)(null);
2595
+ const rangeLabel = (0, import_react10.useMemo)(() => {
2596
+ const fRange = activeRange6(api);
2597
+ return fRange?.getA1Notation?.() ?? null;
2598
+ }, [api]);
2599
+ const hasSelection = activeRange6(api) !== null;
2600
+ const filtered = (0, import_react10.useMemo)(() => {
2601
+ const q = query.trim().toLowerCase();
2602
+ if (!q) return FUNCTIONS;
2603
+ return FUNCTIONS.filter(
2604
+ (fn) => fn.name.toLowerCase().includes(q) || fn.description.toLowerCase().includes(q) || fn.category.toLowerCase().includes(q)
2605
+ );
2606
+ }, [query]);
2607
+ const grouped = (0, import_react10.useMemo)(() => {
2608
+ const byCat = /* @__PURE__ */ new Map();
2609
+ for (const fn of filtered) {
2610
+ const list = byCat.get(fn.category);
2611
+ if (list) list.push(fn);
2612
+ else byCat.set(fn.category, [fn]);
2613
+ }
2614
+ return CATEGORY_ORDER.filter((c) => byCat.has(c)).map((c) => ({
2615
+ category: c,
2616
+ items: byCat.get(c)
2617
+ }));
2618
+ }, [filtered]);
2619
+ const selected = (0, import_react10.useMemo)(
2620
+ () => FUNCTIONS.find((fn) => fn.name === selectedName) ?? null,
2621
+ [selectedName]
2622
+ );
2623
+ const choose = (fn) => {
2624
+ if (insertFunction(api, fn)) onClose();
2625
+ };
2626
+ const insertSelected = () => {
2627
+ if (selected && insertFunction(api, selected)) onClose();
2628
+ };
2629
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
2630
+ Dialog,
2631
+ {
2632
+ title: "Insert function",
2633
+ onClose,
2634
+ width: 460,
2635
+ "data-testid": "cs-insert-function-dialog",
2636
+ footer: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
2637
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", style: DIALOG_BTN_SECONDARY_STYLE, onClick: onClose, children: "Cancel" }),
2638
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2639
+ "button",
2640
+ {
2641
+ type: "button",
2642
+ style: DIALOG_BTN_PRIMARY_STYLE,
2643
+ "data-testid": "cs-insert-function-insert",
2644
+ disabled: !hasSelection || !selected,
2645
+ onClick: insertSelected,
2646
+ children: "Insert"
2647
+ }
2648
+ )
2649
+ ] }),
2650
+ children: [
2651
+ hasSelection ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: RANGE_NOTE_STYLE5, "data-testid": "cs-insert-function-range", children: [
2652
+ "Inserts into ",
2653
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("strong", { children: rangeLabel ?? "the active cell" })
2654
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: RANGE_NOTE_STYLE5, "data-testid": "cs-insert-function-no-selection", children: "Select a cell first, then reopen this dialog." }),
2655
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2656
+ "input",
2657
+ {
2658
+ style: SEARCH_STYLE,
2659
+ "data-testid": "cs-insert-function-search",
2660
+ type: "search",
2661
+ placeholder: "Search functions (name, category, or description)",
2662
+ value: query,
2663
+ autoFocus: true,
2664
+ onChange: (e) => setQuery(e.target.value)
2665
+ }
2666
+ ),
2667
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
2668
+ "div",
2669
+ {
2670
+ style: LIST_STYLE,
2671
+ role: "listbox",
2672
+ "aria-label": "Functions",
2673
+ "data-testid": "cs-insert-function-list",
2674
+ children: [
2675
+ grouped.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: { padding: "12px", fontSize: 13, color: "var(--cs-chrome-muted, #605e5c)" }, children: [
2676
+ "No functions match \u201C",
2677
+ query,
2678
+ "\u201D."
2679
+ ] }),
2680
+ grouped.map((group) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
2681
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: GROUP_HEADER_STYLE, children: group.category }),
2682
+ group.items.map((fn) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
2683
+ "button",
2684
+ {
2685
+ type: "button",
2686
+ role: "option",
2687
+ "aria-selected": selectedName === fn.name,
2688
+ "data-testid": `cs-insert-function-item-${fn.name}`,
2689
+ style: itemStyle(selectedName === fn.name),
2690
+ onClick: () => setSelectedName(fn.name),
2691
+ onDoubleClick: () => choose(fn),
2692
+ children: [
2693
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: ITEM_NAME_STYLE, children: fn.name }),
2694
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: ITEM_DESC_STYLE, children: fn.description })
2695
+ ]
2696
+ },
2697
+ fn.name
2698
+ ))
2699
+ ] }, group.category))
2700
+ ]
2701
+ }
2702
+ ),
2703
+ selected && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: PREVIEW_STYLE, "data-testid": "cs-insert-function-preview", children: [
2704
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: PREVIEW_SYNTAX_STYLE, children: selected.syntax }),
2705
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { children: selected.description })
2706
+ ] })
2707
+ ]
2708
+ }
2709
+ );
2710
+ }
2711
+
2712
+ // src/chrome/NameManagerDialog.tsx
2713
+ var import_react11 = require("react");
2714
+ var import_jsx_runtime12 = require("react/jsx-runtime");
2715
+ var NAME_RE = /^[A-Za-z_][A-Za-z0-9_.]*$/;
2716
+ function workbook(api) {
2717
+ return api.univer.getActiveWorkbook() ?? null;
2718
+ }
2719
+ function readRows(api) {
2720
+ const wb = workbook(api);
2721
+ if (!wb) return [];
2722
+ return wb.getDefinedNames().map((dn) => ({
2723
+ name: dn.getName(),
2724
+ refersTo: dn.getFormulaOrRefString()
2725
+ }));
2726
+ }
2727
+ function selectionRef(api) {
2728
+ const range = workbook(api)?.getActiveSheet()?.getActiveRange();
2729
+ return range?.getA1Notation(true) ?? "";
2730
+ }
2731
+ var ROW_STYLE4 = {
2732
+ display: "grid",
2733
+ gridTemplateColumns: "1fr 1.3fr auto",
2734
+ alignItems: "center",
2735
+ gap: 8,
2736
+ padding: "6px 8px",
2737
+ borderBottom: "1px solid var(--cs-chrome-border, #edeff3)"
2738
+ };
2739
+ var LIST_STYLE2 = {
2740
+ border: "1px solid var(--cs-chrome-border, #cdd3db)",
2741
+ borderRadius: 6,
2742
+ maxHeight: 200,
2743
+ overflow: "auto",
2744
+ marginBottom: 16
2745
+ };
2746
+ var EMPTY_STYLE = {
2747
+ padding: "16px 8px",
2748
+ fontSize: 13,
2749
+ color: "var(--cs-chrome-muted, #605e5c)",
2750
+ textAlign: "center"
2751
+ };
2752
+ var REF_CELL_STYLE = {
2753
+ fontSize: 12,
2754
+ color: "var(--cs-chrome-muted, #605e5c)",
2755
+ overflow: "hidden",
2756
+ textOverflow: "ellipsis",
2757
+ whiteSpace: "nowrap"
2758
+ };
2759
+ var ROW_ACTIONS_STYLE = {
2760
+ display: "flex",
2761
+ gap: 4
2762
+ };
2763
+ var SMALL_BTN_STYLE = {
2764
+ ...DIALOG_BTN_SECONDARY_STYLE,
2765
+ height: 24,
2766
+ padding: "0 8px",
2767
+ fontSize: 12
2768
+ };
2769
+ var SECTION_TITLE_STYLE = {
2770
+ fontSize: 13,
2771
+ fontWeight: 600,
2772
+ color: "var(--cs-chrome-fg, #201f1e)",
2773
+ margin: "0 0 8px"
2774
+ };
2775
+ var ERROR_STYLE = {
2776
+ fontSize: 12,
2777
+ color: "var(--cs-chrome-danger, #b91c1c)",
2778
+ marginBottom: 8
2779
+ };
2780
+ var TWO_COL_STYLE3 = {
2781
+ display: "grid",
2782
+ gridTemplateColumns: "1fr 1.3fr",
2783
+ gap: 8
2784
+ };
2785
+ function NameManagerDialog({ api, onClose }) {
2786
+ const [rows, setRows] = (0, import_react11.useState)(() => readRows(api));
2787
+ const [editingOriginal, setEditingOriginal] = (0, import_react11.useState)(null);
2788
+ const [nameInput, setNameInput] = (0, import_react11.useState)("");
2789
+ const [refInput, setRefInput] = (0, import_react11.useState)("");
2790
+ const [error, setError] = (0, import_react11.useState)(null);
2791
+ const initialSelRef = (0, import_react11.useMemo)(() => selectionRef(api), [api]);
2792
+ const refresh = (0, import_react11.useCallback)(() => setRows(readRows(api)), [api]);
2793
+ const resetForm = (0, import_react11.useCallback)(() => {
2794
+ setEditingOriginal(null);
2795
+ setNameInput("");
2796
+ setRefInput("");
2797
+ setError(null);
2798
+ }, []);
2799
+ const startEdit = (row) => {
2800
+ setEditingOriginal(row.name);
2801
+ setNameInput(row.name);
2802
+ setRefInput(row.refersTo);
2803
+ setError(null);
2804
+ };
2805
+ const useSelection = () => {
2806
+ const ref = selectionRef(api);
2807
+ if (ref) setRefInput(ref);
2808
+ };
2809
+ const remove = (row) => {
2810
+ const wb = workbook(api);
2811
+ if (!wb) return;
2812
+ wb.deleteDefinedName(row.name);
2813
+ if (editingOriginal === row.name) resetForm();
2814
+ refresh();
2815
+ };
2816
+ const submit = () => {
2817
+ const wb = workbook(api);
2818
+ if (!wb) {
2819
+ setError("No active workbook.");
2820
+ return;
2821
+ }
2822
+ const name = nameInput.trim();
2823
+ const ref = refInput.trim();
2824
+ if (!name) {
2825
+ setError("Enter a name.");
2826
+ return;
2827
+ }
2828
+ if (!NAME_RE.test(name)) {
2829
+ setError("Names must start with a letter or underscore and contain no spaces.");
2830
+ return;
2831
+ }
2832
+ if (!ref) {
2833
+ setError("Enter a range or formula (e.g. Sheet1!A1:B2).");
2834
+ return;
2835
+ }
2836
+ const collides = rows.some(
2837
+ (r) => r.name.toLowerCase() === name.toLowerCase() && r.name !== editingOriginal
2838
+ );
2839
+ if (collides) {
2840
+ setError(`A defined name "${name}" already exists.`);
2841
+ return;
2842
+ }
2843
+ if (editingOriginal === null) {
2844
+ wb.insertDefinedName(name, ref);
2845
+ } else {
2846
+ const existing = wb.getDefinedName(editingOriginal);
2847
+ if (existing) {
2848
+ const param = existing.toBuilder().setName(name).setRef(ref).build();
2849
+ wb.updateDefinedNameBuilder(param);
2850
+ } else {
2851
+ wb.insertDefinedName(name, ref);
2852
+ }
2853
+ }
2854
+ refresh();
2855
+ resetForm();
2856
+ };
2857
+ const isEditing = editingOriginal !== null;
2858
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
2859
+ Dialog,
2860
+ {
2861
+ title: "Name manager",
2862
+ onClose,
2863
+ width: 480,
2864
+ "data-testid": "cs-name-manager-dialog",
2865
+ footer: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
2866
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: { flex: 1 } }),
2867
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { type: "button", style: DIALOG_BTN_SECONDARY_STYLE, onClick: onClose, children: "Close" })
2868
+ ] }),
2869
+ children: [
2870
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h3", { style: SECTION_TITLE_STYLE, children: "Defined names" }),
2871
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: LIST_STYLE2, "data-testid": "cs-name-manager-list", children: rows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: EMPTY_STYLE, "data-testid": "cs-name-manager-empty", children: "No defined names yet. Add one below." }) : rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: ROW_STYLE4, "data-testid": "cs-name-manager-row", children: [
2872
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: { fontWeight: 500 }, children: row.name }),
2873
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: REF_CELL_STYLE, title: row.refersTo, children: row.refersTo }),
2874
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { style: ROW_ACTIONS_STYLE, children: [
2875
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2876
+ "button",
2877
+ {
2878
+ type: "button",
2879
+ style: SMALL_BTN_STYLE,
2880
+ "data-testid": "cs-name-manager-edit",
2881
+ onClick: () => startEdit(row),
2882
+ children: "Edit"
2883
+ }
2884
+ ),
2885
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2886
+ "button",
2887
+ {
2888
+ type: "button",
2889
+ style: SMALL_BTN_STYLE,
2890
+ "data-testid": "cs-name-manager-delete",
2891
+ onClick: () => remove(row),
2892
+ children: "Delete"
2893
+ }
2894
+ )
2895
+ ] })
2896
+ ] }, row.name)) }),
2897
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h3", { style: SECTION_TITLE_STYLE, children: isEditing ? "Edit name" : "Add a name" }),
2898
+ error && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: ERROR_STYLE, "data-testid": "cs-name-manager-error", children: error }),
2899
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: TWO_COL_STYLE3, children: [
2900
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("label", { style: DIALOG_FIELD_STYLE, children: [
2901
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: DIALOG_LABEL_STYLE, children: "Name" }),
2902
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2903
+ "input",
2904
+ {
2905
+ style: DIALOG_INPUT_STYLE,
2906
+ "data-testid": "cs-name-manager-name",
2907
+ value: nameInput,
2908
+ placeholder: "MyRange",
2909
+ onChange: (e) => setNameInput(e.target.value)
2910
+ }
2911
+ )
2912
+ ] }),
2913
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("label", { style: DIALOG_FIELD_STYLE, children: [
2914
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: DIALOG_LABEL_STYLE, children: "Refers to" }),
2915
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2916
+ "input",
2917
+ {
2918
+ style: DIALOG_INPUT_STYLE,
2919
+ "data-testid": "cs-name-manager-ref",
2920
+ value: refInput,
2921
+ placeholder: initialSelRef || "Sheet1!A1:B2",
2922
+ onChange: (e) => setRefInput(e.target.value)
2923
+ }
2924
+ )
2925
+ ] })
2926
+ ] }),
2927
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: { display: "flex", gap: 8, marginTop: 4 }, children: [
2928
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2929
+ "button",
2930
+ {
2931
+ type: "button",
2932
+ style: DIALOG_BTN_SECONDARY_STYLE,
2933
+ "data-testid": "cs-name-manager-use-selection",
2934
+ onClick: useSelection,
2935
+ children: "Use selection"
2936
+ }
2937
+ ),
2938
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: { flex: 1 } }),
2939
+ isEditing && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2940
+ "button",
2941
+ {
2942
+ type: "button",
2943
+ style: DIALOG_BTN_SECONDARY_STYLE,
2944
+ "data-testid": "cs-name-manager-cancel-edit",
2945
+ onClick: resetForm,
2946
+ children: "Cancel"
2947
+ }
2948
+ ),
2949
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2950
+ "button",
2951
+ {
2952
+ type: "button",
2953
+ style: DIALOG_BTN_PRIMARY_STYLE,
2954
+ "data-testid": "cs-name-manager-submit",
2955
+ onClick: submit,
2956
+ children: isEditing ? "Update" : "Add"
2957
+ }
2958
+ )
2959
+ ] })
2960
+ ]
2961
+ }
2962
+ );
2963
+ }
2964
+
2965
+ // src/chrome/InsertCellsDialog.tsx
2966
+ var import_react12 = require("react");
2967
+ var import_core2 = require("@univerjs/core");
2968
+ var import_jsx_runtime13 = require("react/jsx-runtime");
2969
+ var SHIFT_OPTIONS = [
2970
+ { value: "right", label: "Shift cells right" },
2971
+ { value: "down", label: "Shift cells down" },
2972
+ { value: "row", label: "Entire row" },
2973
+ { value: "column", label: "Entire column" }
2974
+ ];
2975
+ function activeRange7(api) {
2976
+ return api.univer.getActiveWorkbook()?.getActiveSheet()?.getActiveRange() ?? null;
2977
+ }
2978
+ function applyInsert(api, shift) {
2979
+ const range = activeRange7(api);
2980
+ if (!range) return false;
2981
+ switch (shift) {
2982
+ case "right":
2983
+ range.insertCells(import_core2.Dimension.COLUMNS);
2984
+ return true;
2985
+ case "down":
2986
+ range.insertCells(import_core2.Dimension.ROWS);
2987
+ return true;
2988
+ case "row": {
2989
+ const sheet = api.univer.getActiveWorkbook()?.getActiveSheet();
2990
+ if (!sheet) return false;
2991
+ const startRow = range.getRow();
2992
+ const rowCount = range.getLastRow() - startRow + 1;
2993
+ sheet.insertRows(startRow, rowCount);
2994
+ return true;
2995
+ }
2996
+ case "column": {
2997
+ const sheet = api.univer.getActiveWorkbook()?.getActiveSheet();
2998
+ if (!sheet) return false;
2999
+ const startColumn = range.getColumn();
3000
+ const colCount = range.getLastColumn() - startColumn + 1;
3001
+ sheet.insertColumns(startColumn, colCount);
3002
+ return true;
3003
+ }
3004
+ }
3005
+ }
3006
+ var RANGE_NOTE_STYLE6 = {
3007
+ fontSize: 12,
3008
+ color: "var(--cs-chrome-muted, #605e5c)",
3009
+ marginBottom: 14
3010
+ };
3011
+ var RADIO_STYLE = {
3012
+ display: "flex",
3013
+ alignItems: "center",
3014
+ gap: 8,
3015
+ marginBottom: 10,
3016
+ cursor: "pointer"
3017
+ };
3018
+ function InsertCellsDialog({ api, onClose }) {
3019
+ const [shift, setShift] = (0, import_react12.useState)("down");
3020
+ const rangeLabel = (0, import_react12.useMemo)(() => {
3021
+ const fRange = activeRange7(api);
3022
+ return fRange?.getA1Notation?.() ?? null;
3023
+ }, [api]);
3024
+ const hasSelection = activeRange7(api) !== null;
3025
+ const apply = () => {
3026
+ if (applyInsert(api, shift)) onClose();
3027
+ };
3028
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
3029
+ Dialog,
3030
+ {
3031
+ title: "Insert cells",
3032
+ onClose,
3033
+ width: 380,
3034
+ "data-testid": "cs-insert-cells-dialog",
3035
+ footer: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
3036
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { type: "button", style: DIALOG_BTN_SECONDARY_STYLE, onClick: onClose, children: "Cancel" }),
3037
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3038
+ "button",
3039
+ {
3040
+ type: "button",
3041
+ style: DIALOG_BTN_PRIMARY_STYLE,
3042
+ "data-testid": "cs-insert-cells-apply",
3043
+ disabled: !hasSelection,
3044
+ onClick: apply,
3045
+ children: "Insert"
3046
+ }
3047
+ )
3048
+ ] }),
3049
+ children: [
3050
+ hasSelection ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: RANGE_NOTE_STYLE6, "data-testid": "cs-insert-cells-range", children: [
3051
+ "Insert at ",
3052
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("strong", { children: rangeLabel ?? "the current selection" })
3053
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: RANGE_NOTE_STYLE6, "data-testid": "cs-insert-cells-no-selection", children: "Select one or more cells first, then reopen this dialog." }),
3054
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { role: "radiogroup", "aria-label": "Shift direction", children: SHIFT_OPTIONS.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
3055
+ "label",
3056
+ {
3057
+ style: RADIO_STYLE,
3058
+ "data-testid": `cs-insert-cells-shift-${opt.value}-label`,
3059
+ children: [
3060
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
3061
+ "input",
3062
+ {
3063
+ type: "radio",
3064
+ name: "cs-insert-cells-shift",
3065
+ "data-testid": `cs-insert-cells-shift-${opt.value}`,
3066
+ checked: shift === opt.value,
3067
+ onChange: () => setShift(opt.value)
3068
+ }
3069
+ ),
3070
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: opt.label })
3071
+ ]
3072
+ },
3073
+ opt.value
3074
+ )) })
3075
+ ]
3076
+ }
3077
+ );
3078
+ }
3079
+
3080
+ // src/chrome/DeleteCellsDialog.tsx
3081
+ var import_react13 = require("react");
3082
+ var import_core3 = require("@univerjs/core");
3083
+ var import_jsx_runtime14 = require("react/jsx-runtime");
3084
+ var SHIFT_OPTIONS2 = [
3085
+ { value: "left", label: "Shift cells left" },
3086
+ { value: "up", label: "Shift cells up" },
3087
+ { value: "row", label: "Entire row" },
3088
+ { value: "column", label: "Entire column" }
3089
+ ];
3090
+ function activeRange8(api) {
3091
+ return api.univer.getActiveWorkbook()?.getActiveSheet()?.getActiveRange() ?? null;
3092
+ }
3093
+ function applyDelete(api, choice) {
3094
+ const range = activeRange8(api);
3095
+ if (!range) return false;
3096
+ if (choice === "left") {
3097
+ range.deleteCells(import_core3.Dimension.COLUMNS);
3098
+ return true;
3099
+ }
3100
+ if (choice === "up") {
3101
+ range.deleteCells(import_core3.Dimension.ROWS);
3102
+ return true;
3103
+ }
3104
+ const sheet = api.univer.getActiveWorkbook()?.getActiveSheet();
3105
+ if (!sheet) return false;
3106
+ if (choice === "row") {
3107
+ const start2 = range.getRow();
3108
+ const count2 = range.getLastRow() - start2 + 1;
3109
+ if (count2 <= 0) return false;
3110
+ sheet.deleteRows(start2, count2);
3111
+ return true;
3112
+ }
3113
+ const start = range.getColumn();
3114
+ const count = range.getLastColumn() - start + 1;
3115
+ if (count <= 0) return false;
3116
+ sheet.deleteColumns(start, count);
3117
+ return true;
3118
+ }
3119
+ var RANGE_NOTE_STYLE7 = {
3120
+ fontSize: 12,
3121
+ color: "var(--cs-chrome-muted, #605e5c)",
3122
+ marginBottom: 12
3123
+ };
3124
+ var OPTION_STYLE = {
3125
+ display: "flex",
3126
+ alignItems: "center",
3127
+ gap: 8,
3128
+ padding: "6px 4px",
3129
+ cursor: "pointer"
3130
+ };
3131
+ function DeleteCellsDialog({ api, onClose }) {
3132
+ const [choice, setChoice] = (0, import_react13.useState)("left");
3133
+ const rangeLabel = (0, import_react13.useMemo)(() => {
3134
+ const fRange = activeRange8(api);
3135
+ return fRange?.getA1Notation?.() ?? null;
3136
+ }, [api]);
3137
+ const hasSelection = activeRange8(api) !== null;
3138
+ const apply = () => {
3139
+ if (applyDelete(api, choice)) onClose();
3140
+ };
3141
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
3142
+ Dialog,
3143
+ {
3144
+ title: "Delete cells",
3145
+ onClose,
3146
+ width: 360,
3147
+ "data-testid": "cs-delete-cells-dialog",
3148
+ footer: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
3149
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", style: DIALOG_BTN_SECONDARY_STYLE, onClick: onClose, children: "Cancel" }),
3150
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3151
+ "button",
3152
+ {
3153
+ type: "button",
3154
+ style: DIALOG_BTN_PRIMARY_STYLE,
3155
+ "data-testid": "cs-delete-cells-apply",
3156
+ disabled: !hasSelection,
3157
+ onClick: apply,
3158
+ children: "Delete"
3159
+ }
3160
+ )
3161
+ ] }),
3162
+ children: [
3163
+ hasSelection ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: RANGE_NOTE_STYLE7, "data-testid": "cs-delete-cells-range", children: [
3164
+ "Delete ",
3165
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("strong", { children: rangeLabel ?? "the current selection" }),
3166
+ " and\u2026"
3167
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { style: RANGE_NOTE_STYLE7, "data-testid": "cs-delete-cells-no-selection", children: "Select one or more cells first, then reopen this dialog." }),
3168
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { role: "radiogroup", "aria-label": "Delete option", children: SHIFT_OPTIONS2.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("label", { style: OPTION_STYLE, children: [
3169
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
3170
+ "input",
3171
+ {
3172
+ type: "radio",
3173
+ name: "cs-delete-cells-shift",
3174
+ "data-testid": `cs-delete-cells-${opt.value}`,
3175
+ value: opt.value,
3176
+ checked: choice === opt.value,
3177
+ onChange: () => setChoice(opt.value)
3178
+ }
3179
+ ),
3180
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { children: opt.label })
3181
+ ] }, opt.value)) })
3182
+ ]
3183
+ }
3184
+ );
3185
+ }
3186
+
3187
+ // src/chrome/GoalSeekDialog.tsx
3188
+ var import_react14 = require("react");
3189
+ var import_facade5 = require("@univerjs/sheets-formula/facade");
3190
+ var import_jsx_runtime15 = require("react/jsx-runtime");
3191
+ function getSolver(api) {
3192
+ const worksheet = api.univer.getActiveWorkbook()?.getActiveSheet() ?? null;
3193
+ if (!worksheet) return null;
3194
+ const formula = api.univer.getFormula?.();
3195
+ return {
3196
+ worksheet,
3197
+ settle: async () => {
3198
+ if (formula?.onCalculationEnd) {
3199
+ await formula.onCalculationEnd();
3200
+ return;
3201
+ }
3202
+ await new Promise((resolve) => {
3203
+ if (typeof requestAnimationFrame === "function") requestAnimationFrame(() => resolve());
3204
+ else setTimeout(resolve, 16);
3205
+ });
3206
+ }
3207
+ };
3208
+ }
3209
+ function toNumber(value) {
3210
+ if (typeof value === "number") return value;
3211
+ if (typeof value === "boolean") return value ? 1 : 0;
3212
+ if (typeof value === "string" && value.trim() !== "") return Number(value);
3213
+ return Number.NaN;
3214
+ }
3215
+ function isSingleCellA1(ref) {
3216
+ return /^[A-Za-z]{1,3}[1-9][0-9]*$/.test(ref.trim());
3217
+ }
3218
+ var INITIAL_STATE3 = { setCell: "", toValue: "", byChangingCell: "" };
3219
+ var TOLERANCE = 1e-6;
3220
+ var MAX_ITERATIONS = 100;
3221
+ async function goalSeek(solver, setCellRef, target, changeCellRef) {
3222
+ const setRange = solver.worksheet.getRange(setCellRef);
3223
+ const changeRange = solver.worksheet.getRange(changeCellRef);
3224
+ if (!setRange || !changeRange) return { ok: false, reason: "Could not resolve the cells." };
3225
+ const original = toNumber(changeRange.getValue());
3226
+ const x0Seed = Number.isFinite(original) ? original : 0;
3227
+ const evalAt = async (x) => {
3228
+ changeRange.setValue(x);
3229
+ await solver.settle();
3230
+ return toNumber(setRange.getValue());
3231
+ };
3232
+ const restore = async () => {
3233
+ changeRange.setValue(x0Seed);
3234
+ await solver.settle();
3235
+ };
3236
+ let x0 = x0Seed;
3237
+ let f0 = await evalAt(x0) - target;
3238
+ if (!Number.isFinite(f0)) {
3239
+ await restore();
3240
+ return { ok: false, reason: "The set cell is not a number (check that it holds a formula)." };
3241
+ }
3242
+ if (Math.abs(f0) <= TOLERANCE) return { ok: true, solution: x0, result: f0 + target };
3243
+ let x1 = x0 !== 0 ? x0 * 1.01 + 0.01 : 1;
3244
+ let f1 = await evalAt(x1) - target;
3245
+ for (let i = 0; i < MAX_ITERATIONS; i++) {
3246
+ if (Number.isFinite(f1) && Math.abs(f1) <= TOLERANCE) {
3247
+ return { ok: true, solution: x1, result: f1 + target };
3248
+ }
3249
+ const denom = f1 - f0;
3250
+ let x2;
3251
+ if (!Number.isFinite(f1) || denom === 0) {
3252
+ x2 = x1 + (x1 !== 0 ? x1 * 0.1 : 0.1);
3253
+ } else {
3254
+ const step = f1 * (x1 - x0) / denom;
3255
+ x2 = x1 - step;
3256
+ if (!Number.isFinite(x2)) x2 = x1 + (x1 !== 0 ? x1 * 0.1 : 0.1);
3257
+ }
3258
+ const f2 = await evalAt(x2) - target;
3259
+ x0 = x1;
3260
+ f0 = f1;
3261
+ x1 = x2;
3262
+ f1 = f2;
3263
+ }
3264
+ await restore();
3265
+ return {
3266
+ ok: false,
3267
+ reason: `Could not converge after ${MAX_ITERATIONS} iterations. Try an input cell whose value the set cell actually depends on.`
3268
+ };
3269
+ }
3270
+ var NOTE_STYLE = {
3271
+ fontSize: 12,
3272
+ color: "var(--cs-chrome-muted, #605e5c)",
3273
+ marginBottom: 12
3274
+ };
3275
+ var STATUS_OK_STYLE = {
3276
+ fontSize: 12,
3277
+ color: "var(--cs-chrome-active-fg, #0e7490)",
3278
+ marginTop: 4
3279
+ };
3280
+ var STATUS_ERR_STYLE = {
3281
+ fontSize: 12,
3282
+ color: "var(--cs-chrome-danger, #b91c1c)",
3283
+ marginTop: 4
3284
+ };
3285
+ function GoalSeekDialog({ api, onClose }) {
3286
+ const initialSetCell = (0, import_react14.useMemo)(() => {
3287
+ const range = api.univer.getActiveWorkbook()?.getActiveSheet()?.getActiveRange();
3288
+ const a1 = range?.getA1Notation?.();
3289
+ return a1 && isSingleCellA1(a1) ? a1 : "";
3290
+ }, [api]);
3291
+ const [state, setState] = (0, import_react14.useState)({ ...INITIAL_STATE3, setCell: initialSetCell });
3292
+ const [status, setStatus] = (0, import_react14.useState)({ kind: "idle" });
3293
+ const update = (key, value) => {
3294
+ setState((prev) => ({ ...prev, [key]: value }));
3295
+ setStatus({ kind: "idle" });
3296
+ };
3297
+ const setCellValid = isSingleCellA1(state.setCell);
3298
+ const changeCellValid = isSingleCellA1(state.byChangingCell);
3299
+ const targetNum = Number(state.toValue);
3300
+ const targetValid = state.toValue.trim() !== "" && Number.isFinite(targetNum);
3301
+ const canRun = setCellValid && changeCellValid && targetValid && state.setCell.trim().toUpperCase() !== state.byChangingCell.trim().toUpperCase() && status.kind !== "running";
3302
+ const run = async () => {
3303
+ const solver = getSolver(api);
3304
+ if (!solver) {
3305
+ setStatus({ kind: "error", message: "No active sheet." });
3306
+ return;
3307
+ }
3308
+ setStatus({ kind: "running" });
3309
+ try {
3310
+ const result = await goalSeek(
3311
+ solver,
3312
+ state.setCell.trim(),
3313
+ targetNum,
3314
+ state.byChangingCell.trim()
3315
+ );
3316
+ if (result.ok) {
3317
+ setStatus({ kind: "done", solution: result.solution, result: result.result });
3318
+ } else {
3319
+ setStatus({ kind: "error", message: result.reason });
3320
+ }
3321
+ } catch (err) {
3322
+ setStatus({
3323
+ kind: "error",
3324
+ message: err instanceof Error ? err.message : "Goal seek failed."
3325
+ });
3326
+ }
3327
+ };
3328
+ const running = status.kind === "running";
3329
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
3330
+ Dialog,
3331
+ {
3332
+ title: "Goal seek",
3333
+ onClose,
3334
+ width: 420,
3335
+ "data-testid": "cs-goal-seek-dialog",
3336
+ footer: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
3337
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { type: "button", style: DIALOG_BTN_SECONDARY_STYLE, onClick: onClose, children: status.kind === "done" ? "Done" : "Cancel" }),
3338
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3339
+ "button",
3340
+ {
3341
+ type: "button",
3342
+ style: { ...DIALOG_BTN_PRIMARY_STYLE, opacity: canRun ? 1 : 0.5 },
3343
+ "data-testid": "cs-goal-seek-run",
3344
+ disabled: !canRun,
3345
+ onClick: run,
3346
+ children: running ? "Solving\u2026" : "Solve"
3347
+ }
3348
+ )
3349
+ ] }),
3350
+ children: [
3351
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: NOTE_STYLE, "data-testid": "cs-goal-seek-note", children: "Drives the input cell until the formula cell reaches your target value." }),
3352
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { style: DIALOG_FIELD_STYLE, children: [
3353
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: DIALOG_LABEL_STYLE, children: "Set cell (formula cell)" }),
3354
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3355
+ "input",
3356
+ {
3357
+ style: DIALOG_INPUT_STYLE,
3358
+ "data-testid": "cs-goal-seek-set-cell",
3359
+ placeholder: "e.g. B5",
3360
+ value: state.setCell,
3361
+ onChange: (e) => update("setCell", e.target.value)
3362
+ }
3363
+ )
3364
+ ] }),
3365
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { style: DIALOG_FIELD_STYLE, children: [
3366
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: DIALOG_LABEL_STYLE, children: "To value" }),
3367
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3368
+ "input",
3369
+ {
3370
+ style: DIALOG_INPUT_STYLE,
3371
+ "data-testid": "cs-goal-seek-to-value",
3372
+ type: "number",
3373
+ placeholder: "e.g. 1000",
3374
+ value: state.toValue,
3375
+ onChange: (e) => update("toValue", e.target.value)
3376
+ }
3377
+ )
3378
+ ] }),
3379
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { style: DIALOG_FIELD_STYLE, children: [
3380
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: DIALOG_LABEL_STYLE, children: "By changing cell (input cell)" }),
3381
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3382
+ "input",
3383
+ {
3384
+ style: DIALOG_INPUT_STYLE,
3385
+ "data-testid": "cs-goal-seek-change-cell",
3386
+ placeholder: "e.g. B2",
3387
+ value: state.byChangingCell,
3388
+ onChange: (e) => update("byChangingCell", e.target.value)
3389
+ }
3390
+ )
3391
+ ] }),
3392
+ status.kind === "done" && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: STATUS_OK_STYLE, "data-testid": "cs-goal-seek-success", children: [
3393
+ "Solved: ",
3394
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("strong", { children: state.byChangingCell.trim().toUpperCase() }),
3395
+ " =",
3396
+ " ",
3397
+ formatNumber(status.solution),
3398
+ " makes",
3399
+ " ",
3400
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("strong", { children: state.setCell.trim().toUpperCase() }),
3401
+ " = ",
3402
+ formatNumber(status.result),
3403
+ "."
3404
+ ] }),
3405
+ status.kind === "error" && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: STATUS_ERR_STYLE, "data-testid": "cs-goal-seek-error", children: status.message })
3406
+ ]
3407
+ }
3408
+ );
3409
+ }
3410
+ function formatNumber(n) {
3411
+ if (!Number.isFinite(n)) return String(n);
3412
+ const rounded = Math.round(n * 1e6) / 1e6;
3413
+ return String(rounded);
3414
+ }
3415
+
3416
+ // src/chrome/InsertChartDialog.tsx
3417
+ var import_react15 = require("react");
3418
+ var import_facade6 = require("@univerjs/sheets-drawing-ui/facade");
3419
+ var import_jsx_runtime16 = require("react/jsx-runtime");
3420
+ var CHART_TYPE_OPTIONS = [
3421
+ { value: "column", label: "Column" },
3422
+ { value: "bar", label: "Bar" },
3423
+ { value: "line", label: "Line" },
3424
+ { value: "pie", label: "Pie" }
3425
+ ];
3426
+ var CHART_COMPONENT_KEY = "cs-builtin-chart-preview";
3427
+ var PALETTE = ["#0e7490", "#f59e0b", "#10b981", "#ef4444", "#6366f1", "#ec4899"];
3428
+ function activeRange9(api) {
3429
+ return api.univer.getActiveWorkbook()?.getActiveSheet()?.getActiveRange() ?? null;
3430
+ }
3431
+ function activeSheet2(api) {
3432
+ return api.univer.getActiveWorkbook()?.getActiveSheet() ?? null;
3433
+ }
3434
+ function readRangeNumbers(api) {
3435
+ const snap = api.getSnapshot();
3436
+ const sel = api.getSelection();
3437
+ if (!snap || !sel) return [];
3438
+ const sheet = snap.sheets?.[sel.sheetId];
3439
+ if (!sheet) return [];
3440
+ const { startRow, endRow, startColumn, endColumn } = sel.range;
3441
+ const out = [];
3442
+ for (let r = startRow; r <= endRow; r++) {
3443
+ for (let c = startColumn; c <= endColumn; c++) {
3444
+ const raw = sheet.cellData?.[r]?.[c]?.v;
3445
+ const n = typeof raw === "number" ? raw : Number(raw);
3446
+ if (Number.isFinite(n)) out.push(n);
3447
+ }
3448
+ }
3449
+ return out;
3450
+ }
3451
+ function ensureChartComponent(api) {
3452
+ const uni = api.univer;
3453
+ if (uni.__csChartRegistered || typeof uni.registerComponent !== "function") return;
3454
+ uni.registerComponent(CHART_COMPONENT_KEY, ChartPreview);
3455
+ uni.__csChartRegistered = true;
3456
+ }
3457
+ function insertChart(api, s) {
3458
+ const range = activeRange9(api);
3459
+ const sheet = activeSheet2(api);
3460
+ if (!range || !sheet) return false;
3461
+ ensureChartComponent(api);
3462
+ const values = readRangeNumbers(api);
3463
+ const domSheet = sheet;
3464
+ if (typeof domSheet.addFloatDomToRange !== "function") return false;
3465
+ const result = domSheet.addFloatDomToRange(
3466
+ range,
3467
+ {
3468
+ componentKey: CHART_COMPONENT_KEY,
3469
+ allowTransform: true,
3470
+ data: { type: s.chartType, values, rangeA1: s.rangeA1 }
3471
+ },
3472
+ { width: 360, height: 240, marginX: 0, marginY: 0 },
3473
+ `cs-chart-${Date.now()}`
3474
+ );
3475
+ return result != null;
3476
+ }
3477
+ function ChartPreview({
3478
+ data
3479
+ }) {
3480
+ const type = data?.type ?? "column";
3481
+ const values = (data?.values ?? []).slice(0, 24);
3482
+ const label = data?.rangeA1 ?? "";
3483
+ const wrapStyle = {
3484
+ width: "100%",
3485
+ height: "100%",
3486
+ boxSizing: "border-box",
3487
+ background: "#ffffff",
3488
+ border: "1px solid #cdd3db",
3489
+ borderRadius: 6,
3490
+ padding: 8,
3491
+ display: "flex",
3492
+ flexDirection: "column",
3493
+ font: "12px system-ui, sans-serif",
3494
+ color: "#201f1e",
3495
+ overflow: "hidden"
3496
+ };
3497
+ const titleStyle = {
3498
+ fontWeight: 600,
3499
+ marginBottom: 4,
3500
+ whiteSpace: "nowrap",
3501
+ overflow: "hidden",
3502
+ textOverflow: "ellipsis"
3503
+ };
3504
+ const W = 340;
3505
+ const H = 176;
3506
+ const max = values.length ? Math.max(...values, 0) : 0;
3507
+ const min = values.length ? Math.min(...values, 0) : 0;
3508
+ const span = max - min || 1;
3509
+ let body;
3510
+ if (values.length === 0) {
3511
+ body = /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("text", { x: W / 2, y: H / 2, textAnchor: "middle", fill: "#605e5c", children: "No numeric data in range" });
3512
+ } else if (type === "pie") {
3513
+ const total = values.reduce((a, v) => a + Math.abs(v), 0) || 1;
3514
+ const cx = W / 2;
3515
+ const cy = H / 2;
3516
+ const rad = Math.min(W, H) / 2 - 6;
3517
+ let angle = -Math.PI / 2;
3518
+ body = /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_jsx_runtime16.Fragment, { children: values.map((v, i) => {
3519
+ const frac = Math.abs(v) / total;
3520
+ const next = angle + frac * Math.PI * 2;
3521
+ const x1 = cx + rad * Math.cos(angle);
3522
+ const y1 = cy + rad * Math.sin(angle);
3523
+ const x2 = cx + rad * Math.cos(next);
3524
+ const y2 = cy + rad * Math.sin(next);
3525
+ const large = frac > 0.5 ? 1 : 0;
3526
+ const d = `M ${cx} ${cy} L ${x1} ${y1} A ${rad} ${rad} 0 ${large} 1 ${x2} ${y2} Z`;
3527
+ angle = next;
3528
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("path", { d, fill: PALETTE[i % PALETTE.length] }, i);
3529
+ }) });
3530
+ } else if (type === "line") {
3531
+ const step = values.length > 1 ? W / (values.length - 1) : W;
3532
+ const pts = values.map((v, i) => `${i * step},${H - (v - min) / span * H}`).join(" ");
3533
+ body = /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("polyline", { points: pts, fill: "none", stroke: PALETTE[0], strokeWidth: 2 });
3534
+ } else {
3535
+ const horizontal = type === "bar";
3536
+ const n = values.length;
3537
+ const gap = 4;
3538
+ body = /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_jsx_runtime16.Fragment, { children: values.map((v, i) => {
3539
+ const fill = PALETTE[i % PALETTE.length];
3540
+ if (horizontal) {
3541
+ const bh2 = H / n - gap;
3542
+ const bw2 = (v - min) / span * W;
3543
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3544
+ "rect",
3545
+ {
3546
+ x: 0,
3547
+ y: i * (bh2 + gap),
3548
+ width: Math.max(bw2, 1),
3549
+ height: bh2,
3550
+ fill
3551
+ },
3552
+ i
3553
+ );
3554
+ }
3555
+ const bw = W / n - gap;
3556
+ const bh = (v - min) / span * H;
3557
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3558
+ "rect",
3559
+ {
3560
+ x: i * (bw + gap),
3561
+ y: H - bh,
3562
+ width: bw,
3563
+ height: Math.max(bh, 1),
3564
+ fill
3565
+ },
3566
+ i
3567
+ );
3568
+ }) });
3569
+ }
3570
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: wrapStyle, children: [
3571
+ label && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: titleStyle, children: `${type[0].toUpperCase()}${type.slice(1)} chart \xB7 ${label}` }),
3572
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("svg", { viewBox: `0 0 ${W} ${H}`, width: "100%", height: "100%", preserveAspectRatio: "xMidYMid meet", children: body })
3573
+ ] });
3574
+ }
3575
+ var RANGE_NOTE_STYLE8 = {
3576
+ fontSize: 12,
3577
+ color: "var(--cs-chrome-muted, #605e5c)",
3578
+ marginBottom: 12
3579
+ };
3580
+ var TYPE_ROW_STYLE = {
3581
+ display: "grid",
3582
+ gridTemplateColumns: "repeat(4, 1fr)",
3583
+ gap: 8,
3584
+ marginBottom: 12
3585
+ };
3586
+ function typeCardStyle(selected) {
3587
+ return {
3588
+ display: "flex",
3589
+ flexDirection: "column",
3590
+ alignItems: "center",
3591
+ gap: 6,
3592
+ padding: "10px 4px",
3593
+ border: `1px solid ${selected ? "var(--cs-chrome-active-fg, #0e7490)" : "var(--cs-chrome-border, #cdd3db)"}`,
3594
+ borderRadius: 8,
3595
+ background: selected ? "var(--cs-chrome-active-bg, #ecfeff)" : "var(--cs-chrome-input-bg, #fff)",
3596
+ color: selected ? "var(--cs-chrome-active-fg, #0e7490)" : "var(--cs-chrome-fg, #201f1e)",
3597
+ font: "inherit",
3598
+ fontSize: 12,
3599
+ cursor: "pointer"
3600
+ };
3601
+ }
3602
+ function TypeGlyph({ type }) {
3603
+ const c = "currentColor";
3604
+ switch (type) {
3605
+ case "column":
3606
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("svg", { width: 22, height: 22, viewBox: "0 0 22 22", fill: c, "aria-hidden": "true", children: [
3607
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("rect", { x: 2, y: 10, width: 4, height: 10 }),
3608
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("rect", { x: 9, y: 5, width: 4, height: 15 }),
3609
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("rect", { x: 16, y: 2, width: 4, height: 18 })
3610
+ ] });
3611
+ case "bar":
3612
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("svg", { width: 22, height: 22, viewBox: "0 0 22 22", fill: c, "aria-hidden": "true", children: [
3613
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("rect", { x: 2, y: 2, width: 10, height: 4 }),
3614
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("rect", { x: 2, y: 9, width: 16, height: 4 }),
3615
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("rect", { x: 2, y: 16, width: 7, height: 4 })
3616
+ ] });
3617
+ case "line":
3618
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3619
+ "svg",
3620
+ {
3621
+ width: 22,
3622
+ height: 22,
3623
+ viewBox: "0 0 22 22",
3624
+ fill: "none",
3625
+ stroke: c,
3626
+ strokeWidth: 2,
3627
+ "aria-hidden": "true",
3628
+ children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("polyline", { points: "2,16 8,9 13,13 20,3" })
3629
+ }
3630
+ );
3631
+ case "pie":
3632
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("svg", { width: 22, height: 22, viewBox: "0 0 22 22", fill: c, "aria-hidden": "true", children: [
3633
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("path", { d: "M11 11 L11 2 A9 9 0 0 1 20 11 Z" }),
3634
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("circle", { cx: 11, cy: 11, r: 9, fill: "none", stroke: c, strokeWidth: 1.5 })
3635
+ ] });
3636
+ }
3637
+ }
3638
+ function InsertChartDialog({ api, onClose }) {
3639
+ const rangeLabel = (0, import_react15.useMemo)(() => {
3640
+ const fRange = activeRange9(api);
3641
+ return fRange?.getA1Notation?.() ?? null;
3642
+ }, [api]);
3643
+ const [state, setState] = (0, import_react15.useState)(() => ({
3644
+ chartType: "column",
3645
+ rangeA1: rangeLabel ?? ""
3646
+ }));
3647
+ const hasSelection = activeRange9(api) !== null;
3648
+ const update = (key, value) => setState((prev) => ({ ...prev, [key]: value }));
3649
+ const apply = () => {
3650
+ if (insertChart(api, state)) onClose();
3651
+ };
3652
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
3653
+ Dialog,
3654
+ {
3655
+ title: "Insert chart",
3656
+ onClose,
3657
+ width: 440,
3658
+ "data-testid": "cs-insert-chart-dialog",
3659
+ footer: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
3660
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", style: DIALOG_BTN_SECONDARY_STYLE, onClick: onClose, children: "Cancel" }),
3661
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3662
+ "button",
3663
+ {
3664
+ type: "button",
3665
+ style: DIALOG_BTN_PRIMARY_STYLE,
3666
+ "data-testid": "cs-insert-chart-apply",
3667
+ disabled: !hasSelection,
3668
+ onClick: apply,
3669
+ children: "Insert"
3670
+ }
3671
+ )
3672
+ ] }),
3673
+ children: [
3674
+ hasSelection ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: RANGE_NOTE_STYLE8, "data-testid": "cs-insert-chart-range", children: [
3675
+ "Chart from ",
3676
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("strong", { children: rangeLabel ?? "the current selection" })
3677
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: RANGE_NOTE_STYLE8, "data-testid": "cs-insert-chart-no-selection", children: "Select the data range first, then reopen this dialog." }),
3678
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: DIALOG_LABEL_STYLE, children: "Chart type" }),
3679
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: TYPE_ROW_STYLE, role: "radiogroup", "aria-label": "Chart type", children: CHART_TYPE_OPTIONS.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
3680
+ "button",
3681
+ {
3682
+ type: "button",
3683
+ role: "radio",
3684
+ "aria-checked": state.chartType === opt.value,
3685
+ "data-testid": `cs-insert-chart-type-${opt.value}`,
3686
+ style: typeCardStyle(state.chartType === opt.value),
3687
+ onClick: () => update("chartType", opt.value),
3688
+ children: [
3689
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(TypeGlyph, { type: opt.value }),
3690
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { children: opt.label })
3691
+ ]
3692
+ },
3693
+ opt.value
3694
+ )) }),
3695
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("label", { style: DIALOG_FIELD_STYLE, children: [
3696
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: DIALOG_LABEL_STYLE, children: "Data range" }),
3697
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3698
+ "input",
3699
+ {
3700
+ style: DIALOG_INPUT_STYLE,
3701
+ "data-testid": "cs-insert-chart-range-input",
3702
+ value: state.rangeA1,
3703
+ placeholder: "A1:B5",
3704
+ onChange: (e) => update("rangeA1", e.target.value)
3705
+ }
3706
+ )
3707
+ ] }),
3708
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: RANGE_NOTE_STYLE8, children: "The chart is placed over the current selection. Numeric cells in the range become the series." })
3709
+ ]
3710
+ }
3711
+ );
3712
+ }
3713
+
3714
+ // src/chrome/InsertSparklineDialog.tsx
3715
+ var import_react16 = require("react");
3716
+ var import_jsx_runtime17 = require("react/jsx-runtime");
3717
+ var SPARKLINES_RESOURCE_NAME = "__casual_sheets_sparklines__";
3718
+ var TYPE_OPTIONS = [
3719
+ { value: "line", label: "Line" },
3720
+ { value: "column", label: "Column" },
3721
+ { value: "win-loss", label: "Win / Loss" }
3722
+ ];
3723
+ function activeRange10(api) {
3724
+ return api.univer.getActiveWorkbook()?.getActiveSheet()?.getActiveRange() ?? null;
3725
+ }
3726
+ function colLettersToIndex(letters) {
3727
+ let n = 0;
3728
+ for (let i = 0; i < letters.length; i += 1) {
3729
+ n = n * 26 + (letters.charCodeAt(i) - 64);
3730
+ }
3731
+ return n - 1;
3732
+ }
3733
+ function parseRange(s) {
3734
+ const m = /^\$?([A-Z]+)\$?(\d+)(?::\$?([A-Z]+)\$?(\d+))?$/.exec(s.trim().toUpperCase());
3735
+ if (!m) return null;
3736
+ const c1 = colLettersToIndex(m[1]);
3737
+ const r1 = parseInt(m[2], 10) - 1;
3738
+ const c2 = m[3] ? colLettersToIndex(m[3]) : c1;
3739
+ const r2 = m[4] ? parseInt(m[4], 10) - 1 : r1;
3740
+ return {
3741
+ startRow: Math.min(r1, r2),
3742
+ endRow: Math.max(r1, r2),
3743
+ startColumn: Math.min(c1, c2),
3744
+ endColumn: Math.max(c1, c2)
3745
+ };
3746
+ }
3747
+ function parseSingleCell(s) {
3748
+ const m = /^\$?([A-Z]+)\$?(\d+)$/.exec(s.trim().toUpperCase());
3749
+ if (!m) return null;
3750
+ return { col: colLettersToIndex(m[1]), row: parseInt(m[2], 10) - 1 };
3751
+ }
3752
+ function insertSparkline(api, model) {
3753
+ const data = api.getContent();
3754
+ if (!data) return false;
3755
+ const resources = data.resources ? [...data.resources] : [];
3756
+ const idx = resources.findIndex((r) => r.name === SPARKLINES_RESOURCE_NAME);
3757
+ let existing = [];
3758
+ if (idx >= 0 && resources[idx]?.data) {
3759
+ try {
3760
+ const parsed = JSON.parse(resources[idx].data);
3761
+ if (parsed?.v === 1 && Array.isArray(parsed.sparklines)) existing = parsed.sparklines;
3762
+ } catch {
3763
+ existing = [];
3764
+ }
3765
+ }
3766
+ const payload = { v: 1, sparklines: [...existing, model] };
3767
+ const entry = { name: SPARKLINES_RESOURCE_NAME, data: JSON.stringify(payload) };
3768
+ if (idx >= 0) resources[idx] = entry;
3769
+ else resources.push(entry);
3770
+ const next = { ...data, resources };
3771
+ api.setContent(next);
3772
+ return true;
3773
+ }
3774
+ function activeIds(api) {
3775
+ const wb = api.univer.getActiveWorkbook();
3776
+ const sheet = wb?.getActiveSheet();
3777
+ if (!wb || !sheet) return null;
3778
+ return { unitId: wb.getId(), sheetId: sheet.getSheetId() };
3779
+ }
3780
+ var RANGE_NOTE_STYLE9 = {
3781
+ fontSize: 12,
3782
+ color: "var(--cs-chrome-muted, #605e5c)",
3783
+ marginBottom: 12
3784
+ };
3785
+ var RADIO_ROW_STYLE = {
3786
+ display: "flex",
3787
+ gap: 16,
3788
+ marginTop: 2
3789
+ };
3790
+ var RADIO_OPT_STYLE = {
3791
+ display: "flex",
3792
+ alignItems: "center",
3793
+ gap: 6,
3794
+ cursor: "pointer"
3795
+ };
3796
+ var ERROR_STYLE2 = {
3797
+ fontSize: 12,
3798
+ color: "var(--cs-chrome-danger-fg, #b91c1c)",
3799
+ marginTop: 4
3800
+ };
3801
+ function InsertSparklineDialog({ api, onClose }) {
3802
+ const selectedA1 = (0, import_react16.useMemo)(() => {
3803
+ const fRange = activeRange10(api);
3804
+ return fRange?.getA1Notation?.() ?? "";
3805
+ }, [api]);
3806
+ const [state, setState] = (0, import_react16.useState)({
3807
+ type: "line",
3808
+ sourceA1: selectedA1,
3809
+ anchorA1: ""
3810
+ });
3811
+ const [error, setError] = (0, import_react16.useState)(null);
3812
+ const hasWorkbook = api.univer.getActiveWorkbook() != null;
3813
+ const update = (key, value) => {
3814
+ setState((prev) => ({ ...prev, [key]: value }));
3815
+ setError(null);
3816
+ };
3817
+ const apply = () => {
3818
+ const source = parseRange(state.sourceA1);
3819
+ if (!source) {
3820
+ setError("Data range must be a range like A1:F1.");
3821
+ return;
3822
+ }
3823
+ const anchor = parseSingleCell(state.anchorA1);
3824
+ if (!anchor) {
3825
+ setError("Location must be a single cell like G1.");
3826
+ return;
3827
+ }
3828
+ const ids = activeIds(api);
3829
+ if (!ids) {
3830
+ setError("No active sheet.");
3831
+ return;
3832
+ }
3833
+ const model = {
3834
+ id: `spark-${Math.random().toString(36).slice(2, 10)}`,
3835
+ type: state.type,
3836
+ unitId: ids.unitId,
3837
+ sheetId: ids.sheetId,
3838
+ source,
3839
+ anchor
3840
+ };
3841
+ if (insertSparkline(api, model)) onClose();
3842
+ else setError("Could not read the active workbook.");
3843
+ };
3844
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
3845
+ Dialog,
3846
+ {
3847
+ title: "Insert sparkline",
3848
+ onClose,
3849
+ width: 420,
3850
+ "data-testid": "cs-insert-sparkline-dialog",
3851
+ footer: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
3852
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", style: DIALOG_BTN_SECONDARY_STYLE, onClick: onClose, children: "Cancel" }),
3853
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3854
+ "button",
3855
+ {
3856
+ type: "button",
3857
+ style: DIALOG_BTN_PRIMARY_STYLE,
3858
+ "data-testid": "cs-insert-sparkline-apply",
3859
+ disabled: !hasWorkbook,
3860
+ onClick: apply,
3861
+ children: "Insert"
3862
+ }
3863
+ )
3864
+ ] }),
3865
+ children: [
3866
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: RANGE_NOTE_STYLE9, children: "Draws an in-cell mini-chart in the location cell from the values in the data range." }),
3867
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("label", { style: DIALOG_FIELD_STYLE, children: [
3868
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: DIALOG_LABEL_STYLE, children: "Type" }),
3869
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3870
+ "div",
3871
+ {
3872
+ style: RADIO_ROW_STYLE,
3873
+ role: "radiogroup",
3874
+ "aria-label": "Sparkline type",
3875
+ "data-testid": "cs-insert-sparkline-type",
3876
+ children: TYPE_OPTIONS.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("label", { style: RADIO_OPT_STYLE, children: [
3877
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3878
+ "input",
3879
+ {
3880
+ type: "radio",
3881
+ name: "cs-sparkline-type",
3882
+ value: opt.value,
3883
+ "data-testid": `cs-insert-sparkline-type-${opt.value}`,
3884
+ checked: state.type === opt.value,
3885
+ onChange: () => update("type", opt.value)
3886
+ }
3887
+ ),
3888
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { children: opt.label })
3889
+ ] }, opt.value))
3890
+ }
3891
+ )
3892
+ ] }),
3893
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("label", { style: DIALOG_FIELD_STYLE, children: [
3894
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: DIALOG_LABEL_STYLE, children: "Data range" }),
3895
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3896
+ "input",
3897
+ {
3898
+ style: DIALOG_INPUT_STYLE,
3899
+ "data-testid": "cs-insert-sparkline-source",
3900
+ value: state.sourceA1,
3901
+ placeholder: "A1:F1",
3902
+ spellCheck: false,
3903
+ onChange: (e) => update("sourceA1", e.target.value.toUpperCase())
3904
+ }
3905
+ )
3906
+ ] }),
3907
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("label", { style: DIALOG_FIELD_STYLE, children: [
3908
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: DIALOG_LABEL_STYLE, children: "Location" }),
3909
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3910
+ "input",
3911
+ {
3912
+ style: DIALOG_INPUT_STYLE,
3913
+ "data-testid": "cs-insert-sparkline-anchor",
3914
+ value: state.anchorA1,
3915
+ placeholder: "G1",
3916
+ spellCheck: false,
3917
+ onChange: (e) => update("anchorA1", e.target.value.toUpperCase())
3918
+ }
3919
+ )
3920
+ ] }),
3921
+ error && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: ERROR_STYLE2, "data-testid": "cs-insert-sparkline-error", children: error })
3922
+ ]
3923
+ }
3924
+ );
3925
+ }
3926
+
3927
+ // src/chrome/InsertPivotDialog.tsx
3928
+ var import_react17 = require("react");
3929
+ var import_jsx_runtime18 = require("react/jsx-runtime");
3930
+ var AGG_OPTIONS = [
3931
+ { value: "sum", label: "Sum" },
3932
+ { value: "count", label: "Count" },
3933
+ { value: "average", label: "Average" },
3934
+ { value: "min", label: "Min" },
3935
+ { value: "max", label: "Max" }
3936
+ ];
3937
+ function activeRange11(api) {
3938
+ return api.univer.getActiveWorkbook()?.getActiveSheet()?.getActiveRange() ?? null;
3939
+ }
3940
+ function readSource(api) {
3941
+ const range = activeRange11(api);
3942
+ const grid = range?.getValues?.() ?? [];
3943
+ return Array.isArray(grid) ? grid : [];
3944
+ }
3945
+ function columnLabel(index, headerRow, useHeader) {
3946
+ if (useHeader) {
3947
+ const raw = headerRow?.[index];
3948
+ if (raw != null && String(raw).trim().length > 0) return String(raw);
3949
+ }
3950
+ let n = index;
3951
+ let label = "";
3952
+ do {
3953
+ label = String.fromCharCode(65 + n % 26) + label;
3954
+ n = Math.floor(n / 26) - 1;
3955
+ } while (n >= 0);
3956
+ return `Column ${label}`;
3957
+ }
3958
+ function toNumber2(v) {
3959
+ if (typeof v === "number") return Number.isFinite(v) ? v : null;
3960
+ if (typeof v === "boolean") return v ? 1 : 0;
3961
+ if (typeof v === "string" && v.trim() !== "") {
3962
+ const n = Number(v);
3963
+ return Number.isFinite(n) ? n : null;
3964
+ }
3965
+ return null;
3966
+ }
3967
+ function aggregate(fn, values, rowCount) {
3968
+ if (fn === "count") return rowCount;
3969
+ if (values.length === 0) return 0;
3970
+ switch (fn) {
3971
+ case "sum":
3972
+ return values.reduce((a, b) => a + b, 0);
3973
+ case "average":
3974
+ return values.reduce((a, b) => a + b, 0) / values.length;
3975
+ case "min":
3976
+ return Math.min(...values);
3977
+ case "max":
3978
+ return Math.max(...values);
3979
+ }
3980
+ }
3981
+ function buildPivot(source, useHeader, groupCol, valueCol, fn) {
3982
+ if (source.length === 0) return null;
3983
+ const headerRow = useHeader ? source[0] : void 0;
3984
+ const dataRows = useHeader ? source.slice(1) : source;
3985
+ if (dataRows.length === 0) return null;
3986
+ const order = [];
3987
+ const buckets = /* @__PURE__ */ new Map();
3988
+ const counts = /* @__PURE__ */ new Map();
3989
+ for (const row of dataRows) {
3990
+ const rawKey = row[groupCol];
3991
+ const key = rawKey == null ? "" : String(rawKey);
3992
+ if (!buckets.has(key)) {
3993
+ buckets.set(key, []);
3994
+ counts.set(key, 0);
3995
+ order.push(key);
3996
+ }
3997
+ counts.set(key, (counts.get(key) ?? 0) + 1);
3998
+ const num = toNumber2(row[valueCol]);
3999
+ if (num != null) buckets.get(key).push(num);
4000
+ }
4001
+ const groupHeader = columnLabel(groupCol, headerRow, useHeader);
4002
+ const valueHeader = columnLabel(valueCol, headerRow, useHeader);
4003
+ const aggLabel = AGG_OPTIONS.find((o) => o.value === fn)?.label ?? "Sum";
4004
+ const grid = [[groupHeader, `${aggLabel} of ${valueHeader}`]];
4005
+ for (const key of order) {
4006
+ grid.push([key === "" ? "(blank)" : key, aggregate(fn, buckets.get(key), counts.get(key))]);
4007
+ }
4008
+ return { grid };
4009
+ }
4010
+ function toA1(row, col) {
4011
+ let n = col;
4012
+ let letters = "";
4013
+ do {
4014
+ letters = String.fromCharCode(65 + n % 26) + letters;
4015
+ n = Math.floor(n / 26) - 1;
4016
+ } while (n >= 0);
4017
+ return `${letters}${row + 1}`;
4018
+ }
4019
+ var RANGE_NOTE_STYLE10 = {
4020
+ fontSize: 12,
4021
+ color: "var(--cs-chrome-muted, #605e5c)",
4022
+ marginBottom: 12
4023
+ };
4024
+ var CHECK_STYLE5 = {
4025
+ display: "flex",
4026
+ alignItems: "center",
4027
+ gap: 6,
4028
+ marginBottom: 12,
4029
+ cursor: "pointer"
4030
+ };
4031
+ var RADIO_ROW_STYLE2 = {
4032
+ display: "flex",
4033
+ alignItems: "center",
4034
+ gap: 6,
4035
+ marginBottom: 6,
4036
+ cursor: "pointer"
4037
+ };
4038
+ function InsertPivotDialog({ api, onClose }) {
4039
+ const source = (0, import_react17.useMemo)(() => readSource(api), [api]);
4040
+ const rangeLabel = (0, import_react17.useMemo)(() => {
4041
+ const r = activeRange11(api);
4042
+ return r?.getA1Notation?.() ?? null;
4043
+ }, [api]);
4044
+ const hasSource = source.length > 0 && (source[0]?.length ?? 0) > 0;
4045
+ const colCount = hasSource ? source[0].length : 0;
4046
+ const [state, setState] = (0, import_react17.useState)({
4047
+ useHeader: true,
4048
+ groupCol: 0,
4049
+ valueCol: colCount > 1 ? 1 : 0,
4050
+ aggFn: "sum",
4051
+ destination: "newSheet",
4052
+ sheetName: "Pivot",
4053
+ anchor: ""
4054
+ });
4055
+ const [error, setError] = (0, import_react17.useState)(null);
4056
+ const update = (key, value) => {
4057
+ setState((prev) => ({ ...prev, [key]: value }));
4058
+ setError(null);
4059
+ };
4060
+ const columnOptions = (0, import_react17.useMemo)(() => {
4061
+ const header = state.useHeader ? source[0] : void 0;
4062
+ return Array.from({ length: colCount }, (_, i) => ({
4063
+ value: i,
4064
+ label: columnLabel(i, header, state.useHeader)
4065
+ }));
4066
+ }, [source, colCount, state.useHeader]);
4067
+ const apply = () => {
4068
+ const pivot = buildPivot(source, state.useHeader, state.groupCol, state.valueCol, state.aggFn);
4069
+ if (!pivot) {
4070
+ setError("The selection has no data rows to summarize.");
4071
+ return;
4072
+ }
4073
+ const wb = api.univer.getActiveWorkbook();
4074
+ if (!wb) {
4075
+ setError("No active workbook.");
4076
+ return;
4077
+ }
4078
+ try {
4079
+ if (state.destination === "newSheet") {
4080
+ const name = state.sheetName.trim() || "Pivot";
4081
+ const rows = Math.max(pivot.grid.length + 2, 10);
4082
+ const sheet = wb.create(name, rows, 4);
4083
+ const target = sheet.getRange(`A1:${toA1(pivot.grid.length - 1, 1)}`);
4084
+ target.setValues(pivot.grid);
4085
+ } else {
4086
+ const anchor = state.anchor.trim().toUpperCase();
4087
+ if (!/^[A-Z]+[0-9]+$/.test(anchor)) {
4088
+ setError("Enter a valid top-left cell, e.g. E1.");
4089
+ return;
4090
+ }
4091
+ const sheet = wb.getActiveSheet();
4092
+ if (!sheet) {
4093
+ setError("No active sheet to write into.");
4094
+ return;
4095
+ }
4096
+ const m = /^([A-Z]+)([0-9]+)$/.exec(anchor);
4097
+ let col = 0;
4098
+ for (const ch of m[1]) col = col * 26 + (ch.charCodeAt(0) - 64);
4099
+ col -= 1;
4100
+ const startRow = Number(m[2]) - 1;
4101
+ const end = toA1(startRow + pivot.grid.length - 1, col + 1);
4102
+ sheet.getRange(`${anchor}:${end}`).setValues(pivot.grid);
4103
+ }
4104
+ } catch (e) {
4105
+ setError(e instanceof Error ? e.message : "Failed to create the pivot table.");
4106
+ return;
4107
+ }
4108
+ onClose();
4109
+ };
4110
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
4111
+ Dialog,
4112
+ {
4113
+ title: "Insert pivot table",
4114
+ onClose,
4115
+ width: 460,
4116
+ "data-testid": "cs-insert-pivot-dialog",
4117
+ footer: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
4118
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("button", { type: "button", style: DIALOG_BTN_SECONDARY_STYLE, onClick: onClose, children: "Cancel" }),
4119
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4120
+ "button",
4121
+ {
4122
+ type: "button",
4123
+ style: DIALOG_BTN_PRIMARY_STYLE,
4124
+ "data-testid": "cs-insert-pivot-create",
4125
+ disabled: !hasSource,
4126
+ onClick: apply,
4127
+ children: "Create"
4128
+ }
4129
+ )
4130
+ ] }),
4131
+ children: [
4132
+ hasSource ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: RANGE_NOTE_STYLE10, "data-testid": "cs-insert-pivot-source", children: [
4133
+ "Source data ",
4134
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("strong", { children: rangeLabel ?? "the current selection" }),
4135
+ " (",
4136
+ source.length,
4137
+ " rows \xD7 ",
4138
+ colCount,
4139
+ " columns)"
4140
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: RANGE_NOTE_STYLE10, "data-testid": "cs-insert-pivot-no-selection", children: "Select the source data range first (including its header row), then reopen this dialog." }),
4141
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("label", { style: CHECK_STYLE5, "data-testid": "cs-insert-pivot-header-label", children: [
4142
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4143
+ "input",
4144
+ {
4145
+ type: "checkbox",
4146
+ "data-testid": "cs-insert-pivot-header",
4147
+ checked: state.useHeader,
4148
+ onChange: (e) => update("useHeader", e.target.checked),
4149
+ disabled: !hasSource
4150
+ }
4151
+ ),
4152
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { children: "First row is a header" })
4153
+ ] }),
4154
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("label", { style: DIALOG_FIELD_STYLE, children: [
4155
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: DIALOG_LABEL_STYLE, children: "Rows (group by)" }),
4156
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4157
+ "select",
4158
+ {
4159
+ style: DIALOG_INPUT_STYLE,
4160
+ "data-testid": "cs-insert-pivot-group-col",
4161
+ value: state.groupCol,
4162
+ disabled: !hasSource,
4163
+ onChange: (e) => update("groupCol", Number(e.target.value)),
4164
+ children: columnOptions.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("option", { value: opt.value, children: opt.label }, opt.value))
4165
+ }
4166
+ )
4167
+ ] }),
4168
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("label", { style: DIALOG_FIELD_STYLE, children: [
4169
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: DIALOG_LABEL_STYLE, children: "Values" }),
4170
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4171
+ "select",
4172
+ {
4173
+ style: DIALOG_INPUT_STYLE,
4174
+ "data-testid": "cs-insert-pivot-value-col",
4175
+ value: state.valueCol,
4176
+ disabled: !hasSource,
4177
+ onChange: (e) => update("valueCol", Number(e.target.value)),
4178
+ children: columnOptions.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("option", { value: opt.value, children: opt.label }, opt.value))
4179
+ }
4180
+ )
4181
+ ] }),
4182
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("label", { style: DIALOG_FIELD_STYLE, children: [
4183
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: DIALOG_LABEL_STYLE, children: "Summarize by" }),
4184
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4185
+ "select",
4186
+ {
4187
+ style: DIALOG_INPUT_STYLE,
4188
+ "data-testid": "cs-insert-pivot-agg",
4189
+ value: state.aggFn,
4190
+ disabled: !hasSource,
4191
+ onChange: (e) => update("aggFn", e.target.value),
4192
+ children: AGG_OPTIONS.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("option", { value: opt.value, children: opt.label }, opt.value))
4193
+ }
4194
+ )
4195
+ ] }),
4196
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: DIALOG_FIELD_STYLE, children: [
4197
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: DIALOG_LABEL_STYLE, children: "Destination" }),
4198
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("label", { style: RADIO_ROW_STYLE2, children: [
4199
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4200
+ "input",
4201
+ {
4202
+ type: "radio",
4203
+ name: "cs-insert-pivot-destination",
4204
+ "data-testid": "cs-insert-pivot-dest-new",
4205
+ checked: state.destination === "newSheet",
4206
+ onChange: () => update("destination", "newSheet")
4207
+ }
4208
+ ),
4209
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { children: "New sheet" })
4210
+ ] }),
4211
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("label", { style: RADIO_ROW_STYLE2, children: [
4212
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1169
4213
  "input",
1170
4214
  {
1171
- style: COLOR_INPUT_STYLE,
1172
- "data-testid": "cs-format-cells-fill-color",
1173
- type: "color",
1174
- value: state.fillColor,
1175
- onChange: (e) => update("fillColor", e.target.value)
4215
+ type: "radio",
4216
+ name: "cs-insert-pivot-destination",
4217
+ "data-testid": "cs-insert-pivot-dest-existing",
4218
+ checked: state.destination === "existing",
4219
+ onChange: () => update("destination", "existing")
1176
4220
  }
1177
- )
4221
+ ),
4222
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { children: "Existing sheet (choose top-left cell)" })
1178
4223
  ] })
1179
- ] })
4224
+ ] }),
4225
+ state.destination === "newSheet" ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("label", { style: DIALOG_FIELD_STYLE, children: [
4226
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: DIALOG_LABEL_STYLE, children: "New sheet name" }),
4227
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4228
+ "input",
4229
+ {
4230
+ style: DIALOG_INPUT_STYLE,
4231
+ "data-testid": "cs-insert-pivot-sheet-name",
4232
+ value: state.sheetName,
4233
+ placeholder: "Pivot",
4234
+ onChange: (e) => update("sheetName", e.target.value)
4235
+ }
4236
+ )
4237
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("label", { style: DIALOG_FIELD_STYLE, children: [
4238
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: DIALOG_LABEL_STYLE, children: "Top-left cell (on the active sheet)" }),
4239
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4240
+ "input",
4241
+ {
4242
+ style: DIALOG_INPUT_STYLE,
4243
+ "data-testid": "cs-insert-pivot-anchor",
4244
+ value: state.anchor,
4245
+ placeholder: "e.g. E1",
4246
+ onChange: (e) => update("anchor", e.target.value)
4247
+ }
4248
+ )
4249
+ ] }),
4250
+ error && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4251
+ "div",
4252
+ {
4253
+ style: {
4254
+ ...RANGE_NOTE_STYLE10,
4255
+ color: "var(--cs-chrome-danger, #b3261e)",
4256
+ marginBottom: 0
4257
+ },
4258
+ "data-testid": "cs-insert-pivot-error",
4259
+ role: "alert",
4260
+ children: error
4261
+ }
4262
+ )
1180
4263
  ]
1181
4264
  }
1182
4265
  );
1183
4266
  }
1184
4267
 
1185
4268
  // src/chrome/dialog-context.tsx
1186
- var import_jsx_runtime7 = require("react/jsx-runtime");
4269
+ var import_jsx_runtime19 = require("react/jsx-runtime");
1187
4270
  var BUILT_IN_DIALOGS = {
1188
- "format-cells": FormatCellsDialog
4271
+ "format-cells": FormatCellsDialog,
4272
+ "data-validation": DataValidationDialog,
4273
+ "conditional-formatting": ConditionalFormattingDialog,
4274
+ "custom-sort": CustomSortDialog,
4275
+ "paste-special": PasteSpecialDialog,
4276
+ "insert-function": InsertFunctionDialog,
4277
+ "name-manager": NameManagerDialog,
4278
+ "insert-cells": InsertCellsDialog,
4279
+ "delete-cells": DeleteCellsDialog,
4280
+ "goal-seek": GoalSeekDialog,
4281
+ "insert-chart": InsertChartDialog,
4282
+ "insert-sparkline": InsertSparklineDialog,
4283
+ "insert-pivot": InsertPivotDialog
1189
4284
  };
1190
4285
  function hasBuiltInDialog(kind) {
1191
4286
  return kind in BUILT_IN_DIALOGS || kind === "find-replace";
1192
4287
  }
1193
- var DialogContext = (0, import_react6.createContext)(null);
4288
+ var DialogContext = (0, import_react18.createContext)(null);
1194
4289
  function useDialogs() {
1195
- const ctx = (0, import_react6.useContext)(DialogContext);
4290
+ const ctx = (0, import_react18.useContext)(DialogContext);
1196
4291
  if (!ctx) {
1197
4292
  return {
1198
4293
  openDialog: () => {
@@ -1212,10 +4307,10 @@ function DialogProvider({
1212
4307
  onOpenFindReplace,
1213
4308
  children
1214
4309
  }) {
1215
- const [active, setActive] = (0, import_react6.useState)(null);
4310
+ const [active, setActive] = (0, import_react18.useState)(null);
1216
4311
  const overrides = extensions?.dialogs;
1217
- const hostOwned = (0, import_react6.useMemo)(() => new Set(hostOwnedDialogs ?? []), [hostOwnedDialogs]);
1218
- const openDialog = (0, import_react6.useCallback)(
4312
+ const hostOwned = (0, import_react18.useMemo)(() => new Set(hostOwnedDialogs ?? []), [hostOwnedDialogs]);
4313
+ const openDialog = (0, import_react18.useCallback)(
1219
4314
  (kind, context) => {
1220
4315
  if (overrides?.[kind]) {
1221
4316
  setActive({ kind, context });
@@ -1240,22 +4335,22 @@ function DialogProvider({
1240
4335
  },
1241
4336
  [overrides, onDialogRequest, hostOwned, onOpenFindReplace]
1242
4337
  );
1243
- const closeDialog = (0, import_react6.useCallback)(() => setActive(null), []);
1244
- const canOpen = (0, import_react6.useCallback)(
4338
+ const closeDialog = (0, import_react18.useCallback)(() => setActive(null), []);
4339
+ const canOpen = (0, import_react18.useCallback)(
1245
4340
  (kind) => !!overrides?.[kind] || hasBuiltInDialog(kind) || !!onDialogRequest || hostOwned.has(kind),
1246
4341
  [overrides, onDialogRequest, hostOwned]
1247
4342
  );
1248
- const controllerCanOpen = (0, import_react6.useCallback)(
4343
+ const controllerCanOpen = (0, import_react18.useCallback)(
1249
4344
  (kind) => kind === "find-replace" ? !!onOpenFindReplace || canOpen(kind) : canOpen(kind),
1250
4345
  [canOpen, onOpenFindReplace]
1251
4346
  );
1252
- const controller = (0, import_react6.useMemo)(
4347
+ const controller = (0, import_react18.useMemo)(
1253
4348
  () => ({ openDialog, closeDialog, canOpen: controllerCanOpen }),
1254
4349
  [openDialog, closeDialog, controllerCanOpen]
1255
4350
  );
1256
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(DialogContext.Provider, { value: controller, children: [
4351
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(DialogContext.Provider, { value: controller, children: [
1257
4352
  children,
1258
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(DialogHost, { api, active, overrides, onClose: closeDialog })
4353
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DialogHost, { api, active, overrides, onClose: closeDialog })
1259
4354
  ] });
1260
4355
  }
1261
4356
  function DialogHost({ api, active, overrides, onClose }) {
@@ -1263,11 +4358,11 @@ function DialogHost({ api, active, overrides, onClose }) {
1263
4358
  if (active.kind === "find-replace") return null;
1264
4359
  const Component = overrides?.[active.kind] ?? BUILT_IN_DIALOGS[active.kind];
1265
4360
  if (!Component) return null;
1266
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Component, { api, onClose, context: active.context });
4361
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Component, { api, onClose, context: active.context });
1267
4362
  }
1268
4363
 
1269
4364
  // src/chrome/Toolbar.tsx
1270
- var import_jsx_runtime8 = require("react/jsx-runtime");
4365
+ var import_jsx_runtime20 = require("react/jsx-runtime");
1271
4366
  var NO_STYLE = {
1272
4367
  bold: false,
1273
4368
  italic: false,
@@ -1601,16 +4696,16 @@ function isActive(id, s) {
1601
4696
  }
1602
4697
  function Toolbar({ api, features, extensions }) {
1603
4698
  const dialogs = useDialogs();
1604
- const [active, setActive] = (0, import_react7.useState)(NO_STYLE);
1605
- (0, import_react7.useEffect)(() => {
4699
+ const [active, setActive] = (0, import_react19.useState)(NO_STYLE);
4700
+ (0, import_react19.useEffect)(() => {
1606
4701
  ensureChromeFonts();
1607
4702
  }, []);
1608
- (0, import_react7.useEffect)(() => {
4703
+ (0, import_react19.useEffect)(() => {
1609
4704
  if (!api) return;
1610
4705
  const refresh = () => setActive(readActiveStyle2(api));
1611
4706
  refresh();
1612
4707
  const injector = api.univer._injector;
1613
- const cmd = injector?.get(import_core2.ICommandService);
4708
+ const cmd = injector?.get(import_core4.ICommandService);
1614
4709
  const sub = cmd?.onCommandExecuted(() => refresh());
1615
4710
  return () => sub?.dispose();
1616
4711
  }, [api]);
@@ -1638,7 +4733,7 @@ function Toolbar({ api, features, extensions }) {
1638
4733
  const renderActionButton = (a) => {
1639
4734
  const on = isActive(a.id, active);
1640
4735
  const baseBg = on ? "var(--cs-chrome-active, #e6f3f7)" : "transparent";
1641
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4736
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
1642
4737
  "button",
1643
4738
  {
1644
4739
  type: "button",
@@ -1664,12 +4759,12 @@ function Toolbar({ api, features, extensions }) {
1664
4759
  onMouseLeave: (e) => {
1665
4760
  e.currentTarget.style.background = baseBg;
1666
4761
  },
1667
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Icon, { name: a.icon, size: 20 })
4762
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Icon, { name: a.icon, size: 20 })
1668
4763
  },
1669
4764
  a.id
1670
4765
  );
1671
4766
  };
1672
- const renderIconButton = (id, label, icon, onPress, testid) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4767
+ const renderIconButton = (id, label, icon, onPress, testid) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
1673
4768
  "button",
1674
4769
  {
1675
4770
  type: "button",
@@ -1689,7 +4784,7 @@ function Toolbar({ api, features, extensions }) {
1689
4784
  onMouseLeave: (e) => {
1690
4785
  e.currentTarget.style.background = "transparent";
1691
4786
  },
1692
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Icon, { name: icon, size: 20 })
4787
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Icon, { name: icon, size: 20 })
1693
4788
  },
1694
4789
  id
1695
4790
  );
@@ -1700,8 +4795,8 @@ function Toolbar({ api, features, extensions }) {
1700
4795
  if (visible.length === 0) return null;
1701
4796
  const showDivider = firstGroupRendered;
1702
4797
  firstGroupRendered = true;
1703
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { style: { display: "inline-flex", alignItems: "center" }, children: [
1704
- showDivider && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: DIVIDER_STYLE, "aria-hidden": true }),
4798
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { style: { display: "inline-flex", alignItems: "center" }, children: [
4799
+ showDivider && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { style: DIVIDER_STYLE, "aria-hidden": true }),
1705
4800
  visible.map(renderActionButton)
1706
4801
  ] }, gi);
1707
4802
  });
@@ -1722,9 +4817,9 @@ function Toolbar({ api, features, extensions }) {
1722
4817
  if (e.command) void api.executeCommand(e.command, e.commandParams);
1723
4818
  else e.onClick?.(api);
1724
4819
  };
1725
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: BAR_STYLE, "data-testid": "casual-sheets-toolbar", role: "toolbar", "aria-label": "Editor", children: [
1726
- showFont && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1727
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4820
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: BAR_STYLE, "data-testid": "casual-sheets-toolbar", role: "toolbar", "aria-label": "Editor", children: [
4821
+ showFont && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
4822
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
1728
4823
  "select",
1729
4824
  {
1730
4825
  "aria-label": "Font family",
@@ -1733,10 +4828,10 @@ function Toolbar({ api, features, extensions }) {
1733
4828
  value: familyValue,
1734
4829
  disabled: !api,
1735
4830
  onChange: (e) => dispatch("sheet.command.set-range-font-family", { value: e.target.value }),
1736
- children: familyOptions.map((f) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: f, children: f }, f))
4831
+ children: familyOptions.map((f) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("option", { value: f, children: f }, f))
1737
4832
  }
1738
4833
  ),
1739
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4834
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
1740
4835
  "select",
1741
4836
  {
1742
4837
  "aria-label": "Font size",
@@ -1745,7 +4840,7 @@ function Toolbar({ api, features, extensions }) {
1745
4840
  value: sizeValue,
1746
4841
  disabled: !api,
1747
4842
  onChange: (e) => applyFontSize(Number(e.target.value)),
1748
- children: sizeOptions.map((s) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: s, children: s }, s))
4843
+ children: sizeOptions.map((s) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("option", { value: s, children: s }, s))
1749
4844
  }
1750
4845
  ),
1751
4846
  renderIconButton(
@@ -1762,15 +4857,15 @@ function Toolbar({ api, features, extensions }) {
1762
4857
  () => adjustFontSize(-1),
1763
4858
  "cs-font-size-down"
1764
4859
  ),
1765
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: DIVIDER_STYLE, "aria-hidden": true })
4860
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { style: DIVIDER_STYLE, "aria-hidden": true })
1766
4861
  ] }),
1767
4862
  groupNodes,
1768
- (showColor || showBorders) && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: DIVIDER_STYLE, "aria-hidden": true }),
1769
- showColor && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ColorPicker, { api }),
1770
- showBorders && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(BordersPicker, { api }),
1771
- showNumberDropdown && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1772
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: DIVIDER_STYLE, "aria-hidden": true }),
1773
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4863
+ (showColor || showBorders) && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { style: DIVIDER_STYLE, "aria-hidden": true }),
4864
+ showColor && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ColorPicker, { api }),
4865
+ showBorders && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(BordersPicker, { api }),
4866
+ showNumberDropdown && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
4867
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { style: DIVIDER_STYLE, "aria-hidden": true }),
4868
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
1774
4869
  "select",
1775
4870
  {
1776
4871
  "aria-label": "Number format",
@@ -1779,16 +4874,16 @@ function Toolbar({ api, features, extensions }) {
1779
4874
  value: numberFormatValue,
1780
4875
  disabled: !api,
1781
4876
  onChange: (e) => applyNumberFormat(NUMBER_FORMAT_PATTERNS2[e.target.value]),
1782
- children: NUMBER_FORMAT_OPTIONS2.map((o) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: o.value, children: o.label }, o.value))
4877
+ children: NUMBER_FORMAT_OPTIONS2.map((o) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("option", { value: o.value, children: o.label }, o.value))
1783
4878
  }
1784
4879
  )
1785
4880
  ] }),
1786
- showAutoSum && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1787
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: DIVIDER_STYLE, "aria-hidden": true }),
1788
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(AutoSumPicker, { api })
4881
+ showAutoSum && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
4882
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { style: DIVIDER_STYLE, "aria-hidden": true }),
4883
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(AutoSumPicker, { api })
1789
4884
  ] }),
1790
- showDialogGroup && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1791
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: DIVIDER_STYLE, "aria-hidden": true }),
4885
+ showDialogGroup && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
4886
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { style: DIVIDER_STYLE, "aria-hidden": true }),
1792
4887
  showFormatCells && renderIconButton(
1793
4888
  "format-cells",
1794
4889
  "Format Cells\u2026",
@@ -1811,8 +4906,8 @@ function Toolbar({ api, features, extensions }) {
1811
4906
  "cs-pivot-table"
1812
4907
  )
1813
4908
  ] }),
1814
- toolbarExt.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1815
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: DIVIDER_STYLE, "aria-hidden": true }),
4909
+ toolbarExt.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
4910
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { style: DIVIDER_STYLE, "aria-hidden": true }),
1816
4911
  toolbarExt.map(
1817
4912
  (e) => renderIconButton(e.id, e.label, e.icon, () => runExt(e), `cs-ext-${e.id}`)
1818
4913
  )
@@ -1821,13 +4916,13 @@ function Toolbar({ api, features, extensions }) {
1821
4916
  }
1822
4917
 
1823
4918
  // src/chrome/FormulaBar.tsx
1824
- var import_react9 = require("react");
1825
- var import_core4 = require("@univerjs/core");
4919
+ var import_react21 = require("react");
4920
+ var import_core6 = require("@univerjs/core");
1826
4921
 
1827
4922
  // src/chrome/NameBox.tsx
1828
- var import_react8 = require("react");
1829
- var import_core3 = require("@univerjs/core");
1830
- var import_jsx_runtime9 = require("react/jsx-runtime");
4923
+ var import_react20 = require("react");
4924
+ var import_core5 = require("@univerjs/core");
4925
+ var import_jsx_runtime21 = require("react/jsx-runtime");
1831
4926
  function readDefinedNames(api) {
1832
4927
  if (!api) return [];
1833
4928
  try {
@@ -1937,15 +5032,15 @@ var ITEM_REF_STYLE = {
1937
5032
  flex: "0 0 auto"
1938
5033
  };
1939
5034
  function NameBox({ api }) {
1940
- const [ref, setRef] = (0, import_react8.useState)("");
1941
- const [draft, setDraft] = (0, import_react8.useState)(null);
1942
- const draftRef = (0, import_react8.useRef)(null);
5035
+ const [ref, setRef] = (0, import_react20.useState)("");
5036
+ const [draft, setDraft] = (0, import_react20.useState)(null);
5037
+ const draftRef = (0, import_react20.useRef)(null);
1943
5038
  draftRef.current = draft;
1944
- const inputRef = (0, import_react8.useRef)(null);
1945
- const [open, setOpen] = (0, import_react8.useState)(false);
1946
- const [names, setNames] = (0, import_react8.useState)([]);
1947
- const rootRef = (0, import_react8.useRef)(null);
1948
- (0, import_react8.useEffect)(() => {
5039
+ const inputRef = (0, import_react20.useRef)(null);
5040
+ const [open, setOpen] = (0, import_react20.useState)(false);
5041
+ const [names, setNames] = (0, import_react20.useState)([]);
5042
+ const rootRef = (0, import_react20.useRef)(null);
5043
+ (0, import_react20.useEffect)(() => {
1949
5044
  if (!api) return;
1950
5045
  const refresh = () => {
1951
5046
  if (draftRef.current !== null) return;
@@ -1953,11 +5048,11 @@ function NameBox({ api }) {
1953
5048
  };
1954
5049
  refresh();
1955
5050
  const injector = api.univer._injector;
1956
- const cmd = injector?.get(import_core3.ICommandService);
5051
+ const cmd = injector?.get(import_core5.ICommandService);
1957
5052
  const sub = cmd?.onCommandExecuted(() => refresh());
1958
5053
  return () => sub?.dispose();
1959
5054
  }, [api]);
1960
- (0, import_react8.useEffect)(() => {
5055
+ (0, import_react20.useEffect)(() => {
1961
5056
  if (!open) return;
1962
5057
  const onPointerDown = (e) => {
1963
5058
  if (rootRef.current && !rootRef.current.contains(e.target)) setOpen(false);
@@ -2018,8 +5113,8 @@ function NameBox({ api }) {
2018
5113
  }
2019
5114
  };
2020
5115
  const shown = draft ?? ref;
2021
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { ref: rootRef, style: ROOT_STYLE, children: [
2022
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
5116
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { ref: rootRef, style: ROOT_STYLE, children: [
5117
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2023
5118
  "input",
2024
5119
  {
2025
5120
  ref: inputRef,
@@ -2041,7 +5136,7 @@ function NameBox({ api }) {
2041
5136
  }
2042
5137
  }
2043
5138
  ),
2044
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
5139
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2045
5140
  "button",
2046
5141
  {
2047
5142
  type: "button",
@@ -2060,10 +5155,10 @@ function NameBox({ api }) {
2060
5155
  e.preventDefault();
2061
5156
  toggleDropdown();
2062
5157
  },
2063
- children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Icon, { name: "expand_more", size: 18 })
5158
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Icon, { name: "expand_more", size: 18 })
2064
5159
  }
2065
5160
  ),
2066
- open && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: MENU_STYLE, role: "menu", "data-testid": "cs-namebox-menu", children: names.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { style: MENU_EMPTY_STYLE, children: "No names" }) : names.map((entry, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
5161
+ open && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: MENU_STYLE, role: "menu", "data-testid": "cs-namebox-menu", children: names.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: MENU_EMPTY_STYLE, children: "No names" }) : names.map((entry, i) => /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
2067
5162
  "button",
2068
5163
  {
2069
5164
  type: "button",
@@ -2082,8 +5177,8 @@ function NameBox({ api }) {
2082
5177
  e.currentTarget.style.background = "transparent";
2083
5178
  },
2084
5179
  children: [
2085
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: entry.name }),
2086
- entry.ref && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: ITEM_REF_STYLE, children: entry.ref })
5180
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { children: entry.name }),
5181
+ entry.ref && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { style: ITEM_REF_STYLE, children: entry.ref })
2087
5182
  ]
2088
5183
  },
2089
5184
  entry.name
@@ -2092,8 +5187,8 @@ function NameBox({ api }) {
2092
5187
  }
2093
5188
 
2094
5189
  // src/chrome/FormulaBar.tsx
2095
- var import_jsx_runtime10 = require("react/jsx-runtime");
2096
- var FUNCTIONS = [
5190
+ var import_jsx_runtime22 = require("react/jsx-runtime");
5191
+ var FUNCTIONS2 = [
2097
5192
  "ABS",
2098
5193
  "AND",
2099
5194
  "AVERAGE",
@@ -2197,7 +5292,7 @@ function suggestFor(draft) {
2197
5292
  const m = TOKEN_RE.exec(draft);
2198
5293
  if (!m) return [];
2199
5294
  const prefix = m[1].toUpperCase();
2200
- return FUNCTIONS.filter((f) => f.startsWith(prefix) && f !== prefix).slice(0, 8);
5295
+ return FUNCTIONS2.filter((f) => f.startsWith(prefix) && f !== prefix).slice(0, 8);
2201
5296
  }
2202
5297
  function colToLetters2(col) {
2203
5298
  let s = "";
@@ -2280,13 +5375,13 @@ var AC_ITEM_STYLE = {
2280
5375
  cursor: "pointer"
2281
5376
  };
2282
5377
  function FormulaBar({ api }) {
2283
- const [cell, setCell] = (0, import_react9.useState)(null);
2284
- const [draft, setDraft] = (0, import_react9.useState)(null);
2285
- const draftRef = (0, import_react9.useRef)(null);
5378
+ const [cell, setCell] = (0, import_react21.useState)(null);
5379
+ const [draft, setDraft] = (0, import_react21.useState)(null);
5380
+ const draftRef = (0, import_react21.useRef)(null);
2286
5381
  draftRef.current = draft;
2287
- const [acIdx, setAcIdx] = (0, import_react9.useState)(0);
2288
- const [acDismissed, setAcDismissed] = (0, import_react9.useState)(false);
2289
- (0, import_react9.useEffect)(() => {
5382
+ const [acIdx, setAcIdx] = (0, import_react21.useState)(0);
5383
+ const [acDismissed, setAcDismissed] = (0, import_react21.useState)(false);
5384
+ (0, import_react21.useEffect)(() => {
2290
5385
  if (!api) return;
2291
5386
  const refresh = () => {
2292
5387
  if (draftRef.current !== null) return;
@@ -2294,11 +5389,11 @@ function FormulaBar({ api }) {
2294
5389
  };
2295
5390
  refresh();
2296
5391
  const injector = api.univer._injector;
2297
- const cmd = injector?.get(import_core4.ICommandService);
5392
+ const cmd = injector?.get(import_core6.ICommandService);
2298
5393
  const sub = cmd?.onCommandExecuted(() => refresh());
2299
5394
  return () => sub?.dispose();
2300
5395
  }, [api]);
2301
- const suggestions = (0, import_react9.useMemo)(() => suggestFor(draft), [draft]);
5396
+ const suggestions = (0, import_react21.useMemo)(() => suggestFor(draft), [draft]);
2302
5397
  const acOpen = !acDismissed && suggestions.length > 0;
2303
5398
  const commit = (text) => {
2304
5399
  setDraft(null);
@@ -2356,11 +5451,11 @@ function FormulaBar({ api }) {
2356
5451
  }
2357
5452
  };
2358
5453
  const shown = draft ?? cell?.text ?? "";
2359
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: BAR_STYLE2, "data-testid": "casual-sheets-formula-bar", children: [
2360
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(NameBox, { api }),
2361
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { style: FX_STYLE, "aria-hidden": true, children: "fx" }),
2362
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: INPUT_WRAP_STYLE, children: [
2363
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
5454
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: BAR_STYLE2, "data-testid": "casual-sheets-formula-bar", children: [
5455
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(NameBox, { api }),
5456
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { style: FX_STYLE, "aria-hidden": true, children: "fx" }),
5457
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: INPUT_WRAP_STYLE, children: [
5458
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2364
5459
  "input",
2365
5460
  {
2366
5461
  type: "text",
@@ -2376,7 +5471,7 @@ function FormulaBar({ api }) {
2376
5471
  disabled: !api
2377
5472
  }
2378
5473
  ),
2379
- acOpen && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: AC_STYLE, "data-testid": "cs-formula-suggestions", role: "listbox", children: suggestions.map((fn, i) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
5474
+ acOpen && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: AC_STYLE, "data-testid": "cs-formula-suggestions", role: "listbox", children: suggestions.map((fn, i) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2380
5475
  "button",
2381
5476
  {
2382
5477
  type: "button",
@@ -2402,9 +5497,9 @@ function FormulaBar({ api }) {
2402
5497
  }
2403
5498
 
2404
5499
  // src/chrome/StatusBar.tsx
2405
- var import_react10 = require("react");
2406
- var import_core5 = require("@univerjs/core");
2407
- var import_jsx_runtime11 = require("react/jsx-runtime");
5500
+ var import_react22 = require("react");
5501
+ var import_core7 = require("@univerjs/core");
5502
+ var import_jsx_runtime23 = require("react/jsx-runtime");
2408
5503
  function readStats(api) {
2409
5504
  const sel = api.getSelection();
2410
5505
  const sheet = api.univer.getActiveWorkbook()?.getActiveSheet();
@@ -2487,9 +5582,9 @@ var ZOOM_LEVEL_STYLE = {
2487
5582
  textAlign: "center"
2488
5583
  };
2489
5584
  function StatusBar({ api }) {
2490
- const [stats, setStats] = (0, import_react10.useState)(null);
2491
- const [zoom, setZoom] = (0, import_react10.useState)(1);
2492
- (0, import_react10.useEffect)(() => {
5585
+ const [stats, setStats] = (0, import_react22.useState)(null);
5586
+ const [zoom, setZoom] = (0, import_react22.useState)(1);
5587
+ (0, import_react22.useEffect)(() => {
2493
5588
  if (!api) return;
2494
5589
  const refresh = () => {
2495
5590
  setStats(readStats(api));
@@ -2497,7 +5592,7 @@ function StatusBar({ api }) {
2497
5592
  };
2498
5593
  refresh();
2499
5594
  const injector = api.univer._injector;
2500
- const cmd = injector?.get(import_core5.ICommandService);
5595
+ const cmd = injector?.get(import_core7.ICommandService);
2501
5596
  const sub = cmd?.onCommandExecuted(() => refresh());
2502
5597
  return () => sub?.dispose();
2503
5598
  }, [api]);
@@ -2515,37 +5610,37 @@ function StatusBar({ api }) {
2515
5610
  };
2516
5611
  const zoomBy = (delta) => setZoomRatio(Math.round((zoom + delta) * 100) / 100);
2517
5612
  const resetZoom = () => setZoomRatio(1);
2518
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: BAR_STYLE3, "data-testid": "casual-sheets-status-bar", children: [
2519
- stats && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
2520
- stats.numCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { "data-stat": "average", children: [
5613
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { style: BAR_STYLE3, "data-testid": "casual-sheets-status-bar", children: [
5614
+ stats && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
5615
+ stats.numCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("span", { "data-stat": "average", children: [
2521
5616
  "Average: ",
2522
5617
  fmt(stats.avg)
2523
5618
  ] }),
2524
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { "data-stat": "count", children: [
5619
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("span", { "data-stat": "count", children: [
2525
5620
  "Count: ",
2526
5621
  stats.count
2527
5622
  ] }),
2528
- stats.numCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
2529
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { "data-stat": "num-count", children: [
5623
+ stats.numCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
5624
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("span", { "data-stat": "num-count", children: [
2530
5625
  "Numerical Count: ",
2531
5626
  stats.numCount
2532
5627
  ] }),
2533
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { "data-stat": "min", children: [
5628
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("span", { "data-stat": "min", children: [
2534
5629
  "Min: ",
2535
5630
  fmt(stats.min)
2536
5631
  ] }),
2537
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { "data-stat": "max", children: [
5632
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("span", { "data-stat": "max", children: [
2538
5633
  "Max: ",
2539
5634
  fmt(stats.max)
2540
5635
  ] }),
2541
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { "data-stat": "sum", children: [
5636
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("span", { "data-stat": "sum", children: [
2542
5637
  "Sum: ",
2543
5638
  fmt(stats.sum)
2544
5639
  ] })
2545
5640
  ] })
2546
5641
  ] }),
2547
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { style: ZOOM_GROUP_STYLE, "data-testid": "cs-zoom", children: [
2548
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
5642
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("span", { style: ZOOM_GROUP_STYLE, "data-testid": "cs-zoom", children: [
5643
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2549
5644
  "button",
2550
5645
  {
2551
5646
  type: "button",
@@ -2561,7 +5656,7 @@ function StatusBar({ api }) {
2561
5656
  children: "\u2212"
2562
5657
  }
2563
5658
  ),
2564
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
5659
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
2565
5660
  "button",
2566
5661
  {
2567
5662
  type: "button",
@@ -2580,7 +5675,7 @@ function StatusBar({ api }) {
2580
5675
  ]
2581
5676
  }
2582
5677
  ),
2583
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
5678
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2584
5679
  "button",
2585
5680
  {
2586
5681
  type: "button",
@@ -2601,8 +5696,8 @@ function StatusBar({ api }) {
2601
5696
  }
2602
5697
 
2603
5698
  // src/chrome/MenuBar.tsx
2604
- var import_react11 = require("react");
2605
- var import_core6 = require("@univerjs/core");
5699
+ var import_react23 = require("react");
5700
+ var import_core8 = require("@univerjs/core");
2606
5701
 
2607
5702
  // src/chrome/openExternal.ts
2608
5703
  function openExternal(url) {
@@ -2813,7 +5908,7 @@ function computeVisibleMenus(menus, features, canOpen, ext) {
2813
5908
  }
2814
5909
 
2815
5910
  // src/chrome/MenuBar.tsx
2816
- var import_jsx_runtime12 = require("react/jsx-runtime");
5911
+ var import_jsx_runtime24 = require("react/jsx-runtime");
2817
5912
  function fmtShortcut(shortcut) {
2818
5913
  const isMac = typeof navigator !== "undefined" && /Mac|iPhone|iPad/i.test(navigator.platform || "");
2819
5914
  if (!isMac) return shortcut;
@@ -2833,14 +5928,14 @@ function fmtShortcut(shortcut) {
2833
5928
  function fu(api) {
2834
5929
  return api.univer;
2835
5930
  }
2836
- function activeRange2(api) {
5931
+ function activeRange12(api) {
2837
5932
  return fu(api).getActiveWorkbook()?.getActiveSheet()?.getActiveRange() ?? null;
2838
5933
  }
2839
- function activeSheet(api) {
5934
+ function activeSheet3(api) {
2840
5935
  return fu(api).getActiveWorkbook()?.getActiveSheet() ?? null;
2841
5936
  }
2842
5937
  function selectionA1(api) {
2843
- const range = activeRange2(api);
5938
+ const range = activeRange12(api);
2844
5939
  if (!range) return null;
2845
5940
  try {
2846
5941
  return range.getA1Notation();
@@ -2855,38 +5950,38 @@ var copy = (api) => void api.executeCommand("univer.command.copy");
2855
5950
  var paste = (api) => void api.executeCommand("univer.command.paste");
2856
5951
  var pasteFormattingOnly = (api) => void api.executeCommand("sheet.command.paste-format");
2857
5952
  var insertRowAbove = (api) => {
2858
- const range = activeRange2(api);
2859
- const sheet = activeSheet(api);
5953
+ const range = activeRange12(api);
5954
+ const sheet = activeSheet3(api);
2860
5955
  if (!range || !sheet) return;
2861
5956
  sheet.insertRowBefore(range.getRow());
2862
5957
  };
2863
5958
  var insertRowBelow = (api) => {
2864
- const range = activeRange2(api);
2865
- const sheet = activeSheet(api);
5959
+ const range = activeRange12(api);
5960
+ const sheet = activeSheet3(api);
2866
5961
  if (!range || !sheet) return;
2867
5962
  sheet.insertRowAfter(range.getRow() + range.getHeight() - 1);
2868
5963
  };
2869
5964
  var insertColumnLeft = (api) => {
2870
- const range = activeRange2(api);
2871
- const sheet = activeSheet(api);
5965
+ const range = activeRange12(api);
5966
+ const sheet = activeSheet3(api);
2872
5967
  if (!range || !sheet) return;
2873
5968
  sheet.insertColumnBefore(range.getColumn());
2874
5969
  };
2875
5970
  var insertColumnRight = (api) => {
2876
- const range = activeRange2(api);
2877
- const sheet = activeSheet(api);
5971
+ const range = activeRange12(api);
5972
+ const sheet = activeSheet3(api);
2878
5973
  if (!range || !sheet) return;
2879
5974
  sheet.insertColumnAfter(range.getColumn() + range.getWidth() - 1);
2880
5975
  };
2881
5976
  var deleteSelectedRow = (api) => {
2882
- const range = activeRange2(api);
2883
- const sheet = activeSheet(api);
5977
+ const range = activeRange12(api);
5978
+ const sheet = activeSheet3(api);
2884
5979
  if (!range || !sheet) return;
2885
5980
  sheet.deleteRows(range.getRow(), range.getHeight());
2886
5981
  };
2887
5982
  var deleteSelectedColumn = (api) => {
2888
- const range = activeRange2(api);
2889
- const sheet = activeSheet(api);
5983
+ const range = activeRange12(api);
5984
+ const sheet = activeSheet3(api);
2890
5985
  if (!range || !sheet) return;
2891
5986
  sheet.deleteColumns(range.getColumn(), range.getWidth());
2892
5987
  };
@@ -2897,7 +5992,7 @@ var insertImage = (api) => void api.executeCommand("sheet.command.insert-float-i
2897
5992
  var insertHyperlink = (api) => void api.executeCommand("sheet.operation.insert-hyper-link");
2898
5993
  var insertComment = (api) => void api.executeCommand("sheet.operation.show-comment-modal");
2899
5994
  var insertTodayDate = (api) => {
2900
- const range = activeRange2(api);
5995
+ const range = activeRange12(api);
2901
5996
  if (!range) return;
2902
5997
  const today = /* @__PURE__ */ new Date();
2903
5998
  const pad = (n) => n.toString().padStart(2, "0");
@@ -2905,7 +6000,7 @@ var insertTodayDate = (api) => {
2905
6000
  range.setValue({ v });
2906
6001
  };
2907
6002
  var insertCurrentTime = (api) => {
2908
- const range = activeRange2(api);
6003
+ const range = activeRange12(api);
2909
6004
  if (!range) return;
2910
6005
  const now = /* @__PURE__ */ new Date();
2911
6006
  const pad = (n) => n.toString().padStart(2, "0");
@@ -2914,8 +6009,8 @@ var insertCurrentTime = (api) => {
2914
6009
  };
2915
6010
  var insertTable = (api) => {
2916
6011
  void (async () => {
2917
- const range = activeRange2(api);
2918
- const sheet = activeSheet(api);
6012
+ const range = activeRange12(api);
6013
+ const sheet = activeSheet3(api);
2919
6014
  const wb = fu(api).getActiveWorkbook();
2920
6015
  if (!range || !sheet || !wb) return;
2921
6016
  await ensurePluginByName("table");
@@ -2932,16 +6027,16 @@ var insertTable = (api) => {
2932
6027
  })();
2933
6028
  };
2934
6029
  var autoFitColumns = (api) => {
2935
- const range = activeRange2(api);
2936
- const sheet = activeSheet(api);
6030
+ const range = activeRange12(api);
6031
+ const sheet = activeSheet3(api);
2937
6032
  if (!range || !sheet) return;
2938
6033
  const withAutoWidth = sheet;
2939
6034
  withAutoWidth.setColumnAutoWidth?.(range.getColumn(), range.getWidth());
2940
6035
  };
2941
6036
  var AUTO_FIT_ROW_CAP = 500;
2942
6037
  var autoFitRows = (api) => {
2943
- const range = activeRange2(api);
2944
- const sheet = activeSheet(api);
6038
+ const range = activeRange12(api);
6039
+ const sheet = activeSheet3(api);
2945
6040
  if (!range || !sheet) return;
2946
6041
  const start = range.getRow();
2947
6042
  const count = Math.min(range.getHeight(), AUTO_FIT_ROW_CAP);
@@ -2980,23 +6075,23 @@ var NUM_FORMAT_SHORTCUT = {
2980
6075
  scientific: "Ctrl+Shift+6"
2981
6076
  };
2982
6077
  function setNumberFormatByKey(api, key) {
2983
- const range = activeRange2(api);
6078
+ const range = activeRange12(api);
2984
6079
  range?.setNumberFormat?.(NUMBER_FORMAT_PATTERNS3[key]);
2985
6080
  }
2986
6081
  var increaseDecimal = (api) => void api.executeCommand("sheet.command.numfmt.add.decimal.command");
2987
6082
  var decreaseDecimal = (api) => void api.executeCommand("sheet.command.numfmt.subtract.decimal.command");
2988
6083
  var clearFormat = (api) => void api.executeCommand("sheet.command.clear-selection-format");
2989
6084
  function applyBorders(api, choice) {
2990
- const range = activeRange2(api);
6085
+ const range = activeRange12(api);
2991
6086
  if (!range) return;
2992
- const type = choice === "all" ? import_core6.BorderType.ALL : choice === "outside" ? import_core6.BorderType.OUTSIDE : import_core6.BorderType.NONE;
2993
- const style = choice === "none" ? import_core6.BorderStyleTypes.NONE : import_core6.BorderStyleTypes.THIN;
6087
+ const type = choice === "all" ? import_core8.BorderType.ALL : choice === "outside" ? import_core8.BorderType.OUTSIDE : import_core8.BorderType.NONE;
6088
+ const style = choice === "none" ? import_core8.BorderStyleTypes.NONE : import_core8.BorderStyleTypes.THIN;
2994
6089
  range.setBorder(type, style, "#000000");
2995
6090
  }
2996
6091
  function rowSpan(api) {
2997
6092
  const wb = fu(api).getActiveWorkbook();
2998
- const sheet = activeSheet(api);
2999
- const range = activeRange2(api);
6093
+ const sheet = activeSheet3(api);
6094
+ const range = activeRange12(api);
3000
6095
  if (!wb || !sheet || !range) return null;
3001
6096
  const startRow = range.getRow();
3002
6097
  const endRow = startRow + range.getHeight() - 1;
@@ -3005,8 +6100,8 @@ function rowSpan(api) {
3005
6100
  }
3006
6101
  function colSpan(api) {
3007
6102
  const wb = fu(api).getActiveWorkbook();
3008
- const sheet = activeSheet(api);
3009
- const range = activeRange2(api);
6103
+ const sheet = activeSheet3(api);
6104
+ const range = activeRange12(api);
3010
6105
  if (!wb || !sheet || !range) return null;
3011
6106
  const startColumn = range.getColumn();
3012
6107
  const endColumn = startColumn + range.getWidth() - 1;
@@ -3082,16 +6177,16 @@ var unhideSelectedColumns = (api) => {
3082
6177
  });
3083
6178
  };
3084
6179
  var freezeFirstRow = (api) => {
3085
- activeSheet(api)?.setFrozenRows(1);
6180
+ activeSheet3(api)?.setFrozenRows(1);
3086
6181
  };
3087
6182
  var freezeFirstColumn = (api) => {
3088
- activeSheet(api)?.setFrozenColumns(1);
6183
+ activeSheet3(api)?.setFrozenColumns(1);
3089
6184
  };
3090
6185
  var freezeAtSelection = (api) => void api.executeCommand("sheet.command.set-selection-frozen");
3091
6186
  var unfreezePanes = (api) => void api.executeCommand("sheet.command.cancel-frozen");
3092
6187
  var toggleGridlines = (api) => {
3093
6188
  const wb = fu(api).getActiveWorkbook();
3094
- const sheet = activeSheet(api);
6189
+ const sheet = activeSheet3(api);
3095
6190
  if (!wb || !sheet) return;
3096
6191
  void api.executeCommand("sheet.command.toggle-gridlines", {
3097
6192
  unitId: wb.getId(),
@@ -3101,7 +6196,7 @@ var toggleGridlines = (api) => {
3101
6196
  };
3102
6197
  var toggleCommentPanel = (api) => void api.executeCommand("sheet.operation.toggle-comment-panel");
3103
6198
  var jumpToFirstCell = (api) => {
3104
- activeSheet(api)?.getRange(0, 0).activate();
6199
+ activeSheet3(api)?.getRange(0, 0).activate();
3105
6200
  };
3106
6201
  var switchToPreviousSheet = (api) => switchSheetByDelta(api, -1);
3107
6202
  var switchToNextSheet = (api) => switchSheetByDelta(api, 1);
@@ -3121,7 +6216,7 @@ var showFormulas = (api) => void api.executeCommand("sheet.command.set-show-form
3121
6216
  var sortAsc = (api) => sortRange(api, true);
3122
6217
  var sortDesc = (api) => sortRange(api, false);
3123
6218
  function sortRange(api, ascending) {
3124
- const range = activeRange2(api);
6219
+ const range = activeRange12(api);
3125
6220
  if (!range) return;
3126
6221
  const withSort = range;
3127
6222
  withSort.sort?.({ column: range.getColumn(), ascending });
@@ -3130,8 +6225,8 @@ var toggleFilter = (api) => {
3130
6225
  void (async () => {
3131
6226
  await ensurePluginByName("filter");
3132
6227
  const wb = fu(api).getActiveWorkbook();
3133
- const sheet = activeSheet(api);
3134
- const range = activeRange2(api);
6228
+ const sheet = activeSheet3(api);
6229
+ const range = activeRange12(api);
3135
6230
  if (!wb || !sheet || !range) return;
3136
6231
  const sheetWithFilter = sheet;
3137
6232
  if (sheetWithFilter.getFilter?.()) {
@@ -3837,13 +6932,13 @@ var SUBMENU_PANEL_STYLE = {
3837
6932
  };
3838
6933
  function MenuBar({ api, features = {}, extensions }) {
3839
6934
  const dialogs = useDialogs();
3840
- const [open, setOpen] = (0, import_react11.useState)(null);
3841
- const [openSubmenu, setOpenSubmenu] = (0, import_react11.useState)(null);
3842
- const rootRef = (0, import_react11.useRef)(null);
3843
- (0, import_react11.useEffect)(() => {
6935
+ const [open, setOpen] = (0, import_react23.useState)(null);
6936
+ const [openSubmenu, setOpenSubmenu] = (0, import_react23.useState)(null);
6937
+ const rootRef = (0, import_react23.useRef)(null);
6938
+ (0, import_react23.useEffect)(() => {
3844
6939
  ensureChromeFonts();
3845
6940
  }, []);
3846
- (0, import_react11.useEffect)(() => {
6941
+ (0, import_react23.useEffect)(() => {
3847
6942
  if (open === null) return;
3848
6943
  const onKey = (e) => {
3849
6944
  if (e.key === "Escape") {
@@ -3881,18 +6976,18 @@ function MenuBar({ api, features = {}, extensions }) {
3881
6976
  const visibleMenus = computeVisibleMenus(MENUS, features, dialogs.canOpen, extensions?.menu);
3882
6977
  const renderItems = (items) => items.map((item) => {
3883
6978
  if (item.kind === "separator") {
3884
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: SEPARATOR_STYLE, role: "separator", "aria-hidden": true }, item.id);
6979
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: SEPARATOR_STYLE, role: "separator", "aria-hidden": true }, item.id);
3885
6980
  }
3886
6981
  if (item.kind === "submenu") {
3887
6982
  const isSubOpen = openSubmenu === item.id;
3888
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
6983
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
3889
6984
  "div",
3890
6985
  {
3891
6986
  style: { position: "relative" },
3892
6987
  onMouseEnter: () => setOpenSubmenu(item.id),
3893
6988
  onMouseLeave: () => setOpenSubmenu((cur) => cur === item.id ? null : cur),
3894
6989
  children: [
3895
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
6990
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
3896
6991
  "button",
3897
6992
  {
3898
6993
  type: "button",
@@ -3904,19 +6999,19 @@ function MenuBar({ api, features = {}, extensions }) {
3904
6999
  style: { ...ITEM_STYLE3, opacity: api ? 1 : 0.5 },
3905
7000
  onMouseDown: (e) => e.preventDefault(),
3906
7001
  children: [
3907
- item.icon ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Icon, { name: item.icon, size: 18 }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: { width: 18 }, "aria-hidden": true }),
3908
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { children: item.label }),
3909
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Icon, { name: "chevron_right", size: 18, style: { marginLeft: "auto" } })
7002
+ item.icon ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Icon, { name: item.icon, size: 18 }) : /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { style: { width: 18 }, "aria-hidden": true }),
7003
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { children: item.label }),
7004
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Icon, { name: "chevron_right", size: 18, style: { marginLeft: "auto" } })
3910
7005
  ]
3911
7006
  }
3912
7007
  ),
3913
- isSubOpen && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: SUBMENU_PANEL_STYLE, role: "menu", "aria-label": item.label, children: renderItems(item.items) })
7008
+ isSubOpen && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: SUBMENU_PANEL_STYLE, role: "menu", "aria-label": item.label, children: renderItems(item.items) })
3914
7009
  ]
3915
7010
  },
3916
7011
  item.id
3917
7012
  );
3918
7013
  }
3919
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
7014
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
3920
7015
  "button",
3921
7016
  {
3922
7017
  type: "button",
@@ -3936,15 +7031,15 @@ function MenuBar({ api, features = {}, extensions }) {
3936
7031
  e.currentTarget.style.background = "transparent";
3937
7032
  },
3938
7033
  children: [
3939
- item.icon ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Icon, { name: item.icon, size: 18 }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: { width: 18 }, "aria-hidden": true }),
3940
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { children: item.label }),
3941
- item.shortcut && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { style: SHORTCUT_STYLE, children: fmtShortcut(item.shortcut) })
7034
+ item.icon ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Icon, { name: item.icon, size: 18 }) : /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { style: { width: 18 }, "aria-hidden": true }),
7035
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { children: item.label }),
7036
+ item.shortcut && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { style: SHORTCUT_STYLE, children: fmtShortcut(item.shortcut) })
3942
7037
  ]
3943
7038
  },
3944
7039
  item.id
3945
7040
  );
3946
7041
  });
3947
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7042
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
3948
7043
  "div",
3949
7044
  {
3950
7045
  ref: rootRef,
@@ -3954,8 +7049,8 @@ function MenuBar({ api, features = {}, extensions }) {
3954
7049
  "aria-label": "Menu bar",
3955
7050
  children: visibleMenus.map((menu) => {
3956
7051
  const isOpen = open === menu.id;
3957
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: { position: "relative" }, children: [
3958
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
7052
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { position: "relative" }, children: [
7053
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
3959
7054
  "button",
3960
7055
  {
3961
7056
  type: "button",
@@ -3986,7 +7081,7 @@ function MenuBar({ api, features = {}, extensions }) {
3986
7081
  children: menu.label
3987
7082
  }
3988
7083
  ),
3989
- isOpen && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { style: DROPDOWN_STYLE, role: "menu", "aria-label": menu.label, children: renderItems(menu.items) })
7084
+ isOpen && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: DROPDOWN_STYLE, role: "menu", "aria-label": menu.label, children: renderItems(menu.items) })
3990
7085
  ] }, menu.id);
3991
7086
  })
3992
7087
  }
@@ -3994,9 +7089,9 @@ function MenuBar({ api, features = {}, extensions }) {
3994
7089
  }
3995
7090
 
3996
7091
  // src/chrome/SheetTabs.tsx
3997
- var import_react12 = require("react");
7092
+ var import_react24 = require("react");
3998
7093
  var import_react_dom2 = require("react-dom");
3999
- var import_jsx_runtime13 = require("react/jsx-runtime");
7094
+ var import_jsx_runtime25 = require("react/jsx-runtime");
4000
7095
  var EMPTY = { tabs: [], activeId: null };
4001
7096
  var SHEET_LIST_MUTATIONS = /* @__PURE__ */ new Set([
4002
7097
  "sheet.mutation.insert-sheet",
@@ -4029,7 +7124,7 @@ var BAR_STYLE5 = {
4029
7124
  overflowX: "auto",
4030
7125
  overflowY: "hidden"
4031
7126
  };
4032
- var ADD_BTN_STYLE = {
7127
+ var ADD_BTN_STYLE2 = {
4033
7128
  flex: "0 0 auto",
4034
7129
  width: 28,
4035
7130
  border: "none",
@@ -4077,11 +7172,11 @@ var MENU_ITEM_STYLE2 = {
4077
7172
  cursor: "pointer"
4078
7173
  };
4079
7174
  function SheetTabs({ api }) {
4080
- const [{ tabs, activeId }, setSnapshot] = (0, import_react12.useState)(EMPTY);
4081
- const [renaming, setRenaming] = (0, import_react12.useState)(null);
4082
- const [draft, setDraft] = (0, import_react12.useState)("");
4083
- const [menu, setMenu] = (0, import_react12.useState)(null);
4084
- (0, import_react12.useEffect)(() => {
7175
+ const [{ tabs, activeId }, setSnapshot] = (0, import_react24.useState)(EMPTY);
7176
+ const [renaming, setRenaming] = (0, import_react24.useState)(null);
7177
+ const [draft, setDraft] = (0, import_react24.useState)("");
7178
+ const [menu, setMenu] = (0, import_react24.useState)(null);
7179
+ (0, import_react24.useEffect)(() => {
4085
7180
  if (!api) return;
4086
7181
  const refresh = () => setSnapshot(readSheets(api));
4087
7182
  refresh();
@@ -4109,7 +7204,7 @@ function SheetTabs({ api }) {
4109
7204
  for (const s of subs) s?.dispose?.();
4110
7205
  };
4111
7206
  }, [api]);
4112
- (0, import_react12.useEffect)(() => {
7207
+ (0, import_react24.useEffect)(() => {
4113
7208
  if (!menu) return;
4114
7209
  const close = () => setMenu(null);
4115
7210
  window.addEventListener("pointerdown", close);
@@ -4120,7 +7215,7 @@ function SheetTabs({ api }) {
4120
7215
  };
4121
7216
  }, [menu]);
4122
7217
  if (!api) {
4123
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: BAR_STYLE5, "data-testid": "casual-sheets-tabs" });
7218
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { style: BAR_STYLE5, "data-testid": "casual-sheets-tabs" });
4124
7219
  }
4125
7220
  const visible = tabs.filter((t) => !t.hidden);
4126
7221
  const wb = () => api.univer.getActiveWorkbook();
@@ -4160,11 +7255,11 @@ function SheetTabs({ api }) {
4160
7255
  setRenaming(null);
4161
7256
  }
4162
7257
  };
4163
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: BAR_STYLE5, "data-testid": "casual-sheets-tabs", role: "tablist", children: [
7258
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { style: BAR_STYLE5, "data-testid": "casual-sheets-tabs", role: "tablist", children: [
4164
7259
  visible.map((tab) => {
4165
7260
  const active = tab.id === activeId;
4166
7261
  if (renaming === tab.id) {
4167
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
7262
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
4168
7263
  "input",
4169
7264
  {
4170
7265
  autoFocus: true,
@@ -4178,7 +7273,7 @@ function SheetTabs({ api }) {
4178
7273
  tab.id
4179
7274
  );
4180
7275
  }
4181
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
7276
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
4182
7277
  "button",
4183
7278
  {
4184
7279
  type: "button",
@@ -4217,11 +7312,11 @@ function SheetTabs({ api }) {
4217
7312
  tab.id
4218
7313
  );
4219
7314
  }),
4220
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
7315
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
4221
7316
  "button",
4222
7317
  {
4223
7318
  type: "button",
4224
- style: ADD_BTN_STYLE,
7319
+ style: ADD_BTN_STYLE2,
4225
7320
  "aria-label": "Add sheet",
4226
7321
  title: "Add sheet",
4227
7322
  "data-testid": "cs-tab-add",
@@ -4233,7 +7328,7 @@ function SheetTabs({ api }) {
4233
7328
  }
4234
7329
  ),
4235
7330
  menu && (0, import_react_dom2.createPortal)(
4236
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
7331
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
4237
7332
  "div",
4238
7333
  {
4239
7334
  style: { ...MENU_STYLE2, left: menu.x, top: menu.y, transform: "translateY(-100%)" },
@@ -4241,7 +7336,7 @@ function SheetTabs({ api }) {
4241
7336
  role: "menu",
4242
7337
  onPointerDown: (e) => e.stopPropagation(),
4243
7338
  children: [
4244
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
7339
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
4245
7340
  "button",
4246
7341
  {
4247
7342
  type: "button",
@@ -4256,7 +7351,7 @@ function SheetTabs({ api }) {
4256
7351
  children: "Rename"
4257
7352
  }
4258
7353
  ),
4259
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
7354
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
4260
7355
  "button",
4261
7356
  {
4262
7357
  type: "button",
@@ -4283,8 +7378,8 @@ function SheetTabs({ api }) {
4283
7378
  }
4284
7379
 
4285
7380
  // src/chrome/FindReplace.tsx
4286
- var import_react13 = require("react");
4287
- var import_jsx_runtime14 = require("react/jsx-runtime");
7381
+ var import_react25 = require("react");
7382
+ var import_jsx_runtime26 = require("react/jsx-runtime");
4288
7383
  function findMatches(api, query, matchCase) {
4289
7384
  if (!query) return [];
4290
7385
  const snap = api.getSnapshot();
@@ -4322,7 +7417,7 @@ var WRAP_STYLE = {
4322
7417
  fontSize: 13,
4323
7418
  color: "var(--cs-chrome-fg, #201f1e)"
4324
7419
  };
4325
- var ROW_STYLE4 = { display: "flex", alignItems: "center", gap: 6, marginBottom: 6 };
7420
+ var ROW_STYLE5 = { display: "flex", alignItems: "center", gap: 6, marginBottom: 6 };
4326
7421
  var INPUT_STYLE2 = {
4327
7422
  flex: "1 1 auto",
4328
7423
  height: 26,
@@ -4360,14 +7455,14 @@ var ICON_BTN_STYLE = {
4360
7455
  padding: 0
4361
7456
  };
4362
7457
  function FindReplace({ api, openSignal, openInReplaceMode }) {
4363
- const [open, setOpen] = (0, import_react13.useState)(false);
4364
- const [showReplace, setShowReplace] = (0, import_react13.useState)(false);
4365
- const [query, setQuery] = (0, import_react13.useState)("");
4366
- const [replaceText, setReplaceText] = (0, import_react13.useState)("");
4367
- const [matchCase, setMatchCase] = (0, import_react13.useState)(false);
4368
- const [idx, setIdx] = (0, import_react13.useState)(0);
4369
- const inputRef = (0, import_react13.useRef)(null);
4370
- (0, import_react13.useEffect)(() => {
7458
+ const [open, setOpen] = (0, import_react25.useState)(false);
7459
+ const [showReplace, setShowReplace] = (0, import_react25.useState)(false);
7460
+ const [query, setQuery] = (0, import_react25.useState)("");
7461
+ const [replaceText, setReplaceText] = (0, import_react25.useState)("");
7462
+ const [matchCase, setMatchCase] = (0, import_react25.useState)(false);
7463
+ const [idx, setIdx] = (0, import_react25.useState)(0);
7464
+ const inputRef = (0, import_react25.useRef)(null);
7465
+ (0, import_react25.useEffect)(() => {
4371
7466
  const onKey = (e) => {
4372
7467
  const mod = e.metaKey || e.ctrlKey;
4373
7468
  if (mod && (e.key === "f" || e.key === "F")) {
@@ -4385,8 +7480,8 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
4385
7480
  document.addEventListener("keydown", onKey);
4386
7481
  return () => document.removeEventListener("keydown", onKey);
4387
7482
  }, []);
4388
- const lastSignal = (0, import_react13.useRef)(void 0);
4389
- (0, import_react13.useEffect)(() => {
7483
+ const lastSignal = (0, import_react25.useRef)(void 0);
7484
+ (0, import_react25.useEffect)(() => {
4390
7485
  if (openSignal === void 0) return;
4391
7486
  if (lastSignal.current === openSignal) return;
4392
7487
  lastSignal.current = openSignal;
@@ -4394,11 +7489,11 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
4394
7489
  setOpen(true);
4395
7490
  requestAnimationFrame(() => inputRef.current?.focus());
4396
7491
  }, [openSignal, openInReplaceMode]);
4397
- const matches = (0, import_react13.useMemo)(
7492
+ const matches = (0, import_react25.useMemo)(
4398
7493
  () => api && open && query ? findMatches(api, query, matchCase) : [],
4399
7494
  [api, open, query, matchCase]
4400
7495
  );
4401
- (0, import_react13.useEffect)(() => {
7496
+ (0, import_react25.useEffect)(() => {
4402
7497
  setIdx((i) => matches.length === 0 ? 0 : Math.min(i, matches.length - 1));
4403
7498
  }, [matches.length]);
4404
7499
  if (!open || !api) return null;
@@ -4444,7 +7539,7 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
4444
7539
  };
4445
7540
  const count = matches.length;
4446
7541
  const position = count === 0 ? 0 : Math.min(idx, count - 1) + 1;
4447
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
7542
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
4448
7543
  "div",
4449
7544
  {
4450
7545
  style: WRAP_STYLE,
@@ -4452,8 +7547,8 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
4452
7547
  role: "dialog",
4453
7548
  "aria-label": "Find and replace",
4454
7549
  children: [
4455
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: ROW_STYLE4, children: [
4456
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
7550
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { style: ROW_STYLE5, children: [
7551
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
4457
7552
  "input",
4458
7553
  {
4459
7554
  ref: inputRef,
@@ -4467,7 +7562,7 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
4467
7562
  onKeyDown: onInputKey
4468
7563
  }
4469
7564
  ),
4470
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
7565
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
4471
7566
  "span",
4472
7567
  {
4473
7568
  style: { minWidth: 54, textAlign: "center", color: "var(--cs-chrome-muted, #6b7280)" },
@@ -4479,7 +7574,7 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
4479
7574
  ]
4480
7575
  }
4481
7576
  ),
4482
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
7577
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
4483
7578
  "button",
4484
7579
  {
4485
7580
  type: "button",
@@ -4490,10 +7585,10 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
4490
7585
  e.preventDefault();
4491
7586
  go(-1);
4492
7587
  },
4493
- children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Icon, { name: "keyboard_arrow_up", size: 18 })
7588
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Icon, { name: "keyboard_arrow_up", size: 18 })
4494
7589
  }
4495
7590
  ),
4496
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
7591
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
4497
7592
  "button",
4498
7593
  {
4499
7594
  type: "button",
@@ -4504,10 +7599,10 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
4504
7599
  e.preventDefault();
4505
7600
  go(1);
4506
7601
  },
4507
- children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Icon, { name: "keyboard_arrow_down", size: 18 })
7602
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Icon, { name: "keyboard_arrow_down", size: 18 })
4508
7603
  }
4509
7604
  ),
4510
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
7605
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
4511
7606
  "button",
4512
7607
  {
4513
7608
  type: "button",
@@ -4518,12 +7613,12 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
4518
7613
  e.preventDefault();
4519
7614
  setOpen(false);
4520
7615
  },
4521
- children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Icon, { name: "close", size: 18 })
7616
+ children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Icon, { name: "close", size: 18 })
4522
7617
  }
4523
7618
  )
4524
7619
  ] }),
4525
- showReplace && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: ROW_STYLE4, children: [
4526
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
7620
+ showReplace && /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { style: ROW_STYLE5, children: [
7621
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
4527
7622
  "input",
4528
7623
  {
4529
7624
  type: "text",
@@ -4535,7 +7630,7 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
4535
7630
  onChange: (e) => setReplaceText(e.target.value)
4536
7631
  }
4537
7632
  ),
4538
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
7633
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
4539
7634
  "button",
4540
7635
  {
4541
7636
  type: "button",
@@ -4548,7 +7643,7 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
4548
7643
  children: "Replace"
4549
7644
  }
4550
7645
  ),
4551
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
7646
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
4552
7647
  "button",
4553
7648
  {
4554
7649
  type: "button",
@@ -4562,9 +7657,9 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
4562
7657
  }
4563
7658
  )
4564
7659
  ] }),
4565
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 12 }, children: [
4566
- /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("label", { style: { display: "inline-flex", alignItems: "center", gap: 4, cursor: "pointer" }, children: [
4567
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
7660
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 12 }, children: [
7661
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("label", { style: { display: "inline-flex", alignItems: "center", gap: 4, cursor: "pointer" }, children: [
7662
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
4568
7663
  "input",
4569
7664
  {
4570
7665
  type: "checkbox",
@@ -4575,7 +7670,7 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
4575
7670
  ),
4576
7671
  "Match case"
4577
7672
  ] }),
4578
- !showReplace && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
7673
+ !showReplace && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
4579
7674
  "button",
4580
7675
  {
4581
7676
  type: "button",
@@ -4619,8 +7714,8 @@ function replaceInString(text, query, replacement, matchCase) {
4619
7714
  }
4620
7715
 
4621
7716
  // src/chrome/ChromeTop.tsx
4622
- var import_react14 = require("react");
4623
- var import_jsx_runtime15 = require("react/jsx-runtime");
7717
+ var import_react26 = require("react");
7718
+ var import_jsx_runtime27 = require("react/jsx-runtime");
4624
7719
  function ChromeTop({
4625
7720
  api,
4626
7721
  features,
@@ -4628,13 +7723,13 @@ function ChromeTop({
4628
7723
  hostOwnedDialogs,
4629
7724
  extensions
4630
7725
  }) {
4631
- const [findSignal, setFindSignal] = (0, import_react14.useState)(0);
4632
- const [findReplaceMode, setFindReplaceMode] = (0, import_react14.useState)(false);
4633
- const openFindReplace = (0, import_react14.useCallback)((replaceMode) => {
7726
+ const [findSignal, setFindSignal] = (0, import_react26.useState)(0);
7727
+ const [findReplaceMode, setFindReplaceMode] = (0, import_react26.useState)(false);
7728
+ const openFindReplace = (0, import_react26.useCallback)((replaceMode) => {
4634
7729
  setFindReplaceMode(replaceMode);
4635
7730
  setFindSignal((n) => n + 1);
4636
7731
  }, []);
4637
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
7732
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
4638
7733
  DialogProvider,
4639
7734
  {
4640
7735
  api,
@@ -4643,10 +7738,10 @@ function ChromeTop({
4643
7738
  hostOwnedDialogs,
4644
7739
  onOpenFindReplace: openFindReplace,
4645
7740
  children: [
4646
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(MenuBar, { api, features, extensions }),
4647
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Toolbar, { api, features, extensions }),
4648
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(FormulaBar, { api }),
4649
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
7741
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(MenuBar, { api, features, extensions }),
7742
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(Toolbar, { api, features, extensions }),
7743
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(FormulaBar, { api }),
7744
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
4650
7745
  FindReplace,
4651
7746
  {
4652
7747
  api,
@@ -4660,11 +7755,11 @@ function ChromeTop({
4660
7755
  }
4661
7756
 
4662
7757
  // src/chrome/ChromeBottom.tsx
4663
- var import_jsx_runtime16 = require("react/jsx-runtime");
7758
+ var import_jsx_runtime28 = require("react/jsx-runtime");
4664
7759
  function ChromeBottom({ api }) {
4665
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
4666
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(SheetTabs, { api }),
4667
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(StatusBar, { api })
7760
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_jsx_runtime28.Fragment, { children: [
7761
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(SheetTabs, { api }),
7762
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(StatusBar, { api })
4668
7763
  ] });
4669
7764
  }
4670
7765
  //# sourceMappingURL=chrome.cjs.map