@casualoffice/sheets 0.16.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chrome.cjs +3421 -307
- package/dist/chrome.cjs.map +1 -1
- package/dist/chrome.d.cts +33 -0
- package/dist/chrome.d.ts +33 -0
- package/dist/chrome.js +3380 -266
- package/dist/chrome.js.map +1 -1
- package/dist/embed/embed-runtime.js +152 -150
- package/package.json +1 -1
- package/src/chrome/ConditionalFormattingDialog.tsx +534 -0
- package/src/chrome/CustomSortDialog.tsx +357 -0
- package/src/chrome/DataValidationDialog.tsx +536 -0
- package/src/chrome/DeleteCellsDialog.tsx +183 -0
- package/src/chrome/GoalSeekDialog.tsx +370 -0
- package/src/chrome/InsertCellsDialog.tsx +185 -0
- package/src/chrome/InsertChartDialog.tsx +490 -0
- package/src/chrome/InsertFunctionDialog.tsx +493 -0
- package/src/chrome/InsertPivotDialog.tsx +488 -0
- package/src/chrome/InsertSparklineDialog.tsx +344 -0
- package/src/chrome/MenuBar.feature-gate.unit.test.ts +152 -0
- package/src/chrome/MenuBar.tsx +36 -141
- package/src/chrome/NameManagerDialog.tsx +378 -0
- package/src/chrome/PasteSpecialDialog.tsx +286 -0
- package/src/chrome/dialog-context.tsx +24 -0
- package/src/chrome/menu-model.ts +191 -0
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
|
|
57
|
-
var
|
|
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
|
|
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
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
onChange: (
|
|
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
|
|
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,
|
|
4288
|
+
var DialogContext = (0, import_react18.createContext)(null);
|
|
1194
4289
|
function useDialogs() {
|
|
1195
|
-
const ctx = (0,
|
|
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,
|
|
4310
|
+
const [active, setActive] = (0, import_react18.useState)(null);
|
|
1216
4311
|
const overrides = extensions?.dialogs;
|
|
1217
|
-
const hostOwned = (0,
|
|
1218
|
-
const openDialog = (0,
|
|
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,
|
|
1244
|
-
const canOpen = (0,
|
|
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,
|
|
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,
|
|
4347
|
+
const controller = (0, import_react18.useMemo)(
|
|
1253
4348
|
() => ({ openDialog, closeDialog, canOpen: controllerCanOpen }),
|
|
1254
4349
|
[openDialog, closeDialog, controllerCanOpen]
|
|
1255
4350
|
);
|
|
1256
|
-
return /* @__PURE__ */ (0,
|
|
4351
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(DialogContext.Provider, { value: controller, children: [
|
|
1257
4352
|
children,
|
|
1258
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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
|
|
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,
|
|
1605
|
-
(0,
|
|
4699
|
+
const [active, setActive] = (0, import_react19.useState)(NO_STYLE);
|
|
4700
|
+
(0, import_react19.useEffect)(() => {
|
|
1606
4701
|
ensureChromeFonts();
|
|
1607
4702
|
}, []);
|
|
1608
|
-
(0,
|
|
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(
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
1704
|
-
showDivider && /* @__PURE__ */ (0,
|
|
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,
|
|
1726
|
-
showFont && /* @__PURE__ */ (0,
|
|
1727
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
4831
|
+
children: familyOptions.map((f) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("option", { value: f, children: f }, f))
|
|
1737
4832
|
}
|
|
1738
4833
|
),
|
|
1739
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
4860
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { style: DIVIDER_STYLE, "aria-hidden": true })
|
|
1766
4861
|
] }),
|
|
1767
4862
|
groupNodes,
|
|
1768
|
-
(showColor || showBorders) && /* @__PURE__ */ (0,
|
|
1769
|
-
showColor && /* @__PURE__ */ (0,
|
|
1770
|
-
showBorders && /* @__PURE__ */ (0,
|
|
1771
|
-
showNumberDropdown && /* @__PURE__ */ (0,
|
|
1772
|
-
/* @__PURE__ */ (0,
|
|
1773
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
1787
|
-
/* @__PURE__ */ (0,
|
|
1788
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
1791
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
1815
|
-
/* @__PURE__ */ (0,
|
|
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
|
|
1825
|
-
var
|
|
4919
|
+
var import_react21 = require("react");
|
|
4920
|
+
var import_core6 = require("@univerjs/core");
|
|
1826
4921
|
|
|
1827
4922
|
// src/chrome/NameBox.tsx
|
|
1828
|
-
var
|
|
1829
|
-
var
|
|
1830
|
-
var
|
|
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,
|
|
1941
|
-
const [draft, setDraft] = (0,
|
|
1942
|
-
const draftRef = (0,
|
|
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,
|
|
1945
|
-
const [open, setOpen] = (0,
|
|
1946
|
-
const [names, setNames] = (0,
|
|
1947
|
-
const rootRef = (0,
|
|
1948
|
-
(0,
|
|
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(
|
|
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,
|
|
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,
|
|
2022
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
5158
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Icon, { name: "expand_more", size: 18 })
|
|
2064
5159
|
}
|
|
2065
5160
|
),
|
|
2066
|
-
open && /* @__PURE__ */ (0,
|
|
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,
|
|
2086
|
-
entry.ref && /* @__PURE__ */ (0,
|
|
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
|
|
2096
|
-
var
|
|
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
|
|
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,
|
|
2284
|
-
const [draft, setDraft] = (0,
|
|
2285
|
-
const draftRef = (0,
|
|
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,
|
|
2288
|
-
const [acDismissed, setAcDismissed] = (0,
|
|
2289
|
-
(0,
|
|
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(
|
|
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,
|
|
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,
|
|
2360
|
-
/* @__PURE__ */ (0,
|
|
2361
|
-
/* @__PURE__ */ (0,
|
|
2362
|
-
/* @__PURE__ */ (0,
|
|
2363
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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
|
|
2406
|
-
var
|
|
2407
|
-
var
|
|
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,
|
|
2491
|
-
const [zoom, setZoom] = (0,
|
|
2492
|
-
(0,
|
|
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(
|
|
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,
|
|
2519
|
-
stats && /* @__PURE__ */ (0,
|
|
2520
|
-
stats.numCount > 0 && /* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
2529
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
2548
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
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,
|
|
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
|
|
2605
|
-
var
|
|
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) {
|
|
@@ -2750,8 +5845,70 @@ function ensurePluginByName(group) {
|
|
|
2750
5845
|
return ensurePlugin(currentUniver, group);
|
|
2751
5846
|
}
|
|
2752
5847
|
|
|
5848
|
+
// src/chrome/menu-model.ts
|
|
5849
|
+
function featureOn(feature, features) {
|
|
5850
|
+
if (!feature) return true;
|
|
5851
|
+
return features[feature] !== false;
|
|
5852
|
+
}
|
|
5853
|
+
function keepItem(item, features, canOpen) {
|
|
5854
|
+
if (!featureOn(item.feature, features)) return null;
|
|
5855
|
+
if (item.kind === "separator") return item;
|
|
5856
|
+
if (item.kind === "submenu") {
|
|
5857
|
+
const items = filterItems(item.items, features, canOpen);
|
|
5858
|
+
if (items.length === 0) return null;
|
|
5859
|
+
return { ...item, items };
|
|
5860
|
+
}
|
|
5861
|
+
if (item.dialog && !canOpen(item.dialog)) return null;
|
|
5862
|
+
return item;
|
|
5863
|
+
}
|
|
5864
|
+
function filterItems(items, features, canOpen) {
|
|
5865
|
+
const kept = items.map((i) => keepItem(i, features, canOpen)).filter((i) => i !== null);
|
|
5866
|
+
const out = [];
|
|
5867
|
+
for (const item of kept) {
|
|
5868
|
+
if (item.kind === "separator") {
|
|
5869
|
+
if (out.length === 0) continue;
|
|
5870
|
+
if (out[out.length - 1].kind === "separator") continue;
|
|
5871
|
+
}
|
|
5872
|
+
out.push(item);
|
|
5873
|
+
}
|
|
5874
|
+
while (out.length > 0 && out[out.length - 1].kind === "separator") out.pop();
|
|
5875
|
+
return out;
|
|
5876
|
+
}
|
|
5877
|
+
function withMenuExtensions(menus, ext) {
|
|
5878
|
+
if (!ext || ext.length === 0) return menus;
|
|
5879
|
+
const byMenu = /* @__PURE__ */ new Map();
|
|
5880
|
+
for (const e of ext) {
|
|
5881
|
+
const list = byMenu.get(e.menu) ?? [];
|
|
5882
|
+
list.push(e);
|
|
5883
|
+
byMenu.set(e.menu, list);
|
|
5884
|
+
}
|
|
5885
|
+
return menus.map((menu) => {
|
|
5886
|
+
const extras = byMenu.get(menu.id);
|
|
5887
|
+
if (!extras || extras.length === 0) return menu;
|
|
5888
|
+
const items = [...menu.items, { kind: "separator", id: `ext-sep-${menu.id}` }];
|
|
5889
|
+
for (const e of extras) {
|
|
5890
|
+
items.push({
|
|
5891
|
+
kind: "item",
|
|
5892
|
+
id: `ext-${e.id}`,
|
|
5893
|
+
label: e.label,
|
|
5894
|
+
icon: e.icon,
|
|
5895
|
+
shortcut: e.shortcut,
|
|
5896
|
+
dialog: e.dialog,
|
|
5897
|
+
run: e.onClick ? (api) => e.onClick?.(api) : void 0
|
|
5898
|
+
});
|
|
5899
|
+
}
|
|
5900
|
+
return { ...menu, items };
|
|
5901
|
+
});
|
|
5902
|
+
}
|
|
5903
|
+
function computeVisibleMenus(menus, features, canOpen, ext) {
|
|
5904
|
+
return withMenuExtensions(menus, ext).map((menu) => ({
|
|
5905
|
+
...menu,
|
|
5906
|
+
items: filterItems(menu.items, features, canOpen)
|
|
5907
|
+
})).filter((menu) => featureOn(menu.feature, features) && menu.items.length > 0);
|
|
5908
|
+
}
|
|
5909
|
+
|
|
2753
5910
|
// src/chrome/MenuBar.tsx
|
|
2754
|
-
var
|
|
5911
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
2755
5912
|
function fmtShortcut(shortcut) {
|
|
2756
5913
|
const isMac = typeof navigator !== "undefined" && /Mac|iPhone|iPad/i.test(navigator.platform || "");
|
|
2757
5914
|
if (!isMac) return shortcut;
|
|
@@ -2771,14 +5928,14 @@ function fmtShortcut(shortcut) {
|
|
|
2771
5928
|
function fu(api) {
|
|
2772
5929
|
return api.univer;
|
|
2773
5930
|
}
|
|
2774
|
-
function
|
|
5931
|
+
function activeRange12(api) {
|
|
2775
5932
|
return fu(api).getActiveWorkbook()?.getActiveSheet()?.getActiveRange() ?? null;
|
|
2776
5933
|
}
|
|
2777
|
-
function
|
|
5934
|
+
function activeSheet3(api) {
|
|
2778
5935
|
return fu(api).getActiveWorkbook()?.getActiveSheet() ?? null;
|
|
2779
5936
|
}
|
|
2780
5937
|
function selectionA1(api) {
|
|
2781
|
-
const range =
|
|
5938
|
+
const range = activeRange12(api);
|
|
2782
5939
|
if (!range) return null;
|
|
2783
5940
|
try {
|
|
2784
5941
|
return range.getA1Notation();
|
|
@@ -2793,38 +5950,38 @@ var copy = (api) => void api.executeCommand("univer.command.copy");
|
|
|
2793
5950
|
var paste = (api) => void api.executeCommand("univer.command.paste");
|
|
2794
5951
|
var pasteFormattingOnly = (api) => void api.executeCommand("sheet.command.paste-format");
|
|
2795
5952
|
var insertRowAbove = (api) => {
|
|
2796
|
-
const range =
|
|
2797
|
-
const sheet =
|
|
5953
|
+
const range = activeRange12(api);
|
|
5954
|
+
const sheet = activeSheet3(api);
|
|
2798
5955
|
if (!range || !sheet) return;
|
|
2799
5956
|
sheet.insertRowBefore(range.getRow());
|
|
2800
5957
|
};
|
|
2801
5958
|
var insertRowBelow = (api) => {
|
|
2802
|
-
const range =
|
|
2803
|
-
const sheet =
|
|
5959
|
+
const range = activeRange12(api);
|
|
5960
|
+
const sheet = activeSheet3(api);
|
|
2804
5961
|
if (!range || !sheet) return;
|
|
2805
5962
|
sheet.insertRowAfter(range.getRow() + range.getHeight() - 1);
|
|
2806
5963
|
};
|
|
2807
5964
|
var insertColumnLeft = (api) => {
|
|
2808
|
-
const range =
|
|
2809
|
-
const sheet =
|
|
5965
|
+
const range = activeRange12(api);
|
|
5966
|
+
const sheet = activeSheet3(api);
|
|
2810
5967
|
if (!range || !sheet) return;
|
|
2811
5968
|
sheet.insertColumnBefore(range.getColumn());
|
|
2812
5969
|
};
|
|
2813
5970
|
var insertColumnRight = (api) => {
|
|
2814
|
-
const range =
|
|
2815
|
-
const sheet =
|
|
5971
|
+
const range = activeRange12(api);
|
|
5972
|
+
const sheet = activeSheet3(api);
|
|
2816
5973
|
if (!range || !sheet) return;
|
|
2817
5974
|
sheet.insertColumnAfter(range.getColumn() + range.getWidth() - 1);
|
|
2818
5975
|
};
|
|
2819
5976
|
var deleteSelectedRow = (api) => {
|
|
2820
|
-
const range =
|
|
2821
|
-
const sheet =
|
|
5977
|
+
const range = activeRange12(api);
|
|
5978
|
+
const sheet = activeSheet3(api);
|
|
2822
5979
|
if (!range || !sheet) return;
|
|
2823
5980
|
sheet.deleteRows(range.getRow(), range.getHeight());
|
|
2824
5981
|
};
|
|
2825
5982
|
var deleteSelectedColumn = (api) => {
|
|
2826
|
-
const range =
|
|
2827
|
-
const sheet =
|
|
5983
|
+
const range = activeRange12(api);
|
|
5984
|
+
const sheet = activeSheet3(api);
|
|
2828
5985
|
if (!range || !sheet) return;
|
|
2829
5986
|
sheet.deleteColumns(range.getColumn(), range.getWidth());
|
|
2830
5987
|
};
|
|
@@ -2835,7 +5992,7 @@ var insertImage = (api) => void api.executeCommand("sheet.command.insert-float-i
|
|
|
2835
5992
|
var insertHyperlink = (api) => void api.executeCommand("sheet.operation.insert-hyper-link");
|
|
2836
5993
|
var insertComment = (api) => void api.executeCommand("sheet.operation.show-comment-modal");
|
|
2837
5994
|
var insertTodayDate = (api) => {
|
|
2838
|
-
const range =
|
|
5995
|
+
const range = activeRange12(api);
|
|
2839
5996
|
if (!range) return;
|
|
2840
5997
|
const today = /* @__PURE__ */ new Date();
|
|
2841
5998
|
const pad = (n) => n.toString().padStart(2, "0");
|
|
@@ -2843,7 +6000,7 @@ var insertTodayDate = (api) => {
|
|
|
2843
6000
|
range.setValue({ v });
|
|
2844
6001
|
};
|
|
2845
6002
|
var insertCurrentTime = (api) => {
|
|
2846
|
-
const range =
|
|
6003
|
+
const range = activeRange12(api);
|
|
2847
6004
|
if (!range) return;
|
|
2848
6005
|
const now = /* @__PURE__ */ new Date();
|
|
2849
6006
|
const pad = (n) => n.toString().padStart(2, "0");
|
|
@@ -2852,8 +6009,8 @@ var insertCurrentTime = (api) => {
|
|
|
2852
6009
|
};
|
|
2853
6010
|
var insertTable = (api) => {
|
|
2854
6011
|
void (async () => {
|
|
2855
|
-
const range =
|
|
2856
|
-
const sheet =
|
|
6012
|
+
const range = activeRange12(api);
|
|
6013
|
+
const sheet = activeSheet3(api);
|
|
2857
6014
|
const wb = fu(api).getActiveWorkbook();
|
|
2858
6015
|
if (!range || !sheet || !wb) return;
|
|
2859
6016
|
await ensurePluginByName("table");
|
|
@@ -2870,16 +6027,16 @@ var insertTable = (api) => {
|
|
|
2870
6027
|
})();
|
|
2871
6028
|
};
|
|
2872
6029
|
var autoFitColumns = (api) => {
|
|
2873
|
-
const range =
|
|
2874
|
-
const sheet =
|
|
6030
|
+
const range = activeRange12(api);
|
|
6031
|
+
const sheet = activeSheet3(api);
|
|
2875
6032
|
if (!range || !sheet) return;
|
|
2876
6033
|
const withAutoWidth = sheet;
|
|
2877
6034
|
withAutoWidth.setColumnAutoWidth?.(range.getColumn(), range.getWidth());
|
|
2878
6035
|
};
|
|
2879
6036
|
var AUTO_FIT_ROW_CAP = 500;
|
|
2880
6037
|
var autoFitRows = (api) => {
|
|
2881
|
-
const range =
|
|
2882
|
-
const sheet =
|
|
6038
|
+
const range = activeRange12(api);
|
|
6039
|
+
const sheet = activeSheet3(api);
|
|
2883
6040
|
if (!range || !sheet) return;
|
|
2884
6041
|
const start = range.getRow();
|
|
2885
6042
|
const count = Math.min(range.getHeight(), AUTO_FIT_ROW_CAP);
|
|
@@ -2918,23 +6075,23 @@ var NUM_FORMAT_SHORTCUT = {
|
|
|
2918
6075
|
scientific: "Ctrl+Shift+6"
|
|
2919
6076
|
};
|
|
2920
6077
|
function setNumberFormatByKey(api, key) {
|
|
2921
|
-
const range =
|
|
6078
|
+
const range = activeRange12(api);
|
|
2922
6079
|
range?.setNumberFormat?.(NUMBER_FORMAT_PATTERNS3[key]);
|
|
2923
6080
|
}
|
|
2924
6081
|
var increaseDecimal = (api) => void api.executeCommand("sheet.command.numfmt.add.decimal.command");
|
|
2925
6082
|
var decreaseDecimal = (api) => void api.executeCommand("sheet.command.numfmt.subtract.decimal.command");
|
|
2926
6083
|
var clearFormat = (api) => void api.executeCommand("sheet.command.clear-selection-format");
|
|
2927
6084
|
function applyBorders(api, choice) {
|
|
2928
|
-
const range =
|
|
6085
|
+
const range = activeRange12(api);
|
|
2929
6086
|
if (!range) return;
|
|
2930
|
-
const type = choice === "all" ?
|
|
2931
|
-
const style = choice === "none" ?
|
|
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;
|
|
2932
6089
|
range.setBorder(type, style, "#000000");
|
|
2933
6090
|
}
|
|
2934
6091
|
function rowSpan(api) {
|
|
2935
6092
|
const wb = fu(api).getActiveWorkbook();
|
|
2936
|
-
const sheet =
|
|
2937
|
-
const range =
|
|
6093
|
+
const sheet = activeSheet3(api);
|
|
6094
|
+
const range = activeRange12(api);
|
|
2938
6095
|
if (!wb || !sheet || !range) return null;
|
|
2939
6096
|
const startRow = range.getRow();
|
|
2940
6097
|
const endRow = startRow + range.getHeight() - 1;
|
|
@@ -2943,8 +6100,8 @@ function rowSpan(api) {
|
|
|
2943
6100
|
}
|
|
2944
6101
|
function colSpan(api) {
|
|
2945
6102
|
const wb = fu(api).getActiveWorkbook();
|
|
2946
|
-
const sheet =
|
|
2947
|
-
const range =
|
|
6103
|
+
const sheet = activeSheet3(api);
|
|
6104
|
+
const range = activeRange12(api);
|
|
2948
6105
|
if (!wb || !sheet || !range) return null;
|
|
2949
6106
|
const startColumn = range.getColumn();
|
|
2950
6107
|
const endColumn = startColumn + range.getWidth() - 1;
|
|
@@ -3020,16 +6177,16 @@ var unhideSelectedColumns = (api) => {
|
|
|
3020
6177
|
});
|
|
3021
6178
|
};
|
|
3022
6179
|
var freezeFirstRow = (api) => {
|
|
3023
|
-
|
|
6180
|
+
activeSheet3(api)?.setFrozenRows(1);
|
|
3024
6181
|
};
|
|
3025
6182
|
var freezeFirstColumn = (api) => {
|
|
3026
|
-
|
|
6183
|
+
activeSheet3(api)?.setFrozenColumns(1);
|
|
3027
6184
|
};
|
|
3028
6185
|
var freezeAtSelection = (api) => void api.executeCommand("sheet.command.set-selection-frozen");
|
|
3029
6186
|
var unfreezePanes = (api) => void api.executeCommand("sheet.command.cancel-frozen");
|
|
3030
6187
|
var toggleGridlines = (api) => {
|
|
3031
6188
|
const wb = fu(api).getActiveWorkbook();
|
|
3032
|
-
const sheet =
|
|
6189
|
+
const sheet = activeSheet3(api);
|
|
3033
6190
|
if (!wb || !sheet) return;
|
|
3034
6191
|
void api.executeCommand("sheet.command.toggle-gridlines", {
|
|
3035
6192
|
unitId: wb.getId(),
|
|
@@ -3039,7 +6196,7 @@ var toggleGridlines = (api) => {
|
|
|
3039
6196
|
};
|
|
3040
6197
|
var toggleCommentPanel = (api) => void api.executeCommand("sheet.operation.toggle-comment-panel");
|
|
3041
6198
|
var jumpToFirstCell = (api) => {
|
|
3042
|
-
|
|
6199
|
+
activeSheet3(api)?.getRange(0, 0).activate();
|
|
3043
6200
|
};
|
|
3044
6201
|
var switchToPreviousSheet = (api) => switchSheetByDelta(api, -1);
|
|
3045
6202
|
var switchToNextSheet = (api) => switchSheetByDelta(api, 1);
|
|
@@ -3059,7 +6216,7 @@ var showFormulas = (api) => void api.executeCommand("sheet.command.set-show-form
|
|
|
3059
6216
|
var sortAsc = (api) => sortRange(api, true);
|
|
3060
6217
|
var sortDesc = (api) => sortRange(api, false);
|
|
3061
6218
|
function sortRange(api, ascending) {
|
|
3062
|
-
const range =
|
|
6219
|
+
const range = activeRange12(api);
|
|
3063
6220
|
if (!range) return;
|
|
3064
6221
|
const withSort = range;
|
|
3065
6222
|
withSort.sort?.({ column: range.getColumn(), ascending });
|
|
@@ -3068,8 +6225,8 @@ var toggleFilter = (api) => {
|
|
|
3068
6225
|
void (async () => {
|
|
3069
6226
|
await ensurePluginByName("filter");
|
|
3070
6227
|
const wb = fu(api).getActiveWorkbook();
|
|
3071
|
-
const sheet =
|
|
3072
|
-
const range =
|
|
6228
|
+
const sheet = activeSheet3(api);
|
|
6229
|
+
const range = activeRange12(api);
|
|
3073
6230
|
if (!wb || !sheet || !range) return;
|
|
3074
6231
|
const sheetWithFilter = sheet;
|
|
3075
6232
|
if (sheetWithFilter.getFilter?.()) {
|
|
@@ -3107,7 +6264,8 @@ var MENUS = [
|
|
|
3107
6264
|
id: "about",
|
|
3108
6265
|
label: "About casual sheets",
|
|
3109
6266
|
icon: "help_outline",
|
|
3110
|
-
dialog: "about"
|
|
6267
|
+
dialog: "about",
|
|
6268
|
+
feature: "branding"
|
|
3111
6269
|
}
|
|
3112
6270
|
]
|
|
3113
6271
|
},
|
|
@@ -3655,6 +6813,12 @@ var MENUS = [
|
|
|
3655
6813
|
{
|
|
3656
6814
|
id: "help",
|
|
3657
6815
|
label: "Help",
|
|
6816
|
+
// Whole-menu gate: an embedded host passes `features={{ help: false }}` to
|
|
6817
|
+
// drop the Help menu entirely (no editor-branded surface). Individual
|
|
6818
|
+
// branding links below also carry `feature: 'branding'` so a host that
|
|
6819
|
+
// keeps Help (for keyboard shortcuts) can still hide the About / GitHub
|
|
6820
|
+
// links with `features={{ branding: false }}`.
|
|
6821
|
+
feature: "help",
|
|
3658
6822
|
items: [
|
|
3659
6823
|
{
|
|
3660
6824
|
kind: "item",
|
|
@@ -3664,14 +6828,22 @@ var MENUS = [
|
|
|
3664
6828
|
shortcut: "Ctrl+/",
|
|
3665
6829
|
dialog: "keyboard-shortcuts"
|
|
3666
6830
|
},
|
|
3667
|
-
{ kind: "separator", id: "sep-help" },
|
|
3668
|
-
{
|
|
6831
|
+
{ kind: "separator", id: "sep-help", feature: "branding" },
|
|
6832
|
+
{
|
|
6833
|
+
kind: "item",
|
|
6834
|
+
id: "about",
|
|
6835
|
+
label: "About casual sheets",
|
|
6836
|
+
icon: "info",
|
|
6837
|
+
dialog: "about",
|
|
6838
|
+
feature: "branding"
|
|
6839
|
+
},
|
|
3669
6840
|
{
|
|
3670
6841
|
kind: "item",
|
|
3671
6842
|
id: "github",
|
|
3672
6843
|
label: "View on GitHub",
|
|
3673
6844
|
icon: "open_in_new",
|
|
3674
|
-
run: () => openExternal("https://github.com/CasualOffice/sheets")
|
|
6845
|
+
run: () => openExternal("https://github.com/CasualOffice/sheets"),
|
|
6846
|
+
feature: "branding"
|
|
3675
6847
|
}
|
|
3676
6848
|
]
|
|
3677
6849
|
}
|
|
@@ -3758,69 +6930,15 @@ var SUBMENU_PANEL_STYLE = {
|
|
|
3758
6930
|
boxShadow: "0 6px 20px rgba(0,0,0,0.16)",
|
|
3759
6931
|
zIndex: 1001
|
|
3760
6932
|
};
|
|
3761
|
-
function featureOn(feature, features) {
|
|
3762
|
-
if (!feature) return true;
|
|
3763
|
-
return features[feature] !== false;
|
|
3764
|
-
}
|
|
3765
|
-
function keepItem(item, features, canOpen) {
|
|
3766
|
-
if (!featureOn(item.feature, features)) return null;
|
|
3767
|
-
if (item.kind === "separator") return item;
|
|
3768
|
-
if (item.kind === "submenu") {
|
|
3769
|
-
const items = filterItems(item.items, features, canOpen);
|
|
3770
|
-
if (items.length === 0) return null;
|
|
3771
|
-
return { ...item, items };
|
|
3772
|
-
}
|
|
3773
|
-
if (item.dialog && !canOpen(item.dialog)) return null;
|
|
3774
|
-
return item;
|
|
3775
|
-
}
|
|
3776
|
-
function filterItems(items, features, canOpen) {
|
|
3777
|
-
const kept = items.map((i) => keepItem(i, features, canOpen)).filter((i) => i !== null);
|
|
3778
|
-
const out = [];
|
|
3779
|
-
for (const item of kept) {
|
|
3780
|
-
if (item.kind === "separator") {
|
|
3781
|
-
if (out.length === 0) continue;
|
|
3782
|
-
if (out[out.length - 1].kind === "separator") continue;
|
|
3783
|
-
}
|
|
3784
|
-
out.push(item);
|
|
3785
|
-
}
|
|
3786
|
-
while (out.length > 0 && out[out.length - 1].kind === "separator") out.pop();
|
|
3787
|
-
return out;
|
|
3788
|
-
}
|
|
3789
|
-
function withMenuExtensions(menus, ext) {
|
|
3790
|
-
if (!ext || ext.length === 0) return menus;
|
|
3791
|
-
const byMenu = /* @__PURE__ */ new Map();
|
|
3792
|
-
for (const e of ext) {
|
|
3793
|
-
const list = byMenu.get(e.menu) ?? [];
|
|
3794
|
-
list.push(e);
|
|
3795
|
-
byMenu.set(e.menu, list);
|
|
3796
|
-
}
|
|
3797
|
-
return menus.map((menu) => {
|
|
3798
|
-
const extras = byMenu.get(menu.id);
|
|
3799
|
-
if (!extras || extras.length === 0) return menu;
|
|
3800
|
-
const items = [...menu.items, { kind: "separator", id: `ext-sep-${menu.id}` }];
|
|
3801
|
-
for (const e of extras) {
|
|
3802
|
-
items.push({
|
|
3803
|
-
kind: "item",
|
|
3804
|
-
id: `ext-${e.id}`,
|
|
3805
|
-
label: e.label,
|
|
3806
|
-
icon: e.icon,
|
|
3807
|
-
shortcut: e.shortcut,
|
|
3808
|
-
dialog: e.dialog,
|
|
3809
|
-
run: e.onClick ? (api) => e.onClick?.(api) : void 0
|
|
3810
|
-
});
|
|
3811
|
-
}
|
|
3812
|
-
return { ...menu, items };
|
|
3813
|
-
});
|
|
3814
|
-
}
|
|
3815
6933
|
function MenuBar({ api, features = {}, extensions }) {
|
|
3816
6934
|
const dialogs = useDialogs();
|
|
3817
|
-
const [open, setOpen] = (0,
|
|
3818
|
-
const [openSubmenu, setOpenSubmenu] = (0,
|
|
3819
|
-
const rootRef = (0,
|
|
3820
|
-
(0,
|
|
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)(() => {
|
|
3821
6939
|
ensureChromeFonts();
|
|
3822
6940
|
}, []);
|
|
3823
|
-
(0,
|
|
6941
|
+
(0, import_react23.useEffect)(() => {
|
|
3824
6942
|
if (open === null) return;
|
|
3825
6943
|
const onKey = (e) => {
|
|
3826
6944
|
if (e.key === "Escape") {
|
|
@@ -3855,25 +6973,21 @@ function MenuBar({ api, features = {}, extensions }) {
|
|
|
3855
6973
|
}
|
|
3856
6974
|
item.run?.(api);
|
|
3857
6975
|
};
|
|
3858
|
-
const
|
|
3859
|
-
const visibleMenus = baseMenus.map((menu) => ({
|
|
3860
|
-
...menu,
|
|
3861
|
-
items: filterItems(menu.items, features, dialogs.canOpen)
|
|
3862
|
-
})).filter((menu) => featureOn(menu.feature, features) && menu.items.length > 0);
|
|
6976
|
+
const visibleMenus = computeVisibleMenus(MENUS, features, dialogs.canOpen, extensions?.menu);
|
|
3863
6977
|
const renderItems = (items) => items.map((item) => {
|
|
3864
6978
|
if (item.kind === "separator") {
|
|
3865
|
-
return /* @__PURE__ */ (0,
|
|
6979
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: SEPARATOR_STYLE, role: "separator", "aria-hidden": true }, item.id);
|
|
3866
6980
|
}
|
|
3867
6981
|
if (item.kind === "submenu") {
|
|
3868
6982
|
const isSubOpen = openSubmenu === item.id;
|
|
3869
|
-
return /* @__PURE__ */ (0,
|
|
6983
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
|
|
3870
6984
|
"div",
|
|
3871
6985
|
{
|
|
3872
6986
|
style: { position: "relative" },
|
|
3873
6987
|
onMouseEnter: () => setOpenSubmenu(item.id),
|
|
3874
6988
|
onMouseLeave: () => setOpenSubmenu((cur) => cur === item.id ? null : cur),
|
|
3875
6989
|
children: [
|
|
3876
|
-
/* @__PURE__ */ (0,
|
|
6990
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
|
|
3877
6991
|
"button",
|
|
3878
6992
|
{
|
|
3879
6993
|
type: "button",
|
|
@@ -3885,19 +6999,19 @@ function MenuBar({ api, features = {}, extensions }) {
|
|
|
3885
6999
|
style: { ...ITEM_STYLE3, opacity: api ? 1 : 0.5 },
|
|
3886
7000
|
onMouseDown: (e) => e.preventDefault(),
|
|
3887
7001
|
children: [
|
|
3888
|
-
item.icon ? /* @__PURE__ */ (0,
|
|
3889
|
-
/* @__PURE__ */ (0,
|
|
3890
|
-
/* @__PURE__ */ (0,
|
|
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" } })
|
|
3891
7005
|
]
|
|
3892
7006
|
}
|
|
3893
7007
|
),
|
|
3894
|
-
isSubOpen && /* @__PURE__ */ (0,
|
|
7008
|
+
isSubOpen && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: SUBMENU_PANEL_STYLE, role: "menu", "aria-label": item.label, children: renderItems(item.items) })
|
|
3895
7009
|
]
|
|
3896
7010
|
},
|
|
3897
7011
|
item.id
|
|
3898
7012
|
);
|
|
3899
7013
|
}
|
|
3900
|
-
return /* @__PURE__ */ (0,
|
|
7014
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
|
|
3901
7015
|
"button",
|
|
3902
7016
|
{
|
|
3903
7017
|
type: "button",
|
|
@@ -3917,15 +7031,15 @@ function MenuBar({ api, features = {}, extensions }) {
|
|
|
3917
7031
|
e.currentTarget.style.background = "transparent";
|
|
3918
7032
|
},
|
|
3919
7033
|
children: [
|
|
3920
|
-
item.icon ? /* @__PURE__ */ (0,
|
|
3921
|
-
/* @__PURE__ */ (0,
|
|
3922
|
-
item.shortcut && /* @__PURE__ */ (0,
|
|
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) })
|
|
3923
7037
|
]
|
|
3924
7038
|
},
|
|
3925
7039
|
item.id
|
|
3926
7040
|
);
|
|
3927
7041
|
});
|
|
3928
|
-
return /* @__PURE__ */ (0,
|
|
7042
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
3929
7043
|
"div",
|
|
3930
7044
|
{
|
|
3931
7045
|
ref: rootRef,
|
|
@@ -3935,8 +7049,8 @@ function MenuBar({ api, features = {}, extensions }) {
|
|
|
3935
7049
|
"aria-label": "Menu bar",
|
|
3936
7050
|
children: visibleMenus.map((menu) => {
|
|
3937
7051
|
const isOpen = open === menu.id;
|
|
3938
|
-
return /* @__PURE__ */ (0,
|
|
3939
|
-
/* @__PURE__ */ (0,
|
|
7052
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { position: "relative" }, children: [
|
|
7053
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
3940
7054
|
"button",
|
|
3941
7055
|
{
|
|
3942
7056
|
type: "button",
|
|
@@ -3967,7 +7081,7 @@ function MenuBar({ api, features = {}, extensions }) {
|
|
|
3967
7081
|
children: menu.label
|
|
3968
7082
|
}
|
|
3969
7083
|
),
|
|
3970
|
-
isOpen && /* @__PURE__ */ (0,
|
|
7084
|
+
isOpen && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: DROPDOWN_STYLE, role: "menu", "aria-label": menu.label, children: renderItems(menu.items) })
|
|
3971
7085
|
] }, menu.id);
|
|
3972
7086
|
})
|
|
3973
7087
|
}
|
|
@@ -3975,9 +7089,9 @@ function MenuBar({ api, features = {}, extensions }) {
|
|
|
3975
7089
|
}
|
|
3976
7090
|
|
|
3977
7091
|
// src/chrome/SheetTabs.tsx
|
|
3978
|
-
var
|
|
7092
|
+
var import_react24 = require("react");
|
|
3979
7093
|
var import_react_dom2 = require("react-dom");
|
|
3980
|
-
var
|
|
7094
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
3981
7095
|
var EMPTY = { tabs: [], activeId: null };
|
|
3982
7096
|
var SHEET_LIST_MUTATIONS = /* @__PURE__ */ new Set([
|
|
3983
7097
|
"sheet.mutation.insert-sheet",
|
|
@@ -4010,7 +7124,7 @@ var BAR_STYLE5 = {
|
|
|
4010
7124
|
overflowX: "auto",
|
|
4011
7125
|
overflowY: "hidden"
|
|
4012
7126
|
};
|
|
4013
|
-
var
|
|
7127
|
+
var ADD_BTN_STYLE2 = {
|
|
4014
7128
|
flex: "0 0 auto",
|
|
4015
7129
|
width: 28,
|
|
4016
7130
|
border: "none",
|
|
@@ -4058,11 +7172,11 @@ var MENU_ITEM_STYLE2 = {
|
|
|
4058
7172
|
cursor: "pointer"
|
|
4059
7173
|
};
|
|
4060
7174
|
function SheetTabs({ api }) {
|
|
4061
|
-
const [{ tabs, activeId }, setSnapshot] = (0,
|
|
4062
|
-
const [renaming, setRenaming] = (0,
|
|
4063
|
-
const [draft, setDraft] = (0,
|
|
4064
|
-
const [menu, setMenu] = (0,
|
|
4065
|
-
(0,
|
|
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)(() => {
|
|
4066
7180
|
if (!api) return;
|
|
4067
7181
|
const refresh = () => setSnapshot(readSheets(api));
|
|
4068
7182
|
refresh();
|
|
@@ -4090,7 +7204,7 @@ function SheetTabs({ api }) {
|
|
|
4090
7204
|
for (const s of subs) s?.dispose?.();
|
|
4091
7205
|
};
|
|
4092
7206
|
}, [api]);
|
|
4093
|
-
(0,
|
|
7207
|
+
(0, import_react24.useEffect)(() => {
|
|
4094
7208
|
if (!menu) return;
|
|
4095
7209
|
const close = () => setMenu(null);
|
|
4096
7210
|
window.addEventListener("pointerdown", close);
|
|
@@ -4101,7 +7215,7 @@ function SheetTabs({ api }) {
|
|
|
4101
7215
|
};
|
|
4102
7216
|
}, [menu]);
|
|
4103
7217
|
if (!api) {
|
|
4104
|
-
return /* @__PURE__ */ (0,
|
|
7218
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { style: BAR_STYLE5, "data-testid": "casual-sheets-tabs" });
|
|
4105
7219
|
}
|
|
4106
7220
|
const visible = tabs.filter((t) => !t.hidden);
|
|
4107
7221
|
const wb = () => api.univer.getActiveWorkbook();
|
|
@@ -4141,11 +7255,11 @@ function SheetTabs({ api }) {
|
|
|
4141
7255
|
setRenaming(null);
|
|
4142
7256
|
}
|
|
4143
7257
|
};
|
|
4144
|
-
return /* @__PURE__ */ (0,
|
|
7258
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { style: BAR_STYLE5, "data-testid": "casual-sheets-tabs", role: "tablist", children: [
|
|
4145
7259
|
visible.map((tab) => {
|
|
4146
7260
|
const active = tab.id === activeId;
|
|
4147
7261
|
if (renaming === tab.id) {
|
|
4148
|
-
return /* @__PURE__ */ (0,
|
|
7262
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
4149
7263
|
"input",
|
|
4150
7264
|
{
|
|
4151
7265
|
autoFocus: true,
|
|
@@ -4159,7 +7273,7 @@ function SheetTabs({ api }) {
|
|
|
4159
7273
|
tab.id
|
|
4160
7274
|
);
|
|
4161
7275
|
}
|
|
4162
|
-
return /* @__PURE__ */ (0,
|
|
7276
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
4163
7277
|
"button",
|
|
4164
7278
|
{
|
|
4165
7279
|
type: "button",
|
|
@@ -4198,11 +7312,11 @@ function SheetTabs({ api }) {
|
|
|
4198
7312
|
tab.id
|
|
4199
7313
|
);
|
|
4200
7314
|
}),
|
|
4201
|
-
/* @__PURE__ */ (0,
|
|
7315
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
4202
7316
|
"button",
|
|
4203
7317
|
{
|
|
4204
7318
|
type: "button",
|
|
4205
|
-
style:
|
|
7319
|
+
style: ADD_BTN_STYLE2,
|
|
4206
7320
|
"aria-label": "Add sheet",
|
|
4207
7321
|
title: "Add sheet",
|
|
4208
7322
|
"data-testid": "cs-tab-add",
|
|
@@ -4214,7 +7328,7 @@ function SheetTabs({ api }) {
|
|
|
4214
7328
|
}
|
|
4215
7329
|
),
|
|
4216
7330
|
menu && (0, import_react_dom2.createPortal)(
|
|
4217
|
-
/* @__PURE__ */ (0,
|
|
7331
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
|
|
4218
7332
|
"div",
|
|
4219
7333
|
{
|
|
4220
7334
|
style: { ...MENU_STYLE2, left: menu.x, top: menu.y, transform: "translateY(-100%)" },
|
|
@@ -4222,7 +7336,7 @@ function SheetTabs({ api }) {
|
|
|
4222
7336
|
role: "menu",
|
|
4223
7337
|
onPointerDown: (e) => e.stopPropagation(),
|
|
4224
7338
|
children: [
|
|
4225
|
-
/* @__PURE__ */ (0,
|
|
7339
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
4226
7340
|
"button",
|
|
4227
7341
|
{
|
|
4228
7342
|
type: "button",
|
|
@@ -4237,7 +7351,7 @@ function SheetTabs({ api }) {
|
|
|
4237
7351
|
children: "Rename"
|
|
4238
7352
|
}
|
|
4239
7353
|
),
|
|
4240
|
-
/* @__PURE__ */ (0,
|
|
7354
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
4241
7355
|
"button",
|
|
4242
7356
|
{
|
|
4243
7357
|
type: "button",
|
|
@@ -4264,8 +7378,8 @@ function SheetTabs({ api }) {
|
|
|
4264
7378
|
}
|
|
4265
7379
|
|
|
4266
7380
|
// src/chrome/FindReplace.tsx
|
|
4267
|
-
var
|
|
4268
|
-
var
|
|
7381
|
+
var import_react25 = require("react");
|
|
7382
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
4269
7383
|
function findMatches(api, query, matchCase) {
|
|
4270
7384
|
if (!query) return [];
|
|
4271
7385
|
const snap = api.getSnapshot();
|
|
@@ -4303,7 +7417,7 @@ var WRAP_STYLE = {
|
|
|
4303
7417
|
fontSize: 13,
|
|
4304
7418
|
color: "var(--cs-chrome-fg, #201f1e)"
|
|
4305
7419
|
};
|
|
4306
|
-
var
|
|
7420
|
+
var ROW_STYLE5 = { display: "flex", alignItems: "center", gap: 6, marginBottom: 6 };
|
|
4307
7421
|
var INPUT_STYLE2 = {
|
|
4308
7422
|
flex: "1 1 auto",
|
|
4309
7423
|
height: 26,
|
|
@@ -4341,14 +7455,14 @@ var ICON_BTN_STYLE = {
|
|
|
4341
7455
|
padding: 0
|
|
4342
7456
|
};
|
|
4343
7457
|
function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
4344
|
-
const [open, setOpen] = (0,
|
|
4345
|
-
const [showReplace, setShowReplace] = (0,
|
|
4346
|
-
const [query, setQuery] = (0,
|
|
4347
|
-
const [replaceText, setReplaceText] = (0,
|
|
4348
|
-
const [matchCase, setMatchCase] = (0,
|
|
4349
|
-
const [idx, setIdx] = (0,
|
|
4350
|
-
const inputRef = (0,
|
|
4351
|
-
(0,
|
|
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)(() => {
|
|
4352
7466
|
const onKey = (e) => {
|
|
4353
7467
|
const mod = e.metaKey || e.ctrlKey;
|
|
4354
7468
|
if (mod && (e.key === "f" || e.key === "F")) {
|
|
@@ -4366,8 +7480,8 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
|
4366
7480
|
document.addEventListener("keydown", onKey);
|
|
4367
7481
|
return () => document.removeEventListener("keydown", onKey);
|
|
4368
7482
|
}, []);
|
|
4369
|
-
const lastSignal = (0,
|
|
4370
|
-
(0,
|
|
7483
|
+
const lastSignal = (0, import_react25.useRef)(void 0);
|
|
7484
|
+
(0, import_react25.useEffect)(() => {
|
|
4371
7485
|
if (openSignal === void 0) return;
|
|
4372
7486
|
if (lastSignal.current === openSignal) return;
|
|
4373
7487
|
lastSignal.current = openSignal;
|
|
@@ -4375,11 +7489,11 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
|
4375
7489
|
setOpen(true);
|
|
4376
7490
|
requestAnimationFrame(() => inputRef.current?.focus());
|
|
4377
7491
|
}, [openSignal, openInReplaceMode]);
|
|
4378
|
-
const matches = (0,
|
|
7492
|
+
const matches = (0, import_react25.useMemo)(
|
|
4379
7493
|
() => api && open && query ? findMatches(api, query, matchCase) : [],
|
|
4380
7494
|
[api, open, query, matchCase]
|
|
4381
7495
|
);
|
|
4382
|
-
(0,
|
|
7496
|
+
(0, import_react25.useEffect)(() => {
|
|
4383
7497
|
setIdx((i) => matches.length === 0 ? 0 : Math.min(i, matches.length - 1));
|
|
4384
7498
|
}, [matches.length]);
|
|
4385
7499
|
if (!open || !api) return null;
|
|
@@ -4425,7 +7539,7 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
|
4425
7539
|
};
|
|
4426
7540
|
const count = matches.length;
|
|
4427
7541
|
const position = count === 0 ? 0 : Math.min(idx, count - 1) + 1;
|
|
4428
|
-
return /* @__PURE__ */ (0,
|
|
7542
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
|
|
4429
7543
|
"div",
|
|
4430
7544
|
{
|
|
4431
7545
|
style: WRAP_STYLE,
|
|
@@ -4433,8 +7547,8 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
|
4433
7547
|
role: "dialog",
|
|
4434
7548
|
"aria-label": "Find and replace",
|
|
4435
7549
|
children: [
|
|
4436
|
-
/* @__PURE__ */ (0,
|
|
4437
|
-
/* @__PURE__ */ (0,
|
|
7550
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { style: ROW_STYLE5, children: [
|
|
7551
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
4438
7552
|
"input",
|
|
4439
7553
|
{
|
|
4440
7554
|
ref: inputRef,
|
|
@@ -4448,7 +7562,7 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
|
4448
7562
|
onKeyDown: onInputKey
|
|
4449
7563
|
}
|
|
4450
7564
|
),
|
|
4451
|
-
/* @__PURE__ */ (0,
|
|
7565
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
|
|
4452
7566
|
"span",
|
|
4453
7567
|
{
|
|
4454
7568
|
style: { minWidth: 54, textAlign: "center", color: "var(--cs-chrome-muted, #6b7280)" },
|
|
@@ -4460,7 +7574,7 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
|
4460
7574
|
]
|
|
4461
7575
|
}
|
|
4462
7576
|
),
|
|
4463
|
-
/* @__PURE__ */ (0,
|
|
7577
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
4464
7578
|
"button",
|
|
4465
7579
|
{
|
|
4466
7580
|
type: "button",
|
|
@@ -4471,10 +7585,10 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
|
4471
7585
|
e.preventDefault();
|
|
4472
7586
|
go(-1);
|
|
4473
7587
|
},
|
|
4474
|
-
children: /* @__PURE__ */ (0,
|
|
7588
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Icon, { name: "keyboard_arrow_up", size: 18 })
|
|
4475
7589
|
}
|
|
4476
7590
|
),
|
|
4477
|
-
/* @__PURE__ */ (0,
|
|
7591
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
4478
7592
|
"button",
|
|
4479
7593
|
{
|
|
4480
7594
|
type: "button",
|
|
@@ -4485,10 +7599,10 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
|
4485
7599
|
e.preventDefault();
|
|
4486
7600
|
go(1);
|
|
4487
7601
|
},
|
|
4488
|
-
children: /* @__PURE__ */ (0,
|
|
7602
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Icon, { name: "keyboard_arrow_down", size: 18 })
|
|
4489
7603
|
}
|
|
4490
7604
|
),
|
|
4491
|
-
/* @__PURE__ */ (0,
|
|
7605
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
4492
7606
|
"button",
|
|
4493
7607
|
{
|
|
4494
7608
|
type: "button",
|
|
@@ -4499,12 +7613,12 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
|
4499
7613
|
e.preventDefault();
|
|
4500
7614
|
setOpen(false);
|
|
4501
7615
|
},
|
|
4502
|
-
children: /* @__PURE__ */ (0,
|
|
7616
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Icon, { name: "close", size: 18 })
|
|
4503
7617
|
}
|
|
4504
7618
|
)
|
|
4505
7619
|
] }),
|
|
4506
|
-
showReplace && /* @__PURE__ */ (0,
|
|
4507
|
-
/* @__PURE__ */ (0,
|
|
7620
|
+
showReplace && /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { style: ROW_STYLE5, children: [
|
|
7621
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
4508
7622
|
"input",
|
|
4509
7623
|
{
|
|
4510
7624
|
type: "text",
|
|
@@ -4516,7 +7630,7 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
|
4516
7630
|
onChange: (e) => setReplaceText(e.target.value)
|
|
4517
7631
|
}
|
|
4518
7632
|
),
|
|
4519
|
-
/* @__PURE__ */ (0,
|
|
7633
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
4520
7634
|
"button",
|
|
4521
7635
|
{
|
|
4522
7636
|
type: "button",
|
|
@@ -4529,7 +7643,7 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
|
4529
7643
|
children: "Replace"
|
|
4530
7644
|
}
|
|
4531
7645
|
),
|
|
4532
|
-
/* @__PURE__ */ (0,
|
|
7646
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
4533
7647
|
"button",
|
|
4534
7648
|
{
|
|
4535
7649
|
type: "button",
|
|
@@ -4543,9 +7657,9 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
|
4543
7657
|
}
|
|
4544
7658
|
)
|
|
4545
7659
|
] }),
|
|
4546
|
-
/* @__PURE__ */ (0,
|
|
4547
|
-
/* @__PURE__ */ (0,
|
|
4548
|
-
/* @__PURE__ */ (0,
|
|
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)(
|
|
4549
7663
|
"input",
|
|
4550
7664
|
{
|
|
4551
7665
|
type: "checkbox",
|
|
@@ -4556,7 +7670,7 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
|
4556
7670
|
),
|
|
4557
7671
|
"Match case"
|
|
4558
7672
|
] }),
|
|
4559
|
-
!showReplace && /* @__PURE__ */ (0,
|
|
7673
|
+
!showReplace && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
4560
7674
|
"button",
|
|
4561
7675
|
{
|
|
4562
7676
|
type: "button",
|
|
@@ -4600,8 +7714,8 @@ function replaceInString(text, query, replacement, matchCase) {
|
|
|
4600
7714
|
}
|
|
4601
7715
|
|
|
4602
7716
|
// src/chrome/ChromeTop.tsx
|
|
4603
|
-
var
|
|
4604
|
-
var
|
|
7717
|
+
var import_react26 = require("react");
|
|
7718
|
+
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
4605
7719
|
function ChromeTop({
|
|
4606
7720
|
api,
|
|
4607
7721
|
features,
|
|
@@ -4609,13 +7723,13 @@ function ChromeTop({
|
|
|
4609
7723
|
hostOwnedDialogs,
|
|
4610
7724
|
extensions
|
|
4611
7725
|
}) {
|
|
4612
|
-
const [findSignal, setFindSignal] = (0,
|
|
4613
|
-
const [findReplaceMode, setFindReplaceMode] = (0,
|
|
4614
|
-
const openFindReplace = (0,
|
|
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) => {
|
|
4615
7729
|
setFindReplaceMode(replaceMode);
|
|
4616
7730
|
setFindSignal((n) => n + 1);
|
|
4617
7731
|
}, []);
|
|
4618
|
-
return /* @__PURE__ */ (0,
|
|
7732
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
|
|
4619
7733
|
DialogProvider,
|
|
4620
7734
|
{
|
|
4621
7735
|
api,
|
|
@@ -4624,10 +7738,10 @@ function ChromeTop({
|
|
|
4624
7738
|
hostOwnedDialogs,
|
|
4625
7739
|
onOpenFindReplace: openFindReplace,
|
|
4626
7740
|
children: [
|
|
4627
|
-
/* @__PURE__ */ (0,
|
|
4628
|
-
/* @__PURE__ */ (0,
|
|
4629
|
-
/* @__PURE__ */ (0,
|
|
4630
|
-
/* @__PURE__ */ (0,
|
|
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)(
|
|
4631
7745
|
FindReplace,
|
|
4632
7746
|
{
|
|
4633
7747
|
api,
|
|
@@ -4641,11 +7755,11 @@ function ChromeTop({
|
|
|
4641
7755
|
}
|
|
4642
7756
|
|
|
4643
7757
|
// src/chrome/ChromeBottom.tsx
|
|
4644
|
-
var
|
|
7758
|
+
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
4645
7759
|
function ChromeBottom({ api }) {
|
|
4646
|
-
return /* @__PURE__ */ (0,
|
|
4647
|
-
/* @__PURE__ */ (0,
|
|
4648
|
-
/* @__PURE__ */ (0,
|
|
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 })
|
|
4649
7763
|
] });
|
|
4650
7764
|
}
|
|
4651
7765
|
//# sourceMappingURL=chrome.cjs.map
|