@casualoffice/sheets 0.17.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 +3339 -244
- package/dist/chrome.cjs.map +1 -1
- package/dist/chrome.js +3298 -203
- 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/NameManagerDialog.tsx +378 -0
- package/src/chrome/PasteSpecialDialog.tsx +286 -0
- package/src/chrome/dialog-context.tsx +24 -0
package/dist/chrome.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/chrome/Toolbar.tsx
|
|
2
|
-
import { useEffect as useEffect6, useState as
|
|
2
|
+
import { useEffect as useEffect6, useState as useState18 } from "react";
|
|
3
3
|
import { ICommandService } from "@univerjs/core";
|
|
4
4
|
|
|
5
5
|
// src/chrome/Icon.tsx
|
|
@@ -526,7 +526,7 @@ function AutoSumPicker({ api }) {
|
|
|
526
526
|
}
|
|
527
527
|
|
|
528
528
|
// src/chrome/dialog-context.tsx
|
|
529
|
-
import { createContext, useCallback, useContext, useMemo as
|
|
529
|
+
import { createContext, useCallback as useCallback2, useContext, useMemo as useMemo14, useState as useState17 } from "react";
|
|
530
530
|
|
|
531
531
|
// src/chrome/FormatCellsDialog.tsx
|
|
532
532
|
import { useEffect as useEffect5, useMemo, useState as useState4 } from "react";
|
|
@@ -1128,10 +1128,3105 @@ function FormatCellsDialog({ api, onClose }) {
|
|
|
1128
1128
|
);
|
|
1129
1129
|
}
|
|
1130
1130
|
|
|
1131
|
+
// src/chrome/DataValidationDialog.tsx
|
|
1132
|
+
import { useMemo as useMemo2, useState as useState5 } from "react";
|
|
1133
|
+
import "@univerjs/sheets-data-validation/facade";
|
|
1134
|
+
import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1135
|
+
var RULE_TYPE_OPTIONS = [
|
|
1136
|
+
{ value: "list", label: "List of items" },
|
|
1137
|
+
{ value: "number", label: "Whole number" },
|
|
1138
|
+
{ value: "decimal", label: "Decimal" },
|
|
1139
|
+
{ value: "date", label: "Date" },
|
|
1140
|
+
{ value: "textLength", label: "Text length" },
|
|
1141
|
+
{ value: "checkbox", label: "Checkbox" }
|
|
1142
|
+
];
|
|
1143
|
+
var OPERATOR_OPTIONS = [
|
|
1144
|
+
{ value: "between", label: "Between" },
|
|
1145
|
+
{ value: "notBetween", label: "Not between" },
|
|
1146
|
+
{ value: "equal", label: "Equal to" },
|
|
1147
|
+
{ value: "notEqual", label: "Not equal to" },
|
|
1148
|
+
{ value: "greater", label: "Greater than" },
|
|
1149
|
+
{ value: "greaterEqual", label: "Greater than or equal to" },
|
|
1150
|
+
{ value: "less", label: "Less than" },
|
|
1151
|
+
{ value: "lessEqual", label: "Less than or equal to" }
|
|
1152
|
+
];
|
|
1153
|
+
function isRangeOperator(op) {
|
|
1154
|
+
return op === "between" || op === "notBetween";
|
|
1155
|
+
}
|
|
1156
|
+
function usesOperator(type) {
|
|
1157
|
+
return type === "number" || type === "decimal" || type === "date" || type === "textLength";
|
|
1158
|
+
}
|
|
1159
|
+
var INITIAL_STATE = {
|
|
1160
|
+
ruleType: "list",
|
|
1161
|
+
operator: "between",
|
|
1162
|
+
operand1: "",
|
|
1163
|
+
operand2: "",
|
|
1164
|
+
listItems: "",
|
|
1165
|
+
ignoreBlank: true,
|
|
1166
|
+
showErrorMessage: false,
|
|
1167
|
+
errorMessage: "",
|
|
1168
|
+
showInputMessage: false,
|
|
1169
|
+
inputMessage: ""
|
|
1170
|
+
};
|
|
1171
|
+
function activeRange2(api) {
|
|
1172
|
+
return api.univer.getActiveWorkbook()?.getActiveSheet()?.getActiveRange() ?? null;
|
|
1173
|
+
}
|
|
1174
|
+
function applyValidation(api, s, remove) {
|
|
1175
|
+
const range = activeRange2(api);
|
|
1176
|
+
if (!range) return false;
|
|
1177
|
+
const dvRange = range;
|
|
1178
|
+
if (remove) {
|
|
1179
|
+
dvRange.setDataValidation(null);
|
|
1180
|
+
return true;
|
|
1181
|
+
}
|
|
1182
|
+
const builder = api.univer.newDataValidation();
|
|
1183
|
+
const n1 = Number(s.operand1);
|
|
1184
|
+
const n2 = Number(s.operand2);
|
|
1185
|
+
const wholeNumber = s.ruleType === "number";
|
|
1186
|
+
switch (s.ruleType) {
|
|
1187
|
+
case "list": {
|
|
1188
|
+
const values = s.listItems.split(/[\n,]/).map((v) => v.trim()).filter((v) => v.length > 0);
|
|
1189
|
+
if (values.length === 0) return false;
|
|
1190
|
+
builder.requireValueInList(values, false, true);
|
|
1191
|
+
break;
|
|
1192
|
+
}
|
|
1193
|
+
case "number":
|
|
1194
|
+
case "decimal": {
|
|
1195
|
+
if (!Number.isFinite(n1)) return false;
|
|
1196
|
+
switch (s.operator) {
|
|
1197
|
+
case "between":
|
|
1198
|
+
if (!Number.isFinite(n2)) return false;
|
|
1199
|
+
builder.requireNumberBetween(n1, n2, wholeNumber);
|
|
1200
|
+
break;
|
|
1201
|
+
case "notBetween":
|
|
1202
|
+
if (!Number.isFinite(n2)) return false;
|
|
1203
|
+
builder.requireNumberNotBetween(n1, n2, wholeNumber);
|
|
1204
|
+
break;
|
|
1205
|
+
case "equal":
|
|
1206
|
+
builder.requireNumberEqualTo(n1, wholeNumber);
|
|
1207
|
+
break;
|
|
1208
|
+
case "notEqual":
|
|
1209
|
+
builder.requireNumberNotEqualTo(n1, wholeNumber);
|
|
1210
|
+
break;
|
|
1211
|
+
case "greater":
|
|
1212
|
+
builder.requireNumberGreaterThan(n1, wholeNumber);
|
|
1213
|
+
break;
|
|
1214
|
+
case "greaterEqual":
|
|
1215
|
+
builder.requireNumberGreaterThanOrEqualTo(n1, wholeNumber);
|
|
1216
|
+
break;
|
|
1217
|
+
case "less":
|
|
1218
|
+
builder.requireNumberLessThan(n1, wholeNumber);
|
|
1219
|
+
break;
|
|
1220
|
+
case "lessEqual":
|
|
1221
|
+
builder.requireNumberLessThanOrEqualTo(n1, wholeNumber);
|
|
1222
|
+
break;
|
|
1223
|
+
}
|
|
1224
|
+
break;
|
|
1225
|
+
}
|
|
1226
|
+
case "date": {
|
|
1227
|
+
const d1 = new Date(s.operand1);
|
|
1228
|
+
const d2 = new Date(s.operand2);
|
|
1229
|
+
if (Number.isNaN(d1.getTime())) return false;
|
|
1230
|
+
switch (s.operator) {
|
|
1231
|
+
case "between":
|
|
1232
|
+
if (Number.isNaN(d2.getTime())) return false;
|
|
1233
|
+
builder.requireDateBetween(d1, d2);
|
|
1234
|
+
break;
|
|
1235
|
+
case "notBetween":
|
|
1236
|
+
if (Number.isNaN(d2.getTime())) return false;
|
|
1237
|
+
builder.requireDateNotBetween(d1, d2);
|
|
1238
|
+
break;
|
|
1239
|
+
case "equal":
|
|
1240
|
+
builder.requireDateEqualTo(d1);
|
|
1241
|
+
break;
|
|
1242
|
+
// The builder has no requireDateNotEqualTo; map the remaining operators
|
|
1243
|
+
// to the closest available on/after / on/before methods.
|
|
1244
|
+
case "notEqual":
|
|
1245
|
+
case "greater":
|
|
1246
|
+
builder.requireDateAfter(d1);
|
|
1247
|
+
break;
|
|
1248
|
+
case "greaterEqual":
|
|
1249
|
+
builder.requireDateOnOrAfter(d1);
|
|
1250
|
+
break;
|
|
1251
|
+
case "less":
|
|
1252
|
+
builder.requireDateBefore(d1);
|
|
1253
|
+
break;
|
|
1254
|
+
case "lessEqual":
|
|
1255
|
+
builder.requireDateOnOrBefore(d1);
|
|
1256
|
+
break;
|
|
1257
|
+
}
|
|
1258
|
+
break;
|
|
1259
|
+
}
|
|
1260
|
+
case "textLength": {
|
|
1261
|
+
if (!Number.isFinite(n1)) return false;
|
|
1262
|
+
const anchor = dvRange.getA1Notation?.() ?? "A1";
|
|
1263
|
+
const cell = anchor.split(":")[0];
|
|
1264
|
+
const len = `LEN(${cell})`;
|
|
1265
|
+
let formula;
|
|
1266
|
+
switch (s.operator) {
|
|
1267
|
+
case "between":
|
|
1268
|
+
if (!Number.isFinite(n2)) return false;
|
|
1269
|
+
formula = `=AND(${len}>=${n1}, ${len}<=${n2})`;
|
|
1270
|
+
break;
|
|
1271
|
+
case "notBetween":
|
|
1272
|
+
if (!Number.isFinite(n2)) return false;
|
|
1273
|
+
formula = `=OR(${len}<${n1}, ${len}>${n2})`;
|
|
1274
|
+
break;
|
|
1275
|
+
case "equal":
|
|
1276
|
+
formula = `=${len}=${n1}`;
|
|
1277
|
+
break;
|
|
1278
|
+
case "notEqual":
|
|
1279
|
+
formula = `=${len}<>${n1}`;
|
|
1280
|
+
break;
|
|
1281
|
+
case "greater":
|
|
1282
|
+
formula = `=${len}>${n1}`;
|
|
1283
|
+
break;
|
|
1284
|
+
case "greaterEqual":
|
|
1285
|
+
formula = `=${len}>=${n1}`;
|
|
1286
|
+
break;
|
|
1287
|
+
case "less":
|
|
1288
|
+
formula = `=${len}<${n1}`;
|
|
1289
|
+
break;
|
|
1290
|
+
case "lessEqual":
|
|
1291
|
+
formula = `=${len}<=${n1}`;
|
|
1292
|
+
break;
|
|
1293
|
+
}
|
|
1294
|
+
builder.requireFormulaSatisfied(formula);
|
|
1295
|
+
break;
|
|
1296
|
+
}
|
|
1297
|
+
case "checkbox":
|
|
1298
|
+
builder.requireCheckbox();
|
|
1299
|
+
break;
|
|
1300
|
+
}
|
|
1301
|
+
builder.setAllowBlank(s.ignoreBlank);
|
|
1302
|
+
builder.setOptions({
|
|
1303
|
+
showErrorMessage: s.showErrorMessage,
|
|
1304
|
+
error: s.showErrorMessage ? s.errorMessage : void 0,
|
|
1305
|
+
showInputMessage: s.showInputMessage,
|
|
1306
|
+
prompt: s.showInputMessage ? s.inputMessage : void 0
|
|
1307
|
+
});
|
|
1308
|
+
dvRange.setDataValidation(builder.build());
|
|
1309
|
+
return true;
|
|
1310
|
+
}
|
|
1311
|
+
var CHECK_STYLE2 = {
|
|
1312
|
+
display: "flex",
|
|
1313
|
+
alignItems: "center",
|
|
1314
|
+
gap: 6,
|
|
1315
|
+
marginBottom: 8,
|
|
1316
|
+
cursor: "pointer"
|
|
1317
|
+
};
|
|
1318
|
+
var TEXTAREA_STYLE = {
|
|
1319
|
+
...DIALOG_INPUT_STYLE,
|
|
1320
|
+
height: "auto",
|
|
1321
|
+
minHeight: 64,
|
|
1322
|
+
padding: "6px 8px",
|
|
1323
|
+
resize: "vertical",
|
|
1324
|
+
lineHeight: 1.4
|
|
1325
|
+
};
|
|
1326
|
+
var RANGE_NOTE_STYLE = {
|
|
1327
|
+
fontSize: 12,
|
|
1328
|
+
color: "var(--cs-chrome-muted, #605e5c)",
|
|
1329
|
+
marginBottom: 12
|
|
1330
|
+
};
|
|
1331
|
+
var TWO_COL_STYLE = {
|
|
1332
|
+
display: "grid",
|
|
1333
|
+
gridTemplateColumns: "1fr 1fr",
|
|
1334
|
+
gap: 8
|
|
1335
|
+
};
|
|
1336
|
+
function DataValidationDialog({ api, onClose }) {
|
|
1337
|
+
const [state, setState] = useState5(INITIAL_STATE);
|
|
1338
|
+
const rangeLabel = useMemo2(() => {
|
|
1339
|
+
const fRange = activeRange2(api);
|
|
1340
|
+
return fRange?.getA1Notation?.() ?? null;
|
|
1341
|
+
}, [api]);
|
|
1342
|
+
const hasSelection = activeRange2(api) !== null;
|
|
1343
|
+
const update = (key, value) => setState((prev) => ({ ...prev, [key]: value }));
|
|
1344
|
+
const apply = () => {
|
|
1345
|
+
if (applyValidation(api, state, false)) onClose();
|
|
1346
|
+
};
|
|
1347
|
+
const removeRule = () => {
|
|
1348
|
+
applyValidation(api, state, true);
|
|
1349
|
+
onClose();
|
|
1350
|
+
};
|
|
1351
|
+
const showOperator = usesOperator(state.ruleType);
|
|
1352
|
+
const showSecondOperand = showOperator && isRangeOperator(state.operator);
|
|
1353
|
+
const operandType = state.ruleType === "date" ? "date" : "number";
|
|
1354
|
+
return /* @__PURE__ */ jsxs6(
|
|
1355
|
+
Dialog,
|
|
1356
|
+
{
|
|
1357
|
+
title: "Data validation",
|
|
1358
|
+
onClose,
|
|
1359
|
+
width: 440,
|
|
1360
|
+
"data-testid": "cs-data-validation-dialog",
|
|
1361
|
+
footer: /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
1362
|
+
/* @__PURE__ */ jsx7(
|
|
1363
|
+
"button",
|
|
1364
|
+
{
|
|
1365
|
+
type: "button",
|
|
1366
|
+
style: DIALOG_BTN_SECONDARY_STYLE,
|
|
1367
|
+
"data-testid": "cs-data-validation-remove",
|
|
1368
|
+
onClick: removeRule,
|
|
1369
|
+
children: "Remove rule"
|
|
1370
|
+
}
|
|
1371
|
+
),
|
|
1372
|
+
/* @__PURE__ */ jsx7("span", { style: { flex: 1 } }),
|
|
1373
|
+
/* @__PURE__ */ jsx7("button", { type: "button", style: DIALOG_BTN_SECONDARY_STYLE, onClick: onClose, children: "Cancel" }),
|
|
1374
|
+
/* @__PURE__ */ jsx7(
|
|
1375
|
+
"button",
|
|
1376
|
+
{
|
|
1377
|
+
type: "button",
|
|
1378
|
+
style: DIALOG_BTN_PRIMARY_STYLE,
|
|
1379
|
+
"data-testid": "cs-data-validation-apply",
|
|
1380
|
+
disabled: !hasSelection,
|
|
1381
|
+
onClick: apply,
|
|
1382
|
+
children: "Apply"
|
|
1383
|
+
}
|
|
1384
|
+
)
|
|
1385
|
+
] }),
|
|
1386
|
+
children: [
|
|
1387
|
+
hasSelection ? /* @__PURE__ */ jsxs6("div", { style: RANGE_NOTE_STYLE, "data-testid": "cs-data-validation-range", children: [
|
|
1388
|
+
"Applies to ",
|
|
1389
|
+
/* @__PURE__ */ jsx7("strong", { children: rangeLabel ?? "the current selection" })
|
|
1390
|
+
] }) : /* @__PURE__ */ jsx7("div", { style: RANGE_NOTE_STYLE, "data-testid": "cs-data-validation-no-selection", children: "Select one or more cells first, then reopen this dialog." }),
|
|
1391
|
+
/* @__PURE__ */ jsxs6("label", { style: DIALOG_FIELD_STYLE, children: [
|
|
1392
|
+
/* @__PURE__ */ jsx7("span", { style: DIALOG_LABEL_STYLE, children: "Criteria" }),
|
|
1393
|
+
/* @__PURE__ */ jsx7(
|
|
1394
|
+
"select",
|
|
1395
|
+
{
|
|
1396
|
+
style: DIALOG_INPUT_STYLE,
|
|
1397
|
+
"data-testid": "cs-data-validation-type",
|
|
1398
|
+
value: state.ruleType,
|
|
1399
|
+
onChange: (e) => update("ruleType", e.target.value),
|
|
1400
|
+
children: RULE_TYPE_OPTIONS.map((opt) => /* @__PURE__ */ jsx7("option", { value: opt.value, children: opt.label }, opt.value))
|
|
1401
|
+
}
|
|
1402
|
+
)
|
|
1403
|
+
] }),
|
|
1404
|
+
state.ruleType === "list" && /* @__PURE__ */ jsxs6("label", { style: DIALOG_FIELD_STYLE, children: [
|
|
1405
|
+
/* @__PURE__ */ jsx7("span", { style: DIALOG_LABEL_STYLE, children: "Items (one per line, or comma-separated)" }),
|
|
1406
|
+
/* @__PURE__ */ jsx7(
|
|
1407
|
+
"textarea",
|
|
1408
|
+
{
|
|
1409
|
+
style: TEXTAREA_STYLE,
|
|
1410
|
+
"data-testid": "cs-data-validation-list-items",
|
|
1411
|
+
value: state.listItems,
|
|
1412
|
+
placeholder: "Yes\nNo\nMaybe",
|
|
1413
|
+
onChange: (e) => update("listItems", e.target.value)
|
|
1414
|
+
}
|
|
1415
|
+
)
|
|
1416
|
+
] }),
|
|
1417
|
+
showOperator && /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
1418
|
+
/* @__PURE__ */ jsxs6("label", { style: DIALOG_FIELD_STYLE, children: [
|
|
1419
|
+
/* @__PURE__ */ jsx7("span", { style: DIALOG_LABEL_STYLE, children: "Condition" }),
|
|
1420
|
+
/* @__PURE__ */ jsx7(
|
|
1421
|
+
"select",
|
|
1422
|
+
{
|
|
1423
|
+
style: DIALOG_INPUT_STYLE,
|
|
1424
|
+
"data-testid": "cs-data-validation-operator",
|
|
1425
|
+
value: state.operator,
|
|
1426
|
+
onChange: (e) => update("operator", e.target.value),
|
|
1427
|
+
children: OPERATOR_OPTIONS.map((opt) => /* @__PURE__ */ jsx7("option", { value: opt.value, children: opt.label }, opt.value))
|
|
1428
|
+
}
|
|
1429
|
+
)
|
|
1430
|
+
] }),
|
|
1431
|
+
/* @__PURE__ */ jsxs6("div", { style: showSecondOperand ? TWO_COL_STYLE : void 0, children: [
|
|
1432
|
+
/* @__PURE__ */ jsxs6("label", { style: DIALOG_FIELD_STYLE, children: [
|
|
1433
|
+
/* @__PURE__ */ jsx7("span", { style: DIALOG_LABEL_STYLE, children: showSecondOperand ? "Minimum" : "Value" }),
|
|
1434
|
+
/* @__PURE__ */ jsx7(
|
|
1435
|
+
"input",
|
|
1436
|
+
{
|
|
1437
|
+
style: DIALOG_INPUT_STYLE,
|
|
1438
|
+
"data-testid": "cs-data-validation-operand1",
|
|
1439
|
+
type: operandType,
|
|
1440
|
+
value: state.operand1,
|
|
1441
|
+
onChange: (e) => update("operand1", e.target.value)
|
|
1442
|
+
}
|
|
1443
|
+
)
|
|
1444
|
+
] }),
|
|
1445
|
+
showSecondOperand && /* @__PURE__ */ jsxs6("label", { style: DIALOG_FIELD_STYLE, children: [
|
|
1446
|
+
/* @__PURE__ */ jsx7("span", { style: DIALOG_LABEL_STYLE, children: "Maximum" }),
|
|
1447
|
+
/* @__PURE__ */ jsx7(
|
|
1448
|
+
"input",
|
|
1449
|
+
{
|
|
1450
|
+
style: DIALOG_INPUT_STYLE,
|
|
1451
|
+
"data-testid": "cs-data-validation-operand2",
|
|
1452
|
+
type: operandType,
|
|
1453
|
+
value: state.operand2,
|
|
1454
|
+
onChange: (e) => update("operand2", e.target.value)
|
|
1455
|
+
}
|
|
1456
|
+
)
|
|
1457
|
+
] })
|
|
1458
|
+
] })
|
|
1459
|
+
] }),
|
|
1460
|
+
state.ruleType === "checkbox" && /* @__PURE__ */ jsx7("div", { style: RANGE_NOTE_STYLE, children: "Each cell in the selection becomes a checkbox (checked = TRUE)." }),
|
|
1461
|
+
/* @__PURE__ */ jsxs6("label", { style: CHECK_STYLE2, "data-testid": "cs-data-validation-ignore-blank-label", children: [
|
|
1462
|
+
/* @__PURE__ */ jsx7(
|
|
1463
|
+
"input",
|
|
1464
|
+
{
|
|
1465
|
+
type: "checkbox",
|
|
1466
|
+
"data-testid": "cs-data-validation-ignore-blank",
|
|
1467
|
+
checked: state.ignoreBlank,
|
|
1468
|
+
onChange: (e) => update("ignoreBlank", e.target.checked)
|
|
1469
|
+
}
|
|
1470
|
+
),
|
|
1471
|
+
/* @__PURE__ */ jsx7("span", { children: "Ignore blank cells" })
|
|
1472
|
+
] }),
|
|
1473
|
+
/* @__PURE__ */ jsxs6("label", { style: CHECK_STYLE2, children: [
|
|
1474
|
+
/* @__PURE__ */ jsx7(
|
|
1475
|
+
"input",
|
|
1476
|
+
{
|
|
1477
|
+
type: "checkbox",
|
|
1478
|
+
"data-testid": "cs-data-validation-show-input",
|
|
1479
|
+
checked: state.showInputMessage,
|
|
1480
|
+
onChange: (e) => update("showInputMessage", e.target.checked)
|
|
1481
|
+
}
|
|
1482
|
+
),
|
|
1483
|
+
/* @__PURE__ */ jsx7("span", { children: "Show input message" })
|
|
1484
|
+
] }),
|
|
1485
|
+
state.showInputMessage && /* @__PURE__ */ jsxs6("label", { style: DIALOG_FIELD_STYLE, children: [
|
|
1486
|
+
/* @__PURE__ */ jsx7("span", { style: DIALOG_LABEL_STYLE, children: "Input message" }),
|
|
1487
|
+
/* @__PURE__ */ jsx7(
|
|
1488
|
+
"input",
|
|
1489
|
+
{
|
|
1490
|
+
style: DIALOG_INPUT_STYLE,
|
|
1491
|
+
"data-testid": "cs-data-validation-input-message",
|
|
1492
|
+
value: state.inputMessage,
|
|
1493
|
+
onChange: (e) => update("inputMessage", e.target.value)
|
|
1494
|
+
}
|
|
1495
|
+
)
|
|
1496
|
+
] }),
|
|
1497
|
+
/* @__PURE__ */ jsxs6("label", { style: CHECK_STYLE2, children: [
|
|
1498
|
+
/* @__PURE__ */ jsx7(
|
|
1499
|
+
"input",
|
|
1500
|
+
{
|
|
1501
|
+
type: "checkbox",
|
|
1502
|
+
"data-testid": "cs-data-validation-show-error",
|
|
1503
|
+
checked: state.showErrorMessage,
|
|
1504
|
+
onChange: (e) => update("showErrorMessage", e.target.checked)
|
|
1505
|
+
}
|
|
1506
|
+
),
|
|
1507
|
+
/* @__PURE__ */ jsx7("span", { children: "Show error message on invalid input" })
|
|
1508
|
+
] }),
|
|
1509
|
+
state.showErrorMessage && /* @__PURE__ */ jsxs6("label", { style: DIALOG_FIELD_STYLE, children: [
|
|
1510
|
+
/* @__PURE__ */ jsx7("span", { style: DIALOG_LABEL_STYLE, children: "Error message" }),
|
|
1511
|
+
/* @__PURE__ */ jsx7(
|
|
1512
|
+
"input",
|
|
1513
|
+
{
|
|
1514
|
+
style: DIALOG_INPUT_STYLE,
|
|
1515
|
+
"data-testid": "cs-data-validation-error-message",
|
|
1516
|
+
value: state.errorMessage,
|
|
1517
|
+
onChange: (e) => update("errorMessage", e.target.value)
|
|
1518
|
+
}
|
|
1519
|
+
)
|
|
1520
|
+
] })
|
|
1521
|
+
]
|
|
1522
|
+
}
|
|
1523
|
+
);
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
// src/chrome/ConditionalFormattingDialog.tsx
|
|
1527
|
+
import { useMemo as useMemo3, useState as useState6 } from "react";
|
|
1528
|
+
import "@univerjs/sheets-conditional-formatting/facade";
|
|
1529
|
+
import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1530
|
+
var RULE_TYPE_OPTIONS2 = [
|
|
1531
|
+
{ value: "cellValue", label: "Cell value" },
|
|
1532
|
+
{ value: "textContains", label: "Text contains" },
|
|
1533
|
+
{ value: "topN", label: "Top N" },
|
|
1534
|
+
{ value: "bottomN", label: "Bottom N" },
|
|
1535
|
+
{ value: "colorScale", label: "Color scale" }
|
|
1536
|
+
];
|
|
1537
|
+
var OPERATOR_OPTIONS2 = [
|
|
1538
|
+
{ value: "greater", label: "Greater than" },
|
|
1539
|
+
{ value: "greaterEqual", label: "Greater than or equal to" },
|
|
1540
|
+
{ value: "less", label: "Less than" },
|
|
1541
|
+
{ value: "lessEqual", label: "Less than or equal to" },
|
|
1542
|
+
{ value: "equal", label: "Equal to" },
|
|
1543
|
+
{ value: "notEqual", label: "Not equal to" },
|
|
1544
|
+
{ value: "between", label: "Between" },
|
|
1545
|
+
{ value: "notBetween", label: "Not between" }
|
|
1546
|
+
];
|
|
1547
|
+
function isRangeOperator2(op) {
|
|
1548
|
+
return op === "between" || op === "notBetween";
|
|
1549
|
+
}
|
|
1550
|
+
var INITIAL_STATE2 = {
|
|
1551
|
+
ruleType: "cellValue",
|
|
1552
|
+
operator: "greater",
|
|
1553
|
+
operand1: "",
|
|
1554
|
+
operand2: "",
|
|
1555
|
+
text: "",
|
|
1556
|
+
rankN: "10",
|
|
1557
|
+
rankPercent: false,
|
|
1558
|
+
fillColor: "#fce8b2",
|
|
1559
|
+
textColor: "#7f6000",
|
|
1560
|
+
scaleMinColor: "#ffffff",
|
|
1561
|
+
scaleMaxColor: "#57bb8a"
|
|
1562
|
+
};
|
|
1563
|
+
function activeRange3(api) {
|
|
1564
|
+
return api.univer.getActiveWorkbook()?.getActiveSheet()?.getActiveRange() ?? null;
|
|
1565
|
+
}
|
|
1566
|
+
function activeSheet(api) {
|
|
1567
|
+
return api.univer.getActiveWorkbook()?.getActiveSheet() ?? null;
|
|
1568
|
+
}
|
|
1569
|
+
function usesHighlight(type) {
|
|
1570
|
+
return type !== "colorScale";
|
|
1571
|
+
}
|
|
1572
|
+
function applyRule(api, s) {
|
|
1573
|
+
const range = activeRange3(api);
|
|
1574
|
+
const sheet = activeSheet(api);
|
|
1575
|
+
if (!range || !sheet) return false;
|
|
1576
|
+
const cfSheet = sheet;
|
|
1577
|
+
const iRange = range.getRange();
|
|
1578
|
+
const n1 = Number(s.operand1);
|
|
1579
|
+
const n2 = Number(s.operand2);
|
|
1580
|
+
const rankN = Number(s.rankN);
|
|
1581
|
+
let builder = cfSheet.newConditionalFormattingRule();
|
|
1582
|
+
switch (s.ruleType) {
|
|
1583
|
+
case "cellValue": {
|
|
1584
|
+
if (!Number.isFinite(n1)) return false;
|
|
1585
|
+
switch (s.operator) {
|
|
1586
|
+
case "greater":
|
|
1587
|
+
builder = builder.whenNumberGreaterThan(n1);
|
|
1588
|
+
break;
|
|
1589
|
+
case "greaterEqual":
|
|
1590
|
+
builder = builder.whenNumberGreaterThanOrEqualTo(n1);
|
|
1591
|
+
break;
|
|
1592
|
+
case "less":
|
|
1593
|
+
builder = builder.whenNumberLessThan(n1);
|
|
1594
|
+
break;
|
|
1595
|
+
case "lessEqual":
|
|
1596
|
+
builder = builder.whenNumberLessThanOrEqualTo(n1);
|
|
1597
|
+
break;
|
|
1598
|
+
case "equal":
|
|
1599
|
+
builder = builder.whenNumberEqualTo(n1);
|
|
1600
|
+
break;
|
|
1601
|
+
case "notEqual":
|
|
1602
|
+
builder = builder.whenNumberNotEqualTo(n1);
|
|
1603
|
+
break;
|
|
1604
|
+
case "between":
|
|
1605
|
+
if (!Number.isFinite(n2)) return false;
|
|
1606
|
+
builder = builder.whenNumberBetween(n1, n2);
|
|
1607
|
+
break;
|
|
1608
|
+
case "notBetween":
|
|
1609
|
+
if (!Number.isFinite(n2)) return false;
|
|
1610
|
+
builder = builder.whenNumberNotBetween(n1, n2);
|
|
1611
|
+
break;
|
|
1612
|
+
}
|
|
1613
|
+
break;
|
|
1614
|
+
}
|
|
1615
|
+
case "textContains": {
|
|
1616
|
+
const text = s.text.trim();
|
|
1617
|
+
if (text.length === 0) return false;
|
|
1618
|
+
builder = builder.whenTextContains(text);
|
|
1619
|
+
break;
|
|
1620
|
+
}
|
|
1621
|
+
case "topN":
|
|
1622
|
+
case "bottomN": {
|
|
1623
|
+
if (!Number.isFinite(rankN) || rankN <= 0) return false;
|
|
1624
|
+
builder = builder.setRank({
|
|
1625
|
+
isBottom: s.ruleType === "bottomN",
|
|
1626
|
+
isPercent: s.rankPercent,
|
|
1627
|
+
value: rankN
|
|
1628
|
+
});
|
|
1629
|
+
break;
|
|
1630
|
+
}
|
|
1631
|
+
case "colorScale": {
|
|
1632
|
+
builder = builder.setColorScale([
|
|
1633
|
+
{ index: 0, color: s.scaleMinColor, value: { type: "min" } },
|
|
1634
|
+
{ index: 1, color: s.scaleMaxColor, value: { type: "max" } }
|
|
1635
|
+
]);
|
|
1636
|
+
break;
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
if (usesHighlight(s.ruleType)) {
|
|
1640
|
+
builder = builder.setBackground(s.fillColor);
|
|
1641
|
+
builder = builder.setFontColor(s.textColor);
|
|
1642
|
+
}
|
|
1643
|
+
const rule = builder.setRanges([iRange]).build();
|
|
1644
|
+
cfSheet.addConditionalFormattingRule(rule);
|
|
1645
|
+
return true;
|
|
1646
|
+
}
|
|
1647
|
+
var RANGE_NOTE_STYLE2 = {
|
|
1648
|
+
fontSize: 12,
|
|
1649
|
+
color: "var(--cs-chrome-muted, #605e5c)",
|
|
1650
|
+
marginBottom: 12
|
|
1651
|
+
};
|
|
1652
|
+
var CHECK_STYLE3 = {
|
|
1653
|
+
display: "flex",
|
|
1654
|
+
alignItems: "center",
|
|
1655
|
+
gap: 6,
|
|
1656
|
+
marginBottom: 8,
|
|
1657
|
+
cursor: "pointer"
|
|
1658
|
+
};
|
|
1659
|
+
var COLOR_INPUT_STYLE2 = {
|
|
1660
|
+
width: 48,
|
|
1661
|
+
height: 30,
|
|
1662
|
+
padding: 2,
|
|
1663
|
+
border: "1px solid var(--cs-chrome-border, #cdd3db)",
|
|
1664
|
+
borderRadius: 6,
|
|
1665
|
+
background: "var(--cs-chrome-input-bg, #fff)",
|
|
1666
|
+
cursor: "pointer"
|
|
1667
|
+
};
|
|
1668
|
+
var TWO_COL_STYLE2 = {
|
|
1669
|
+
display: "grid",
|
|
1670
|
+
gridTemplateColumns: "1fr 1fr",
|
|
1671
|
+
gap: 8
|
|
1672
|
+
};
|
|
1673
|
+
function ConditionalFormattingDialog({ api, onClose }) {
|
|
1674
|
+
const [state, setState] = useState6(INITIAL_STATE2);
|
|
1675
|
+
const rangeLabel = useMemo3(() => {
|
|
1676
|
+
const fRange = activeRange3(api);
|
|
1677
|
+
return fRange?.getA1Notation?.() ?? null;
|
|
1678
|
+
}, [api]);
|
|
1679
|
+
const hasSelection = activeRange3(api) !== null;
|
|
1680
|
+
const update = (key, value) => setState((prev) => ({ ...prev, [key]: value }));
|
|
1681
|
+
const apply = () => {
|
|
1682
|
+
if (applyRule(api, state)) onClose();
|
|
1683
|
+
};
|
|
1684
|
+
const clearRules = () => {
|
|
1685
|
+
const sheet = activeSheet(api);
|
|
1686
|
+
sheet?.clearConditionalFormatRules?.();
|
|
1687
|
+
onClose();
|
|
1688
|
+
};
|
|
1689
|
+
const showOperator = state.ruleType === "cellValue";
|
|
1690
|
+
const showSecondOperand = showOperator && isRangeOperator2(state.operator);
|
|
1691
|
+
const isRank = state.ruleType === "topN" || state.ruleType === "bottomN";
|
|
1692
|
+
return /* @__PURE__ */ jsxs7(
|
|
1693
|
+
Dialog,
|
|
1694
|
+
{
|
|
1695
|
+
title: "Conditional formatting",
|
|
1696
|
+
onClose,
|
|
1697
|
+
width: 440,
|
|
1698
|
+
"data-testid": "cs-conditional-formatting-dialog",
|
|
1699
|
+
footer: /* @__PURE__ */ jsxs7(Fragment3, { children: [
|
|
1700
|
+
/* @__PURE__ */ jsx8(
|
|
1701
|
+
"button",
|
|
1702
|
+
{
|
|
1703
|
+
type: "button",
|
|
1704
|
+
style: DIALOG_BTN_SECONDARY_STYLE,
|
|
1705
|
+
"data-testid": "cs-conditional-formatting-clear",
|
|
1706
|
+
onClick: clearRules,
|
|
1707
|
+
children: "Clear rules"
|
|
1708
|
+
}
|
|
1709
|
+
),
|
|
1710
|
+
/* @__PURE__ */ jsx8("span", { style: { flex: 1 } }),
|
|
1711
|
+
/* @__PURE__ */ jsx8("button", { type: "button", style: DIALOG_BTN_SECONDARY_STYLE, onClick: onClose, children: "Cancel" }),
|
|
1712
|
+
/* @__PURE__ */ jsx8(
|
|
1713
|
+
"button",
|
|
1714
|
+
{
|
|
1715
|
+
type: "button",
|
|
1716
|
+
style: DIALOG_BTN_PRIMARY_STYLE,
|
|
1717
|
+
"data-testid": "cs-conditional-formatting-apply",
|
|
1718
|
+
disabled: !hasSelection,
|
|
1719
|
+
onClick: apply,
|
|
1720
|
+
children: "Apply"
|
|
1721
|
+
}
|
|
1722
|
+
)
|
|
1723
|
+
] }),
|
|
1724
|
+
children: [
|
|
1725
|
+
hasSelection ? /* @__PURE__ */ jsxs7("div", { style: RANGE_NOTE_STYLE2, "data-testid": "cs-conditional-formatting-range", children: [
|
|
1726
|
+
"Applies to ",
|
|
1727
|
+
/* @__PURE__ */ jsx8("strong", { children: rangeLabel ?? "the current selection" })
|
|
1728
|
+
] }) : /* @__PURE__ */ jsx8("div", { style: RANGE_NOTE_STYLE2, "data-testid": "cs-conditional-formatting-no-selection", children: "Select one or more cells first, then reopen this dialog." }),
|
|
1729
|
+
/* @__PURE__ */ jsxs7("label", { style: DIALOG_FIELD_STYLE, children: [
|
|
1730
|
+
/* @__PURE__ */ jsx8("span", { style: DIALOG_LABEL_STYLE, children: "Condition type" }),
|
|
1731
|
+
/* @__PURE__ */ jsx8(
|
|
1732
|
+
"select",
|
|
1733
|
+
{
|
|
1734
|
+
style: DIALOG_INPUT_STYLE,
|
|
1735
|
+
"data-testid": "cs-conditional-formatting-type",
|
|
1736
|
+
value: state.ruleType,
|
|
1737
|
+
onChange: (e) => update("ruleType", e.target.value),
|
|
1738
|
+
children: RULE_TYPE_OPTIONS2.map((opt) => /* @__PURE__ */ jsx8("option", { value: opt.value, children: opt.label }, opt.value))
|
|
1739
|
+
}
|
|
1740
|
+
)
|
|
1741
|
+
] }),
|
|
1742
|
+
showOperator && /* @__PURE__ */ jsxs7(Fragment3, { children: [
|
|
1743
|
+
/* @__PURE__ */ jsxs7("label", { style: DIALOG_FIELD_STYLE, children: [
|
|
1744
|
+
/* @__PURE__ */ jsx8("span", { style: DIALOG_LABEL_STYLE, children: "Condition" }),
|
|
1745
|
+
/* @__PURE__ */ jsx8(
|
|
1746
|
+
"select",
|
|
1747
|
+
{
|
|
1748
|
+
style: DIALOG_INPUT_STYLE,
|
|
1749
|
+
"data-testid": "cs-conditional-formatting-operator",
|
|
1750
|
+
value: state.operator,
|
|
1751
|
+
onChange: (e) => update("operator", e.target.value),
|
|
1752
|
+
children: OPERATOR_OPTIONS2.map((opt) => /* @__PURE__ */ jsx8("option", { value: opt.value, children: opt.label }, opt.value))
|
|
1753
|
+
}
|
|
1754
|
+
)
|
|
1755
|
+
] }),
|
|
1756
|
+
/* @__PURE__ */ jsxs7("div", { style: showSecondOperand ? TWO_COL_STYLE2 : void 0, children: [
|
|
1757
|
+
/* @__PURE__ */ jsxs7("label", { style: DIALOG_FIELD_STYLE, children: [
|
|
1758
|
+
/* @__PURE__ */ jsx8("span", { style: DIALOG_LABEL_STYLE, children: showSecondOperand ? "Minimum" : "Value" }),
|
|
1759
|
+
/* @__PURE__ */ jsx8(
|
|
1760
|
+
"input",
|
|
1761
|
+
{
|
|
1762
|
+
style: DIALOG_INPUT_STYLE,
|
|
1763
|
+
"data-testid": "cs-conditional-formatting-operand1",
|
|
1764
|
+
type: "number",
|
|
1765
|
+
value: state.operand1,
|
|
1766
|
+
onChange: (e) => update("operand1", e.target.value)
|
|
1767
|
+
}
|
|
1768
|
+
)
|
|
1769
|
+
] }),
|
|
1770
|
+
showSecondOperand && /* @__PURE__ */ jsxs7("label", { style: DIALOG_FIELD_STYLE, children: [
|
|
1771
|
+
/* @__PURE__ */ jsx8("span", { style: DIALOG_LABEL_STYLE, children: "Maximum" }),
|
|
1772
|
+
/* @__PURE__ */ jsx8(
|
|
1773
|
+
"input",
|
|
1774
|
+
{
|
|
1775
|
+
style: DIALOG_INPUT_STYLE,
|
|
1776
|
+
"data-testid": "cs-conditional-formatting-operand2",
|
|
1777
|
+
type: "number",
|
|
1778
|
+
value: state.operand2,
|
|
1779
|
+
onChange: (e) => update("operand2", e.target.value)
|
|
1780
|
+
}
|
|
1781
|
+
)
|
|
1782
|
+
] })
|
|
1783
|
+
] })
|
|
1784
|
+
] }),
|
|
1785
|
+
state.ruleType === "textContains" && /* @__PURE__ */ jsxs7("label", { style: DIALOG_FIELD_STYLE, children: [
|
|
1786
|
+
/* @__PURE__ */ jsx8("span", { style: DIALOG_LABEL_STYLE, children: "Text contains" }),
|
|
1787
|
+
/* @__PURE__ */ jsx8(
|
|
1788
|
+
"input",
|
|
1789
|
+
{
|
|
1790
|
+
style: DIALOG_INPUT_STYLE,
|
|
1791
|
+
"data-testid": "cs-conditional-formatting-text",
|
|
1792
|
+
value: state.text,
|
|
1793
|
+
placeholder: "e.g. urgent",
|
|
1794
|
+
onChange: (e) => update("text", e.target.value)
|
|
1795
|
+
}
|
|
1796
|
+
)
|
|
1797
|
+
] }),
|
|
1798
|
+
isRank && /* @__PURE__ */ jsxs7(Fragment3, { children: [
|
|
1799
|
+
/* @__PURE__ */ jsxs7("label", { style: DIALOG_FIELD_STYLE, children: [
|
|
1800
|
+
/* @__PURE__ */ jsxs7("span", { style: DIALOG_LABEL_STYLE, children: [
|
|
1801
|
+
state.ruleType === "topN" ? "Top" : "Bottom",
|
|
1802
|
+
" ",
|
|
1803
|
+
state.rankPercent ? "percent" : "count"
|
|
1804
|
+
] }),
|
|
1805
|
+
/* @__PURE__ */ jsx8(
|
|
1806
|
+
"input",
|
|
1807
|
+
{
|
|
1808
|
+
style: DIALOG_INPUT_STYLE,
|
|
1809
|
+
"data-testid": "cs-conditional-formatting-rank-n",
|
|
1810
|
+
type: "number",
|
|
1811
|
+
min: 1,
|
|
1812
|
+
value: state.rankN,
|
|
1813
|
+
onChange: (e) => update("rankN", e.target.value)
|
|
1814
|
+
}
|
|
1815
|
+
)
|
|
1816
|
+
] }),
|
|
1817
|
+
/* @__PURE__ */ jsxs7("label", { style: CHECK_STYLE3, children: [
|
|
1818
|
+
/* @__PURE__ */ jsx8(
|
|
1819
|
+
"input",
|
|
1820
|
+
{
|
|
1821
|
+
type: "checkbox",
|
|
1822
|
+
"data-testid": "cs-conditional-formatting-rank-percent",
|
|
1823
|
+
checked: state.rankPercent,
|
|
1824
|
+
onChange: (e) => update("rankPercent", e.target.checked)
|
|
1825
|
+
}
|
|
1826
|
+
),
|
|
1827
|
+
/* @__PURE__ */ jsx8("span", { children: "Interpret as percent" })
|
|
1828
|
+
] })
|
|
1829
|
+
] }),
|
|
1830
|
+
state.ruleType === "colorScale" ? /* @__PURE__ */ jsxs7("div", { style: TWO_COL_STYLE2, children: [
|
|
1831
|
+
/* @__PURE__ */ jsxs7("label", { style: DIALOG_FIELD_STYLE, children: [
|
|
1832
|
+
/* @__PURE__ */ jsx8("span", { style: DIALOG_LABEL_STYLE, children: "Min color" }),
|
|
1833
|
+
/* @__PURE__ */ jsx8(
|
|
1834
|
+
"input",
|
|
1835
|
+
{
|
|
1836
|
+
style: COLOR_INPUT_STYLE2,
|
|
1837
|
+
"data-testid": "cs-conditional-formatting-scale-min",
|
|
1838
|
+
type: "color",
|
|
1839
|
+
value: state.scaleMinColor,
|
|
1840
|
+
onChange: (e) => update("scaleMinColor", e.target.value)
|
|
1841
|
+
}
|
|
1842
|
+
)
|
|
1843
|
+
] }),
|
|
1844
|
+
/* @__PURE__ */ jsxs7("label", { style: DIALOG_FIELD_STYLE, children: [
|
|
1845
|
+
/* @__PURE__ */ jsx8("span", { style: DIALOG_LABEL_STYLE, children: "Max color" }),
|
|
1846
|
+
/* @__PURE__ */ jsx8(
|
|
1847
|
+
"input",
|
|
1848
|
+
{
|
|
1849
|
+
style: COLOR_INPUT_STYLE2,
|
|
1850
|
+
"data-testid": "cs-conditional-formatting-scale-max",
|
|
1851
|
+
type: "color",
|
|
1852
|
+
value: state.scaleMaxColor,
|
|
1853
|
+
onChange: (e) => update("scaleMaxColor", e.target.value)
|
|
1854
|
+
}
|
|
1855
|
+
)
|
|
1856
|
+
] })
|
|
1857
|
+
] }) : /* @__PURE__ */ jsxs7("div", { style: TWO_COL_STYLE2, children: [
|
|
1858
|
+
/* @__PURE__ */ jsxs7("label", { style: DIALOG_FIELD_STYLE, children: [
|
|
1859
|
+
/* @__PURE__ */ jsx8("span", { style: DIALOG_LABEL_STYLE, children: "Fill color" }),
|
|
1860
|
+
/* @__PURE__ */ jsx8(
|
|
1861
|
+
"input",
|
|
1862
|
+
{
|
|
1863
|
+
style: COLOR_INPUT_STYLE2,
|
|
1864
|
+
"data-testid": "cs-conditional-formatting-fill-color",
|
|
1865
|
+
type: "color",
|
|
1866
|
+
value: state.fillColor,
|
|
1867
|
+
onChange: (e) => update("fillColor", e.target.value)
|
|
1868
|
+
}
|
|
1869
|
+
)
|
|
1870
|
+
] }),
|
|
1871
|
+
/* @__PURE__ */ jsxs7("label", { style: DIALOG_FIELD_STYLE, children: [
|
|
1872
|
+
/* @__PURE__ */ jsx8("span", { style: DIALOG_LABEL_STYLE, children: "Text color" }),
|
|
1873
|
+
/* @__PURE__ */ jsx8(
|
|
1874
|
+
"input",
|
|
1875
|
+
{
|
|
1876
|
+
style: COLOR_INPUT_STYLE2,
|
|
1877
|
+
"data-testid": "cs-conditional-formatting-text-color",
|
|
1878
|
+
type: "color",
|
|
1879
|
+
value: state.textColor,
|
|
1880
|
+
onChange: (e) => update("textColor", e.target.value)
|
|
1881
|
+
}
|
|
1882
|
+
)
|
|
1883
|
+
] })
|
|
1884
|
+
] })
|
|
1885
|
+
]
|
|
1886
|
+
}
|
|
1887
|
+
);
|
|
1888
|
+
}
|
|
1889
|
+
|
|
1890
|
+
// src/chrome/CustomSortDialog.tsx
|
|
1891
|
+
import { useMemo as useMemo4, useState as useState7 } from "react";
|
|
1892
|
+
import "@univerjs/sheets-sort/facade";
|
|
1893
|
+
import { Fragment as Fragment4, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1894
|
+
function activeRange4(api) {
|
|
1895
|
+
return api.univer.getActiveWorkbook()?.getActiveSheet()?.getActiveRange() ?? null;
|
|
1896
|
+
}
|
|
1897
|
+
function columnLetter(index) {
|
|
1898
|
+
let n = index;
|
|
1899
|
+
let label = "";
|
|
1900
|
+
do {
|
|
1901
|
+
label = String.fromCharCode(65 + n % 26) + label;
|
|
1902
|
+
n = Math.floor(n / 26) - 1;
|
|
1903
|
+
} while (n >= 0);
|
|
1904
|
+
return label;
|
|
1905
|
+
}
|
|
1906
|
+
function readRangeInfo(api) {
|
|
1907
|
+
const range = activeRange4(api);
|
|
1908
|
+
if (!range) return null;
|
|
1909
|
+
const values = range.getValues?.();
|
|
1910
|
+
return {
|
|
1911
|
+
startRow: range.getRow(),
|
|
1912
|
+
startColumn: range.getColumn(),
|
|
1913
|
+
width: range.getWidth(),
|
|
1914
|
+
height: range.getHeight(),
|
|
1915
|
+
a1: range.getA1Notation?.() ?? null,
|
|
1916
|
+
firstRow: values && values.length > 0 ? values[0] : null
|
|
1917
|
+
};
|
|
1918
|
+
}
|
|
1919
|
+
function applySort(api, info, levels, hasHeader) {
|
|
1920
|
+
const worksheet = api.univer.getActiveWorkbook()?.getActiveSheet();
|
|
1921
|
+
if (!worksheet) return false;
|
|
1922
|
+
const bandStartRow = hasHeader ? info.startRow + 1 : info.startRow;
|
|
1923
|
+
const bandHeight = hasHeader ? info.height - 1 : info.height;
|
|
1924
|
+
if (bandHeight <= 0) return false;
|
|
1925
|
+
const band = worksheet.getRange(bandStartRow, info.startColumn, bandHeight, info.width);
|
|
1926
|
+
const specs = levels.filter((l) => l.column >= 0 && l.column < info.width).map((l) => ({ column: l.column, ascending: l.ascending }));
|
|
1927
|
+
if (specs.length === 0) return false;
|
|
1928
|
+
band.sort(specs);
|
|
1929
|
+
return true;
|
|
1930
|
+
}
|
|
1931
|
+
var RANGE_NOTE_STYLE3 = {
|
|
1932
|
+
fontSize: 12,
|
|
1933
|
+
color: "var(--cs-chrome-muted, #605e5c)",
|
|
1934
|
+
marginBottom: 12
|
|
1935
|
+
};
|
|
1936
|
+
var CHECK_STYLE4 = {
|
|
1937
|
+
display: "flex",
|
|
1938
|
+
alignItems: "center",
|
|
1939
|
+
gap: 6,
|
|
1940
|
+
marginBottom: 12,
|
|
1941
|
+
cursor: "pointer"
|
|
1942
|
+
};
|
|
1943
|
+
var LEVEL_ROW_STYLE = {
|
|
1944
|
+
display: "grid",
|
|
1945
|
+
gridTemplateColumns: "auto 1fr 130px 28px",
|
|
1946
|
+
alignItems: "center",
|
|
1947
|
+
gap: 8,
|
|
1948
|
+
marginBottom: 8
|
|
1949
|
+
};
|
|
1950
|
+
var LEVEL_PREFIX_STYLE = {
|
|
1951
|
+
fontSize: 12,
|
|
1952
|
+
color: "var(--cs-chrome-muted, #605e5c)",
|
|
1953
|
+
whiteSpace: "nowrap"
|
|
1954
|
+
};
|
|
1955
|
+
var REMOVE_BTN_STYLE = {
|
|
1956
|
+
width: 28,
|
|
1957
|
+
height: 30,
|
|
1958
|
+
display: "inline-flex",
|
|
1959
|
+
alignItems: "center",
|
|
1960
|
+
justifyContent: "center",
|
|
1961
|
+
border: "1px solid var(--cs-chrome-border, #cdd3db)",
|
|
1962
|
+
borderRadius: 6,
|
|
1963
|
+
background: "var(--cs-chrome-input-bg, #fff)",
|
|
1964
|
+
color: "var(--cs-chrome-muted, #605e5c)",
|
|
1965
|
+
cursor: "pointer",
|
|
1966
|
+
padding: 0,
|
|
1967
|
+
fontSize: 16,
|
|
1968
|
+
lineHeight: 1
|
|
1969
|
+
};
|
|
1970
|
+
var ADD_BTN_STYLE = {
|
|
1971
|
+
...DIALOG_BTN_SECONDARY_STYLE,
|
|
1972
|
+
marginTop: 4
|
|
1973
|
+
};
|
|
1974
|
+
function CustomSortDialog({ api, onClose }) {
|
|
1975
|
+
const info = useMemo4(() => readRangeInfo(api), [api]);
|
|
1976
|
+
const hasSelection = info !== null;
|
|
1977
|
+
const width = info?.width ?? 1;
|
|
1978
|
+
const [hasHeader, setHasHeader] = useState7(false);
|
|
1979
|
+
const [levels, setLevels] = useState7([{ column: 0, ascending: true }]);
|
|
1980
|
+
const columnOptions = useMemo4(() => {
|
|
1981
|
+
const startCol = info?.startColumn ?? 0;
|
|
1982
|
+
return Array.from({ length: width }, (_, offset) => {
|
|
1983
|
+
const headerCell = hasHeader ? info?.firstRow?.[offset] : void 0;
|
|
1984
|
+
const headerText = headerCell !== void 0 && headerCell !== null && String(headerCell).trim() !== "" ? String(headerCell) : null;
|
|
1985
|
+
const letter = columnLetter(startCol + offset);
|
|
1986
|
+
return {
|
|
1987
|
+
value: offset,
|
|
1988
|
+
label: headerText ? `${headerText} (${letter})` : `Column ${letter}`
|
|
1989
|
+
};
|
|
1990
|
+
});
|
|
1991
|
+
}, [width, hasHeader, info]);
|
|
1992
|
+
const updateLevel = (index, patch) => setLevels((prev) => prev.map((l, i) => i === index ? { ...l, ...patch } : l));
|
|
1993
|
+
const addLevel = () => {
|
|
1994
|
+
const used = new Set(levels.map((l) => l.column));
|
|
1995
|
+
let next = 0;
|
|
1996
|
+
for (let c = 0; c < width; c += 1) {
|
|
1997
|
+
if (!used.has(c)) {
|
|
1998
|
+
next = c;
|
|
1999
|
+
break;
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
setLevels((prev) => [...prev, { column: next, ascending: true }]);
|
|
2003
|
+
};
|
|
2004
|
+
const removeLevel = (index) => setLevels((prev) => prev.length <= 1 ? prev : prev.filter((_, i) => i !== index));
|
|
2005
|
+
const apply = () => {
|
|
2006
|
+
if (info && applySort(api, info, levels, hasHeader)) onClose();
|
|
2007
|
+
};
|
|
2008
|
+
const canApply = hasSelection && levels.length > 0;
|
|
2009
|
+
return /* @__PURE__ */ jsxs8(
|
|
2010
|
+
Dialog,
|
|
2011
|
+
{
|
|
2012
|
+
title: "Sort range",
|
|
2013
|
+
onClose,
|
|
2014
|
+
width: 480,
|
|
2015
|
+
"data-testid": "cs-custom-sort-dialog",
|
|
2016
|
+
footer: /* @__PURE__ */ jsxs8(Fragment4, { children: [
|
|
2017
|
+
/* @__PURE__ */ jsx9("button", { type: "button", style: DIALOG_BTN_SECONDARY_STYLE, onClick: onClose, children: "Cancel" }),
|
|
2018
|
+
/* @__PURE__ */ jsx9(
|
|
2019
|
+
"button",
|
|
2020
|
+
{
|
|
2021
|
+
type: "button",
|
|
2022
|
+
style: DIALOG_BTN_PRIMARY_STYLE,
|
|
2023
|
+
"data-testid": "cs-custom-sort-apply",
|
|
2024
|
+
disabled: !canApply,
|
|
2025
|
+
onClick: apply,
|
|
2026
|
+
children: "Sort"
|
|
2027
|
+
}
|
|
2028
|
+
)
|
|
2029
|
+
] }),
|
|
2030
|
+
children: [
|
|
2031
|
+
hasSelection ? /* @__PURE__ */ jsxs8("div", { style: RANGE_NOTE_STYLE3, "data-testid": "cs-custom-sort-range", children: [
|
|
2032
|
+
"Sort ",
|
|
2033
|
+
/* @__PURE__ */ jsx9("strong", { children: info?.a1 ?? "the current selection" })
|
|
2034
|
+
] }) : /* @__PURE__ */ jsx9("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." }),
|
|
2035
|
+
/* @__PURE__ */ jsxs8("label", { style: CHECK_STYLE4, "data-testid": "cs-custom-sort-has-header-label", children: [
|
|
2036
|
+
/* @__PURE__ */ jsx9(
|
|
2037
|
+
"input",
|
|
2038
|
+
{
|
|
2039
|
+
type: "checkbox",
|
|
2040
|
+
"data-testid": "cs-custom-sort-has-header",
|
|
2041
|
+
checked: hasHeader,
|
|
2042
|
+
onChange: (e) => setHasHeader(e.target.checked)
|
|
2043
|
+
}
|
|
2044
|
+
),
|
|
2045
|
+
/* @__PURE__ */ jsx9("span", { children: "Data has header row" })
|
|
2046
|
+
] }),
|
|
2047
|
+
/* @__PURE__ */ jsxs8("div", { style: DIALOG_FIELD_STYLE, children: [
|
|
2048
|
+
/* @__PURE__ */ jsx9("span", { style: DIALOG_LABEL_STYLE, children: "Sort by" }),
|
|
2049
|
+
levels.map((level, index) => /* @__PURE__ */ jsxs8("div", { style: LEVEL_ROW_STYLE, "data-testid": `cs-custom-sort-level-${index}`, children: [
|
|
2050
|
+
/* @__PURE__ */ jsx9("span", { style: LEVEL_PREFIX_STYLE, children: index === 0 ? "Sort by" : "then by" }),
|
|
2051
|
+
/* @__PURE__ */ jsx9(
|
|
2052
|
+
"select",
|
|
2053
|
+
{
|
|
2054
|
+
style: DIALOG_INPUT_STYLE,
|
|
2055
|
+
"data-testid": `cs-custom-sort-column-${index}`,
|
|
2056
|
+
value: level.column,
|
|
2057
|
+
onChange: (e) => updateLevel(index, { column: Number(e.target.value) }),
|
|
2058
|
+
children: columnOptions.map((opt) => /* @__PURE__ */ jsx9("option", { value: opt.value, children: opt.label }, opt.value))
|
|
2059
|
+
}
|
|
2060
|
+
),
|
|
2061
|
+
/* @__PURE__ */ jsxs8(
|
|
2062
|
+
"select",
|
|
2063
|
+
{
|
|
2064
|
+
style: DIALOG_INPUT_STYLE,
|
|
2065
|
+
"data-testid": `cs-custom-sort-order-${index}`,
|
|
2066
|
+
value: level.ascending ? "asc" : "desc",
|
|
2067
|
+
onChange: (e) => updateLevel(index, { ascending: e.target.value === "asc" }),
|
|
2068
|
+
children: [
|
|
2069
|
+
/* @__PURE__ */ jsx9("option", { value: "asc", children: "A \u2192 Z (ascending)" }),
|
|
2070
|
+
/* @__PURE__ */ jsx9("option", { value: "desc", children: "Z \u2192 A (descending)" })
|
|
2071
|
+
]
|
|
2072
|
+
}
|
|
2073
|
+
),
|
|
2074
|
+
/* @__PURE__ */ jsx9(
|
|
2075
|
+
"button",
|
|
2076
|
+
{
|
|
2077
|
+
type: "button",
|
|
2078
|
+
style: REMOVE_BTN_STYLE,
|
|
2079
|
+
"data-testid": `cs-custom-sort-remove-${index}`,
|
|
2080
|
+
"aria-label": "Remove sort level",
|
|
2081
|
+
disabled: levels.length <= 1,
|
|
2082
|
+
onClick: () => removeLevel(index),
|
|
2083
|
+
children: "\u2212"
|
|
2084
|
+
}
|
|
2085
|
+
)
|
|
2086
|
+
] }, index)),
|
|
2087
|
+
/* @__PURE__ */ jsx9(
|
|
2088
|
+
"button",
|
|
2089
|
+
{
|
|
2090
|
+
type: "button",
|
|
2091
|
+
style: ADD_BTN_STYLE,
|
|
2092
|
+
"data-testid": "cs-custom-sort-add-level",
|
|
2093
|
+
disabled: levels.length >= width,
|
|
2094
|
+
onClick: addLevel,
|
|
2095
|
+
children: "Add another sort level"
|
|
2096
|
+
}
|
|
2097
|
+
)
|
|
2098
|
+
] })
|
|
2099
|
+
]
|
|
2100
|
+
}
|
|
2101
|
+
);
|
|
2102
|
+
}
|
|
2103
|
+
|
|
2104
|
+
// src/chrome/PasteSpecialDialog.tsx
|
|
2105
|
+
import { useMemo as useMemo5, useState as useState8 } from "react";
|
|
2106
|
+
import "@univerjs/sheets-ui/facade";
|
|
2107
|
+
import { Fragment as Fragment5, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2108
|
+
var MODE_OPTIONS = [
|
|
2109
|
+
{
|
|
2110
|
+
value: "values",
|
|
2111
|
+
label: "Values only",
|
|
2112
|
+
hint: "Paste cell values, dropping formatting and formulas."
|
|
2113
|
+
},
|
|
2114
|
+
{
|
|
2115
|
+
value: "formats",
|
|
2116
|
+
label: "Formats only",
|
|
2117
|
+
hint: "Paste number formats, fonts, borders and fills \u2014 no values."
|
|
2118
|
+
},
|
|
2119
|
+
{
|
|
2120
|
+
value: "formulas",
|
|
2121
|
+
label: "Formulas only",
|
|
2122
|
+
hint: "Paste formulas, adjusting relative references."
|
|
2123
|
+
},
|
|
2124
|
+
{
|
|
2125
|
+
value: "transpose",
|
|
2126
|
+
label: "Transpose",
|
|
2127
|
+
hint: "Flip the current selection \u2014 rows become columns and columns become rows, in place."
|
|
2128
|
+
}
|
|
2129
|
+
];
|
|
2130
|
+
var PASTE_HOOK = {
|
|
2131
|
+
values: "special-paste-value",
|
|
2132
|
+
formats: "special-paste-format",
|
|
2133
|
+
formulas: "special-paste-formula"
|
|
2134
|
+
};
|
|
2135
|
+
var SHEET_PASTE_COMMAND = "sheet.command.paste";
|
|
2136
|
+
function activeRange5(api) {
|
|
2137
|
+
return api.univer.getActiveWorkbook()?.getActiveSheet()?.getActiveRange() ?? null;
|
|
2138
|
+
}
|
|
2139
|
+
function transposeSelection(api) {
|
|
2140
|
+
const sheet = api.univer.getActiveWorkbook()?.getActiveSheet();
|
|
2141
|
+
const range = sheet?.getActiveRange();
|
|
2142
|
+
if (!sheet || !range) return false;
|
|
2143
|
+
const rows = range.getHeight();
|
|
2144
|
+
const cols = range.getWidth();
|
|
2145
|
+
if (rows <= 0 || cols <= 0) return false;
|
|
2146
|
+
const values = range.getValues();
|
|
2147
|
+
const transposed = [];
|
|
2148
|
+
for (let c = 0; c < cols; c++) {
|
|
2149
|
+
const newRow = [];
|
|
2150
|
+
for (let r = 0; r < rows; r++) {
|
|
2151
|
+
newRow.push(values[r]?.[c] ?? null);
|
|
2152
|
+
}
|
|
2153
|
+
transposed.push(newRow);
|
|
2154
|
+
}
|
|
2155
|
+
const target = sheet.getRange(range.getRow(), range.getColumn(), cols, rows);
|
|
2156
|
+
target.setValues(transposed);
|
|
2157
|
+
return true;
|
|
2158
|
+
}
|
|
2159
|
+
async function applyPasteSpecial(api, mode) {
|
|
2160
|
+
if (activeRange5(api) === null) return false;
|
|
2161
|
+
if (mode === "transpose") {
|
|
2162
|
+
return transposeSelection(api);
|
|
2163
|
+
}
|
|
2164
|
+
return api.executeCommand(SHEET_PASTE_COMMAND, { value: PASTE_HOOK[mode] });
|
|
2165
|
+
}
|
|
2166
|
+
var MODE_RADIO_ROW_STYLE = {
|
|
2167
|
+
display: "flex",
|
|
2168
|
+
alignItems: "flex-start",
|
|
2169
|
+
gap: 8,
|
|
2170
|
+
padding: "8px 10px",
|
|
2171
|
+
border: "1px solid var(--cs-chrome-border, #cdd3db)",
|
|
2172
|
+
borderRadius: 6,
|
|
2173
|
+
marginBottom: 8,
|
|
2174
|
+
cursor: "pointer"
|
|
2175
|
+
};
|
|
2176
|
+
var MODE_RADIO_ROW_ACTIVE_STYLE = {
|
|
2177
|
+
...MODE_RADIO_ROW_STYLE,
|
|
2178
|
+
borderColor: "var(--cs-chrome-active-fg, #0e7490)",
|
|
2179
|
+
background: "var(--cs-chrome-active-bg, rgba(14, 116, 144, 0.06))"
|
|
2180
|
+
};
|
|
2181
|
+
var MODE_LABEL_STYLE = {
|
|
2182
|
+
fontSize: 13,
|
|
2183
|
+
fontWeight: 500,
|
|
2184
|
+
color: "var(--cs-chrome-fg, #201f1e)"
|
|
2185
|
+
};
|
|
2186
|
+
var MODE_HINT_STYLE = {
|
|
2187
|
+
fontSize: 12,
|
|
2188
|
+
color: "var(--cs-chrome-muted, #605e5c)",
|
|
2189
|
+
marginTop: 2,
|
|
2190
|
+
lineHeight: 1.35
|
|
2191
|
+
};
|
|
2192
|
+
var RANGE_NOTE_STYLE4 = {
|
|
2193
|
+
fontSize: 12,
|
|
2194
|
+
color: "var(--cs-chrome-muted, #605e5c)",
|
|
2195
|
+
marginBottom: 12
|
|
2196
|
+
};
|
|
2197
|
+
function PasteSpecialDialog({ api, onClose }) {
|
|
2198
|
+
const [mode, setMode] = useState8("values");
|
|
2199
|
+
const rangeLabel = useMemo5(() => {
|
|
2200
|
+
const fRange = activeRange5(api);
|
|
2201
|
+
return fRange?.getA1Notation?.() ?? null;
|
|
2202
|
+
}, [api]);
|
|
2203
|
+
const hasSelection = activeRange5(api) !== null;
|
|
2204
|
+
const apply = () => {
|
|
2205
|
+
void applyPasteSpecial(api, mode).then((ok) => {
|
|
2206
|
+
if (ok) onClose();
|
|
2207
|
+
});
|
|
2208
|
+
};
|
|
2209
|
+
return /* @__PURE__ */ jsxs9(
|
|
2210
|
+
Dialog,
|
|
2211
|
+
{
|
|
2212
|
+
title: "Paste special",
|
|
2213
|
+
onClose,
|
|
2214
|
+
width: 440,
|
|
2215
|
+
"data-testid": "cs-paste-special-dialog",
|
|
2216
|
+
footer: /* @__PURE__ */ jsxs9(Fragment5, { children: [
|
|
2217
|
+
/* @__PURE__ */ jsx10("button", { type: "button", style: DIALOG_BTN_SECONDARY_STYLE, onClick: onClose, children: "Cancel" }),
|
|
2218
|
+
/* @__PURE__ */ jsx10(
|
|
2219
|
+
"button",
|
|
2220
|
+
{
|
|
2221
|
+
type: "button",
|
|
2222
|
+
style: DIALOG_BTN_PRIMARY_STYLE,
|
|
2223
|
+
"data-testid": "cs-paste-special-apply",
|
|
2224
|
+
disabled: !hasSelection,
|
|
2225
|
+
onClick: apply,
|
|
2226
|
+
children: "Paste"
|
|
2227
|
+
}
|
|
2228
|
+
)
|
|
2229
|
+
] }),
|
|
2230
|
+
children: [
|
|
2231
|
+
hasSelection ? /* @__PURE__ */ jsxs9("div", { style: RANGE_NOTE_STYLE4, "data-testid": "cs-paste-special-range", children: [
|
|
2232
|
+
mode === "transpose" ? "Transposes" : "Pastes into",
|
|
2233
|
+
" ",
|
|
2234
|
+
/* @__PURE__ */ jsx10("strong", { children: rangeLabel ?? "the current selection" })
|
|
2235
|
+
] }) : /* @__PURE__ */ jsx10("div", { style: RANGE_NOTE_STYLE4, "data-testid": "cs-paste-special-no-selection", children: "Select the destination cell(s) first, then reopen this dialog." }),
|
|
2236
|
+
/* @__PURE__ */ jsxs9("div", { style: DIALOG_FIELD_STYLE, children: [
|
|
2237
|
+
/* @__PURE__ */ jsx10("span", { style: DIALOG_LABEL_STYLE, children: "Paste" }),
|
|
2238
|
+
/* @__PURE__ */ jsx10("div", { role: "radiogroup", "aria-label": "Paste special mode", children: MODE_OPTIONS.map((opt) => {
|
|
2239
|
+
const active = mode === opt.value;
|
|
2240
|
+
return /* @__PURE__ */ jsxs9(
|
|
2241
|
+
"label",
|
|
2242
|
+
{
|
|
2243
|
+
style: active ? MODE_RADIO_ROW_ACTIVE_STYLE : MODE_RADIO_ROW_STYLE,
|
|
2244
|
+
"data-testid": `cs-paste-special-mode-${opt.value}`,
|
|
2245
|
+
children: [
|
|
2246
|
+
/* @__PURE__ */ jsx10(
|
|
2247
|
+
"input",
|
|
2248
|
+
{
|
|
2249
|
+
type: "radio",
|
|
2250
|
+
name: "cs-paste-special-mode",
|
|
2251
|
+
value: opt.value,
|
|
2252
|
+
checked: active,
|
|
2253
|
+
onChange: () => setMode(opt.value),
|
|
2254
|
+
style: { marginTop: 2 }
|
|
2255
|
+
}
|
|
2256
|
+
),
|
|
2257
|
+
/* @__PURE__ */ jsxs9("span", { children: [
|
|
2258
|
+
/* @__PURE__ */ jsx10("span", { style: MODE_LABEL_STYLE, children: opt.label }),
|
|
2259
|
+
/* @__PURE__ */ jsx10("span", { style: MODE_HINT_STYLE, children: opt.hint })
|
|
2260
|
+
] })
|
|
2261
|
+
]
|
|
2262
|
+
},
|
|
2263
|
+
opt.value
|
|
2264
|
+
);
|
|
2265
|
+
}) })
|
|
2266
|
+
] })
|
|
2267
|
+
]
|
|
2268
|
+
}
|
|
2269
|
+
);
|
|
2270
|
+
}
|
|
2271
|
+
|
|
2272
|
+
// src/chrome/InsertFunctionDialog.tsx
|
|
2273
|
+
import { useMemo as useMemo6, useState as useState9 } from "react";
|
|
2274
|
+
import { Fragment as Fragment6, jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2275
|
+
var FUNCTIONS = [
|
|
2276
|
+
// Math
|
|
2277
|
+
{
|
|
2278
|
+
name: "SUM",
|
|
2279
|
+
category: "Math",
|
|
2280
|
+
description: "Sum of a set of numbers.",
|
|
2281
|
+
syntax: "SUM(value1, [value2, \u2026])"
|
|
2282
|
+
},
|
|
2283
|
+
{
|
|
2284
|
+
name: "PRODUCT",
|
|
2285
|
+
category: "Math",
|
|
2286
|
+
description: "Product of a set of numbers.",
|
|
2287
|
+
syntax: "PRODUCT(factor1, [factor2, \u2026])"
|
|
2288
|
+
},
|
|
2289
|
+
{
|
|
2290
|
+
name: "ROUND",
|
|
2291
|
+
category: "Math",
|
|
2292
|
+
description: "Round a number to a given number of places.",
|
|
2293
|
+
syntax: "ROUND(value, [places])"
|
|
2294
|
+
},
|
|
2295
|
+
{
|
|
2296
|
+
name: "ABS",
|
|
2297
|
+
category: "Math",
|
|
2298
|
+
description: "Absolute value of a number.",
|
|
2299
|
+
syntax: "ABS(value)"
|
|
2300
|
+
},
|
|
2301
|
+
{
|
|
2302
|
+
name: "MOD",
|
|
2303
|
+
category: "Math",
|
|
2304
|
+
description: "Remainder after division.",
|
|
2305
|
+
syntax: "MOD(dividend, divisor)"
|
|
2306
|
+
},
|
|
2307
|
+
{
|
|
2308
|
+
name: "POWER",
|
|
2309
|
+
category: "Math",
|
|
2310
|
+
description: "A number raised to a power.",
|
|
2311
|
+
syntax: "POWER(base, exponent)"
|
|
2312
|
+
},
|
|
2313
|
+
// Statistical
|
|
2314
|
+
{
|
|
2315
|
+
name: "AVERAGE",
|
|
2316
|
+
category: "Statistical",
|
|
2317
|
+
description: "Arithmetic mean of a set of numbers.",
|
|
2318
|
+
syntax: "AVERAGE(value1, [value2, \u2026])"
|
|
2319
|
+
},
|
|
2320
|
+
{
|
|
2321
|
+
name: "COUNT",
|
|
2322
|
+
category: "Statistical",
|
|
2323
|
+
description: "Count of numeric values in a range.",
|
|
2324
|
+
syntax: "COUNT(value1, [value2, \u2026])"
|
|
2325
|
+
},
|
|
2326
|
+
{
|
|
2327
|
+
name: "COUNTA",
|
|
2328
|
+
category: "Statistical",
|
|
2329
|
+
description: "Count of non-empty values in a range.",
|
|
2330
|
+
syntax: "COUNTA(value1, [value2, \u2026])"
|
|
2331
|
+
},
|
|
2332
|
+
{
|
|
2333
|
+
name: "MAX",
|
|
2334
|
+
category: "Statistical",
|
|
2335
|
+
description: "Largest value in a set.",
|
|
2336
|
+
syntax: "MAX(value1, [value2, \u2026])"
|
|
2337
|
+
},
|
|
2338
|
+
{
|
|
2339
|
+
name: "MIN",
|
|
2340
|
+
category: "Statistical",
|
|
2341
|
+
description: "Smallest value in a set.",
|
|
2342
|
+
syntax: "MIN(value1, [value2, \u2026])"
|
|
2343
|
+
},
|
|
2344
|
+
{
|
|
2345
|
+
name: "MEDIAN",
|
|
2346
|
+
category: "Statistical",
|
|
2347
|
+
description: "Median of a set of numbers.",
|
|
2348
|
+
syntax: "MEDIAN(value1, [value2, \u2026])"
|
|
2349
|
+
},
|
|
2350
|
+
// Logical
|
|
2351
|
+
{
|
|
2352
|
+
name: "IF",
|
|
2353
|
+
category: "Logical",
|
|
2354
|
+
description: "Return one value if a condition is true, another if false.",
|
|
2355
|
+
syntax: "IF(condition, value_if_true, value_if_false)"
|
|
2356
|
+
},
|
|
2357
|
+
{
|
|
2358
|
+
name: "IFS",
|
|
2359
|
+
category: "Logical",
|
|
2360
|
+
description: "Test multiple conditions, return the first match.",
|
|
2361
|
+
syntax: "IFS(condition1, value1, [condition2, value2, \u2026])"
|
|
2362
|
+
},
|
|
2363
|
+
{
|
|
2364
|
+
name: "AND",
|
|
2365
|
+
category: "Logical",
|
|
2366
|
+
description: "True when all arguments are true.",
|
|
2367
|
+
syntax: "AND(logical1, [logical2, \u2026])"
|
|
2368
|
+
},
|
|
2369
|
+
{
|
|
2370
|
+
name: "OR",
|
|
2371
|
+
category: "Logical",
|
|
2372
|
+
description: "True when any argument is true.",
|
|
2373
|
+
syntax: "OR(logical1, [logical2, \u2026])"
|
|
2374
|
+
},
|
|
2375
|
+
{
|
|
2376
|
+
name: "IFERROR",
|
|
2377
|
+
category: "Logical",
|
|
2378
|
+
description: "Return a fallback when a formula errors.",
|
|
2379
|
+
syntax: "IFERROR(value, value_if_error)"
|
|
2380
|
+
},
|
|
2381
|
+
// Lookup
|
|
2382
|
+
{
|
|
2383
|
+
name: "VLOOKUP",
|
|
2384
|
+
category: "Lookup",
|
|
2385
|
+
description: "Search a column for a key, return a value from the same row.",
|
|
2386
|
+
syntax: "VLOOKUP(search_key, range, index, [is_sorted])"
|
|
2387
|
+
},
|
|
2388
|
+
{
|
|
2389
|
+
name: "HLOOKUP",
|
|
2390
|
+
category: "Lookup",
|
|
2391
|
+
description: "Search a row for a key, return a value from the same column.",
|
|
2392
|
+
syntax: "HLOOKUP(search_key, range, index, [is_sorted])"
|
|
2393
|
+
},
|
|
2394
|
+
{
|
|
2395
|
+
name: "INDEX",
|
|
2396
|
+
category: "Lookup",
|
|
2397
|
+
description: "Value at a given row/column offset in a range.",
|
|
2398
|
+
syntax: "INDEX(reference, [row], [column])"
|
|
2399
|
+
},
|
|
2400
|
+
{
|
|
2401
|
+
name: "MATCH",
|
|
2402
|
+
category: "Lookup",
|
|
2403
|
+
description: "Position of a value within a range.",
|
|
2404
|
+
syntax: "MATCH(search_key, range, [search_type])"
|
|
2405
|
+
},
|
|
2406
|
+
{
|
|
2407
|
+
name: "XLOOKUP",
|
|
2408
|
+
category: "Lookup",
|
|
2409
|
+
description: "Search a range and return a corresponding value.",
|
|
2410
|
+
syntax: "XLOOKUP(search_key, lookup_range, result_range)"
|
|
2411
|
+
},
|
|
2412
|
+
// Text
|
|
2413
|
+
{
|
|
2414
|
+
name: "CONCATENATE",
|
|
2415
|
+
category: "Text",
|
|
2416
|
+
description: "Join strings end to end.",
|
|
2417
|
+
syntax: "CONCATENATE(string1, [string2, \u2026])"
|
|
2418
|
+
},
|
|
2419
|
+
{
|
|
2420
|
+
name: "LEFT",
|
|
2421
|
+
category: "Text",
|
|
2422
|
+
description: "Leftmost characters of a string.",
|
|
2423
|
+
syntax: "LEFT(string, [num_chars])"
|
|
2424
|
+
},
|
|
2425
|
+
{
|
|
2426
|
+
name: "RIGHT",
|
|
2427
|
+
category: "Text",
|
|
2428
|
+
description: "Rightmost characters of a string.",
|
|
2429
|
+
syntax: "RIGHT(string, [num_chars])"
|
|
2430
|
+
},
|
|
2431
|
+
{
|
|
2432
|
+
name: "MID",
|
|
2433
|
+
category: "Text",
|
|
2434
|
+
description: "Characters from the middle of a string.",
|
|
2435
|
+
syntax: "MID(string, start, length)"
|
|
2436
|
+
},
|
|
2437
|
+
{ name: "LEN", category: "Text", description: "Length of a string.", syntax: "LEN(string)" },
|
|
2438
|
+
{
|
|
2439
|
+
name: "TRIM",
|
|
2440
|
+
category: "Text",
|
|
2441
|
+
description: "Remove leading/trailing/duplicate spaces.",
|
|
2442
|
+
syntax: "TRIM(string)"
|
|
2443
|
+
},
|
|
2444
|
+
// Date
|
|
2445
|
+
{ name: "TODAY", category: "Date", description: "Today's date.", syntax: "TODAY()" },
|
|
2446
|
+
{ name: "NOW", category: "Date", description: "Current date and time.", syntax: "NOW()" },
|
|
2447
|
+
{
|
|
2448
|
+
name: "DATE",
|
|
2449
|
+
category: "Date",
|
|
2450
|
+
description: "Build a date from year, month, day.",
|
|
2451
|
+
syntax: "DATE(year, month, day)"
|
|
2452
|
+
},
|
|
2453
|
+
{
|
|
2454
|
+
name: "DATEDIF",
|
|
2455
|
+
category: "Date",
|
|
2456
|
+
description: "Difference between two dates in a chosen unit.",
|
|
2457
|
+
syntax: "DATEDIF(start_date, end_date, unit)"
|
|
2458
|
+
}
|
|
2459
|
+
];
|
|
2460
|
+
var CATEGORY_ORDER = ["Math", "Statistical", "Logical", "Lookup", "Text", "Date"];
|
|
2461
|
+
function activeRange6(api) {
|
|
2462
|
+
return api.univer.getActiveWorkbook()?.getActiveSheet()?.getActiveRange() ?? null;
|
|
2463
|
+
}
|
|
2464
|
+
function insertFunction(api, fn) {
|
|
2465
|
+
const range = activeRange6(api);
|
|
2466
|
+
if (!range) return false;
|
|
2467
|
+
const zeroArg = fn.syntax.endsWith("()");
|
|
2468
|
+
const formula = zeroArg ? `=${fn.name}()` : `=${fn.name}(`;
|
|
2469
|
+
range.setValue(formula);
|
|
2470
|
+
api.focus();
|
|
2471
|
+
return true;
|
|
2472
|
+
}
|
|
2473
|
+
var SEARCH_STYLE = {
|
|
2474
|
+
...DIALOG_INPUT_STYLE,
|
|
2475
|
+
width: "100%",
|
|
2476
|
+
marginBottom: 12
|
|
2477
|
+
};
|
|
2478
|
+
var RANGE_NOTE_STYLE5 = {
|
|
2479
|
+
fontSize: 12,
|
|
2480
|
+
color: "var(--cs-chrome-muted, #605e5c)",
|
|
2481
|
+
marginBottom: 12
|
|
2482
|
+
};
|
|
2483
|
+
var LIST_STYLE = {
|
|
2484
|
+
maxHeight: 280,
|
|
2485
|
+
overflow: "auto",
|
|
2486
|
+
border: "1px solid var(--cs-chrome-border, #edeff3)",
|
|
2487
|
+
borderRadius: 8
|
|
2488
|
+
};
|
|
2489
|
+
var GROUP_HEADER_STYLE = {
|
|
2490
|
+
position: "sticky",
|
|
2491
|
+
top: 0,
|
|
2492
|
+
padding: "6px 12px",
|
|
2493
|
+
fontSize: 11,
|
|
2494
|
+
fontWeight: 600,
|
|
2495
|
+
letterSpacing: 0.4,
|
|
2496
|
+
textTransform: "uppercase",
|
|
2497
|
+
color: "var(--cs-chrome-muted, #605e5c)",
|
|
2498
|
+
background: "var(--cs-chrome-input-bg, #fff)",
|
|
2499
|
+
borderBottom: "1px solid var(--cs-chrome-border, #edeff3)"
|
|
2500
|
+
};
|
|
2501
|
+
function itemStyle(selected) {
|
|
2502
|
+
return {
|
|
2503
|
+
display: "block",
|
|
2504
|
+
width: "100%",
|
|
2505
|
+
textAlign: "left",
|
|
2506
|
+
padding: "8px 12px",
|
|
2507
|
+
border: "none",
|
|
2508
|
+
borderBottom: "1px solid var(--cs-chrome-border, #f3f4f6)",
|
|
2509
|
+
background: selected ? "var(--cs-chrome-hover-bg, #eef6f8)" : "transparent",
|
|
2510
|
+
color: "var(--cs-chrome-fg, #201f1e)",
|
|
2511
|
+
font: "inherit",
|
|
2512
|
+
fontSize: 13,
|
|
2513
|
+
cursor: "pointer"
|
|
2514
|
+
};
|
|
2515
|
+
}
|
|
2516
|
+
var ITEM_NAME_STYLE = {
|
|
2517
|
+
fontWeight: 600,
|
|
2518
|
+
fontFamily: "var(--cs-chrome-mono, ui-monospace, SFMono-Regular, Menlo, monospace)"
|
|
2519
|
+
};
|
|
2520
|
+
var ITEM_DESC_STYLE = {
|
|
2521
|
+
fontSize: 12,
|
|
2522
|
+
color: "var(--cs-chrome-muted, #605e5c)",
|
|
2523
|
+
marginTop: 2
|
|
2524
|
+
};
|
|
2525
|
+
var PREVIEW_STYLE = {
|
|
2526
|
+
marginTop: 12,
|
|
2527
|
+
padding: "10px 12px",
|
|
2528
|
+
borderRadius: 8,
|
|
2529
|
+
background: "var(--cs-chrome-subtle-bg, #f6f8fa)",
|
|
2530
|
+
fontSize: 12,
|
|
2531
|
+
color: "var(--cs-chrome-fg, #201f1e)"
|
|
2532
|
+
};
|
|
2533
|
+
var PREVIEW_SYNTAX_STYLE = {
|
|
2534
|
+
fontFamily: "var(--cs-chrome-mono, ui-monospace, SFMono-Regular, Menlo, monospace)",
|
|
2535
|
+
fontWeight: 600,
|
|
2536
|
+
marginBottom: 4
|
|
2537
|
+
};
|
|
2538
|
+
function InsertFunctionDialog({ api, onClose }) {
|
|
2539
|
+
const [query, setQuery] = useState9("");
|
|
2540
|
+
const [selectedName, setSelectedName] = useState9(null);
|
|
2541
|
+
const rangeLabel = useMemo6(() => {
|
|
2542
|
+
const fRange = activeRange6(api);
|
|
2543
|
+
return fRange?.getA1Notation?.() ?? null;
|
|
2544
|
+
}, [api]);
|
|
2545
|
+
const hasSelection = activeRange6(api) !== null;
|
|
2546
|
+
const filtered = useMemo6(() => {
|
|
2547
|
+
const q = query.trim().toLowerCase();
|
|
2548
|
+
if (!q) return FUNCTIONS;
|
|
2549
|
+
return FUNCTIONS.filter(
|
|
2550
|
+
(fn) => fn.name.toLowerCase().includes(q) || fn.description.toLowerCase().includes(q) || fn.category.toLowerCase().includes(q)
|
|
2551
|
+
);
|
|
2552
|
+
}, [query]);
|
|
2553
|
+
const grouped = useMemo6(() => {
|
|
2554
|
+
const byCat = /* @__PURE__ */ new Map();
|
|
2555
|
+
for (const fn of filtered) {
|
|
2556
|
+
const list = byCat.get(fn.category);
|
|
2557
|
+
if (list) list.push(fn);
|
|
2558
|
+
else byCat.set(fn.category, [fn]);
|
|
2559
|
+
}
|
|
2560
|
+
return CATEGORY_ORDER.filter((c) => byCat.has(c)).map((c) => ({
|
|
2561
|
+
category: c,
|
|
2562
|
+
items: byCat.get(c)
|
|
2563
|
+
}));
|
|
2564
|
+
}, [filtered]);
|
|
2565
|
+
const selected = useMemo6(
|
|
2566
|
+
() => FUNCTIONS.find((fn) => fn.name === selectedName) ?? null,
|
|
2567
|
+
[selectedName]
|
|
2568
|
+
);
|
|
2569
|
+
const choose = (fn) => {
|
|
2570
|
+
if (insertFunction(api, fn)) onClose();
|
|
2571
|
+
};
|
|
2572
|
+
const insertSelected = () => {
|
|
2573
|
+
if (selected && insertFunction(api, selected)) onClose();
|
|
2574
|
+
};
|
|
2575
|
+
return /* @__PURE__ */ jsxs10(
|
|
2576
|
+
Dialog,
|
|
2577
|
+
{
|
|
2578
|
+
title: "Insert function",
|
|
2579
|
+
onClose,
|
|
2580
|
+
width: 460,
|
|
2581
|
+
"data-testid": "cs-insert-function-dialog",
|
|
2582
|
+
footer: /* @__PURE__ */ jsxs10(Fragment6, { children: [
|
|
2583
|
+
/* @__PURE__ */ jsx11("button", { type: "button", style: DIALOG_BTN_SECONDARY_STYLE, onClick: onClose, children: "Cancel" }),
|
|
2584
|
+
/* @__PURE__ */ jsx11(
|
|
2585
|
+
"button",
|
|
2586
|
+
{
|
|
2587
|
+
type: "button",
|
|
2588
|
+
style: DIALOG_BTN_PRIMARY_STYLE,
|
|
2589
|
+
"data-testid": "cs-insert-function-insert",
|
|
2590
|
+
disabled: !hasSelection || !selected,
|
|
2591
|
+
onClick: insertSelected,
|
|
2592
|
+
children: "Insert"
|
|
2593
|
+
}
|
|
2594
|
+
)
|
|
2595
|
+
] }),
|
|
2596
|
+
children: [
|
|
2597
|
+
hasSelection ? /* @__PURE__ */ jsxs10("div", { style: RANGE_NOTE_STYLE5, "data-testid": "cs-insert-function-range", children: [
|
|
2598
|
+
"Inserts into ",
|
|
2599
|
+
/* @__PURE__ */ jsx11("strong", { children: rangeLabel ?? "the active cell" })
|
|
2600
|
+
] }) : /* @__PURE__ */ jsx11("div", { style: RANGE_NOTE_STYLE5, "data-testid": "cs-insert-function-no-selection", children: "Select a cell first, then reopen this dialog." }),
|
|
2601
|
+
/* @__PURE__ */ jsx11(
|
|
2602
|
+
"input",
|
|
2603
|
+
{
|
|
2604
|
+
style: SEARCH_STYLE,
|
|
2605
|
+
"data-testid": "cs-insert-function-search",
|
|
2606
|
+
type: "search",
|
|
2607
|
+
placeholder: "Search functions (name, category, or description)",
|
|
2608
|
+
value: query,
|
|
2609
|
+
autoFocus: true,
|
|
2610
|
+
onChange: (e) => setQuery(e.target.value)
|
|
2611
|
+
}
|
|
2612
|
+
),
|
|
2613
|
+
/* @__PURE__ */ jsxs10(
|
|
2614
|
+
"div",
|
|
2615
|
+
{
|
|
2616
|
+
style: LIST_STYLE,
|
|
2617
|
+
role: "listbox",
|
|
2618
|
+
"aria-label": "Functions",
|
|
2619
|
+
"data-testid": "cs-insert-function-list",
|
|
2620
|
+
children: [
|
|
2621
|
+
grouped.length === 0 && /* @__PURE__ */ jsxs10("div", { style: { padding: "12px", fontSize: 13, color: "var(--cs-chrome-muted, #605e5c)" }, children: [
|
|
2622
|
+
"No functions match \u201C",
|
|
2623
|
+
query,
|
|
2624
|
+
"\u201D."
|
|
2625
|
+
] }),
|
|
2626
|
+
grouped.map((group) => /* @__PURE__ */ jsxs10("div", { children: [
|
|
2627
|
+
/* @__PURE__ */ jsx11("div", { style: GROUP_HEADER_STYLE, children: group.category }),
|
|
2628
|
+
group.items.map((fn) => /* @__PURE__ */ jsxs10(
|
|
2629
|
+
"button",
|
|
2630
|
+
{
|
|
2631
|
+
type: "button",
|
|
2632
|
+
role: "option",
|
|
2633
|
+
"aria-selected": selectedName === fn.name,
|
|
2634
|
+
"data-testid": `cs-insert-function-item-${fn.name}`,
|
|
2635
|
+
style: itemStyle(selectedName === fn.name),
|
|
2636
|
+
onClick: () => setSelectedName(fn.name),
|
|
2637
|
+
onDoubleClick: () => choose(fn),
|
|
2638
|
+
children: [
|
|
2639
|
+
/* @__PURE__ */ jsx11("div", { style: ITEM_NAME_STYLE, children: fn.name }),
|
|
2640
|
+
/* @__PURE__ */ jsx11("div", { style: ITEM_DESC_STYLE, children: fn.description })
|
|
2641
|
+
]
|
|
2642
|
+
},
|
|
2643
|
+
fn.name
|
|
2644
|
+
))
|
|
2645
|
+
] }, group.category))
|
|
2646
|
+
]
|
|
2647
|
+
}
|
|
2648
|
+
),
|
|
2649
|
+
selected && /* @__PURE__ */ jsxs10("div", { style: PREVIEW_STYLE, "data-testid": "cs-insert-function-preview", children: [
|
|
2650
|
+
/* @__PURE__ */ jsx11("div", { style: PREVIEW_SYNTAX_STYLE, children: selected.syntax }),
|
|
2651
|
+
/* @__PURE__ */ jsx11("div", { children: selected.description })
|
|
2652
|
+
] })
|
|
2653
|
+
]
|
|
2654
|
+
}
|
|
2655
|
+
);
|
|
2656
|
+
}
|
|
2657
|
+
|
|
2658
|
+
// src/chrome/NameManagerDialog.tsx
|
|
2659
|
+
import { useCallback, useMemo as useMemo7, useState as useState10 } from "react";
|
|
2660
|
+
import { Fragment as Fragment7, jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2661
|
+
var NAME_RE = /^[A-Za-z_][A-Za-z0-9_.]*$/;
|
|
2662
|
+
function workbook(api) {
|
|
2663
|
+
return api.univer.getActiveWorkbook() ?? null;
|
|
2664
|
+
}
|
|
2665
|
+
function readRows(api) {
|
|
2666
|
+
const wb = workbook(api);
|
|
2667
|
+
if (!wb) return [];
|
|
2668
|
+
return wb.getDefinedNames().map((dn) => ({
|
|
2669
|
+
name: dn.getName(),
|
|
2670
|
+
refersTo: dn.getFormulaOrRefString()
|
|
2671
|
+
}));
|
|
2672
|
+
}
|
|
2673
|
+
function selectionRef(api) {
|
|
2674
|
+
const range = workbook(api)?.getActiveSheet()?.getActiveRange();
|
|
2675
|
+
return range?.getA1Notation(true) ?? "";
|
|
2676
|
+
}
|
|
2677
|
+
var ROW_STYLE4 = {
|
|
2678
|
+
display: "grid",
|
|
2679
|
+
gridTemplateColumns: "1fr 1.3fr auto",
|
|
2680
|
+
alignItems: "center",
|
|
2681
|
+
gap: 8,
|
|
2682
|
+
padding: "6px 8px",
|
|
2683
|
+
borderBottom: "1px solid var(--cs-chrome-border, #edeff3)"
|
|
2684
|
+
};
|
|
2685
|
+
var LIST_STYLE2 = {
|
|
2686
|
+
border: "1px solid var(--cs-chrome-border, #cdd3db)",
|
|
2687
|
+
borderRadius: 6,
|
|
2688
|
+
maxHeight: 200,
|
|
2689
|
+
overflow: "auto",
|
|
2690
|
+
marginBottom: 16
|
|
2691
|
+
};
|
|
2692
|
+
var EMPTY_STYLE = {
|
|
2693
|
+
padding: "16px 8px",
|
|
2694
|
+
fontSize: 13,
|
|
2695
|
+
color: "var(--cs-chrome-muted, #605e5c)",
|
|
2696
|
+
textAlign: "center"
|
|
2697
|
+
};
|
|
2698
|
+
var REF_CELL_STYLE = {
|
|
2699
|
+
fontSize: 12,
|
|
2700
|
+
color: "var(--cs-chrome-muted, #605e5c)",
|
|
2701
|
+
overflow: "hidden",
|
|
2702
|
+
textOverflow: "ellipsis",
|
|
2703
|
+
whiteSpace: "nowrap"
|
|
2704
|
+
};
|
|
2705
|
+
var ROW_ACTIONS_STYLE = {
|
|
2706
|
+
display: "flex",
|
|
2707
|
+
gap: 4
|
|
2708
|
+
};
|
|
2709
|
+
var SMALL_BTN_STYLE = {
|
|
2710
|
+
...DIALOG_BTN_SECONDARY_STYLE,
|
|
2711
|
+
height: 24,
|
|
2712
|
+
padding: "0 8px",
|
|
2713
|
+
fontSize: 12
|
|
2714
|
+
};
|
|
2715
|
+
var SECTION_TITLE_STYLE = {
|
|
2716
|
+
fontSize: 13,
|
|
2717
|
+
fontWeight: 600,
|
|
2718
|
+
color: "var(--cs-chrome-fg, #201f1e)",
|
|
2719
|
+
margin: "0 0 8px"
|
|
2720
|
+
};
|
|
2721
|
+
var ERROR_STYLE = {
|
|
2722
|
+
fontSize: 12,
|
|
2723
|
+
color: "var(--cs-chrome-danger, #b91c1c)",
|
|
2724
|
+
marginBottom: 8
|
|
2725
|
+
};
|
|
2726
|
+
var TWO_COL_STYLE3 = {
|
|
2727
|
+
display: "grid",
|
|
2728
|
+
gridTemplateColumns: "1fr 1.3fr",
|
|
2729
|
+
gap: 8
|
|
2730
|
+
};
|
|
2731
|
+
function NameManagerDialog({ api, onClose }) {
|
|
2732
|
+
const [rows, setRows] = useState10(() => readRows(api));
|
|
2733
|
+
const [editingOriginal, setEditingOriginal] = useState10(null);
|
|
2734
|
+
const [nameInput, setNameInput] = useState10("");
|
|
2735
|
+
const [refInput, setRefInput] = useState10("");
|
|
2736
|
+
const [error, setError] = useState10(null);
|
|
2737
|
+
const initialSelRef = useMemo7(() => selectionRef(api), [api]);
|
|
2738
|
+
const refresh = useCallback(() => setRows(readRows(api)), [api]);
|
|
2739
|
+
const resetForm = useCallback(() => {
|
|
2740
|
+
setEditingOriginal(null);
|
|
2741
|
+
setNameInput("");
|
|
2742
|
+
setRefInput("");
|
|
2743
|
+
setError(null);
|
|
2744
|
+
}, []);
|
|
2745
|
+
const startEdit = (row) => {
|
|
2746
|
+
setEditingOriginal(row.name);
|
|
2747
|
+
setNameInput(row.name);
|
|
2748
|
+
setRefInput(row.refersTo);
|
|
2749
|
+
setError(null);
|
|
2750
|
+
};
|
|
2751
|
+
const useSelection = () => {
|
|
2752
|
+
const ref = selectionRef(api);
|
|
2753
|
+
if (ref) setRefInput(ref);
|
|
2754
|
+
};
|
|
2755
|
+
const remove = (row) => {
|
|
2756
|
+
const wb = workbook(api);
|
|
2757
|
+
if (!wb) return;
|
|
2758
|
+
wb.deleteDefinedName(row.name);
|
|
2759
|
+
if (editingOriginal === row.name) resetForm();
|
|
2760
|
+
refresh();
|
|
2761
|
+
};
|
|
2762
|
+
const submit = () => {
|
|
2763
|
+
const wb = workbook(api);
|
|
2764
|
+
if (!wb) {
|
|
2765
|
+
setError("No active workbook.");
|
|
2766
|
+
return;
|
|
2767
|
+
}
|
|
2768
|
+
const name = nameInput.trim();
|
|
2769
|
+
const ref = refInput.trim();
|
|
2770
|
+
if (!name) {
|
|
2771
|
+
setError("Enter a name.");
|
|
2772
|
+
return;
|
|
2773
|
+
}
|
|
2774
|
+
if (!NAME_RE.test(name)) {
|
|
2775
|
+
setError("Names must start with a letter or underscore and contain no spaces.");
|
|
2776
|
+
return;
|
|
2777
|
+
}
|
|
2778
|
+
if (!ref) {
|
|
2779
|
+
setError("Enter a range or formula (e.g. Sheet1!A1:B2).");
|
|
2780
|
+
return;
|
|
2781
|
+
}
|
|
2782
|
+
const collides = rows.some(
|
|
2783
|
+
(r) => r.name.toLowerCase() === name.toLowerCase() && r.name !== editingOriginal
|
|
2784
|
+
);
|
|
2785
|
+
if (collides) {
|
|
2786
|
+
setError(`A defined name "${name}" already exists.`);
|
|
2787
|
+
return;
|
|
2788
|
+
}
|
|
2789
|
+
if (editingOriginal === null) {
|
|
2790
|
+
wb.insertDefinedName(name, ref);
|
|
2791
|
+
} else {
|
|
2792
|
+
const existing = wb.getDefinedName(editingOriginal);
|
|
2793
|
+
if (existing) {
|
|
2794
|
+
const param = existing.toBuilder().setName(name).setRef(ref).build();
|
|
2795
|
+
wb.updateDefinedNameBuilder(param);
|
|
2796
|
+
} else {
|
|
2797
|
+
wb.insertDefinedName(name, ref);
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2800
|
+
refresh();
|
|
2801
|
+
resetForm();
|
|
2802
|
+
};
|
|
2803
|
+
const isEditing = editingOriginal !== null;
|
|
2804
|
+
return /* @__PURE__ */ jsxs11(
|
|
2805
|
+
Dialog,
|
|
2806
|
+
{
|
|
2807
|
+
title: "Name manager",
|
|
2808
|
+
onClose,
|
|
2809
|
+
width: 480,
|
|
2810
|
+
"data-testid": "cs-name-manager-dialog",
|
|
2811
|
+
footer: /* @__PURE__ */ jsxs11(Fragment7, { children: [
|
|
2812
|
+
/* @__PURE__ */ jsx12("span", { style: { flex: 1 } }),
|
|
2813
|
+
/* @__PURE__ */ jsx12("button", { type: "button", style: DIALOG_BTN_SECONDARY_STYLE, onClick: onClose, children: "Close" })
|
|
2814
|
+
] }),
|
|
2815
|
+
children: [
|
|
2816
|
+
/* @__PURE__ */ jsx12("h3", { style: SECTION_TITLE_STYLE, children: "Defined names" }),
|
|
2817
|
+
/* @__PURE__ */ jsx12("div", { style: LIST_STYLE2, "data-testid": "cs-name-manager-list", children: rows.length === 0 ? /* @__PURE__ */ jsx12("div", { style: EMPTY_STYLE, "data-testid": "cs-name-manager-empty", children: "No defined names yet. Add one below." }) : rows.map((row) => /* @__PURE__ */ jsxs11("div", { style: ROW_STYLE4, "data-testid": "cs-name-manager-row", children: [
|
|
2818
|
+
/* @__PURE__ */ jsx12("span", { style: { fontWeight: 500 }, children: row.name }),
|
|
2819
|
+
/* @__PURE__ */ jsx12("span", { style: REF_CELL_STYLE, title: row.refersTo, children: row.refersTo }),
|
|
2820
|
+
/* @__PURE__ */ jsxs11("span", { style: ROW_ACTIONS_STYLE, children: [
|
|
2821
|
+
/* @__PURE__ */ jsx12(
|
|
2822
|
+
"button",
|
|
2823
|
+
{
|
|
2824
|
+
type: "button",
|
|
2825
|
+
style: SMALL_BTN_STYLE,
|
|
2826
|
+
"data-testid": "cs-name-manager-edit",
|
|
2827
|
+
onClick: () => startEdit(row),
|
|
2828
|
+
children: "Edit"
|
|
2829
|
+
}
|
|
2830
|
+
),
|
|
2831
|
+
/* @__PURE__ */ jsx12(
|
|
2832
|
+
"button",
|
|
2833
|
+
{
|
|
2834
|
+
type: "button",
|
|
2835
|
+
style: SMALL_BTN_STYLE,
|
|
2836
|
+
"data-testid": "cs-name-manager-delete",
|
|
2837
|
+
onClick: () => remove(row),
|
|
2838
|
+
children: "Delete"
|
|
2839
|
+
}
|
|
2840
|
+
)
|
|
2841
|
+
] })
|
|
2842
|
+
] }, row.name)) }),
|
|
2843
|
+
/* @__PURE__ */ jsx12("h3", { style: SECTION_TITLE_STYLE, children: isEditing ? "Edit name" : "Add a name" }),
|
|
2844
|
+
error && /* @__PURE__ */ jsx12("div", { style: ERROR_STYLE, "data-testid": "cs-name-manager-error", children: error }),
|
|
2845
|
+
/* @__PURE__ */ jsxs11("div", { style: TWO_COL_STYLE3, children: [
|
|
2846
|
+
/* @__PURE__ */ jsxs11("label", { style: DIALOG_FIELD_STYLE, children: [
|
|
2847
|
+
/* @__PURE__ */ jsx12("span", { style: DIALOG_LABEL_STYLE, children: "Name" }),
|
|
2848
|
+
/* @__PURE__ */ jsx12(
|
|
2849
|
+
"input",
|
|
2850
|
+
{
|
|
2851
|
+
style: DIALOG_INPUT_STYLE,
|
|
2852
|
+
"data-testid": "cs-name-manager-name",
|
|
2853
|
+
value: nameInput,
|
|
2854
|
+
placeholder: "MyRange",
|
|
2855
|
+
onChange: (e) => setNameInput(e.target.value)
|
|
2856
|
+
}
|
|
2857
|
+
)
|
|
2858
|
+
] }),
|
|
2859
|
+
/* @__PURE__ */ jsxs11("label", { style: DIALOG_FIELD_STYLE, children: [
|
|
2860
|
+
/* @__PURE__ */ jsx12("span", { style: DIALOG_LABEL_STYLE, children: "Refers to" }),
|
|
2861
|
+
/* @__PURE__ */ jsx12(
|
|
2862
|
+
"input",
|
|
2863
|
+
{
|
|
2864
|
+
style: DIALOG_INPUT_STYLE,
|
|
2865
|
+
"data-testid": "cs-name-manager-ref",
|
|
2866
|
+
value: refInput,
|
|
2867
|
+
placeholder: initialSelRef || "Sheet1!A1:B2",
|
|
2868
|
+
onChange: (e) => setRefInput(e.target.value)
|
|
2869
|
+
}
|
|
2870
|
+
)
|
|
2871
|
+
] })
|
|
2872
|
+
] }),
|
|
2873
|
+
/* @__PURE__ */ jsxs11("div", { style: { display: "flex", gap: 8, marginTop: 4 }, children: [
|
|
2874
|
+
/* @__PURE__ */ jsx12(
|
|
2875
|
+
"button",
|
|
2876
|
+
{
|
|
2877
|
+
type: "button",
|
|
2878
|
+
style: DIALOG_BTN_SECONDARY_STYLE,
|
|
2879
|
+
"data-testid": "cs-name-manager-use-selection",
|
|
2880
|
+
onClick: useSelection,
|
|
2881
|
+
children: "Use selection"
|
|
2882
|
+
}
|
|
2883
|
+
),
|
|
2884
|
+
/* @__PURE__ */ jsx12("span", { style: { flex: 1 } }),
|
|
2885
|
+
isEditing && /* @__PURE__ */ jsx12(
|
|
2886
|
+
"button",
|
|
2887
|
+
{
|
|
2888
|
+
type: "button",
|
|
2889
|
+
style: DIALOG_BTN_SECONDARY_STYLE,
|
|
2890
|
+
"data-testid": "cs-name-manager-cancel-edit",
|
|
2891
|
+
onClick: resetForm,
|
|
2892
|
+
children: "Cancel"
|
|
2893
|
+
}
|
|
2894
|
+
),
|
|
2895
|
+
/* @__PURE__ */ jsx12(
|
|
2896
|
+
"button",
|
|
2897
|
+
{
|
|
2898
|
+
type: "button",
|
|
2899
|
+
style: DIALOG_BTN_PRIMARY_STYLE,
|
|
2900
|
+
"data-testid": "cs-name-manager-submit",
|
|
2901
|
+
onClick: submit,
|
|
2902
|
+
children: isEditing ? "Update" : "Add"
|
|
2903
|
+
}
|
|
2904
|
+
)
|
|
2905
|
+
] })
|
|
2906
|
+
]
|
|
2907
|
+
}
|
|
2908
|
+
);
|
|
2909
|
+
}
|
|
2910
|
+
|
|
2911
|
+
// src/chrome/InsertCellsDialog.tsx
|
|
2912
|
+
import { useMemo as useMemo8, useState as useState11 } from "react";
|
|
2913
|
+
import { Dimension } from "@univerjs/core";
|
|
2914
|
+
import { Fragment as Fragment8, jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2915
|
+
var SHIFT_OPTIONS = [
|
|
2916
|
+
{ value: "right", label: "Shift cells right" },
|
|
2917
|
+
{ value: "down", label: "Shift cells down" },
|
|
2918
|
+
{ value: "row", label: "Entire row" },
|
|
2919
|
+
{ value: "column", label: "Entire column" }
|
|
2920
|
+
];
|
|
2921
|
+
function activeRange7(api) {
|
|
2922
|
+
return api.univer.getActiveWorkbook()?.getActiveSheet()?.getActiveRange() ?? null;
|
|
2923
|
+
}
|
|
2924
|
+
function applyInsert(api, shift) {
|
|
2925
|
+
const range = activeRange7(api);
|
|
2926
|
+
if (!range) return false;
|
|
2927
|
+
switch (shift) {
|
|
2928
|
+
case "right":
|
|
2929
|
+
range.insertCells(Dimension.COLUMNS);
|
|
2930
|
+
return true;
|
|
2931
|
+
case "down":
|
|
2932
|
+
range.insertCells(Dimension.ROWS);
|
|
2933
|
+
return true;
|
|
2934
|
+
case "row": {
|
|
2935
|
+
const sheet = api.univer.getActiveWorkbook()?.getActiveSheet();
|
|
2936
|
+
if (!sheet) return false;
|
|
2937
|
+
const startRow = range.getRow();
|
|
2938
|
+
const rowCount = range.getLastRow() - startRow + 1;
|
|
2939
|
+
sheet.insertRows(startRow, rowCount);
|
|
2940
|
+
return true;
|
|
2941
|
+
}
|
|
2942
|
+
case "column": {
|
|
2943
|
+
const sheet = api.univer.getActiveWorkbook()?.getActiveSheet();
|
|
2944
|
+
if (!sheet) return false;
|
|
2945
|
+
const startColumn = range.getColumn();
|
|
2946
|
+
const colCount = range.getLastColumn() - startColumn + 1;
|
|
2947
|
+
sheet.insertColumns(startColumn, colCount);
|
|
2948
|
+
return true;
|
|
2949
|
+
}
|
|
2950
|
+
}
|
|
2951
|
+
}
|
|
2952
|
+
var RANGE_NOTE_STYLE6 = {
|
|
2953
|
+
fontSize: 12,
|
|
2954
|
+
color: "var(--cs-chrome-muted, #605e5c)",
|
|
2955
|
+
marginBottom: 14
|
|
2956
|
+
};
|
|
2957
|
+
var RADIO_STYLE = {
|
|
2958
|
+
display: "flex",
|
|
2959
|
+
alignItems: "center",
|
|
2960
|
+
gap: 8,
|
|
2961
|
+
marginBottom: 10,
|
|
2962
|
+
cursor: "pointer"
|
|
2963
|
+
};
|
|
2964
|
+
function InsertCellsDialog({ api, onClose }) {
|
|
2965
|
+
const [shift, setShift] = useState11("down");
|
|
2966
|
+
const rangeLabel = useMemo8(() => {
|
|
2967
|
+
const fRange = activeRange7(api);
|
|
2968
|
+
return fRange?.getA1Notation?.() ?? null;
|
|
2969
|
+
}, [api]);
|
|
2970
|
+
const hasSelection = activeRange7(api) !== null;
|
|
2971
|
+
const apply = () => {
|
|
2972
|
+
if (applyInsert(api, shift)) onClose();
|
|
2973
|
+
};
|
|
2974
|
+
return /* @__PURE__ */ jsxs12(
|
|
2975
|
+
Dialog,
|
|
2976
|
+
{
|
|
2977
|
+
title: "Insert cells",
|
|
2978
|
+
onClose,
|
|
2979
|
+
width: 380,
|
|
2980
|
+
"data-testid": "cs-insert-cells-dialog",
|
|
2981
|
+
footer: /* @__PURE__ */ jsxs12(Fragment8, { children: [
|
|
2982
|
+
/* @__PURE__ */ jsx13("button", { type: "button", style: DIALOG_BTN_SECONDARY_STYLE, onClick: onClose, children: "Cancel" }),
|
|
2983
|
+
/* @__PURE__ */ jsx13(
|
|
2984
|
+
"button",
|
|
2985
|
+
{
|
|
2986
|
+
type: "button",
|
|
2987
|
+
style: DIALOG_BTN_PRIMARY_STYLE,
|
|
2988
|
+
"data-testid": "cs-insert-cells-apply",
|
|
2989
|
+
disabled: !hasSelection,
|
|
2990
|
+
onClick: apply,
|
|
2991
|
+
children: "Insert"
|
|
2992
|
+
}
|
|
2993
|
+
)
|
|
2994
|
+
] }),
|
|
2995
|
+
children: [
|
|
2996
|
+
hasSelection ? /* @__PURE__ */ jsxs12("div", { style: RANGE_NOTE_STYLE6, "data-testid": "cs-insert-cells-range", children: [
|
|
2997
|
+
"Insert at ",
|
|
2998
|
+
/* @__PURE__ */ jsx13("strong", { children: rangeLabel ?? "the current selection" })
|
|
2999
|
+
] }) : /* @__PURE__ */ jsx13("div", { style: RANGE_NOTE_STYLE6, "data-testid": "cs-insert-cells-no-selection", children: "Select one or more cells first, then reopen this dialog." }),
|
|
3000
|
+
/* @__PURE__ */ jsx13("div", { role: "radiogroup", "aria-label": "Shift direction", children: SHIFT_OPTIONS.map((opt) => /* @__PURE__ */ jsxs12(
|
|
3001
|
+
"label",
|
|
3002
|
+
{
|
|
3003
|
+
style: RADIO_STYLE,
|
|
3004
|
+
"data-testid": `cs-insert-cells-shift-${opt.value}-label`,
|
|
3005
|
+
children: [
|
|
3006
|
+
/* @__PURE__ */ jsx13(
|
|
3007
|
+
"input",
|
|
3008
|
+
{
|
|
3009
|
+
type: "radio",
|
|
3010
|
+
name: "cs-insert-cells-shift",
|
|
3011
|
+
"data-testid": `cs-insert-cells-shift-${opt.value}`,
|
|
3012
|
+
checked: shift === opt.value,
|
|
3013
|
+
onChange: () => setShift(opt.value)
|
|
3014
|
+
}
|
|
3015
|
+
),
|
|
3016
|
+
/* @__PURE__ */ jsx13("span", { children: opt.label })
|
|
3017
|
+
]
|
|
3018
|
+
},
|
|
3019
|
+
opt.value
|
|
3020
|
+
)) })
|
|
3021
|
+
]
|
|
3022
|
+
}
|
|
3023
|
+
);
|
|
3024
|
+
}
|
|
3025
|
+
|
|
3026
|
+
// src/chrome/DeleteCellsDialog.tsx
|
|
3027
|
+
import { useMemo as useMemo9, useState as useState12 } from "react";
|
|
3028
|
+
import { Dimension as Dimension2 } from "@univerjs/core";
|
|
3029
|
+
import { Fragment as Fragment9, jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
3030
|
+
var SHIFT_OPTIONS2 = [
|
|
3031
|
+
{ value: "left", label: "Shift cells left" },
|
|
3032
|
+
{ value: "up", label: "Shift cells up" },
|
|
3033
|
+
{ value: "row", label: "Entire row" },
|
|
3034
|
+
{ value: "column", label: "Entire column" }
|
|
3035
|
+
];
|
|
3036
|
+
function activeRange8(api) {
|
|
3037
|
+
return api.univer.getActiveWorkbook()?.getActiveSheet()?.getActiveRange() ?? null;
|
|
3038
|
+
}
|
|
3039
|
+
function applyDelete(api, choice) {
|
|
3040
|
+
const range = activeRange8(api);
|
|
3041
|
+
if (!range) return false;
|
|
3042
|
+
if (choice === "left") {
|
|
3043
|
+
range.deleteCells(Dimension2.COLUMNS);
|
|
3044
|
+
return true;
|
|
3045
|
+
}
|
|
3046
|
+
if (choice === "up") {
|
|
3047
|
+
range.deleteCells(Dimension2.ROWS);
|
|
3048
|
+
return true;
|
|
3049
|
+
}
|
|
3050
|
+
const sheet = api.univer.getActiveWorkbook()?.getActiveSheet();
|
|
3051
|
+
if (!sheet) return false;
|
|
3052
|
+
if (choice === "row") {
|
|
3053
|
+
const start2 = range.getRow();
|
|
3054
|
+
const count2 = range.getLastRow() - start2 + 1;
|
|
3055
|
+
if (count2 <= 0) return false;
|
|
3056
|
+
sheet.deleteRows(start2, count2);
|
|
3057
|
+
return true;
|
|
3058
|
+
}
|
|
3059
|
+
const start = range.getColumn();
|
|
3060
|
+
const count = range.getLastColumn() - start + 1;
|
|
3061
|
+
if (count <= 0) return false;
|
|
3062
|
+
sheet.deleteColumns(start, count);
|
|
3063
|
+
return true;
|
|
3064
|
+
}
|
|
3065
|
+
var RANGE_NOTE_STYLE7 = {
|
|
3066
|
+
fontSize: 12,
|
|
3067
|
+
color: "var(--cs-chrome-muted, #605e5c)",
|
|
3068
|
+
marginBottom: 12
|
|
3069
|
+
};
|
|
3070
|
+
var OPTION_STYLE = {
|
|
3071
|
+
display: "flex",
|
|
3072
|
+
alignItems: "center",
|
|
3073
|
+
gap: 8,
|
|
3074
|
+
padding: "6px 4px",
|
|
3075
|
+
cursor: "pointer"
|
|
3076
|
+
};
|
|
3077
|
+
function DeleteCellsDialog({ api, onClose }) {
|
|
3078
|
+
const [choice, setChoice] = useState12("left");
|
|
3079
|
+
const rangeLabel = useMemo9(() => {
|
|
3080
|
+
const fRange = activeRange8(api);
|
|
3081
|
+
return fRange?.getA1Notation?.() ?? null;
|
|
3082
|
+
}, [api]);
|
|
3083
|
+
const hasSelection = activeRange8(api) !== null;
|
|
3084
|
+
const apply = () => {
|
|
3085
|
+
if (applyDelete(api, choice)) onClose();
|
|
3086
|
+
};
|
|
3087
|
+
return /* @__PURE__ */ jsxs13(
|
|
3088
|
+
Dialog,
|
|
3089
|
+
{
|
|
3090
|
+
title: "Delete cells",
|
|
3091
|
+
onClose,
|
|
3092
|
+
width: 360,
|
|
3093
|
+
"data-testid": "cs-delete-cells-dialog",
|
|
3094
|
+
footer: /* @__PURE__ */ jsxs13(Fragment9, { children: [
|
|
3095
|
+
/* @__PURE__ */ jsx14("button", { type: "button", style: DIALOG_BTN_SECONDARY_STYLE, onClick: onClose, children: "Cancel" }),
|
|
3096
|
+
/* @__PURE__ */ jsx14(
|
|
3097
|
+
"button",
|
|
3098
|
+
{
|
|
3099
|
+
type: "button",
|
|
3100
|
+
style: DIALOG_BTN_PRIMARY_STYLE,
|
|
3101
|
+
"data-testid": "cs-delete-cells-apply",
|
|
3102
|
+
disabled: !hasSelection,
|
|
3103
|
+
onClick: apply,
|
|
3104
|
+
children: "Delete"
|
|
3105
|
+
}
|
|
3106
|
+
)
|
|
3107
|
+
] }),
|
|
3108
|
+
children: [
|
|
3109
|
+
hasSelection ? /* @__PURE__ */ jsxs13("div", { style: RANGE_NOTE_STYLE7, "data-testid": "cs-delete-cells-range", children: [
|
|
3110
|
+
"Delete ",
|
|
3111
|
+
/* @__PURE__ */ jsx14("strong", { children: rangeLabel ?? "the current selection" }),
|
|
3112
|
+
" and\u2026"
|
|
3113
|
+
] }) : /* @__PURE__ */ jsx14("div", { style: RANGE_NOTE_STYLE7, "data-testid": "cs-delete-cells-no-selection", children: "Select one or more cells first, then reopen this dialog." }),
|
|
3114
|
+
/* @__PURE__ */ jsx14("div", { role: "radiogroup", "aria-label": "Delete option", children: SHIFT_OPTIONS2.map((opt) => /* @__PURE__ */ jsxs13("label", { style: OPTION_STYLE, children: [
|
|
3115
|
+
/* @__PURE__ */ jsx14(
|
|
3116
|
+
"input",
|
|
3117
|
+
{
|
|
3118
|
+
type: "radio",
|
|
3119
|
+
name: "cs-delete-cells-shift",
|
|
3120
|
+
"data-testid": `cs-delete-cells-${opt.value}`,
|
|
3121
|
+
value: opt.value,
|
|
3122
|
+
checked: choice === opt.value,
|
|
3123
|
+
onChange: () => setChoice(opt.value)
|
|
3124
|
+
}
|
|
3125
|
+
),
|
|
3126
|
+
/* @__PURE__ */ jsx14("span", { children: opt.label })
|
|
3127
|
+
] }, opt.value)) })
|
|
3128
|
+
]
|
|
3129
|
+
}
|
|
3130
|
+
);
|
|
3131
|
+
}
|
|
3132
|
+
|
|
3133
|
+
// src/chrome/GoalSeekDialog.tsx
|
|
3134
|
+
import { useMemo as useMemo10, useState as useState13 } from "react";
|
|
3135
|
+
import "@univerjs/sheets-formula/facade";
|
|
3136
|
+
import { Fragment as Fragment10, jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
3137
|
+
function getSolver(api) {
|
|
3138
|
+
const worksheet = api.univer.getActiveWorkbook()?.getActiveSheet() ?? null;
|
|
3139
|
+
if (!worksheet) return null;
|
|
3140
|
+
const formula = api.univer.getFormula?.();
|
|
3141
|
+
return {
|
|
3142
|
+
worksheet,
|
|
3143
|
+
settle: async () => {
|
|
3144
|
+
if (formula?.onCalculationEnd) {
|
|
3145
|
+
await formula.onCalculationEnd();
|
|
3146
|
+
return;
|
|
3147
|
+
}
|
|
3148
|
+
await new Promise((resolve) => {
|
|
3149
|
+
if (typeof requestAnimationFrame === "function") requestAnimationFrame(() => resolve());
|
|
3150
|
+
else setTimeout(resolve, 16);
|
|
3151
|
+
});
|
|
3152
|
+
}
|
|
3153
|
+
};
|
|
3154
|
+
}
|
|
3155
|
+
function toNumber(value) {
|
|
3156
|
+
if (typeof value === "number") return value;
|
|
3157
|
+
if (typeof value === "boolean") return value ? 1 : 0;
|
|
3158
|
+
if (typeof value === "string" && value.trim() !== "") return Number(value);
|
|
3159
|
+
return Number.NaN;
|
|
3160
|
+
}
|
|
3161
|
+
function isSingleCellA1(ref) {
|
|
3162
|
+
return /^[A-Za-z]{1,3}[1-9][0-9]*$/.test(ref.trim());
|
|
3163
|
+
}
|
|
3164
|
+
var INITIAL_STATE3 = { setCell: "", toValue: "", byChangingCell: "" };
|
|
3165
|
+
var TOLERANCE = 1e-6;
|
|
3166
|
+
var MAX_ITERATIONS = 100;
|
|
3167
|
+
async function goalSeek(solver, setCellRef, target, changeCellRef) {
|
|
3168
|
+
const setRange = solver.worksheet.getRange(setCellRef);
|
|
3169
|
+
const changeRange = solver.worksheet.getRange(changeCellRef);
|
|
3170
|
+
if (!setRange || !changeRange) return { ok: false, reason: "Could not resolve the cells." };
|
|
3171
|
+
const original = toNumber(changeRange.getValue());
|
|
3172
|
+
const x0Seed = Number.isFinite(original) ? original : 0;
|
|
3173
|
+
const evalAt = async (x) => {
|
|
3174
|
+
changeRange.setValue(x);
|
|
3175
|
+
await solver.settle();
|
|
3176
|
+
return toNumber(setRange.getValue());
|
|
3177
|
+
};
|
|
3178
|
+
const restore = async () => {
|
|
3179
|
+
changeRange.setValue(x0Seed);
|
|
3180
|
+
await solver.settle();
|
|
3181
|
+
};
|
|
3182
|
+
let x0 = x0Seed;
|
|
3183
|
+
let f0 = await evalAt(x0) - target;
|
|
3184
|
+
if (!Number.isFinite(f0)) {
|
|
3185
|
+
await restore();
|
|
3186
|
+
return { ok: false, reason: "The set cell is not a number (check that it holds a formula)." };
|
|
3187
|
+
}
|
|
3188
|
+
if (Math.abs(f0) <= TOLERANCE) return { ok: true, solution: x0, result: f0 + target };
|
|
3189
|
+
let x1 = x0 !== 0 ? x0 * 1.01 + 0.01 : 1;
|
|
3190
|
+
let f1 = await evalAt(x1) - target;
|
|
3191
|
+
for (let i = 0; i < MAX_ITERATIONS; i++) {
|
|
3192
|
+
if (Number.isFinite(f1) && Math.abs(f1) <= TOLERANCE) {
|
|
3193
|
+
return { ok: true, solution: x1, result: f1 + target };
|
|
3194
|
+
}
|
|
3195
|
+
const denom = f1 - f0;
|
|
3196
|
+
let x2;
|
|
3197
|
+
if (!Number.isFinite(f1) || denom === 0) {
|
|
3198
|
+
x2 = x1 + (x1 !== 0 ? x1 * 0.1 : 0.1);
|
|
3199
|
+
} else {
|
|
3200
|
+
const step = f1 * (x1 - x0) / denom;
|
|
3201
|
+
x2 = x1 - step;
|
|
3202
|
+
if (!Number.isFinite(x2)) x2 = x1 + (x1 !== 0 ? x1 * 0.1 : 0.1);
|
|
3203
|
+
}
|
|
3204
|
+
const f2 = await evalAt(x2) - target;
|
|
3205
|
+
x0 = x1;
|
|
3206
|
+
f0 = f1;
|
|
3207
|
+
x1 = x2;
|
|
3208
|
+
f1 = f2;
|
|
3209
|
+
}
|
|
3210
|
+
await restore();
|
|
3211
|
+
return {
|
|
3212
|
+
ok: false,
|
|
3213
|
+
reason: `Could not converge after ${MAX_ITERATIONS} iterations. Try an input cell whose value the set cell actually depends on.`
|
|
3214
|
+
};
|
|
3215
|
+
}
|
|
3216
|
+
var NOTE_STYLE = {
|
|
3217
|
+
fontSize: 12,
|
|
3218
|
+
color: "var(--cs-chrome-muted, #605e5c)",
|
|
3219
|
+
marginBottom: 12
|
|
3220
|
+
};
|
|
3221
|
+
var STATUS_OK_STYLE = {
|
|
3222
|
+
fontSize: 12,
|
|
3223
|
+
color: "var(--cs-chrome-active-fg, #0e7490)",
|
|
3224
|
+
marginTop: 4
|
|
3225
|
+
};
|
|
3226
|
+
var STATUS_ERR_STYLE = {
|
|
3227
|
+
fontSize: 12,
|
|
3228
|
+
color: "var(--cs-chrome-danger, #b91c1c)",
|
|
3229
|
+
marginTop: 4
|
|
3230
|
+
};
|
|
3231
|
+
function GoalSeekDialog({ api, onClose }) {
|
|
3232
|
+
const initialSetCell = useMemo10(() => {
|
|
3233
|
+
const range = api.univer.getActiveWorkbook()?.getActiveSheet()?.getActiveRange();
|
|
3234
|
+
const a1 = range?.getA1Notation?.();
|
|
3235
|
+
return a1 && isSingleCellA1(a1) ? a1 : "";
|
|
3236
|
+
}, [api]);
|
|
3237
|
+
const [state, setState] = useState13({ ...INITIAL_STATE3, setCell: initialSetCell });
|
|
3238
|
+
const [status, setStatus] = useState13({ kind: "idle" });
|
|
3239
|
+
const update = (key, value) => {
|
|
3240
|
+
setState((prev) => ({ ...prev, [key]: value }));
|
|
3241
|
+
setStatus({ kind: "idle" });
|
|
3242
|
+
};
|
|
3243
|
+
const setCellValid = isSingleCellA1(state.setCell);
|
|
3244
|
+
const changeCellValid = isSingleCellA1(state.byChangingCell);
|
|
3245
|
+
const targetNum = Number(state.toValue);
|
|
3246
|
+
const targetValid = state.toValue.trim() !== "" && Number.isFinite(targetNum);
|
|
3247
|
+
const canRun = setCellValid && changeCellValid && targetValid && state.setCell.trim().toUpperCase() !== state.byChangingCell.trim().toUpperCase() && status.kind !== "running";
|
|
3248
|
+
const run = async () => {
|
|
3249
|
+
const solver = getSolver(api);
|
|
3250
|
+
if (!solver) {
|
|
3251
|
+
setStatus({ kind: "error", message: "No active sheet." });
|
|
3252
|
+
return;
|
|
3253
|
+
}
|
|
3254
|
+
setStatus({ kind: "running" });
|
|
3255
|
+
try {
|
|
3256
|
+
const result = await goalSeek(
|
|
3257
|
+
solver,
|
|
3258
|
+
state.setCell.trim(),
|
|
3259
|
+
targetNum,
|
|
3260
|
+
state.byChangingCell.trim()
|
|
3261
|
+
);
|
|
3262
|
+
if (result.ok) {
|
|
3263
|
+
setStatus({ kind: "done", solution: result.solution, result: result.result });
|
|
3264
|
+
} else {
|
|
3265
|
+
setStatus({ kind: "error", message: result.reason });
|
|
3266
|
+
}
|
|
3267
|
+
} catch (err) {
|
|
3268
|
+
setStatus({
|
|
3269
|
+
kind: "error",
|
|
3270
|
+
message: err instanceof Error ? err.message : "Goal seek failed."
|
|
3271
|
+
});
|
|
3272
|
+
}
|
|
3273
|
+
};
|
|
3274
|
+
const running = status.kind === "running";
|
|
3275
|
+
return /* @__PURE__ */ jsxs14(
|
|
3276
|
+
Dialog,
|
|
3277
|
+
{
|
|
3278
|
+
title: "Goal seek",
|
|
3279
|
+
onClose,
|
|
3280
|
+
width: 420,
|
|
3281
|
+
"data-testid": "cs-goal-seek-dialog",
|
|
3282
|
+
footer: /* @__PURE__ */ jsxs14(Fragment10, { children: [
|
|
3283
|
+
/* @__PURE__ */ jsx15("button", { type: "button", style: DIALOG_BTN_SECONDARY_STYLE, onClick: onClose, children: status.kind === "done" ? "Done" : "Cancel" }),
|
|
3284
|
+
/* @__PURE__ */ jsx15(
|
|
3285
|
+
"button",
|
|
3286
|
+
{
|
|
3287
|
+
type: "button",
|
|
3288
|
+
style: { ...DIALOG_BTN_PRIMARY_STYLE, opacity: canRun ? 1 : 0.5 },
|
|
3289
|
+
"data-testid": "cs-goal-seek-run",
|
|
3290
|
+
disabled: !canRun,
|
|
3291
|
+
onClick: run,
|
|
3292
|
+
children: running ? "Solving\u2026" : "Solve"
|
|
3293
|
+
}
|
|
3294
|
+
)
|
|
3295
|
+
] }),
|
|
3296
|
+
children: [
|
|
3297
|
+
/* @__PURE__ */ jsx15("div", { style: NOTE_STYLE, "data-testid": "cs-goal-seek-note", children: "Drives the input cell until the formula cell reaches your target value." }),
|
|
3298
|
+
/* @__PURE__ */ jsxs14("label", { style: DIALOG_FIELD_STYLE, children: [
|
|
3299
|
+
/* @__PURE__ */ jsx15("span", { style: DIALOG_LABEL_STYLE, children: "Set cell (formula cell)" }),
|
|
3300
|
+
/* @__PURE__ */ jsx15(
|
|
3301
|
+
"input",
|
|
3302
|
+
{
|
|
3303
|
+
style: DIALOG_INPUT_STYLE,
|
|
3304
|
+
"data-testid": "cs-goal-seek-set-cell",
|
|
3305
|
+
placeholder: "e.g. B5",
|
|
3306
|
+
value: state.setCell,
|
|
3307
|
+
onChange: (e) => update("setCell", e.target.value)
|
|
3308
|
+
}
|
|
3309
|
+
)
|
|
3310
|
+
] }),
|
|
3311
|
+
/* @__PURE__ */ jsxs14("label", { style: DIALOG_FIELD_STYLE, children: [
|
|
3312
|
+
/* @__PURE__ */ jsx15("span", { style: DIALOG_LABEL_STYLE, children: "To value" }),
|
|
3313
|
+
/* @__PURE__ */ jsx15(
|
|
3314
|
+
"input",
|
|
3315
|
+
{
|
|
3316
|
+
style: DIALOG_INPUT_STYLE,
|
|
3317
|
+
"data-testid": "cs-goal-seek-to-value",
|
|
3318
|
+
type: "number",
|
|
3319
|
+
placeholder: "e.g. 1000",
|
|
3320
|
+
value: state.toValue,
|
|
3321
|
+
onChange: (e) => update("toValue", e.target.value)
|
|
3322
|
+
}
|
|
3323
|
+
)
|
|
3324
|
+
] }),
|
|
3325
|
+
/* @__PURE__ */ jsxs14("label", { style: DIALOG_FIELD_STYLE, children: [
|
|
3326
|
+
/* @__PURE__ */ jsx15("span", { style: DIALOG_LABEL_STYLE, children: "By changing cell (input cell)" }),
|
|
3327
|
+
/* @__PURE__ */ jsx15(
|
|
3328
|
+
"input",
|
|
3329
|
+
{
|
|
3330
|
+
style: DIALOG_INPUT_STYLE,
|
|
3331
|
+
"data-testid": "cs-goal-seek-change-cell",
|
|
3332
|
+
placeholder: "e.g. B2",
|
|
3333
|
+
value: state.byChangingCell,
|
|
3334
|
+
onChange: (e) => update("byChangingCell", e.target.value)
|
|
3335
|
+
}
|
|
3336
|
+
)
|
|
3337
|
+
] }),
|
|
3338
|
+
status.kind === "done" && /* @__PURE__ */ jsxs14("div", { style: STATUS_OK_STYLE, "data-testid": "cs-goal-seek-success", children: [
|
|
3339
|
+
"Solved: ",
|
|
3340
|
+
/* @__PURE__ */ jsx15("strong", { children: state.byChangingCell.trim().toUpperCase() }),
|
|
3341
|
+
" =",
|
|
3342
|
+
" ",
|
|
3343
|
+
formatNumber(status.solution),
|
|
3344
|
+
" makes",
|
|
3345
|
+
" ",
|
|
3346
|
+
/* @__PURE__ */ jsx15("strong", { children: state.setCell.trim().toUpperCase() }),
|
|
3347
|
+
" = ",
|
|
3348
|
+
formatNumber(status.result),
|
|
3349
|
+
"."
|
|
3350
|
+
] }),
|
|
3351
|
+
status.kind === "error" && /* @__PURE__ */ jsx15("div", { style: STATUS_ERR_STYLE, "data-testid": "cs-goal-seek-error", children: status.message })
|
|
3352
|
+
]
|
|
3353
|
+
}
|
|
3354
|
+
);
|
|
3355
|
+
}
|
|
3356
|
+
function formatNumber(n) {
|
|
3357
|
+
if (!Number.isFinite(n)) return String(n);
|
|
3358
|
+
const rounded = Math.round(n * 1e6) / 1e6;
|
|
3359
|
+
return String(rounded);
|
|
3360
|
+
}
|
|
3361
|
+
|
|
3362
|
+
// src/chrome/InsertChartDialog.tsx
|
|
3363
|
+
import { useMemo as useMemo11, useState as useState14 } from "react";
|
|
3364
|
+
import "@univerjs/sheets-drawing-ui/facade";
|
|
3365
|
+
import { Fragment as Fragment11, jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
3366
|
+
var CHART_TYPE_OPTIONS = [
|
|
3367
|
+
{ value: "column", label: "Column" },
|
|
3368
|
+
{ value: "bar", label: "Bar" },
|
|
3369
|
+
{ value: "line", label: "Line" },
|
|
3370
|
+
{ value: "pie", label: "Pie" }
|
|
3371
|
+
];
|
|
3372
|
+
var CHART_COMPONENT_KEY = "cs-builtin-chart-preview";
|
|
3373
|
+
var PALETTE = ["#0e7490", "#f59e0b", "#10b981", "#ef4444", "#6366f1", "#ec4899"];
|
|
3374
|
+
function activeRange9(api) {
|
|
3375
|
+
return api.univer.getActiveWorkbook()?.getActiveSheet()?.getActiveRange() ?? null;
|
|
3376
|
+
}
|
|
3377
|
+
function activeSheet2(api) {
|
|
3378
|
+
return api.univer.getActiveWorkbook()?.getActiveSheet() ?? null;
|
|
3379
|
+
}
|
|
3380
|
+
function readRangeNumbers(api) {
|
|
3381
|
+
const snap = api.getSnapshot();
|
|
3382
|
+
const sel = api.getSelection();
|
|
3383
|
+
if (!snap || !sel) return [];
|
|
3384
|
+
const sheet = snap.sheets?.[sel.sheetId];
|
|
3385
|
+
if (!sheet) return [];
|
|
3386
|
+
const { startRow, endRow, startColumn, endColumn } = sel.range;
|
|
3387
|
+
const out = [];
|
|
3388
|
+
for (let r = startRow; r <= endRow; r++) {
|
|
3389
|
+
for (let c = startColumn; c <= endColumn; c++) {
|
|
3390
|
+
const raw = sheet.cellData?.[r]?.[c]?.v;
|
|
3391
|
+
const n = typeof raw === "number" ? raw : Number(raw);
|
|
3392
|
+
if (Number.isFinite(n)) out.push(n);
|
|
3393
|
+
}
|
|
3394
|
+
}
|
|
3395
|
+
return out;
|
|
3396
|
+
}
|
|
3397
|
+
function ensureChartComponent(api) {
|
|
3398
|
+
const uni = api.univer;
|
|
3399
|
+
if (uni.__csChartRegistered || typeof uni.registerComponent !== "function") return;
|
|
3400
|
+
uni.registerComponent(CHART_COMPONENT_KEY, ChartPreview);
|
|
3401
|
+
uni.__csChartRegistered = true;
|
|
3402
|
+
}
|
|
3403
|
+
function insertChart(api, s) {
|
|
3404
|
+
const range = activeRange9(api);
|
|
3405
|
+
const sheet = activeSheet2(api);
|
|
3406
|
+
if (!range || !sheet) return false;
|
|
3407
|
+
ensureChartComponent(api);
|
|
3408
|
+
const values = readRangeNumbers(api);
|
|
3409
|
+
const domSheet = sheet;
|
|
3410
|
+
if (typeof domSheet.addFloatDomToRange !== "function") return false;
|
|
3411
|
+
const result = domSheet.addFloatDomToRange(
|
|
3412
|
+
range,
|
|
3413
|
+
{
|
|
3414
|
+
componentKey: CHART_COMPONENT_KEY,
|
|
3415
|
+
allowTransform: true,
|
|
3416
|
+
data: { type: s.chartType, values, rangeA1: s.rangeA1 }
|
|
3417
|
+
},
|
|
3418
|
+
{ width: 360, height: 240, marginX: 0, marginY: 0 },
|
|
3419
|
+
`cs-chart-${Date.now()}`
|
|
3420
|
+
);
|
|
3421
|
+
return result != null;
|
|
3422
|
+
}
|
|
3423
|
+
function ChartPreview({
|
|
3424
|
+
data
|
|
3425
|
+
}) {
|
|
3426
|
+
const type = data?.type ?? "column";
|
|
3427
|
+
const values = (data?.values ?? []).slice(0, 24);
|
|
3428
|
+
const label = data?.rangeA1 ?? "";
|
|
3429
|
+
const wrapStyle = {
|
|
3430
|
+
width: "100%",
|
|
3431
|
+
height: "100%",
|
|
3432
|
+
boxSizing: "border-box",
|
|
3433
|
+
background: "#ffffff",
|
|
3434
|
+
border: "1px solid #cdd3db",
|
|
3435
|
+
borderRadius: 6,
|
|
3436
|
+
padding: 8,
|
|
3437
|
+
display: "flex",
|
|
3438
|
+
flexDirection: "column",
|
|
3439
|
+
font: "12px system-ui, sans-serif",
|
|
3440
|
+
color: "#201f1e",
|
|
3441
|
+
overflow: "hidden"
|
|
3442
|
+
};
|
|
3443
|
+
const titleStyle = {
|
|
3444
|
+
fontWeight: 600,
|
|
3445
|
+
marginBottom: 4,
|
|
3446
|
+
whiteSpace: "nowrap",
|
|
3447
|
+
overflow: "hidden",
|
|
3448
|
+
textOverflow: "ellipsis"
|
|
3449
|
+
};
|
|
3450
|
+
const W = 340;
|
|
3451
|
+
const H = 176;
|
|
3452
|
+
const max = values.length ? Math.max(...values, 0) : 0;
|
|
3453
|
+
const min = values.length ? Math.min(...values, 0) : 0;
|
|
3454
|
+
const span = max - min || 1;
|
|
3455
|
+
let body;
|
|
3456
|
+
if (values.length === 0) {
|
|
3457
|
+
body = /* @__PURE__ */ jsx16("text", { x: W / 2, y: H / 2, textAnchor: "middle", fill: "#605e5c", children: "No numeric data in range" });
|
|
3458
|
+
} else if (type === "pie") {
|
|
3459
|
+
const total = values.reduce((a, v) => a + Math.abs(v), 0) || 1;
|
|
3460
|
+
const cx = W / 2;
|
|
3461
|
+
const cy = H / 2;
|
|
3462
|
+
const rad = Math.min(W, H) / 2 - 6;
|
|
3463
|
+
let angle = -Math.PI / 2;
|
|
3464
|
+
body = /* @__PURE__ */ jsx16(Fragment11, { children: values.map((v, i) => {
|
|
3465
|
+
const frac = Math.abs(v) / total;
|
|
3466
|
+
const next = angle + frac * Math.PI * 2;
|
|
3467
|
+
const x1 = cx + rad * Math.cos(angle);
|
|
3468
|
+
const y1 = cy + rad * Math.sin(angle);
|
|
3469
|
+
const x2 = cx + rad * Math.cos(next);
|
|
3470
|
+
const y2 = cy + rad * Math.sin(next);
|
|
3471
|
+
const large = frac > 0.5 ? 1 : 0;
|
|
3472
|
+
const d = `M ${cx} ${cy} L ${x1} ${y1} A ${rad} ${rad} 0 ${large} 1 ${x2} ${y2} Z`;
|
|
3473
|
+
angle = next;
|
|
3474
|
+
return /* @__PURE__ */ jsx16("path", { d, fill: PALETTE[i % PALETTE.length] }, i);
|
|
3475
|
+
}) });
|
|
3476
|
+
} else if (type === "line") {
|
|
3477
|
+
const step = values.length > 1 ? W / (values.length - 1) : W;
|
|
3478
|
+
const pts = values.map((v, i) => `${i * step},${H - (v - min) / span * H}`).join(" ");
|
|
3479
|
+
body = /* @__PURE__ */ jsx16("polyline", { points: pts, fill: "none", stroke: PALETTE[0], strokeWidth: 2 });
|
|
3480
|
+
} else {
|
|
3481
|
+
const horizontal = type === "bar";
|
|
3482
|
+
const n = values.length;
|
|
3483
|
+
const gap = 4;
|
|
3484
|
+
body = /* @__PURE__ */ jsx16(Fragment11, { children: values.map((v, i) => {
|
|
3485
|
+
const fill = PALETTE[i % PALETTE.length];
|
|
3486
|
+
if (horizontal) {
|
|
3487
|
+
const bh2 = H / n - gap;
|
|
3488
|
+
const bw2 = (v - min) / span * W;
|
|
3489
|
+
return /* @__PURE__ */ jsx16(
|
|
3490
|
+
"rect",
|
|
3491
|
+
{
|
|
3492
|
+
x: 0,
|
|
3493
|
+
y: i * (bh2 + gap),
|
|
3494
|
+
width: Math.max(bw2, 1),
|
|
3495
|
+
height: bh2,
|
|
3496
|
+
fill
|
|
3497
|
+
},
|
|
3498
|
+
i
|
|
3499
|
+
);
|
|
3500
|
+
}
|
|
3501
|
+
const bw = W / n - gap;
|
|
3502
|
+
const bh = (v - min) / span * H;
|
|
3503
|
+
return /* @__PURE__ */ jsx16(
|
|
3504
|
+
"rect",
|
|
3505
|
+
{
|
|
3506
|
+
x: i * (bw + gap),
|
|
3507
|
+
y: H - bh,
|
|
3508
|
+
width: bw,
|
|
3509
|
+
height: Math.max(bh, 1),
|
|
3510
|
+
fill
|
|
3511
|
+
},
|
|
3512
|
+
i
|
|
3513
|
+
);
|
|
3514
|
+
}) });
|
|
3515
|
+
}
|
|
3516
|
+
return /* @__PURE__ */ jsxs15("div", { style: wrapStyle, children: [
|
|
3517
|
+
label && /* @__PURE__ */ jsx16("div", { style: titleStyle, children: `${type[0].toUpperCase()}${type.slice(1)} chart \xB7 ${label}` }),
|
|
3518
|
+
/* @__PURE__ */ jsx16("svg", { viewBox: `0 0 ${W} ${H}`, width: "100%", height: "100%", preserveAspectRatio: "xMidYMid meet", children: body })
|
|
3519
|
+
] });
|
|
3520
|
+
}
|
|
3521
|
+
var RANGE_NOTE_STYLE8 = {
|
|
3522
|
+
fontSize: 12,
|
|
3523
|
+
color: "var(--cs-chrome-muted, #605e5c)",
|
|
3524
|
+
marginBottom: 12
|
|
3525
|
+
};
|
|
3526
|
+
var TYPE_ROW_STYLE = {
|
|
3527
|
+
display: "grid",
|
|
3528
|
+
gridTemplateColumns: "repeat(4, 1fr)",
|
|
3529
|
+
gap: 8,
|
|
3530
|
+
marginBottom: 12
|
|
3531
|
+
};
|
|
3532
|
+
function typeCardStyle(selected) {
|
|
3533
|
+
return {
|
|
3534
|
+
display: "flex",
|
|
3535
|
+
flexDirection: "column",
|
|
3536
|
+
alignItems: "center",
|
|
3537
|
+
gap: 6,
|
|
3538
|
+
padding: "10px 4px",
|
|
3539
|
+
border: `1px solid ${selected ? "var(--cs-chrome-active-fg, #0e7490)" : "var(--cs-chrome-border, #cdd3db)"}`,
|
|
3540
|
+
borderRadius: 8,
|
|
3541
|
+
background: selected ? "var(--cs-chrome-active-bg, #ecfeff)" : "var(--cs-chrome-input-bg, #fff)",
|
|
3542
|
+
color: selected ? "var(--cs-chrome-active-fg, #0e7490)" : "var(--cs-chrome-fg, #201f1e)",
|
|
3543
|
+
font: "inherit",
|
|
3544
|
+
fontSize: 12,
|
|
3545
|
+
cursor: "pointer"
|
|
3546
|
+
};
|
|
3547
|
+
}
|
|
3548
|
+
function TypeGlyph({ type }) {
|
|
3549
|
+
const c = "currentColor";
|
|
3550
|
+
switch (type) {
|
|
3551
|
+
case "column":
|
|
3552
|
+
return /* @__PURE__ */ jsxs15("svg", { width: 22, height: 22, viewBox: "0 0 22 22", fill: c, "aria-hidden": "true", children: [
|
|
3553
|
+
/* @__PURE__ */ jsx16("rect", { x: 2, y: 10, width: 4, height: 10 }),
|
|
3554
|
+
/* @__PURE__ */ jsx16("rect", { x: 9, y: 5, width: 4, height: 15 }),
|
|
3555
|
+
/* @__PURE__ */ jsx16("rect", { x: 16, y: 2, width: 4, height: 18 })
|
|
3556
|
+
] });
|
|
3557
|
+
case "bar":
|
|
3558
|
+
return /* @__PURE__ */ jsxs15("svg", { width: 22, height: 22, viewBox: "0 0 22 22", fill: c, "aria-hidden": "true", children: [
|
|
3559
|
+
/* @__PURE__ */ jsx16("rect", { x: 2, y: 2, width: 10, height: 4 }),
|
|
3560
|
+
/* @__PURE__ */ jsx16("rect", { x: 2, y: 9, width: 16, height: 4 }),
|
|
3561
|
+
/* @__PURE__ */ jsx16("rect", { x: 2, y: 16, width: 7, height: 4 })
|
|
3562
|
+
] });
|
|
3563
|
+
case "line":
|
|
3564
|
+
return /* @__PURE__ */ jsx16(
|
|
3565
|
+
"svg",
|
|
3566
|
+
{
|
|
3567
|
+
width: 22,
|
|
3568
|
+
height: 22,
|
|
3569
|
+
viewBox: "0 0 22 22",
|
|
3570
|
+
fill: "none",
|
|
3571
|
+
stroke: c,
|
|
3572
|
+
strokeWidth: 2,
|
|
3573
|
+
"aria-hidden": "true",
|
|
3574
|
+
children: /* @__PURE__ */ jsx16("polyline", { points: "2,16 8,9 13,13 20,3" })
|
|
3575
|
+
}
|
|
3576
|
+
);
|
|
3577
|
+
case "pie":
|
|
3578
|
+
return /* @__PURE__ */ jsxs15("svg", { width: 22, height: 22, viewBox: "0 0 22 22", fill: c, "aria-hidden": "true", children: [
|
|
3579
|
+
/* @__PURE__ */ jsx16("path", { d: "M11 11 L11 2 A9 9 0 0 1 20 11 Z" }),
|
|
3580
|
+
/* @__PURE__ */ jsx16("circle", { cx: 11, cy: 11, r: 9, fill: "none", stroke: c, strokeWidth: 1.5 })
|
|
3581
|
+
] });
|
|
3582
|
+
}
|
|
3583
|
+
}
|
|
3584
|
+
function InsertChartDialog({ api, onClose }) {
|
|
3585
|
+
const rangeLabel = useMemo11(() => {
|
|
3586
|
+
const fRange = activeRange9(api);
|
|
3587
|
+
return fRange?.getA1Notation?.() ?? null;
|
|
3588
|
+
}, [api]);
|
|
3589
|
+
const [state, setState] = useState14(() => ({
|
|
3590
|
+
chartType: "column",
|
|
3591
|
+
rangeA1: rangeLabel ?? ""
|
|
3592
|
+
}));
|
|
3593
|
+
const hasSelection = activeRange9(api) !== null;
|
|
3594
|
+
const update = (key, value) => setState((prev) => ({ ...prev, [key]: value }));
|
|
3595
|
+
const apply = () => {
|
|
3596
|
+
if (insertChart(api, state)) onClose();
|
|
3597
|
+
};
|
|
3598
|
+
return /* @__PURE__ */ jsxs15(
|
|
3599
|
+
Dialog,
|
|
3600
|
+
{
|
|
3601
|
+
title: "Insert chart",
|
|
3602
|
+
onClose,
|
|
3603
|
+
width: 440,
|
|
3604
|
+
"data-testid": "cs-insert-chart-dialog",
|
|
3605
|
+
footer: /* @__PURE__ */ jsxs15(Fragment11, { children: [
|
|
3606
|
+
/* @__PURE__ */ jsx16("button", { type: "button", style: DIALOG_BTN_SECONDARY_STYLE, onClick: onClose, children: "Cancel" }),
|
|
3607
|
+
/* @__PURE__ */ jsx16(
|
|
3608
|
+
"button",
|
|
3609
|
+
{
|
|
3610
|
+
type: "button",
|
|
3611
|
+
style: DIALOG_BTN_PRIMARY_STYLE,
|
|
3612
|
+
"data-testid": "cs-insert-chart-apply",
|
|
3613
|
+
disabled: !hasSelection,
|
|
3614
|
+
onClick: apply,
|
|
3615
|
+
children: "Insert"
|
|
3616
|
+
}
|
|
3617
|
+
)
|
|
3618
|
+
] }),
|
|
3619
|
+
children: [
|
|
3620
|
+
hasSelection ? /* @__PURE__ */ jsxs15("div", { style: RANGE_NOTE_STYLE8, "data-testid": "cs-insert-chart-range", children: [
|
|
3621
|
+
"Chart from ",
|
|
3622
|
+
/* @__PURE__ */ jsx16("strong", { children: rangeLabel ?? "the current selection" })
|
|
3623
|
+
] }) : /* @__PURE__ */ jsx16("div", { style: RANGE_NOTE_STYLE8, "data-testid": "cs-insert-chart-no-selection", children: "Select the data range first, then reopen this dialog." }),
|
|
3624
|
+
/* @__PURE__ */ jsx16("div", { style: DIALOG_LABEL_STYLE, children: "Chart type" }),
|
|
3625
|
+
/* @__PURE__ */ jsx16("div", { style: TYPE_ROW_STYLE, role: "radiogroup", "aria-label": "Chart type", children: CHART_TYPE_OPTIONS.map((opt) => /* @__PURE__ */ jsxs15(
|
|
3626
|
+
"button",
|
|
3627
|
+
{
|
|
3628
|
+
type: "button",
|
|
3629
|
+
role: "radio",
|
|
3630
|
+
"aria-checked": state.chartType === opt.value,
|
|
3631
|
+
"data-testid": `cs-insert-chart-type-${opt.value}`,
|
|
3632
|
+
style: typeCardStyle(state.chartType === opt.value),
|
|
3633
|
+
onClick: () => update("chartType", opt.value),
|
|
3634
|
+
children: [
|
|
3635
|
+
/* @__PURE__ */ jsx16(TypeGlyph, { type: opt.value }),
|
|
3636
|
+
/* @__PURE__ */ jsx16("span", { children: opt.label })
|
|
3637
|
+
]
|
|
3638
|
+
},
|
|
3639
|
+
opt.value
|
|
3640
|
+
)) }),
|
|
3641
|
+
/* @__PURE__ */ jsxs15("label", { style: DIALOG_FIELD_STYLE, children: [
|
|
3642
|
+
/* @__PURE__ */ jsx16("span", { style: DIALOG_LABEL_STYLE, children: "Data range" }),
|
|
3643
|
+
/* @__PURE__ */ jsx16(
|
|
3644
|
+
"input",
|
|
3645
|
+
{
|
|
3646
|
+
style: DIALOG_INPUT_STYLE,
|
|
3647
|
+
"data-testid": "cs-insert-chart-range-input",
|
|
3648
|
+
value: state.rangeA1,
|
|
3649
|
+
placeholder: "A1:B5",
|
|
3650
|
+
onChange: (e) => update("rangeA1", e.target.value)
|
|
3651
|
+
}
|
|
3652
|
+
)
|
|
3653
|
+
] }),
|
|
3654
|
+
/* @__PURE__ */ jsx16("div", { style: RANGE_NOTE_STYLE8, children: "The chart is placed over the current selection. Numeric cells in the range become the series." })
|
|
3655
|
+
]
|
|
3656
|
+
}
|
|
3657
|
+
);
|
|
3658
|
+
}
|
|
3659
|
+
|
|
3660
|
+
// src/chrome/InsertSparklineDialog.tsx
|
|
3661
|
+
import { useMemo as useMemo12, useState as useState15 } from "react";
|
|
3662
|
+
import { Fragment as Fragment12, jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
3663
|
+
var SPARKLINES_RESOURCE_NAME = "__casual_sheets_sparklines__";
|
|
3664
|
+
var TYPE_OPTIONS = [
|
|
3665
|
+
{ value: "line", label: "Line" },
|
|
3666
|
+
{ value: "column", label: "Column" },
|
|
3667
|
+
{ value: "win-loss", label: "Win / Loss" }
|
|
3668
|
+
];
|
|
3669
|
+
function activeRange10(api) {
|
|
3670
|
+
return api.univer.getActiveWorkbook()?.getActiveSheet()?.getActiveRange() ?? null;
|
|
3671
|
+
}
|
|
3672
|
+
function colLettersToIndex(letters) {
|
|
3673
|
+
let n = 0;
|
|
3674
|
+
for (let i = 0; i < letters.length; i += 1) {
|
|
3675
|
+
n = n * 26 + (letters.charCodeAt(i) - 64);
|
|
3676
|
+
}
|
|
3677
|
+
return n - 1;
|
|
3678
|
+
}
|
|
3679
|
+
function parseRange(s) {
|
|
3680
|
+
const m = /^\$?([A-Z]+)\$?(\d+)(?::\$?([A-Z]+)\$?(\d+))?$/.exec(s.trim().toUpperCase());
|
|
3681
|
+
if (!m) return null;
|
|
3682
|
+
const c1 = colLettersToIndex(m[1]);
|
|
3683
|
+
const r1 = parseInt(m[2], 10) - 1;
|
|
3684
|
+
const c2 = m[3] ? colLettersToIndex(m[3]) : c1;
|
|
3685
|
+
const r2 = m[4] ? parseInt(m[4], 10) - 1 : r1;
|
|
3686
|
+
return {
|
|
3687
|
+
startRow: Math.min(r1, r2),
|
|
3688
|
+
endRow: Math.max(r1, r2),
|
|
3689
|
+
startColumn: Math.min(c1, c2),
|
|
3690
|
+
endColumn: Math.max(c1, c2)
|
|
3691
|
+
};
|
|
3692
|
+
}
|
|
3693
|
+
function parseSingleCell(s) {
|
|
3694
|
+
const m = /^\$?([A-Z]+)\$?(\d+)$/.exec(s.trim().toUpperCase());
|
|
3695
|
+
if (!m) return null;
|
|
3696
|
+
return { col: colLettersToIndex(m[1]), row: parseInt(m[2], 10) - 1 };
|
|
3697
|
+
}
|
|
3698
|
+
function insertSparkline(api, model) {
|
|
3699
|
+
const data = api.getContent();
|
|
3700
|
+
if (!data) return false;
|
|
3701
|
+
const resources = data.resources ? [...data.resources] : [];
|
|
3702
|
+
const idx = resources.findIndex((r) => r.name === SPARKLINES_RESOURCE_NAME);
|
|
3703
|
+
let existing = [];
|
|
3704
|
+
if (idx >= 0 && resources[idx]?.data) {
|
|
3705
|
+
try {
|
|
3706
|
+
const parsed = JSON.parse(resources[idx].data);
|
|
3707
|
+
if (parsed?.v === 1 && Array.isArray(parsed.sparklines)) existing = parsed.sparklines;
|
|
3708
|
+
} catch {
|
|
3709
|
+
existing = [];
|
|
3710
|
+
}
|
|
3711
|
+
}
|
|
3712
|
+
const payload = { v: 1, sparklines: [...existing, model] };
|
|
3713
|
+
const entry = { name: SPARKLINES_RESOURCE_NAME, data: JSON.stringify(payload) };
|
|
3714
|
+
if (idx >= 0) resources[idx] = entry;
|
|
3715
|
+
else resources.push(entry);
|
|
3716
|
+
const next = { ...data, resources };
|
|
3717
|
+
api.setContent(next);
|
|
3718
|
+
return true;
|
|
3719
|
+
}
|
|
3720
|
+
function activeIds(api) {
|
|
3721
|
+
const wb = api.univer.getActiveWorkbook();
|
|
3722
|
+
const sheet = wb?.getActiveSheet();
|
|
3723
|
+
if (!wb || !sheet) return null;
|
|
3724
|
+
return { unitId: wb.getId(), sheetId: sheet.getSheetId() };
|
|
3725
|
+
}
|
|
3726
|
+
var RANGE_NOTE_STYLE9 = {
|
|
3727
|
+
fontSize: 12,
|
|
3728
|
+
color: "var(--cs-chrome-muted, #605e5c)",
|
|
3729
|
+
marginBottom: 12
|
|
3730
|
+
};
|
|
3731
|
+
var RADIO_ROW_STYLE = {
|
|
3732
|
+
display: "flex",
|
|
3733
|
+
gap: 16,
|
|
3734
|
+
marginTop: 2
|
|
3735
|
+
};
|
|
3736
|
+
var RADIO_OPT_STYLE = {
|
|
3737
|
+
display: "flex",
|
|
3738
|
+
alignItems: "center",
|
|
3739
|
+
gap: 6,
|
|
3740
|
+
cursor: "pointer"
|
|
3741
|
+
};
|
|
3742
|
+
var ERROR_STYLE2 = {
|
|
3743
|
+
fontSize: 12,
|
|
3744
|
+
color: "var(--cs-chrome-danger-fg, #b91c1c)",
|
|
3745
|
+
marginTop: 4
|
|
3746
|
+
};
|
|
3747
|
+
function InsertSparklineDialog({ api, onClose }) {
|
|
3748
|
+
const selectedA1 = useMemo12(() => {
|
|
3749
|
+
const fRange = activeRange10(api);
|
|
3750
|
+
return fRange?.getA1Notation?.() ?? "";
|
|
3751
|
+
}, [api]);
|
|
3752
|
+
const [state, setState] = useState15({
|
|
3753
|
+
type: "line",
|
|
3754
|
+
sourceA1: selectedA1,
|
|
3755
|
+
anchorA1: ""
|
|
3756
|
+
});
|
|
3757
|
+
const [error, setError] = useState15(null);
|
|
3758
|
+
const hasWorkbook = api.univer.getActiveWorkbook() != null;
|
|
3759
|
+
const update = (key, value) => {
|
|
3760
|
+
setState((prev) => ({ ...prev, [key]: value }));
|
|
3761
|
+
setError(null);
|
|
3762
|
+
};
|
|
3763
|
+
const apply = () => {
|
|
3764
|
+
const source = parseRange(state.sourceA1);
|
|
3765
|
+
if (!source) {
|
|
3766
|
+
setError("Data range must be a range like A1:F1.");
|
|
3767
|
+
return;
|
|
3768
|
+
}
|
|
3769
|
+
const anchor = parseSingleCell(state.anchorA1);
|
|
3770
|
+
if (!anchor) {
|
|
3771
|
+
setError("Location must be a single cell like G1.");
|
|
3772
|
+
return;
|
|
3773
|
+
}
|
|
3774
|
+
const ids = activeIds(api);
|
|
3775
|
+
if (!ids) {
|
|
3776
|
+
setError("No active sheet.");
|
|
3777
|
+
return;
|
|
3778
|
+
}
|
|
3779
|
+
const model = {
|
|
3780
|
+
id: `spark-${Math.random().toString(36).slice(2, 10)}`,
|
|
3781
|
+
type: state.type,
|
|
3782
|
+
unitId: ids.unitId,
|
|
3783
|
+
sheetId: ids.sheetId,
|
|
3784
|
+
source,
|
|
3785
|
+
anchor
|
|
3786
|
+
};
|
|
3787
|
+
if (insertSparkline(api, model)) onClose();
|
|
3788
|
+
else setError("Could not read the active workbook.");
|
|
3789
|
+
};
|
|
3790
|
+
return /* @__PURE__ */ jsxs16(
|
|
3791
|
+
Dialog,
|
|
3792
|
+
{
|
|
3793
|
+
title: "Insert sparkline",
|
|
3794
|
+
onClose,
|
|
3795
|
+
width: 420,
|
|
3796
|
+
"data-testid": "cs-insert-sparkline-dialog",
|
|
3797
|
+
footer: /* @__PURE__ */ jsxs16(Fragment12, { children: [
|
|
3798
|
+
/* @__PURE__ */ jsx17("button", { type: "button", style: DIALOG_BTN_SECONDARY_STYLE, onClick: onClose, children: "Cancel" }),
|
|
3799
|
+
/* @__PURE__ */ jsx17(
|
|
3800
|
+
"button",
|
|
3801
|
+
{
|
|
3802
|
+
type: "button",
|
|
3803
|
+
style: DIALOG_BTN_PRIMARY_STYLE,
|
|
3804
|
+
"data-testid": "cs-insert-sparkline-apply",
|
|
3805
|
+
disabled: !hasWorkbook,
|
|
3806
|
+
onClick: apply,
|
|
3807
|
+
children: "Insert"
|
|
3808
|
+
}
|
|
3809
|
+
)
|
|
3810
|
+
] }),
|
|
3811
|
+
children: [
|
|
3812
|
+
/* @__PURE__ */ jsx17("div", { style: RANGE_NOTE_STYLE9, children: "Draws an in-cell mini-chart in the location cell from the values in the data range." }),
|
|
3813
|
+
/* @__PURE__ */ jsxs16("label", { style: DIALOG_FIELD_STYLE, children: [
|
|
3814
|
+
/* @__PURE__ */ jsx17("span", { style: DIALOG_LABEL_STYLE, children: "Type" }),
|
|
3815
|
+
/* @__PURE__ */ jsx17(
|
|
3816
|
+
"div",
|
|
3817
|
+
{
|
|
3818
|
+
style: RADIO_ROW_STYLE,
|
|
3819
|
+
role: "radiogroup",
|
|
3820
|
+
"aria-label": "Sparkline type",
|
|
3821
|
+
"data-testid": "cs-insert-sparkline-type",
|
|
3822
|
+
children: TYPE_OPTIONS.map((opt) => /* @__PURE__ */ jsxs16("label", { style: RADIO_OPT_STYLE, children: [
|
|
3823
|
+
/* @__PURE__ */ jsx17(
|
|
3824
|
+
"input",
|
|
3825
|
+
{
|
|
3826
|
+
type: "radio",
|
|
3827
|
+
name: "cs-sparkline-type",
|
|
3828
|
+
value: opt.value,
|
|
3829
|
+
"data-testid": `cs-insert-sparkline-type-${opt.value}`,
|
|
3830
|
+
checked: state.type === opt.value,
|
|
3831
|
+
onChange: () => update("type", opt.value)
|
|
3832
|
+
}
|
|
3833
|
+
),
|
|
3834
|
+
/* @__PURE__ */ jsx17("span", { children: opt.label })
|
|
3835
|
+
] }, opt.value))
|
|
3836
|
+
}
|
|
3837
|
+
)
|
|
3838
|
+
] }),
|
|
3839
|
+
/* @__PURE__ */ jsxs16("label", { style: DIALOG_FIELD_STYLE, children: [
|
|
3840
|
+
/* @__PURE__ */ jsx17("span", { style: DIALOG_LABEL_STYLE, children: "Data range" }),
|
|
3841
|
+
/* @__PURE__ */ jsx17(
|
|
3842
|
+
"input",
|
|
3843
|
+
{
|
|
3844
|
+
style: DIALOG_INPUT_STYLE,
|
|
3845
|
+
"data-testid": "cs-insert-sparkline-source",
|
|
3846
|
+
value: state.sourceA1,
|
|
3847
|
+
placeholder: "A1:F1",
|
|
3848
|
+
spellCheck: false,
|
|
3849
|
+
onChange: (e) => update("sourceA1", e.target.value.toUpperCase())
|
|
3850
|
+
}
|
|
3851
|
+
)
|
|
3852
|
+
] }),
|
|
3853
|
+
/* @__PURE__ */ jsxs16("label", { style: DIALOG_FIELD_STYLE, children: [
|
|
3854
|
+
/* @__PURE__ */ jsx17("span", { style: DIALOG_LABEL_STYLE, children: "Location" }),
|
|
3855
|
+
/* @__PURE__ */ jsx17(
|
|
3856
|
+
"input",
|
|
3857
|
+
{
|
|
3858
|
+
style: DIALOG_INPUT_STYLE,
|
|
3859
|
+
"data-testid": "cs-insert-sparkline-anchor",
|
|
3860
|
+
value: state.anchorA1,
|
|
3861
|
+
placeholder: "G1",
|
|
3862
|
+
spellCheck: false,
|
|
3863
|
+
onChange: (e) => update("anchorA1", e.target.value.toUpperCase())
|
|
3864
|
+
}
|
|
3865
|
+
)
|
|
3866
|
+
] }),
|
|
3867
|
+
error && /* @__PURE__ */ jsx17("div", { style: ERROR_STYLE2, "data-testid": "cs-insert-sparkline-error", children: error })
|
|
3868
|
+
]
|
|
3869
|
+
}
|
|
3870
|
+
);
|
|
3871
|
+
}
|
|
3872
|
+
|
|
3873
|
+
// src/chrome/InsertPivotDialog.tsx
|
|
3874
|
+
import { useMemo as useMemo13, useState as useState16 } from "react";
|
|
3875
|
+
import { Fragment as Fragment13, jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
3876
|
+
var AGG_OPTIONS = [
|
|
3877
|
+
{ value: "sum", label: "Sum" },
|
|
3878
|
+
{ value: "count", label: "Count" },
|
|
3879
|
+
{ value: "average", label: "Average" },
|
|
3880
|
+
{ value: "min", label: "Min" },
|
|
3881
|
+
{ value: "max", label: "Max" }
|
|
3882
|
+
];
|
|
3883
|
+
function activeRange11(api) {
|
|
3884
|
+
return api.univer.getActiveWorkbook()?.getActiveSheet()?.getActiveRange() ?? null;
|
|
3885
|
+
}
|
|
3886
|
+
function readSource(api) {
|
|
3887
|
+
const range = activeRange11(api);
|
|
3888
|
+
const grid = range?.getValues?.() ?? [];
|
|
3889
|
+
return Array.isArray(grid) ? grid : [];
|
|
3890
|
+
}
|
|
3891
|
+
function columnLabel(index, headerRow, useHeader) {
|
|
3892
|
+
if (useHeader) {
|
|
3893
|
+
const raw = headerRow?.[index];
|
|
3894
|
+
if (raw != null && String(raw).trim().length > 0) return String(raw);
|
|
3895
|
+
}
|
|
3896
|
+
let n = index;
|
|
3897
|
+
let label = "";
|
|
3898
|
+
do {
|
|
3899
|
+
label = String.fromCharCode(65 + n % 26) + label;
|
|
3900
|
+
n = Math.floor(n / 26) - 1;
|
|
3901
|
+
} while (n >= 0);
|
|
3902
|
+
return `Column ${label}`;
|
|
3903
|
+
}
|
|
3904
|
+
function toNumber2(v) {
|
|
3905
|
+
if (typeof v === "number") return Number.isFinite(v) ? v : null;
|
|
3906
|
+
if (typeof v === "boolean") return v ? 1 : 0;
|
|
3907
|
+
if (typeof v === "string" && v.trim() !== "") {
|
|
3908
|
+
const n = Number(v);
|
|
3909
|
+
return Number.isFinite(n) ? n : null;
|
|
3910
|
+
}
|
|
3911
|
+
return null;
|
|
3912
|
+
}
|
|
3913
|
+
function aggregate(fn, values, rowCount) {
|
|
3914
|
+
if (fn === "count") return rowCount;
|
|
3915
|
+
if (values.length === 0) return 0;
|
|
3916
|
+
switch (fn) {
|
|
3917
|
+
case "sum":
|
|
3918
|
+
return values.reduce((a, b) => a + b, 0);
|
|
3919
|
+
case "average":
|
|
3920
|
+
return values.reduce((a, b) => a + b, 0) / values.length;
|
|
3921
|
+
case "min":
|
|
3922
|
+
return Math.min(...values);
|
|
3923
|
+
case "max":
|
|
3924
|
+
return Math.max(...values);
|
|
3925
|
+
}
|
|
3926
|
+
}
|
|
3927
|
+
function buildPivot(source, useHeader, groupCol, valueCol, fn) {
|
|
3928
|
+
if (source.length === 0) return null;
|
|
3929
|
+
const headerRow = useHeader ? source[0] : void 0;
|
|
3930
|
+
const dataRows = useHeader ? source.slice(1) : source;
|
|
3931
|
+
if (dataRows.length === 0) return null;
|
|
3932
|
+
const order = [];
|
|
3933
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
3934
|
+
const counts = /* @__PURE__ */ new Map();
|
|
3935
|
+
for (const row of dataRows) {
|
|
3936
|
+
const rawKey = row[groupCol];
|
|
3937
|
+
const key = rawKey == null ? "" : String(rawKey);
|
|
3938
|
+
if (!buckets.has(key)) {
|
|
3939
|
+
buckets.set(key, []);
|
|
3940
|
+
counts.set(key, 0);
|
|
3941
|
+
order.push(key);
|
|
3942
|
+
}
|
|
3943
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
3944
|
+
const num = toNumber2(row[valueCol]);
|
|
3945
|
+
if (num != null) buckets.get(key).push(num);
|
|
3946
|
+
}
|
|
3947
|
+
const groupHeader = columnLabel(groupCol, headerRow, useHeader);
|
|
3948
|
+
const valueHeader = columnLabel(valueCol, headerRow, useHeader);
|
|
3949
|
+
const aggLabel = AGG_OPTIONS.find((o) => o.value === fn)?.label ?? "Sum";
|
|
3950
|
+
const grid = [[groupHeader, `${aggLabel} of ${valueHeader}`]];
|
|
3951
|
+
for (const key of order) {
|
|
3952
|
+
grid.push([key === "" ? "(blank)" : key, aggregate(fn, buckets.get(key), counts.get(key))]);
|
|
3953
|
+
}
|
|
3954
|
+
return { grid };
|
|
3955
|
+
}
|
|
3956
|
+
function toA1(row, col) {
|
|
3957
|
+
let n = col;
|
|
3958
|
+
let letters = "";
|
|
3959
|
+
do {
|
|
3960
|
+
letters = String.fromCharCode(65 + n % 26) + letters;
|
|
3961
|
+
n = Math.floor(n / 26) - 1;
|
|
3962
|
+
} while (n >= 0);
|
|
3963
|
+
return `${letters}${row + 1}`;
|
|
3964
|
+
}
|
|
3965
|
+
var RANGE_NOTE_STYLE10 = {
|
|
3966
|
+
fontSize: 12,
|
|
3967
|
+
color: "var(--cs-chrome-muted, #605e5c)",
|
|
3968
|
+
marginBottom: 12
|
|
3969
|
+
};
|
|
3970
|
+
var CHECK_STYLE5 = {
|
|
3971
|
+
display: "flex",
|
|
3972
|
+
alignItems: "center",
|
|
3973
|
+
gap: 6,
|
|
3974
|
+
marginBottom: 12,
|
|
3975
|
+
cursor: "pointer"
|
|
3976
|
+
};
|
|
3977
|
+
var RADIO_ROW_STYLE2 = {
|
|
3978
|
+
display: "flex",
|
|
3979
|
+
alignItems: "center",
|
|
3980
|
+
gap: 6,
|
|
3981
|
+
marginBottom: 6,
|
|
3982
|
+
cursor: "pointer"
|
|
3983
|
+
};
|
|
3984
|
+
function InsertPivotDialog({ api, onClose }) {
|
|
3985
|
+
const source = useMemo13(() => readSource(api), [api]);
|
|
3986
|
+
const rangeLabel = useMemo13(() => {
|
|
3987
|
+
const r = activeRange11(api);
|
|
3988
|
+
return r?.getA1Notation?.() ?? null;
|
|
3989
|
+
}, [api]);
|
|
3990
|
+
const hasSource = source.length > 0 && (source[0]?.length ?? 0) > 0;
|
|
3991
|
+
const colCount = hasSource ? source[0].length : 0;
|
|
3992
|
+
const [state, setState] = useState16({
|
|
3993
|
+
useHeader: true,
|
|
3994
|
+
groupCol: 0,
|
|
3995
|
+
valueCol: colCount > 1 ? 1 : 0,
|
|
3996
|
+
aggFn: "sum",
|
|
3997
|
+
destination: "newSheet",
|
|
3998
|
+
sheetName: "Pivot",
|
|
3999
|
+
anchor: ""
|
|
4000
|
+
});
|
|
4001
|
+
const [error, setError] = useState16(null);
|
|
4002
|
+
const update = (key, value) => {
|
|
4003
|
+
setState((prev) => ({ ...prev, [key]: value }));
|
|
4004
|
+
setError(null);
|
|
4005
|
+
};
|
|
4006
|
+
const columnOptions = useMemo13(() => {
|
|
4007
|
+
const header = state.useHeader ? source[0] : void 0;
|
|
4008
|
+
return Array.from({ length: colCount }, (_, i) => ({
|
|
4009
|
+
value: i,
|
|
4010
|
+
label: columnLabel(i, header, state.useHeader)
|
|
4011
|
+
}));
|
|
4012
|
+
}, [source, colCount, state.useHeader]);
|
|
4013
|
+
const apply = () => {
|
|
4014
|
+
const pivot = buildPivot(source, state.useHeader, state.groupCol, state.valueCol, state.aggFn);
|
|
4015
|
+
if (!pivot) {
|
|
4016
|
+
setError("The selection has no data rows to summarize.");
|
|
4017
|
+
return;
|
|
4018
|
+
}
|
|
4019
|
+
const wb = api.univer.getActiveWorkbook();
|
|
4020
|
+
if (!wb) {
|
|
4021
|
+
setError("No active workbook.");
|
|
4022
|
+
return;
|
|
4023
|
+
}
|
|
4024
|
+
try {
|
|
4025
|
+
if (state.destination === "newSheet") {
|
|
4026
|
+
const name = state.sheetName.trim() || "Pivot";
|
|
4027
|
+
const rows = Math.max(pivot.grid.length + 2, 10);
|
|
4028
|
+
const sheet = wb.create(name, rows, 4);
|
|
4029
|
+
const target = sheet.getRange(`A1:${toA1(pivot.grid.length - 1, 1)}`);
|
|
4030
|
+
target.setValues(pivot.grid);
|
|
4031
|
+
} else {
|
|
4032
|
+
const anchor = state.anchor.trim().toUpperCase();
|
|
4033
|
+
if (!/^[A-Z]+[0-9]+$/.test(anchor)) {
|
|
4034
|
+
setError("Enter a valid top-left cell, e.g. E1.");
|
|
4035
|
+
return;
|
|
4036
|
+
}
|
|
4037
|
+
const sheet = wb.getActiveSheet();
|
|
4038
|
+
if (!sheet) {
|
|
4039
|
+
setError("No active sheet to write into.");
|
|
4040
|
+
return;
|
|
4041
|
+
}
|
|
4042
|
+
const m = /^([A-Z]+)([0-9]+)$/.exec(anchor);
|
|
4043
|
+
let col = 0;
|
|
4044
|
+
for (const ch of m[1]) col = col * 26 + (ch.charCodeAt(0) - 64);
|
|
4045
|
+
col -= 1;
|
|
4046
|
+
const startRow = Number(m[2]) - 1;
|
|
4047
|
+
const end = toA1(startRow + pivot.grid.length - 1, col + 1);
|
|
4048
|
+
sheet.getRange(`${anchor}:${end}`).setValues(pivot.grid);
|
|
4049
|
+
}
|
|
4050
|
+
} catch (e) {
|
|
4051
|
+
setError(e instanceof Error ? e.message : "Failed to create the pivot table.");
|
|
4052
|
+
return;
|
|
4053
|
+
}
|
|
4054
|
+
onClose();
|
|
4055
|
+
};
|
|
4056
|
+
return /* @__PURE__ */ jsxs17(
|
|
4057
|
+
Dialog,
|
|
4058
|
+
{
|
|
4059
|
+
title: "Insert pivot table",
|
|
4060
|
+
onClose,
|
|
4061
|
+
width: 460,
|
|
4062
|
+
"data-testid": "cs-insert-pivot-dialog",
|
|
4063
|
+
footer: /* @__PURE__ */ jsxs17(Fragment13, { children: [
|
|
4064
|
+
/* @__PURE__ */ jsx18("button", { type: "button", style: DIALOG_BTN_SECONDARY_STYLE, onClick: onClose, children: "Cancel" }),
|
|
4065
|
+
/* @__PURE__ */ jsx18(
|
|
4066
|
+
"button",
|
|
4067
|
+
{
|
|
4068
|
+
type: "button",
|
|
4069
|
+
style: DIALOG_BTN_PRIMARY_STYLE,
|
|
4070
|
+
"data-testid": "cs-insert-pivot-create",
|
|
4071
|
+
disabled: !hasSource,
|
|
4072
|
+
onClick: apply,
|
|
4073
|
+
children: "Create"
|
|
4074
|
+
}
|
|
4075
|
+
)
|
|
4076
|
+
] }),
|
|
4077
|
+
children: [
|
|
4078
|
+
hasSource ? /* @__PURE__ */ jsxs17("div", { style: RANGE_NOTE_STYLE10, "data-testid": "cs-insert-pivot-source", children: [
|
|
4079
|
+
"Source data ",
|
|
4080
|
+
/* @__PURE__ */ jsx18("strong", { children: rangeLabel ?? "the current selection" }),
|
|
4081
|
+
" (",
|
|
4082
|
+
source.length,
|
|
4083
|
+
" rows \xD7 ",
|
|
4084
|
+
colCount,
|
|
4085
|
+
" columns)"
|
|
4086
|
+
] }) : /* @__PURE__ */ jsx18("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." }),
|
|
4087
|
+
/* @__PURE__ */ jsxs17("label", { style: CHECK_STYLE5, "data-testid": "cs-insert-pivot-header-label", children: [
|
|
4088
|
+
/* @__PURE__ */ jsx18(
|
|
4089
|
+
"input",
|
|
4090
|
+
{
|
|
4091
|
+
type: "checkbox",
|
|
4092
|
+
"data-testid": "cs-insert-pivot-header",
|
|
4093
|
+
checked: state.useHeader,
|
|
4094
|
+
onChange: (e) => update("useHeader", e.target.checked),
|
|
4095
|
+
disabled: !hasSource
|
|
4096
|
+
}
|
|
4097
|
+
),
|
|
4098
|
+
/* @__PURE__ */ jsx18("span", { children: "First row is a header" })
|
|
4099
|
+
] }),
|
|
4100
|
+
/* @__PURE__ */ jsxs17("label", { style: DIALOG_FIELD_STYLE, children: [
|
|
4101
|
+
/* @__PURE__ */ jsx18("span", { style: DIALOG_LABEL_STYLE, children: "Rows (group by)" }),
|
|
4102
|
+
/* @__PURE__ */ jsx18(
|
|
4103
|
+
"select",
|
|
4104
|
+
{
|
|
4105
|
+
style: DIALOG_INPUT_STYLE,
|
|
4106
|
+
"data-testid": "cs-insert-pivot-group-col",
|
|
4107
|
+
value: state.groupCol,
|
|
4108
|
+
disabled: !hasSource,
|
|
4109
|
+
onChange: (e) => update("groupCol", Number(e.target.value)),
|
|
4110
|
+
children: columnOptions.map((opt) => /* @__PURE__ */ jsx18("option", { value: opt.value, children: opt.label }, opt.value))
|
|
4111
|
+
}
|
|
4112
|
+
)
|
|
4113
|
+
] }),
|
|
4114
|
+
/* @__PURE__ */ jsxs17("label", { style: DIALOG_FIELD_STYLE, children: [
|
|
4115
|
+
/* @__PURE__ */ jsx18("span", { style: DIALOG_LABEL_STYLE, children: "Values" }),
|
|
4116
|
+
/* @__PURE__ */ jsx18(
|
|
4117
|
+
"select",
|
|
4118
|
+
{
|
|
4119
|
+
style: DIALOG_INPUT_STYLE,
|
|
4120
|
+
"data-testid": "cs-insert-pivot-value-col",
|
|
4121
|
+
value: state.valueCol,
|
|
4122
|
+
disabled: !hasSource,
|
|
4123
|
+
onChange: (e) => update("valueCol", Number(e.target.value)),
|
|
4124
|
+
children: columnOptions.map((opt) => /* @__PURE__ */ jsx18("option", { value: opt.value, children: opt.label }, opt.value))
|
|
4125
|
+
}
|
|
4126
|
+
)
|
|
4127
|
+
] }),
|
|
4128
|
+
/* @__PURE__ */ jsxs17("label", { style: DIALOG_FIELD_STYLE, children: [
|
|
4129
|
+
/* @__PURE__ */ jsx18("span", { style: DIALOG_LABEL_STYLE, children: "Summarize by" }),
|
|
4130
|
+
/* @__PURE__ */ jsx18(
|
|
4131
|
+
"select",
|
|
4132
|
+
{
|
|
4133
|
+
style: DIALOG_INPUT_STYLE,
|
|
4134
|
+
"data-testid": "cs-insert-pivot-agg",
|
|
4135
|
+
value: state.aggFn,
|
|
4136
|
+
disabled: !hasSource,
|
|
4137
|
+
onChange: (e) => update("aggFn", e.target.value),
|
|
4138
|
+
children: AGG_OPTIONS.map((opt) => /* @__PURE__ */ jsx18("option", { value: opt.value, children: opt.label }, opt.value))
|
|
4139
|
+
}
|
|
4140
|
+
)
|
|
4141
|
+
] }),
|
|
4142
|
+
/* @__PURE__ */ jsxs17("div", { style: DIALOG_FIELD_STYLE, children: [
|
|
4143
|
+
/* @__PURE__ */ jsx18("span", { style: DIALOG_LABEL_STYLE, children: "Destination" }),
|
|
4144
|
+
/* @__PURE__ */ jsxs17("label", { style: RADIO_ROW_STYLE2, children: [
|
|
4145
|
+
/* @__PURE__ */ jsx18(
|
|
4146
|
+
"input",
|
|
4147
|
+
{
|
|
4148
|
+
type: "radio",
|
|
4149
|
+
name: "cs-insert-pivot-destination",
|
|
4150
|
+
"data-testid": "cs-insert-pivot-dest-new",
|
|
4151
|
+
checked: state.destination === "newSheet",
|
|
4152
|
+
onChange: () => update("destination", "newSheet")
|
|
4153
|
+
}
|
|
4154
|
+
),
|
|
4155
|
+
/* @__PURE__ */ jsx18("span", { children: "New sheet" })
|
|
4156
|
+
] }),
|
|
4157
|
+
/* @__PURE__ */ jsxs17("label", { style: RADIO_ROW_STYLE2, children: [
|
|
4158
|
+
/* @__PURE__ */ jsx18(
|
|
4159
|
+
"input",
|
|
4160
|
+
{
|
|
4161
|
+
type: "radio",
|
|
4162
|
+
name: "cs-insert-pivot-destination",
|
|
4163
|
+
"data-testid": "cs-insert-pivot-dest-existing",
|
|
4164
|
+
checked: state.destination === "existing",
|
|
4165
|
+
onChange: () => update("destination", "existing")
|
|
4166
|
+
}
|
|
4167
|
+
),
|
|
4168
|
+
/* @__PURE__ */ jsx18("span", { children: "Existing sheet (choose top-left cell)" })
|
|
4169
|
+
] })
|
|
4170
|
+
] }),
|
|
4171
|
+
state.destination === "newSheet" ? /* @__PURE__ */ jsxs17("label", { style: DIALOG_FIELD_STYLE, children: [
|
|
4172
|
+
/* @__PURE__ */ jsx18("span", { style: DIALOG_LABEL_STYLE, children: "New sheet name" }),
|
|
4173
|
+
/* @__PURE__ */ jsx18(
|
|
4174
|
+
"input",
|
|
4175
|
+
{
|
|
4176
|
+
style: DIALOG_INPUT_STYLE,
|
|
4177
|
+
"data-testid": "cs-insert-pivot-sheet-name",
|
|
4178
|
+
value: state.sheetName,
|
|
4179
|
+
placeholder: "Pivot",
|
|
4180
|
+
onChange: (e) => update("sheetName", e.target.value)
|
|
4181
|
+
}
|
|
4182
|
+
)
|
|
4183
|
+
] }) : /* @__PURE__ */ jsxs17("label", { style: DIALOG_FIELD_STYLE, children: [
|
|
4184
|
+
/* @__PURE__ */ jsx18("span", { style: DIALOG_LABEL_STYLE, children: "Top-left cell (on the active sheet)" }),
|
|
4185
|
+
/* @__PURE__ */ jsx18(
|
|
4186
|
+
"input",
|
|
4187
|
+
{
|
|
4188
|
+
style: DIALOG_INPUT_STYLE,
|
|
4189
|
+
"data-testid": "cs-insert-pivot-anchor",
|
|
4190
|
+
value: state.anchor,
|
|
4191
|
+
placeholder: "e.g. E1",
|
|
4192
|
+
onChange: (e) => update("anchor", e.target.value)
|
|
4193
|
+
}
|
|
4194
|
+
)
|
|
4195
|
+
] }),
|
|
4196
|
+
error && /* @__PURE__ */ jsx18(
|
|
4197
|
+
"div",
|
|
4198
|
+
{
|
|
4199
|
+
style: {
|
|
4200
|
+
...RANGE_NOTE_STYLE10,
|
|
4201
|
+
color: "var(--cs-chrome-danger, #b3261e)",
|
|
4202
|
+
marginBottom: 0
|
|
4203
|
+
},
|
|
4204
|
+
"data-testid": "cs-insert-pivot-error",
|
|
4205
|
+
role: "alert",
|
|
4206
|
+
children: error
|
|
4207
|
+
}
|
|
4208
|
+
)
|
|
4209
|
+
]
|
|
4210
|
+
}
|
|
4211
|
+
);
|
|
4212
|
+
}
|
|
4213
|
+
|
|
1131
4214
|
// src/chrome/dialog-context.tsx
|
|
1132
|
-
import { jsx as
|
|
4215
|
+
import { jsx as jsx19, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
1133
4216
|
var BUILT_IN_DIALOGS = {
|
|
1134
|
-
"format-cells": FormatCellsDialog
|
|
4217
|
+
"format-cells": FormatCellsDialog,
|
|
4218
|
+
"data-validation": DataValidationDialog,
|
|
4219
|
+
"conditional-formatting": ConditionalFormattingDialog,
|
|
4220
|
+
"custom-sort": CustomSortDialog,
|
|
4221
|
+
"paste-special": PasteSpecialDialog,
|
|
4222
|
+
"insert-function": InsertFunctionDialog,
|
|
4223
|
+
"name-manager": NameManagerDialog,
|
|
4224
|
+
"insert-cells": InsertCellsDialog,
|
|
4225
|
+
"delete-cells": DeleteCellsDialog,
|
|
4226
|
+
"goal-seek": GoalSeekDialog,
|
|
4227
|
+
"insert-chart": InsertChartDialog,
|
|
4228
|
+
"insert-sparkline": InsertSparklineDialog,
|
|
4229
|
+
"insert-pivot": InsertPivotDialog
|
|
1135
4230
|
};
|
|
1136
4231
|
function hasBuiltInDialog(kind) {
|
|
1137
4232
|
return kind in BUILT_IN_DIALOGS || kind === "find-replace";
|
|
@@ -1158,10 +4253,10 @@ function DialogProvider({
|
|
|
1158
4253
|
onOpenFindReplace,
|
|
1159
4254
|
children
|
|
1160
4255
|
}) {
|
|
1161
|
-
const [active, setActive] =
|
|
4256
|
+
const [active, setActive] = useState17(null);
|
|
1162
4257
|
const overrides = extensions?.dialogs;
|
|
1163
|
-
const hostOwned =
|
|
1164
|
-
const openDialog =
|
|
4258
|
+
const hostOwned = useMemo14(() => new Set(hostOwnedDialogs ?? []), [hostOwnedDialogs]);
|
|
4259
|
+
const openDialog = useCallback2(
|
|
1165
4260
|
(kind, context) => {
|
|
1166
4261
|
if (overrides?.[kind]) {
|
|
1167
4262
|
setActive({ kind, context });
|
|
@@ -1186,22 +4281,22 @@ function DialogProvider({
|
|
|
1186
4281
|
},
|
|
1187
4282
|
[overrides, onDialogRequest, hostOwned, onOpenFindReplace]
|
|
1188
4283
|
);
|
|
1189
|
-
const closeDialog =
|
|
1190
|
-
const canOpen =
|
|
4284
|
+
const closeDialog = useCallback2(() => setActive(null), []);
|
|
4285
|
+
const canOpen = useCallback2(
|
|
1191
4286
|
(kind) => !!overrides?.[kind] || hasBuiltInDialog(kind) || !!onDialogRequest || hostOwned.has(kind),
|
|
1192
4287
|
[overrides, onDialogRequest, hostOwned]
|
|
1193
4288
|
);
|
|
1194
|
-
const controllerCanOpen =
|
|
4289
|
+
const controllerCanOpen = useCallback2(
|
|
1195
4290
|
(kind) => kind === "find-replace" ? !!onOpenFindReplace || canOpen(kind) : canOpen(kind),
|
|
1196
4291
|
[canOpen, onOpenFindReplace]
|
|
1197
4292
|
);
|
|
1198
|
-
const controller =
|
|
4293
|
+
const controller = useMemo14(
|
|
1199
4294
|
() => ({ openDialog, closeDialog, canOpen: controllerCanOpen }),
|
|
1200
4295
|
[openDialog, closeDialog, controllerCanOpen]
|
|
1201
4296
|
);
|
|
1202
|
-
return /* @__PURE__ */
|
|
4297
|
+
return /* @__PURE__ */ jsxs18(DialogContext.Provider, { value: controller, children: [
|
|
1203
4298
|
children,
|
|
1204
|
-
/* @__PURE__ */
|
|
4299
|
+
/* @__PURE__ */ jsx19(DialogHost, { api, active, overrides, onClose: closeDialog })
|
|
1205
4300
|
] });
|
|
1206
4301
|
}
|
|
1207
4302
|
function DialogHost({ api, active, overrides, onClose }) {
|
|
@@ -1209,11 +4304,11 @@ function DialogHost({ api, active, overrides, onClose }) {
|
|
|
1209
4304
|
if (active.kind === "find-replace") return null;
|
|
1210
4305
|
const Component = overrides?.[active.kind] ?? BUILT_IN_DIALOGS[active.kind];
|
|
1211
4306
|
if (!Component) return null;
|
|
1212
|
-
return /* @__PURE__ */
|
|
4307
|
+
return /* @__PURE__ */ jsx19(Component, { api, onClose, context: active.context });
|
|
1213
4308
|
}
|
|
1214
4309
|
|
|
1215
4310
|
// src/chrome/Toolbar.tsx
|
|
1216
|
-
import { Fragment as
|
|
4311
|
+
import { Fragment as Fragment14, jsx as jsx20, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
1217
4312
|
var NO_STYLE = {
|
|
1218
4313
|
bold: false,
|
|
1219
4314
|
italic: false,
|
|
@@ -1547,7 +4642,7 @@ function isActive(id, s) {
|
|
|
1547
4642
|
}
|
|
1548
4643
|
function Toolbar({ api, features, extensions }) {
|
|
1549
4644
|
const dialogs = useDialogs();
|
|
1550
|
-
const [active, setActive] =
|
|
4645
|
+
const [active, setActive] = useState18(NO_STYLE);
|
|
1551
4646
|
useEffect6(() => {
|
|
1552
4647
|
ensureChromeFonts();
|
|
1553
4648
|
}, []);
|
|
@@ -1584,7 +4679,7 @@ function Toolbar({ api, features, extensions }) {
|
|
|
1584
4679
|
const renderActionButton = (a) => {
|
|
1585
4680
|
const on = isActive(a.id, active);
|
|
1586
4681
|
const baseBg = on ? "var(--cs-chrome-active, #e6f3f7)" : "transparent";
|
|
1587
|
-
return /* @__PURE__ */
|
|
4682
|
+
return /* @__PURE__ */ jsx20(
|
|
1588
4683
|
"button",
|
|
1589
4684
|
{
|
|
1590
4685
|
type: "button",
|
|
@@ -1610,12 +4705,12 @@ function Toolbar({ api, features, extensions }) {
|
|
|
1610
4705
|
onMouseLeave: (e) => {
|
|
1611
4706
|
e.currentTarget.style.background = baseBg;
|
|
1612
4707
|
},
|
|
1613
|
-
children: /* @__PURE__ */
|
|
4708
|
+
children: /* @__PURE__ */ jsx20(Icon, { name: a.icon, size: 20 })
|
|
1614
4709
|
},
|
|
1615
4710
|
a.id
|
|
1616
4711
|
);
|
|
1617
4712
|
};
|
|
1618
|
-
const renderIconButton = (id, label, icon, onPress, testid) => /* @__PURE__ */
|
|
4713
|
+
const renderIconButton = (id, label, icon, onPress, testid) => /* @__PURE__ */ jsx20(
|
|
1619
4714
|
"button",
|
|
1620
4715
|
{
|
|
1621
4716
|
type: "button",
|
|
@@ -1635,7 +4730,7 @@ function Toolbar({ api, features, extensions }) {
|
|
|
1635
4730
|
onMouseLeave: (e) => {
|
|
1636
4731
|
e.currentTarget.style.background = "transparent";
|
|
1637
4732
|
},
|
|
1638
|
-
children: /* @__PURE__ */
|
|
4733
|
+
children: /* @__PURE__ */ jsx20(Icon, { name: icon, size: 20 })
|
|
1639
4734
|
},
|
|
1640
4735
|
id
|
|
1641
4736
|
);
|
|
@@ -1646,8 +4741,8 @@ function Toolbar({ api, features, extensions }) {
|
|
|
1646
4741
|
if (visible.length === 0) return null;
|
|
1647
4742
|
const showDivider = firstGroupRendered;
|
|
1648
4743
|
firstGroupRendered = true;
|
|
1649
|
-
return /* @__PURE__ */
|
|
1650
|
-
showDivider && /* @__PURE__ */
|
|
4744
|
+
return /* @__PURE__ */ jsxs19("span", { style: { display: "inline-flex", alignItems: "center" }, children: [
|
|
4745
|
+
showDivider && /* @__PURE__ */ jsx20("span", { style: DIVIDER_STYLE, "aria-hidden": true }),
|
|
1651
4746
|
visible.map(renderActionButton)
|
|
1652
4747
|
] }, gi);
|
|
1653
4748
|
});
|
|
@@ -1668,9 +4763,9 @@ function Toolbar({ api, features, extensions }) {
|
|
|
1668
4763
|
if (e.command) void api.executeCommand(e.command, e.commandParams);
|
|
1669
4764
|
else e.onClick?.(api);
|
|
1670
4765
|
};
|
|
1671
|
-
return /* @__PURE__ */
|
|
1672
|
-
showFont && /* @__PURE__ */
|
|
1673
|
-
/* @__PURE__ */
|
|
4766
|
+
return /* @__PURE__ */ jsxs19("div", { style: BAR_STYLE, "data-testid": "casual-sheets-toolbar", role: "toolbar", "aria-label": "Editor", children: [
|
|
4767
|
+
showFont && /* @__PURE__ */ jsxs19(Fragment14, { children: [
|
|
4768
|
+
/* @__PURE__ */ jsx20(
|
|
1674
4769
|
"select",
|
|
1675
4770
|
{
|
|
1676
4771
|
"aria-label": "Font family",
|
|
@@ -1679,10 +4774,10 @@ function Toolbar({ api, features, extensions }) {
|
|
|
1679
4774
|
value: familyValue,
|
|
1680
4775
|
disabled: !api,
|
|
1681
4776
|
onChange: (e) => dispatch("sheet.command.set-range-font-family", { value: e.target.value }),
|
|
1682
|
-
children: familyOptions.map((f) => /* @__PURE__ */
|
|
4777
|
+
children: familyOptions.map((f) => /* @__PURE__ */ jsx20("option", { value: f, children: f }, f))
|
|
1683
4778
|
}
|
|
1684
4779
|
),
|
|
1685
|
-
/* @__PURE__ */
|
|
4780
|
+
/* @__PURE__ */ jsx20(
|
|
1686
4781
|
"select",
|
|
1687
4782
|
{
|
|
1688
4783
|
"aria-label": "Font size",
|
|
@@ -1691,7 +4786,7 @@ function Toolbar({ api, features, extensions }) {
|
|
|
1691
4786
|
value: sizeValue,
|
|
1692
4787
|
disabled: !api,
|
|
1693
4788
|
onChange: (e) => applyFontSize(Number(e.target.value)),
|
|
1694
|
-
children: sizeOptions.map((s) => /* @__PURE__ */
|
|
4789
|
+
children: sizeOptions.map((s) => /* @__PURE__ */ jsx20("option", { value: s, children: s }, s))
|
|
1695
4790
|
}
|
|
1696
4791
|
),
|
|
1697
4792
|
renderIconButton(
|
|
@@ -1708,15 +4803,15 @@ function Toolbar({ api, features, extensions }) {
|
|
|
1708
4803
|
() => adjustFontSize(-1),
|
|
1709
4804
|
"cs-font-size-down"
|
|
1710
4805
|
),
|
|
1711
|
-
/* @__PURE__ */
|
|
4806
|
+
/* @__PURE__ */ jsx20("span", { style: DIVIDER_STYLE, "aria-hidden": true })
|
|
1712
4807
|
] }),
|
|
1713
4808
|
groupNodes,
|
|
1714
|
-
(showColor || showBorders) && /* @__PURE__ */
|
|
1715
|
-
showColor && /* @__PURE__ */
|
|
1716
|
-
showBorders && /* @__PURE__ */
|
|
1717
|
-
showNumberDropdown && /* @__PURE__ */
|
|
1718
|
-
/* @__PURE__ */
|
|
1719
|
-
/* @__PURE__ */
|
|
4809
|
+
(showColor || showBorders) && /* @__PURE__ */ jsx20("span", { style: DIVIDER_STYLE, "aria-hidden": true }),
|
|
4810
|
+
showColor && /* @__PURE__ */ jsx20(ColorPicker, { api }),
|
|
4811
|
+
showBorders && /* @__PURE__ */ jsx20(BordersPicker, { api }),
|
|
4812
|
+
showNumberDropdown && /* @__PURE__ */ jsxs19(Fragment14, { children: [
|
|
4813
|
+
/* @__PURE__ */ jsx20("span", { style: DIVIDER_STYLE, "aria-hidden": true }),
|
|
4814
|
+
/* @__PURE__ */ jsx20(
|
|
1720
4815
|
"select",
|
|
1721
4816
|
{
|
|
1722
4817
|
"aria-label": "Number format",
|
|
@@ -1725,16 +4820,16 @@ function Toolbar({ api, features, extensions }) {
|
|
|
1725
4820
|
value: numberFormatValue,
|
|
1726
4821
|
disabled: !api,
|
|
1727
4822
|
onChange: (e) => applyNumberFormat(NUMBER_FORMAT_PATTERNS2[e.target.value]),
|
|
1728
|
-
children: NUMBER_FORMAT_OPTIONS2.map((o) => /* @__PURE__ */
|
|
4823
|
+
children: NUMBER_FORMAT_OPTIONS2.map((o) => /* @__PURE__ */ jsx20("option", { value: o.value, children: o.label }, o.value))
|
|
1729
4824
|
}
|
|
1730
4825
|
)
|
|
1731
4826
|
] }),
|
|
1732
|
-
showAutoSum && /* @__PURE__ */
|
|
1733
|
-
/* @__PURE__ */
|
|
1734
|
-
/* @__PURE__ */
|
|
4827
|
+
showAutoSum && /* @__PURE__ */ jsxs19(Fragment14, { children: [
|
|
4828
|
+
/* @__PURE__ */ jsx20("span", { style: DIVIDER_STYLE, "aria-hidden": true }),
|
|
4829
|
+
/* @__PURE__ */ jsx20(AutoSumPicker, { api })
|
|
1735
4830
|
] }),
|
|
1736
|
-
showDialogGroup && /* @__PURE__ */
|
|
1737
|
-
/* @__PURE__ */
|
|
4831
|
+
showDialogGroup && /* @__PURE__ */ jsxs19(Fragment14, { children: [
|
|
4832
|
+
/* @__PURE__ */ jsx20("span", { style: DIVIDER_STYLE, "aria-hidden": true }),
|
|
1738
4833
|
showFormatCells && renderIconButton(
|
|
1739
4834
|
"format-cells",
|
|
1740
4835
|
"Format Cells\u2026",
|
|
@@ -1757,8 +4852,8 @@ function Toolbar({ api, features, extensions }) {
|
|
|
1757
4852
|
"cs-pivot-table"
|
|
1758
4853
|
)
|
|
1759
4854
|
] }),
|
|
1760
|
-
toolbarExt.length > 0 && /* @__PURE__ */
|
|
1761
|
-
/* @__PURE__ */
|
|
4855
|
+
toolbarExt.length > 0 && /* @__PURE__ */ jsxs19(Fragment14, { children: [
|
|
4856
|
+
/* @__PURE__ */ jsx20("span", { style: DIVIDER_STYLE, "aria-hidden": true }),
|
|
1762
4857
|
toolbarExt.map(
|
|
1763
4858
|
(e) => renderIconButton(e.id, e.label, e.icon, () => runExt(e), `cs-ext-${e.id}`)
|
|
1764
4859
|
)
|
|
@@ -1769,16 +4864,16 @@ function Toolbar({ api, features, extensions }) {
|
|
|
1769
4864
|
// src/chrome/FormulaBar.tsx
|
|
1770
4865
|
import {
|
|
1771
4866
|
useEffect as useEffect8,
|
|
1772
|
-
useMemo as
|
|
4867
|
+
useMemo as useMemo15,
|
|
1773
4868
|
useRef as useRef6,
|
|
1774
|
-
useState as
|
|
4869
|
+
useState as useState20
|
|
1775
4870
|
} from "react";
|
|
1776
4871
|
import { ICommandService as ICommandService3 } from "@univerjs/core";
|
|
1777
4872
|
|
|
1778
4873
|
// src/chrome/NameBox.tsx
|
|
1779
|
-
import { useEffect as useEffect7, useRef as useRef5, useState as
|
|
4874
|
+
import { useEffect as useEffect7, useRef as useRef5, useState as useState19 } from "react";
|
|
1780
4875
|
import { ICommandService as ICommandService2 } from "@univerjs/core";
|
|
1781
|
-
import { jsx as
|
|
4876
|
+
import { jsx as jsx21, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
1782
4877
|
function readDefinedNames(api) {
|
|
1783
4878
|
if (!api) return [];
|
|
1784
4879
|
try {
|
|
@@ -1888,13 +4983,13 @@ var ITEM_REF_STYLE = {
|
|
|
1888
4983
|
flex: "0 0 auto"
|
|
1889
4984
|
};
|
|
1890
4985
|
function NameBox({ api }) {
|
|
1891
|
-
const [ref, setRef] =
|
|
1892
|
-
const [draft, setDraft] =
|
|
4986
|
+
const [ref, setRef] = useState19("");
|
|
4987
|
+
const [draft, setDraft] = useState19(null);
|
|
1893
4988
|
const draftRef = useRef5(null);
|
|
1894
4989
|
draftRef.current = draft;
|
|
1895
4990
|
const inputRef = useRef5(null);
|
|
1896
|
-
const [open, setOpen] =
|
|
1897
|
-
const [names, setNames] =
|
|
4991
|
+
const [open, setOpen] = useState19(false);
|
|
4992
|
+
const [names, setNames] = useState19([]);
|
|
1898
4993
|
const rootRef = useRef5(null);
|
|
1899
4994
|
useEffect7(() => {
|
|
1900
4995
|
if (!api) return;
|
|
@@ -1969,8 +5064,8 @@ function NameBox({ api }) {
|
|
|
1969
5064
|
}
|
|
1970
5065
|
};
|
|
1971
5066
|
const shown = draft ?? ref;
|
|
1972
|
-
return /* @__PURE__ */
|
|
1973
|
-
/* @__PURE__ */
|
|
5067
|
+
return /* @__PURE__ */ jsxs20("div", { ref: rootRef, style: ROOT_STYLE, children: [
|
|
5068
|
+
/* @__PURE__ */ jsx21(
|
|
1974
5069
|
"input",
|
|
1975
5070
|
{
|
|
1976
5071
|
ref: inputRef,
|
|
@@ -1992,7 +5087,7 @@ function NameBox({ api }) {
|
|
|
1992
5087
|
}
|
|
1993
5088
|
}
|
|
1994
5089
|
),
|
|
1995
|
-
/* @__PURE__ */
|
|
5090
|
+
/* @__PURE__ */ jsx21(
|
|
1996
5091
|
"button",
|
|
1997
5092
|
{
|
|
1998
5093
|
type: "button",
|
|
@@ -2011,10 +5106,10 @@ function NameBox({ api }) {
|
|
|
2011
5106
|
e.preventDefault();
|
|
2012
5107
|
toggleDropdown();
|
|
2013
5108
|
},
|
|
2014
|
-
children: /* @__PURE__ */
|
|
5109
|
+
children: /* @__PURE__ */ jsx21(Icon, { name: "expand_more", size: 18 })
|
|
2015
5110
|
}
|
|
2016
5111
|
),
|
|
2017
|
-
open && /* @__PURE__ */
|
|
5112
|
+
open && /* @__PURE__ */ jsx21("div", { style: MENU_STYLE, role: "menu", "data-testid": "cs-namebox-menu", children: names.length === 0 ? /* @__PURE__ */ jsx21("div", { style: MENU_EMPTY_STYLE, children: "No names" }) : names.map((entry, i) => /* @__PURE__ */ jsxs20(
|
|
2018
5113
|
"button",
|
|
2019
5114
|
{
|
|
2020
5115
|
type: "button",
|
|
@@ -2033,8 +5128,8 @@ function NameBox({ api }) {
|
|
|
2033
5128
|
e.currentTarget.style.background = "transparent";
|
|
2034
5129
|
},
|
|
2035
5130
|
children: [
|
|
2036
|
-
/* @__PURE__ */
|
|
2037
|
-
entry.ref && /* @__PURE__ */
|
|
5131
|
+
/* @__PURE__ */ jsx21("span", { children: entry.name }),
|
|
5132
|
+
entry.ref && /* @__PURE__ */ jsx21("span", { style: ITEM_REF_STYLE, children: entry.ref })
|
|
2038
5133
|
]
|
|
2039
5134
|
},
|
|
2040
5135
|
entry.name
|
|
@@ -2043,8 +5138,8 @@ function NameBox({ api }) {
|
|
|
2043
5138
|
}
|
|
2044
5139
|
|
|
2045
5140
|
// src/chrome/FormulaBar.tsx
|
|
2046
|
-
import { jsx as
|
|
2047
|
-
var
|
|
5141
|
+
import { jsx as jsx22, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
5142
|
+
var FUNCTIONS2 = [
|
|
2048
5143
|
"ABS",
|
|
2049
5144
|
"AND",
|
|
2050
5145
|
"AVERAGE",
|
|
@@ -2148,7 +5243,7 @@ function suggestFor(draft) {
|
|
|
2148
5243
|
const m = TOKEN_RE.exec(draft);
|
|
2149
5244
|
if (!m) return [];
|
|
2150
5245
|
const prefix = m[1].toUpperCase();
|
|
2151
|
-
return
|
|
5246
|
+
return FUNCTIONS2.filter((f) => f.startsWith(prefix) && f !== prefix).slice(0, 8);
|
|
2152
5247
|
}
|
|
2153
5248
|
function colToLetters2(col) {
|
|
2154
5249
|
let s = "";
|
|
@@ -2231,12 +5326,12 @@ var AC_ITEM_STYLE = {
|
|
|
2231
5326
|
cursor: "pointer"
|
|
2232
5327
|
};
|
|
2233
5328
|
function FormulaBar({ api }) {
|
|
2234
|
-
const [cell, setCell] =
|
|
2235
|
-
const [draft, setDraft] =
|
|
5329
|
+
const [cell, setCell] = useState20(null);
|
|
5330
|
+
const [draft, setDraft] = useState20(null);
|
|
2236
5331
|
const draftRef = useRef6(null);
|
|
2237
5332
|
draftRef.current = draft;
|
|
2238
|
-
const [acIdx, setAcIdx] =
|
|
2239
|
-
const [acDismissed, setAcDismissed] =
|
|
5333
|
+
const [acIdx, setAcIdx] = useState20(0);
|
|
5334
|
+
const [acDismissed, setAcDismissed] = useState20(false);
|
|
2240
5335
|
useEffect8(() => {
|
|
2241
5336
|
if (!api) return;
|
|
2242
5337
|
const refresh = () => {
|
|
@@ -2249,7 +5344,7 @@ function FormulaBar({ api }) {
|
|
|
2249
5344
|
const sub = cmd?.onCommandExecuted(() => refresh());
|
|
2250
5345
|
return () => sub?.dispose();
|
|
2251
5346
|
}, [api]);
|
|
2252
|
-
const suggestions =
|
|
5347
|
+
const suggestions = useMemo15(() => suggestFor(draft), [draft]);
|
|
2253
5348
|
const acOpen = !acDismissed && suggestions.length > 0;
|
|
2254
5349
|
const commit = (text) => {
|
|
2255
5350
|
setDraft(null);
|
|
@@ -2307,11 +5402,11 @@ function FormulaBar({ api }) {
|
|
|
2307
5402
|
}
|
|
2308
5403
|
};
|
|
2309
5404
|
const shown = draft ?? cell?.text ?? "";
|
|
2310
|
-
return /* @__PURE__ */
|
|
2311
|
-
/* @__PURE__ */
|
|
2312
|
-
/* @__PURE__ */
|
|
2313
|
-
/* @__PURE__ */
|
|
2314
|
-
/* @__PURE__ */
|
|
5405
|
+
return /* @__PURE__ */ jsxs21("div", { style: BAR_STYLE2, "data-testid": "casual-sheets-formula-bar", children: [
|
|
5406
|
+
/* @__PURE__ */ jsx22(NameBox, { api }),
|
|
5407
|
+
/* @__PURE__ */ jsx22("span", { style: FX_STYLE, "aria-hidden": true, children: "fx" }),
|
|
5408
|
+
/* @__PURE__ */ jsxs21("div", { style: INPUT_WRAP_STYLE, children: [
|
|
5409
|
+
/* @__PURE__ */ jsx22(
|
|
2315
5410
|
"input",
|
|
2316
5411
|
{
|
|
2317
5412
|
type: "text",
|
|
@@ -2327,7 +5422,7 @@ function FormulaBar({ api }) {
|
|
|
2327
5422
|
disabled: !api
|
|
2328
5423
|
}
|
|
2329
5424
|
),
|
|
2330
|
-
acOpen && /* @__PURE__ */
|
|
5425
|
+
acOpen && /* @__PURE__ */ jsx22("div", { style: AC_STYLE, "data-testid": "cs-formula-suggestions", role: "listbox", children: suggestions.map((fn, i) => /* @__PURE__ */ jsx22(
|
|
2331
5426
|
"button",
|
|
2332
5427
|
{
|
|
2333
5428
|
type: "button",
|
|
@@ -2353,9 +5448,9 @@ function FormulaBar({ api }) {
|
|
|
2353
5448
|
}
|
|
2354
5449
|
|
|
2355
5450
|
// src/chrome/StatusBar.tsx
|
|
2356
|
-
import { useEffect as useEffect9, useState as
|
|
5451
|
+
import { useEffect as useEffect9, useState as useState21 } from "react";
|
|
2357
5452
|
import { ICommandService as ICommandService4 } from "@univerjs/core";
|
|
2358
|
-
import { Fragment as
|
|
5453
|
+
import { Fragment as Fragment15, jsx as jsx23, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
2359
5454
|
function readStats(api) {
|
|
2360
5455
|
const sel = api.getSelection();
|
|
2361
5456
|
const sheet = api.univer.getActiveWorkbook()?.getActiveSheet();
|
|
@@ -2438,8 +5533,8 @@ var ZOOM_LEVEL_STYLE = {
|
|
|
2438
5533
|
textAlign: "center"
|
|
2439
5534
|
};
|
|
2440
5535
|
function StatusBar({ api }) {
|
|
2441
|
-
const [stats, setStats] =
|
|
2442
|
-
const [zoom, setZoom] =
|
|
5536
|
+
const [stats, setStats] = useState21(null);
|
|
5537
|
+
const [zoom, setZoom] = useState21(1);
|
|
2443
5538
|
useEffect9(() => {
|
|
2444
5539
|
if (!api) return;
|
|
2445
5540
|
const refresh = () => {
|
|
@@ -2466,37 +5561,37 @@ function StatusBar({ api }) {
|
|
|
2466
5561
|
};
|
|
2467
5562
|
const zoomBy = (delta) => setZoomRatio(Math.round((zoom + delta) * 100) / 100);
|
|
2468
5563
|
const resetZoom = () => setZoomRatio(1);
|
|
2469
|
-
return /* @__PURE__ */
|
|
2470
|
-
stats && /* @__PURE__ */
|
|
2471
|
-
stats.numCount > 0 && /* @__PURE__ */
|
|
5564
|
+
return /* @__PURE__ */ jsxs22("div", { style: BAR_STYLE3, "data-testid": "casual-sheets-status-bar", children: [
|
|
5565
|
+
stats && /* @__PURE__ */ jsxs22(Fragment15, { children: [
|
|
5566
|
+
stats.numCount > 0 && /* @__PURE__ */ jsxs22("span", { "data-stat": "average", children: [
|
|
2472
5567
|
"Average: ",
|
|
2473
5568
|
fmt(stats.avg)
|
|
2474
5569
|
] }),
|
|
2475
|
-
/* @__PURE__ */
|
|
5570
|
+
/* @__PURE__ */ jsxs22("span", { "data-stat": "count", children: [
|
|
2476
5571
|
"Count: ",
|
|
2477
5572
|
stats.count
|
|
2478
5573
|
] }),
|
|
2479
|
-
stats.numCount > 0 && /* @__PURE__ */
|
|
2480
|
-
/* @__PURE__ */
|
|
5574
|
+
stats.numCount > 0 && /* @__PURE__ */ jsxs22(Fragment15, { children: [
|
|
5575
|
+
/* @__PURE__ */ jsxs22("span", { "data-stat": "num-count", children: [
|
|
2481
5576
|
"Numerical Count: ",
|
|
2482
5577
|
stats.numCount
|
|
2483
5578
|
] }),
|
|
2484
|
-
/* @__PURE__ */
|
|
5579
|
+
/* @__PURE__ */ jsxs22("span", { "data-stat": "min", children: [
|
|
2485
5580
|
"Min: ",
|
|
2486
5581
|
fmt(stats.min)
|
|
2487
5582
|
] }),
|
|
2488
|
-
/* @__PURE__ */
|
|
5583
|
+
/* @__PURE__ */ jsxs22("span", { "data-stat": "max", children: [
|
|
2489
5584
|
"Max: ",
|
|
2490
5585
|
fmt(stats.max)
|
|
2491
5586
|
] }),
|
|
2492
|
-
/* @__PURE__ */
|
|
5587
|
+
/* @__PURE__ */ jsxs22("span", { "data-stat": "sum", children: [
|
|
2493
5588
|
"Sum: ",
|
|
2494
5589
|
fmt(stats.sum)
|
|
2495
5590
|
] })
|
|
2496
5591
|
] })
|
|
2497
5592
|
] }),
|
|
2498
|
-
/* @__PURE__ */
|
|
2499
|
-
/* @__PURE__ */
|
|
5593
|
+
/* @__PURE__ */ jsxs22("span", { style: ZOOM_GROUP_STYLE, "data-testid": "cs-zoom", children: [
|
|
5594
|
+
/* @__PURE__ */ jsx23(
|
|
2500
5595
|
"button",
|
|
2501
5596
|
{
|
|
2502
5597
|
type: "button",
|
|
@@ -2512,7 +5607,7 @@ function StatusBar({ api }) {
|
|
|
2512
5607
|
children: "\u2212"
|
|
2513
5608
|
}
|
|
2514
5609
|
),
|
|
2515
|
-
/* @__PURE__ */
|
|
5610
|
+
/* @__PURE__ */ jsxs22(
|
|
2516
5611
|
"button",
|
|
2517
5612
|
{
|
|
2518
5613
|
type: "button",
|
|
@@ -2531,7 +5626,7 @@ function StatusBar({ api }) {
|
|
|
2531
5626
|
]
|
|
2532
5627
|
}
|
|
2533
5628
|
),
|
|
2534
|
-
/* @__PURE__ */
|
|
5629
|
+
/* @__PURE__ */ jsx23(
|
|
2535
5630
|
"button",
|
|
2536
5631
|
{
|
|
2537
5632
|
type: "button",
|
|
@@ -2552,7 +5647,7 @@ function StatusBar({ api }) {
|
|
|
2552
5647
|
}
|
|
2553
5648
|
|
|
2554
5649
|
// src/chrome/MenuBar.tsx
|
|
2555
|
-
import { useEffect as useEffect10, useRef as useRef7, useState as
|
|
5650
|
+
import { useEffect as useEffect10, useRef as useRef7, useState as useState22 } from "react";
|
|
2556
5651
|
import { BorderStyleTypes as BorderStyleTypes2, BorderType as BorderType2 } from "@univerjs/core";
|
|
2557
5652
|
|
|
2558
5653
|
// src/chrome/openExternal.ts
|
|
@@ -2764,7 +5859,7 @@ function computeVisibleMenus(menus, features, canOpen, ext) {
|
|
|
2764
5859
|
}
|
|
2765
5860
|
|
|
2766
5861
|
// src/chrome/MenuBar.tsx
|
|
2767
|
-
import { jsx as
|
|
5862
|
+
import { jsx as jsx24, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
2768
5863
|
function fmtShortcut(shortcut) {
|
|
2769
5864
|
const isMac = typeof navigator !== "undefined" && /Mac|iPhone|iPad/i.test(navigator.platform || "");
|
|
2770
5865
|
if (!isMac) return shortcut;
|
|
@@ -2784,14 +5879,14 @@ function fmtShortcut(shortcut) {
|
|
|
2784
5879
|
function fu(api) {
|
|
2785
5880
|
return api.univer;
|
|
2786
5881
|
}
|
|
2787
|
-
function
|
|
5882
|
+
function activeRange12(api) {
|
|
2788
5883
|
return fu(api).getActiveWorkbook()?.getActiveSheet()?.getActiveRange() ?? null;
|
|
2789
5884
|
}
|
|
2790
|
-
function
|
|
5885
|
+
function activeSheet3(api) {
|
|
2791
5886
|
return fu(api).getActiveWorkbook()?.getActiveSheet() ?? null;
|
|
2792
5887
|
}
|
|
2793
5888
|
function selectionA1(api) {
|
|
2794
|
-
const range =
|
|
5889
|
+
const range = activeRange12(api);
|
|
2795
5890
|
if (!range) return null;
|
|
2796
5891
|
try {
|
|
2797
5892
|
return range.getA1Notation();
|
|
@@ -2806,38 +5901,38 @@ var copy = (api) => void api.executeCommand("univer.command.copy");
|
|
|
2806
5901
|
var paste = (api) => void api.executeCommand("univer.command.paste");
|
|
2807
5902
|
var pasteFormattingOnly = (api) => void api.executeCommand("sheet.command.paste-format");
|
|
2808
5903
|
var insertRowAbove = (api) => {
|
|
2809
|
-
const range =
|
|
2810
|
-
const sheet =
|
|
5904
|
+
const range = activeRange12(api);
|
|
5905
|
+
const sheet = activeSheet3(api);
|
|
2811
5906
|
if (!range || !sheet) return;
|
|
2812
5907
|
sheet.insertRowBefore(range.getRow());
|
|
2813
5908
|
};
|
|
2814
5909
|
var insertRowBelow = (api) => {
|
|
2815
|
-
const range =
|
|
2816
|
-
const sheet =
|
|
5910
|
+
const range = activeRange12(api);
|
|
5911
|
+
const sheet = activeSheet3(api);
|
|
2817
5912
|
if (!range || !sheet) return;
|
|
2818
5913
|
sheet.insertRowAfter(range.getRow() + range.getHeight() - 1);
|
|
2819
5914
|
};
|
|
2820
5915
|
var insertColumnLeft = (api) => {
|
|
2821
|
-
const range =
|
|
2822
|
-
const sheet =
|
|
5916
|
+
const range = activeRange12(api);
|
|
5917
|
+
const sheet = activeSheet3(api);
|
|
2823
5918
|
if (!range || !sheet) return;
|
|
2824
5919
|
sheet.insertColumnBefore(range.getColumn());
|
|
2825
5920
|
};
|
|
2826
5921
|
var insertColumnRight = (api) => {
|
|
2827
|
-
const range =
|
|
2828
|
-
const sheet =
|
|
5922
|
+
const range = activeRange12(api);
|
|
5923
|
+
const sheet = activeSheet3(api);
|
|
2829
5924
|
if (!range || !sheet) return;
|
|
2830
5925
|
sheet.insertColumnAfter(range.getColumn() + range.getWidth() - 1);
|
|
2831
5926
|
};
|
|
2832
5927
|
var deleteSelectedRow = (api) => {
|
|
2833
|
-
const range =
|
|
2834
|
-
const sheet =
|
|
5928
|
+
const range = activeRange12(api);
|
|
5929
|
+
const sheet = activeSheet3(api);
|
|
2835
5930
|
if (!range || !sheet) return;
|
|
2836
5931
|
sheet.deleteRows(range.getRow(), range.getHeight());
|
|
2837
5932
|
};
|
|
2838
5933
|
var deleteSelectedColumn = (api) => {
|
|
2839
|
-
const range =
|
|
2840
|
-
const sheet =
|
|
5934
|
+
const range = activeRange12(api);
|
|
5935
|
+
const sheet = activeSheet3(api);
|
|
2841
5936
|
if (!range || !sheet) return;
|
|
2842
5937
|
sheet.deleteColumns(range.getColumn(), range.getWidth());
|
|
2843
5938
|
};
|
|
@@ -2848,7 +5943,7 @@ var insertImage = (api) => void api.executeCommand("sheet.command.insert-float-i
|
|
|
2848
5943
|
var insertHyperlink = (api) => void api.executeCommand("sheet.operation.insert-hyper-link");
|
|
2849
5944
|
var insertComment = (api) => void api.executeCommand("sheet.operation.show-comment-modal");
|
|
2850
5945
|
var insertTodayDate = (api) => {
|
|
2851
|
-
const range =
|
|
5946
|
+
const range = activeRange12(api);
|
|
2852
5947
|
if (!range) return;
|
|
2853
5948
|
const today = /* @__PURE__ */ new Date();
|
|
2854
5949
|
const pad = (n) => n.toString().padStart(2, "0");
|
|
@@ -2856,7 +5951,7 @@ var insertTodayDate = (api) => {
|
|
|
2856
5951
|
range.setValue({ v });
|
|
2857
5952
|
};
|
|
2858
5953
|
var insertCurrentTime = (api) => {
|
|
2859
|
-
const range =
|
|
5954
|
+
const range = activeRange12(api);
|
|
2860
5955
|
if (!range) return;
|
|
2861
5956
|
const now = /* @__PURE__ */ new Date();
|
|
2862
5957
|
const pad = (n) => n.toString().padStart(2, "0");
|
|
@@ -2865,8 +5960,8 @@ var insertCurrentTime = (api) => {
|
|
|
2865
5960
|
};
|
|
2866
5961
|
var insertTable = (api) => {
|
|
2867
5962
|
void (async () => {
|
|
2868
|
-
const range =
|
|
2869
|
-
const sheet =
|
|
5963
|
+
const range = activeRange12(api);
|
|
5964
|
+
const sheet = activeSheet3(api);
|
|
2870
5965
|
const wb = fu(api).getActiveWorkbook();
|
|
2871
5966
|
if (!range || !sheet || !wb) return;
|
|
2872
5967
|
await ensurePluginByName("table");
|
|
@@ -2883,16 +5978,16 @@ var insertTable = (api) => {
|
|
|
2883
5978
|
})();
|
|
2884
5979
|
};
|
|
2885
5980
|
var autoFitColumns = (api) => {
|
|
2886
|
-
const range =
|
|
2887
|
-
const sheet =
|
|
5981
|
+
const range = activeRange12(api);
|
|
5982
|
+
const sheet = activeSheet3(api);
|
|
2888
5983
|
if (!range || !sheet) return;
|
|
2889
5984
|
const withAutoWidth = sheet;
|
|
2890
5985
|
withAutoWidth.setColumnAutoWidth?.(range.getColumn(), range.getWidth());
|
|
2891
5986
|
};
|
|
2892
5987
|
var AUTO_FIT_ROW_CAP = 500;
|
|
2893
5988
|
var autoFitRows = (api) => {
|
|
2894
|
-
const range =
|
|
2895
|
-
const sheet =
|
|
5989
|
+
const range = activeRange12(api);
|
|
5990
|
+
const sheet = activeSheet3(api);
|
|
2896
5991
|
if (!range || !sheet) return;
|
|
2897
5992
|
const start = range.getRow();
|
|
2898
5993
|
const count = Math.min(range.getHeight(), AUTO_FIT_ROW_CAP);
|
|
@@ -2931,14 +6026,14 @@ var NUM_FORMAT_SHORTCUT = {
|
|
|
2931
6026
|
scientific: "Ctrl+Shift+6"
|
|
2932
6027
|
};
|
|
2933
6028
|
function setNumberFormatByKey(api, key) {
|
|
2934
|
-
const range =
|
|
6029
|
+
const range = activeRange12(api);
|
|
2935
6030
|
range?.setNumberFormat?.(NUMBER_FORMAT_PATTERNS3[key]);
|
|
2936
6031
|
}
|
|
2937
6032
|
var increaseDecimal = (api) => void api.executeCommand("sheet.command.numfmt.add.decimal.command");
|
|
2938
6033
|
var decreaseDecimal = (api) => void api.executeCommand("sheet.command.numfmt.subtract.decimal.command");
|
|
2939
6034
|
var clearFormat = (api) => void api.executeCommand("sheet.command.clear-selection-format");
|
|
2940
6035
|
function applyBorders(api, choice) {
|
|
2941
|
-
const range =
|
|
6036
|
+
const range = activeRange12(api);
|
|
2942
6037
|
if (!range) return;
|
|
2943
6038
|
const type = choice === "all" ? BorderType2.ALL : choice === "outside" ? BorderType2.OUTSIDE : BorderType2.NONE;
|
|
2944
6039
|
const style = choice === "none" ? BorderStyleTypes2.NONE : BorderStyleTypes2.THIN;
|
|
@@ -2946,8 +6041,8 @@ function applyBorders(api, choice) {
|
|
|
2946
6041
|
}
|
|
2947
6042
|
function rowSpan(api) {
|
|
2948
6043
|
const wb = fu(api).getActiveWorkbook();
|
|
2949
|
-
const sheet =
|
|
2950
|
-
const range =
|
|
6044
|
+
const sheet = activeSheet3(api);
|
|
6045
|
+
const range = activeRange12(api);
|
|
2951
6046
|
if (!wb || !sheet || !range) return null;
|
|
2952
6047
|
const startRow = range.getRow();
|
|
2953
6048
|
const endRow = startRow + range.getHeight() - 1;
|
|
@@ -2956,8 +6051,8 @@ function rowSpan(api) {
|
|
|
2956
6051
|
}
|
|
2957
6052
|
function colSpan(api) {
|
|
2958
6053
|
const wb = fu(api).getActiveWorkbook();
|
|
2959
|
-
const sheet =
|
|
2960
|
-
const range =
|
|
6054
|
+
const sheet = activeSheet3(api);
|
|
6055
|
+
const range = activeRange12(api);
|
|
2961
6056
|
if (!wb || !sheet || !range) return null;
|
|
2962
6057
|
const startColumn = range.getColumn();
|
|
2963
6058
|
const endColumn = startColumn + range.getWidth() - 1;
|
|
@@ -3033,16 +6128,16 @@ var unhideSelectedColumns = (api) => {
|
|
|
3033
6128
|
});
|
|
3034
6129
|
};
|
|
3035
6130
|
var freezeFirstRow = (api) => {
|
|
3036
|
-
|
|
6131
|
+
activeSheet3(api)?.setFrozenRows(1);
|
|
3037
6132
|
};
|
|
3038
6133
|
var freezeFirstColumn = (api) => {
|
|
3039
|
-
|
|
6134
|
+
activeSheet3(api)?.setFrozenColumns(1);
|
|
3040
6135
|
};
|
|
3041
6136
|
var freezeAtSelection = (api) => void api.executeCommand("sheet.command.set-selection-frozen");
|
|
3042
6137
|
var unfreezePanes = (api) => void api.executeCommand("sheet.command.cancel-frozen");
|
|
3043
6138
|
var toggleGridlines = (api) => {
|
|
3044
6139
|
const wb = fu(api).getActiveWorkbook();
|
|
3045
|
-
const sheet =
|
|
6140
|
+
const sheet = activeSheet3(api);
|
|
3046
6141
|
if (!wb || !sheet) return;
|
|
3047
6142
|
void api.executeCommand("sheet.command.toggle-gridlines", {
|
|
3048
6143
|
unitId: wb.getId(),
|
|
@@ -3052,7 +6147,7 @@ var toggleGridlines = (api) => {
|
|
|
3052
6147
|
};
|
|
3053
6148
|
var toggleCommentPanel = (api) => void api.executeCommand("sheet.operation.toggle-comment-panel");
|
|
3054
6149
|
var jumpToFirstCell = (api) => {
|
|
3055
|
-
|
|
6150
|
+
activeSheet3(api)?.getRange(0, 0).activate();
|
|
3056
6151
|
};
|
|
3057
6152
|
var switchToPreviousSheet = (api) => switchSheetByDelta(api, -1);
|
|
3058
6153
|
var switchToNextSheet = (api) => switchSheetByDelta(api, 1);
|
|
@@ -3072,7 +6167,7 @@ var showFormulas = (api) => void api.executeCommand("sheet.command.set-show-form
|
|
|
3072
6167
|
var sortAsc = (api) => sortRange(api, true);
|
|
3073
6168
|
var sortDesc = (api) => sortRange(api, false);
|
|
3074
6169
|
function sortRange(api, ascending) {
|
|
3075
|
-
const range =
|
|
6170
|
+
const range = activeRange12(api);
|
|
3076
6171
|
if (!range) return;
|
|
3077
6172
|
const withSort = range;
|
|
3078
6173
|
withSort.sort?.({ column: range.getColumn(), ascending });
|
|
@@ -3081,8 +6176,8 @@ var toggleFilter = (api) => {
|
|
|
3081
6176
|
void (async () => {
|
|
3082
6177
|
await ensurePluginByName("filter");
|
|
3083
6178
|
const wb = fu(api).getActiveWorkbook();
|
|
3084
|
-
const sheet =
|
|
3085
|
-
const range =
|
|
6179
|
+
const sheet = activeSheet3(api);
|
|
6180
|
+
const range = activeRange12(api);
|
|
3086
6181
|
if (!wb || !sheet || !range) return;
|
|
3087
6182
|
const sheetWithFilter = sheet;
|
|
3088
6183
|
if (sheetWithFilter.getFilter?.()) {
|
|
@@ -3788,8 +6883,8 @@ var SUBMENU_PANEL_STYLE = {
|
|
|
3788
6883
|
};
|
|
3789
6884
|
function MenuBar({ api, features = {}, extensions }) {
|
|
3790
6885
|
const dialogs = useDialogs();
|
|
3791
|
-
const [open, setOpen] =
|
|
3792
|
-
const [openSubmenu, setOpenSubmenu] =
|
|
6886
|
+
const [open, setOpen] = useState22(null);
|
|
6887
|
+
const [openSubmenu, setOpenSubmenu] = useState22(null);
|
|
3793
6888
|
const rootRef = useRef7(null);
|
|
3794
6889
|
useEffect10(() => {
|
|
3795
6890
|
ensureChromeFonts();
|
|
@@ -3832,18 +6927,18 @@ function MenuBar({ api, features = {}, extensions }) {
|
|
|
3832
6927
|
const visibleMenus = computeVisibleMenus(MENUS, features, dialogs.canOpen, extensions?.menu);
|
|
3833
6928
|
const renderItems = (items) => items.map((item) => {
|
|
3834
6929
|
if (item.kind === "separator") {
|
|
3835
|
-
return /* @__PURE__ */
|
|
6930
|
+
return /* @__PURE__ */ jsx24("div", { style: SEPARATOR_STYLE, role: "separator", "aria-hidden": true }, item.id);
|
|
3836
6931
|
}
|
|
3837
6932
|
if (item.kind === "submenu") {
|
|
3838
6933
|
const isSubOpen = openSubmenu === item.id;
|
|
3839
|
-
return /* @__PURE__ */
|
|
6934
|
+
return /* @__PURE__ */ jsxs23(
|
|
3840
6935
|
"div",
|
|
3841
6936
|
{
|
|
3842
6937
|
style: { position: "relative" },
|
|
3843
6938
|
onMouseEnter: () => setOpenSubmenu(item.id),
|
|
3844
6939
|
onMouseLeave: () => setOpenSubmenu((cur) => cur === item.id ? null : cur),
|
|
3845
6940
|
children: [
|
|
3846
|
-
/* @__PURE__ */
|
|
6941
|
+
/* @__PURE__ */ jsxs23(
|
|
3847
6942
|
"button",
|
|
3848
6943
|
{
|
|
3849
6944
|
type: "button",
|
|
@@ -3855,19 +6950,19 @@ function MenuBar({ api, features = {}, extensions }) {
|
|
|
3855
6950
|
style: { ...ITEM_STYLE3, opacity: api ? 1 : 0.5 },
|
|
3856
6951
|
onMouseDown: (e) => e.preventDefault(),
|
|
3857
6952
|
children: [
|
|
3858
|
-
item.icon ? /* @__PURE__ */
|
|
3859
|
-
/* @__PURE__ */
|
|
3860
|
-
/* @__PURE__ */
|
|
6953
|
+
item.icon ? /* @__PURE__ */ jsx24(Icon, { name: item.icon, size: 18 }) : /* @__PURE__ */ jsx24("span", { style: { width: 18 }, "aria-hidden": true }),
|
|
6954
|
+
/* @__PURE__ */ jsx24("span", { children: item.label }),
|
|
6955
|
+
/* @__PURE__ */ jsx24(Icon, { name: "chevron_right", size: 18, style: { marginLeft: "auto" } })
|
|
3861
6956
|
]
|
|
3862
6957
|
}
|
|
3863
6958
|
),
|
|
3864
|
-
isSubOpen && /* @__PURE__ */
|
|
6959
|
+
isSubOpen && /* @__PURE__ */ jsx24("div", { style: SUBMENU_PANEL_STYLE, role: "menu", "aria-label": item.label, children: renderItems(item.items) })
|
|
3865
6960
|
]
|
|
3866
6961
|
},
|
|
3867
6962
|
item.id
|
|
3868
6963
|
);
|
|
3869
6964
|
}
|
|
3870
|
-
return /* @__PURE__ */
|
|
6965
|
+
return /* @__PURE__ */ jsxs23(
|
|
3871
6966
|
"button",
|
|
3872
6967
|
{
|
|
3873
6968
|
type: "button",
|
|
@@ -3887,15 +6982,15 @@ function MenuBar({ api, features = {}, extensions }) {
|
|
|
3887
6982
|
e.currentTarget.style.background = "transparent";
|
|
3888
6983
|
},
|
|
3889
6984
|
children: [
|
|
3890
|
-
item.icon ? /* @__PURE__ */
|
|
3891
|
-
/* @__PURE__ */
|
|
3892
|
-
item.shortcut && /* @__PURE__ */
|
|
6985
|
+
item.icon ? /* @__PURE__ */ jsx24(Icon, { name: item.icon, size: 18 }) : /* @__PURE__ */ jsx24("span", { style: { width: 18 }, "aria-hidden": true }),
|
|
6986
|
+
/* @__PURE__ */ jsx24("span", { children: item.label }),
|
|
6987
|
+
item.shortcut && /* @__PURE__ */ jsx24("span", { style: SHORTCUT_STYLE, children: fmtShortcut(item.shortcut) })
|
|
3893
6988
|
]
|
|
3894
6989
|
},
|
|
3895
6990
|
item.id
|
|
3896
6991
|
);
|
|
3897
6992
|
});
|
|
3898
|
-
return /* @__PURE__ */
|
|
6993
|
+
return /* @__PURE__ */ jsx24(
|
|
3899
6994
|
"div",
|
|
3900
6995
|
{
|
|
3901
6996
|
ref: rootRef,
|
|
@@ -3905,8 +7000,8 @@ function MenuBar({ api, features = {}, extensions }) {
|
|
|
3905
7000
|
"aria-label": "Menu bar",
|
|
3906
7001
|
children: visibleMenus.map((menu) => {
|
|
3907
7002
|
const isOpen = open === menu.id;
|
|
3908
|
-
return /* @__PURE__ */
|
|
3909
|
-
/* @__PURE__ */
|
|
7003
|
+
return /* @__PURE__ */ jsxs23("div", { style: { position: "relative" }, children: [
|
|
7004
|
+
/* @__PURE__ */ jsx24(
|
|
3910
7005
|
"button",
|
|
3911
7006
|
{
|
|
3912
7007
|
type: "button",
|
|
@@ -3937,7 +7032,7 @@ function MenuBar({ api, features = {}, extensions }) {
|
|
|
3937
7032
|
children: menu.label
|
|
3938
7033
|
}
|
|
3939
7034
|
),
|
|
3940
|
-
isOpen && /* @__PURE__ */
|
|
7035
|
+
isOpen && /* @__PURE__ */ jsx24("div", { style: DROPDOWN_STYLE, role: "menu", "aria-label": menu.label, children: renderItems(menu.items) })
|
|
3941
7036
|
] }, menu.id);
|
|
3942
7037
|
})
|
|
3943
7038
|
}
|
|
@@ -3945,9 +7040,9 @@ function MenuBar({ api, features = {}, extensions }) {
|
|
|
3945
7040
|
}
|
|
3946
7041
|
|
|
3947
7042
|
// src/chrome/SheetTabs.tsx
|
|
3948
|
-
import { useEffect as useEffect11, useState as
|
|
7043
|
+
import { useEffect as useEffect11, useState as useState23 } from "react";
|
|
3949
7044
|
import { createPortal as createPortal2 } from "react-dom";
|
|
3950
|
-
import { jsx as
|
|
7045
|
+
import { jsx as jsx25, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
3951
7046
|
var EMPTY = { tabs: [], activeId: null };
|
|
3952
7047
|
var SHEET_LIST_MUTATIONS = /* @__PURE__ */ new Set([
|
|
3953
7048
|
"sheet.mutation.insert-sheet",
|
|
@@ -3980,7 +7075,7 @@ var BAR_STYLE5 = {
|
|
|
3980
7075
|
overflowX: "auto",
|
|
3981
7076
|
overflowY: "hidden"
|
|
3982
7077
|
};
|
|
3983
|
-
var
|
|
7078
|
+
var ADD_BTN_STYLE2 = {
|
|
3984
7079
|
flex: "0 0 auto",
|
|
3985
7080
|
width: 28,
|
|
3986
7081
|
border: "none",
|
|
@@ -4028,10 +7123,10 @@ var MENU_ITEM_STYLE2 = {
|
|
|
4028
7123
|
cursor: "pointer"
|
|
4029
7124
|
};
|
|
4030
7125
|
function SheetTabs({ api }) {
|
|
4031
|
-
const [{ tabs, activeId }, setSnapshot] =
|
|
4032
|
-
const [renaming, setRenaming] =
|
|
4033
|
-
const [draft, setDraft] =
|
|
4034
|
-
const [menu, setMenu] =
|
|
7126
|
+
const [{ tabs, activeId }, setSnapshot] = useState23(EMPTY);
|
|
7127
|
+
const [renaming, setRenaming] = useState23(null);
|
|
7128
|
+
const [draft, setDraft] = useState23("");
|
|
7129
|
+
const [menu, setMenu] = useState23(null);
|
|
4035
7130
|
useEffect11(() => {
|
|
4036
7131
|
if (!api) return;
|
|
4037
7132
|
const refresh = () => setSnapshot(readSheets(api));
|
|
@@ -4071,7 +7166,7 @@ function SheetTabs({ api }) {
|
|
|
4071
7166
|
};
|
|
4072
7167
|
}, [menu]);
|
|
4073
7168
|
if (!api) {
|
|
4074
|
-
return /* @__PURE__ */
|
|
7169
|
+
return /* @__PURE__ */ jsx25("div", { style: BAR_STYLE5, "data-testid": "casual-sheets-tabs" });
|
|
4075
7170
|
}
|
|
4076
7171
|
const visible = tabs.filter((t) => !t.hidden);
|
|
4077
7172
|
const wb = () => api.univer.getActiveWorkbook();
|
|
@@ -4111,11 +7206,11 @@ function SheetTabs({ api }) {
|
|
|
4111
7206
|
setRenaming(null);
|
|
4112
7207
|
}
|
|
4113
7208
|
};
|
|
4114
|
-
return /* @__PURE__ */
|
|
7209
|
+
return /* @__PURE__ */ jsxs24("div", { style: BAR_STYLE5, "data-testid": "casual-sheets-tabs", role: "tablist", children: [
|
|
4115
7210
|
visible.map((tab) => {
|
|
4116
7211
|
const active = tab.id === activeId;
|
|
4117
7212
|
if (renaming === tab.id) {
|
|
4118
|
-
return /* @__PURE__ */
|
|
7213
|
+
return /* @__PURE__ */ jsx25(
|
|
4119
7214
|
"input",
|
|
4120
7215
|
{
|
|
4121
7216
|
autoFocus: true,
|
|
@@ -4129,7 +7224,7 @@ function SheetTabs({ api }) {
|
|
|
4129
7224
|
tab.id
|
|
4130
7225
|
);
|
|
4131
7226
|
}
|
|
4132
|
-
return /* @__PURE__ */
|
|
7227
|
+
return /* @__PURE__ */ jsx25(
|
|
4133
7228
|
"button",
|
|
4134
7229
|
{
|
|
4135
7230
|
type: "button",
|
|
@@ -4168,11 +7263,11 @@ function SheetTabs({ api }) {
|
|
|
4168
7263
|
tab.id
|
|
4169
7264
|
);
|
|
4170
7265
|
}),
|
|
4171
|
-
/* @__PURE__ */
|
|
7266
|
+
/* @__PURE__ */ jsx25(
|
|
4172
7267
|
"button",
|
|
4173
7268
|
{
|
|
4174
7269
|
type: "button",
|
|
4175
|
-
style:
|
|
7270
|
+
style: ADD_BTN_STYLE2,
|
|
4176
7271
|
"aria-label": "Add sheet",
|
|
4177
7272
|
title: "Add sheet",
|
|
4178
7273
|
"data-testid": "cs-tab-add",
|
|
@@ -4184,7 +7279,7 @@ function SheetTabs({ api }) {
|
|
|
4184
7279
|
}
|
|
4185
7280
|
),
|
|
4186
7281
|
menu && createPortal2(
|
|
4187
|
-
/* @__PURE__ */
|
|
7282
|
+
/* @__PURE__ */ jsxs24(
|
|
4188
7283
|
"div",
|
|
4189
7284
|
{
|
|
4190
7285
|
style: { ...MENU_STYLE2, left: menu.x, top: menu.y, transform: "translateY(-100%)" },
|
|
@@ -4192,7 +7287,7 @@ function SheetTabs({ api }) {
|
|
|
4192
7287
|
role: "menu",
|
|
4193
7288
|
onPointerDown: (e) => e.stopPropagation(),
|
|
4194
7289
|
children: [
|
|
4195
|
-
/* @__PURE__ */
|
|
7290
|
+
/* @__PURE__ */ jsx25(
|
|
4196
7291
|
"button",
|
|
4197
7292
|
{
|
|
4198
7293
|
type: "button",
|
|
@@ -4207,7 +7302,7 @@ function SheetTabs({ api }) {
|
|
|
4207
7302
|
children: "Rename"
|
|
4208
7303
|
}
|
|
4209
7304
|
),
|
|
4210
|
-
/* @__PURE__ */
|
|
7305
|
+
/* @__PURE__ */ jsx25(
|
|
4211
7306
|
"button",
|
|
4212
7307
|
{
|
|
4213
7308
|
type: "button",
|
|
@@ -4236,11 +7331,11 @@ function SheetTabs({ api }) {
|
|
|
4236
7331
|
// src/chrome/FindReplace.tsx
|
|
4237
7332
|
import {
|
|
4238
7333
|
useEffect as useEffect12,
|
|
4239
|
-
useMemo as
|
|
7334
|
+
useMemo as useMemo16,
|
|
4240
7335
|
useRef as useRef8,
|
|
4241
|
-
useState as
|
|
7336
|
+
useState as useState24
|
|
4242
7337
|
} from "react";
|
|
4243
|
-
import { jsx as
|
|
7338
|
+
import { jsx as jsx26, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
4244
7339
|
function findMatches(api, query, matchCase) {
|
|
4245
7340
|
if (!query) return [];
|
|
4246
7341
|
const snap = api.getSnapshot();
|
|
@@ -4278,7 +7373,7 @@ var WRAP_STYLE = {
|
|
|
4278
7373
|
fontSize: 13,
|
|
4279
7374
|
color: "var(--cs-chrome-fg, #201f1e)"
|
|
4280
7375
|
};
|
|
4281
|
-
var
|
|
7376
|
+
var ROW_STYLE5 = { display: "flex", alignItems: "center", gap: 6, marginBottom: 6 };
|
|
4282
7377
|
var INPUT_STYLE2 = {
|
|
4283
7378
|
flex: "1 1 auto",
|
|
4284
7379
|
height: 26,
|
|
@@ -4316,12 +7411,12 @@ var ICON_BTN_STYLE = {
|
|
|
4316
7411
|
padding: 0
|
|
4317
7412
|
};
|
|
4318
7413
|
function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
4319
|
-
const [open, setOpen] =
|
|
4320
|
-
const [showReplace, setShowReplace] =
|
|
4321
|
-
const [query, setQuery] =
|
|
4322
|
-
const [replaceText, setReplaceText] =
|
|
4323
|
-
const [matchCase, setMatchCase] =
|
|
4324
|
-
const [idx, setIdx] =
|
|
7414
|
+
const [open, setOpen] = useState24(false);
|
|
7415
|
+
const [showReplace, setShowReplace] = useState24(false);
|
|
7416
|
+
const [query, setQuery] = useState24("");
|
|
7417
|
+
const [replaceText, setReplaceText] = useState24("");
|
|
7418
|
+
const [matchCase, setMatchCase] = useState24(false);
|
|
7419
|
+
const [idx, setIdx] = useState24(0);
|
|
4325
7420
|
const inputRef = useRef8(null);
|
|
4326
7421
|
useEffect12(() => {
|
|
4327
7422
|
const onKey = (e) => {
|
|
@@ -4350,7 +7445,7 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
|
4350
7445
|
setOpen(true);
|
|
4351
7446
|
requestAnimationFrame(() => inputRef.current?.focus());
|
|
4352
7447
|
}, [openSignal, openInReplaceMode]);
|
|
4353
|
-
const matches =
|
|
7448
|
+
const matches = useMemo16(
|
|
4354
7449
|
() => api && open && query ? findMatches(api, query, matchCase) : [],
|
|
4355
7450
|
[api, open, query, matchCase]
|
|
4356
7451
|
);
|
|
@@ -4400,7 +7495,7 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
|
4400
7495
|
};
|
|
4401
7496
|
const count = matches.length;
|
|
4402
7497
|
const position = count === 0 ? 0 : Math.min(idx, count - 1) + 1;
|
|
4403
|
-
return /* @__PURE__ */
|
|
7498
|
+
return /* @__PURE__ */ jsxs25(
|
|
4404
7499
|
"div",
|
|
4405
7500
|
{
|
|
4406
7501
|
style: WRAP_STYLE,
|
|
@@ -4408,8 +7503,8 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
|
4408
7503
|
role: "dialog",
|
|
4409
7504
|
"aria-label": "Find and replace",
|
|
4410
7505
|
children: [
|
|
4411
|
-
/* @__PURE__ */
|
|
4412
|
-
/* @__PURE__ */
|
|
7506
|
+
/* @__PURE__ */ jsxs25("div", { style: ROW_STYLE5, children: [
|
|
7507
|
+
/* @__PURE__ */ jsx26(
|
|
4413
7508
|
"input",
|
|
4414
7509
|
{
|
|
4415
7510
|
ref: inputRef,
|
|
@@ -4423,7 +7518,7 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
|
4423
7518
|
onKeyDown: onInputKey
|
|
4424
7519
|
}
|
|
4425
7520
|
),
|
|
4426
|
-
/* @__PURE__ */
|
|
7521
|
+
/* @__PURE__ */ jsxs25(
|
|
4427
7522
|
"span",
|
|
4428
7523
|
{
|
|
4429
7524
|
style: { minWidth: 54, textAlign: "center", color: "var(--cs-chrome-muted, #6b7280)" },
|
|
@@ -4435,7 +7530,7 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
|
4435
7530
|
]
|
|
4436
7531
|
}
|
|
4437
7532
|
),
|
|
4438
|
-
/* @__PURE__ */
|
|
7533
|
+
/* @__PURE__ */ jsx26(
|
|
4439
7534
|
"button",
|
|
4440
7535
|
{
|
|
4441
7536
|
type: "button",
|
|
@@ -4446,10 +7541,10 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
|
4446
7541
|
e.preventDefault();
|
|
4447
7542
|
go(-1);
|
|
4448
7543
|
},
|
|
4449
|
-
children: /* @__PURE__ */
|
|
7544
|
+
children: /* @__PURE__ */ jsx26(Icon, { name: "keyboard_arrow_up", size: 18 })
|
|
4450
7545
|
}
|
|
4451
7546
|
),
|
|
4452
|
-
/* @__PURE__ */
|
|
7547
|
+
/* @__PURE__ */ jsx26(
|
|
4453
7548
|
"button",
|
|
4454
7549
|
{
|
|
4455
7550
|
type: "button",
|
|
@@ -4460,10 +7555,10 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
|
4460
7555
|
e.preventDefault();
|
|
4461
7556
|
go(1);
|
|
4462
7557
|
},
|
|
4463
|
-
children: /* @__PURE__ */
|
|
7558
|
+
children: /* @__PURE__ */ jsx26(Icon, { name: "keyboard_arrow_down", size: 18 })
|
|
4464
7559
|
}
|
|
4465
7560
|
),
|
|
4466
|
-
/* @__PURE__ */
|
|
7561
|
+
/* @__PURE__ */ jsx26(
|
|
4467
7562
|
"button",
|
|
4468
7563
|
{
|
|
4469
7564
|
type: "button",
|
|
@@ -4474,12 +7569,12 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
|
4474
7569
|
e.preventDefault();
|
|
4475
7570
|
setOpen(false);
|
|
4476
7571
|
},
|
|
4477
|
-
children: /* @__PURE__ */
|
|
7572
|
+
children: /* @__PURE__ */ jsx26(Icon, { name: "close", size: 18 })
|
|
4478
7573
|
}
|
|
4479
7574
|
)
|
|
4480
7575
|
] }),
|
|
4481
|
-
showReplace && /* @__PURE__ */
|
|
4482
|
-
/* @__PURE__ */
|
|
7576
|
+
showReplace && /* @__PURE__ */ jsxs25("div", { style: ROW_STYLE5, children: [
|
|
7577
|
+
/* @__PURE__ */ jsx26(
|
|
4483
7578
|
"input",
|
|
4484
7579
|
{
|
|
4485
7580
|
type: "text",
|
|
@@ -4491,7 +7586,7 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
|
4491
7586
|
onChange: (e) => setReplaceText(e.target.value)
|
|
4492
7587
|
}
|
|
4493
7588
|
),
|
|
4494
|
-
/* @__PURE__ */
|
|
7589
|
+
/* @__PURE__ */ jsx26(
|
|
4495
7590
|
"button",
|
|
4496
7591
|
{
|
|
4497
7592
|
type: "button",
|
|
@@ -4504,7 +7599,7 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
|
4504
7599
|
children: "Replace"
|
|
4505
7600
|
}
|
|
4506
7601
|
),
|
|
4507
|
-
/* @__PURE__ */
|
|
7602
|
+
/* @__PURE__ */ jsx26(
|
|
4508
7603
|
"button",
|
|
4509
7604
|
{
|
|
4510
7605
|
type: "button",
|
|
@@ -4518,9 +7613,9 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
|
4518
7613
|
}
|
|
4519
7614
|
)
|
|
4520
7615
|
] }),
|
|
4521
|
-
/* @__PURE__ */
|
|
4522
|
-
/* @__PURE__ */
|
|
4523
|
-
/* @__PURE__ */
|
|
7616
|
+
/* @__PURE__ */ jsxs25("div", { style: { display: "flex", alignItems: "center", gap: 12 }, children: [
|
|
7617
|
+
/* @__PURE__ */ jsxs25("label", { style: { display: "inline-flex", alignItems: "center", gap: 4, cursor: "pointer" }, children: [
|
|
7618
|
+
/* @__PURE__ */ jsx26(
|
|
4524
7619
|
"input",
|
|
4525
7620
|
{
|
|
4526
7621
|
type: "checkbox",
|
|
@@ -4531,7 +7626,7 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
|
4531
7626
|
),
|
|
4532
7627
|
"Match case"
|
|
4533
7628
|
] }),
|
|
4534
|
-
!showReplace && /* @__PURE__ */
|
|
7629
|
+
!showReplace && /* @__PURE__ */ jsx26(
|
|
4535
7630
|
"button",
|
|
4536
7631
|
{
|
|
4537
7632
|
type: "button",
|
|
@@ -4575,8 +7670,8 @@ function replaceInString(text, query, replacement, matchCase) {
|
|
|
4575
7670
|
}
|
|
4576
7671
|
|
|
4577
7672
|
// src/chrome/ChromeTop.tsx
|
|
4578
|
-
import { useCallback as
|
|
4579
|
-
import { jsx as
|
|
7673
|
+
import { useCallback as useCallback3, useState as useState25 } from "react";
|
|
7674
|
+
import { jsx as jsx27, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
4580
7675
|
function ChromeTop({
|
|
4581
7676
|
api,
|
|
4582
7677
|
features,
|
|
@@ -4584,13 +7679,13 @@ function ChromeTop({
|
|
|
4584
7679
|
hostOwnedDialogs,
|
|
4585
7680
|
extensions
|
|
4586
7681
|
}) {
|
|
4587
|
-
const [findSignal, setFindSignal] =
|
|
4588
|
-
const [findReplaceMode, setFindReplaceMode] =
|
|
4589
|
-
const openFindReplace =
|
|
7682
|
+
const [findSignal, setFindSignal] = useState25(0);
|
|
7683
|
+
const [findReplaceMode, setFindReplaceMode] = useState25(false);
|
|
7684
|
+
const openFindReplace = useCallback3((replaceMode) => {
|
|
4590
7685
|
setFindReplaceMode(replaceMode);
|
|
4591
7686
|
setFindSignal((n) => n + 1);
|
|
4592
7687
|
}, []);
|
|
4593
|
-
return /* @__PURE__ */
|
|
7688
|
+
return /* @__PURE__ */ jsxs26(
|
|
4594
7689
|
DialogProvider,
|
|
4595
7690
|
{
|
|
4596
7691
|
api,
|
|
@@ -4599,10 +7694,10 @@ function ChromeTop({
|
|
|
4599
7694
|
hostOwnedDialogs,
|
|
4600
7695
|
onOpenFindReplace: openFindReplace,
|
|
4601
7696
|
children: [
|
|
4602
|
-
/* @__PURE__ */
|
|
4603
|
-
/* @__PURE__ */
|
|
4604
|
-
/* @__PURE__ */
|
|
4605
|
-
/* @__PURE__ */
|
|
7697
|
+
/* @__PURE__ */ jsx27(MenuBar, { api, features, extensions }),
|
|
7698
|
+
/* @__PURE__ */ jsx27(Toolbar, { api, features, extensions }),
|
|
7699
|
+
/* @__PURE__ */ jsx27(FormulaBar, { api }),
|
|
7700
|
+
/* @__PURE__ */ jsx27(
|
|
4606
7701
|
FindReplace,
|
|
4607
7702
|
{
|
|
4608
7703
|
api,
|
|
@@ -4616,11 +7711,11 @@ function ChromeTop({
|
|
|
4616
7711
|
}
|
|
4617
7712
|
|
|
4618
7713
|
// src/chrome/ChromeBottom.tsx
|
|
4619
|
-
import { Fragment as
|
|
7714
|
+
import { Fragment as Fragment16, jsx as jsx28, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
4620
7715
|
function ChromeBottom({ api }) {
|
|
4621
|
-
return /* @__PURE__ */
|
|
4622
|
-
/* @__PURE__ */
|
|
4623
|
-
/* @__PURE__ */
|
|
7716
|
+
return /* @__PURE__ */ jsxs27(Fragment16, { children: [
|
|
7717
|
+
/* @__PURE__ */ jsx28(SheetTabs, { api }),
|
|
7718
|
+
/* @__PURE__ */ jsx28(StatusBar, { api })
|
|
4624
7719
|
] });
|
|
4625
7720
|
}
|
|
4626
7721
|
export {
|