@casualoffice/sheets 0.16.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chrome.cjs +3421 -307
- package/dist/chrome.cjs.map +1 -1
- package/dist/chrome.d.cts +33 -0
- package/dist/chrome.d.ts +33 -0
- package/dist/chrome.js +3380 -266
- package/dist/chrome.js.map +1 -1
- package/dist/embed/embed-runtime.js +152 -150
- package/package.json +1 -1
- package/src/chrome/ConditionalFormattingDialog.tsx +534 -0
- package/src/chrome/CustomSortDialog.tsx +357 -0
- package/src/chrome/DataValidationDialog.tsx +536 -0
- package/src/chrome/DeleteCellsDialog.tsx +183 -0
- package/src/chrome/GoalSeekDialog.tsx +370 -0
- package/src/chrome/InsertCellsDialog.tsx +185 -0
- package/src/chrome/InsertChartDialog.tsx +490 -0
- package/src/chrome/InsertFunctionDialog.tsx +493 -0
- package/src/chrome/InsertPivotDialog.tsx +488 -0
- package/src/chrome/InsertSparklineDialog.tsx +344 -0
- package/src/chrome/MenuBar.feature-gate.unit.test.ts +152 -0
- package/src/chrome/MenuBar.tsx +36 -141
- package/src/chrome/NameManagerDialog.tsx +378 -0
- package/src/chrome/PasteSpecialDialog.tsx +286 -0
- package/src/chrome/dialog-context.tsx +24 -0
- package/src/chrome/menu-model.ts +191 -0
package/dist/chrome.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
|
|
@@ -2701,8 +5796,70 @@ function ensurePluginByName(group) {
|
|
|
2701
5796
|
return ensurePlugin(currentUniver, group);
|
|
2702
5797
|
}
|
|
2703
5798
|
|
|
5799
|
+
// src/chrome/menu-model.ts
|
|
5800
|
+
function featureOn(feature, features) {
|
|
5801
|
+
if (!feature) return true;
|
|
5802
|
+
return features[feature] !== false;
|
|
5803
|
+
}
|
|
5804
|
+
function keepItem(item, features, canOpen) {
|
|
5805
|
+
if (!featureOn(item.feature, features)) return null;
|
|
5806
|
+
if (item.kind === "separator") return item;
|
|
5807
|
+
if (item.kind === "submenu") {
|
|
5808
|
+
const items = filterItems(item.items, features, canOpen);
|
|
5809
|
+
if (items.length === 0) return null;
|
|
5810
|
+
return { ...item, items };
|
|
5811
|
+
}
|
|
5812
|
+
if (item.dialog && !canOpen(item.dialog)) return null;
|
|
5813
|
+
return item;
|
|
5814
|
+
}
|
|
5815
|
+
function filterItems(items, features, canOpen) {
|
|
5816
|
+
const kept = items.map((i) => keepItem(i, features, canOpen)).filter((i) => i !== null);
|
|
5817
|
+
const out = [];
|
|
5818
|
+
for (const item of kept) {
|
|
5819
|
+
if (item.kind === "separator") {
|
|
5820
|
+
if (out.length === 0) continue;
|
|
5821
|
+
if (out[out.length - 1].kind === "separator") continue;
|
|
5822
|
+
}
|
|
5823
|
+
out.push(item);
|
|
5824
|
+
}
|
|
5825
|
+
while (out.length > 0 && out[out.length - 1].kind === "separator") out.pop();
|
|
5826
|
+
return out;
|
|
5827
|
+
}
|
|
5828
|
+
function withMenuExtensions(menus, ext) {
|
|
5829
|
+
if (!ext || ext.length === 0) return menus;
|
|
5830
|
+
const byMenu = /* @__PURE__ */ new Map();
|
|
5831
|
+
for (const e of ext) {
|
|
5832
|
+
const list = byMenu.get(e.menu) ?? [];
|
|
5833
|
+
list.push(e);
|
|
5834
|
+
byMenu.set(e.menu, list);
|
|
5835
|
+
}
|
|
5836
|
+
return menus.map((menu) => {
|
|
5837
|
+
const extras = byMenu.get(menu.id);
|
|
5838
|
+
if (!extras || extras.length === 0) return menu;
|
|
5839
|
+
const items = [...menu.items, { kind: "separator", id: `ext-sep-${menu.id}` }];
|
|
5840
|
+
for (const e of extras) {
|
|
5841
|
+
items.push({
|
|
5842
|
+
kind: "item",
|
|
5843
|
+
id: `ext-${e.id}`,
|
|
5844
|
+
label: e.label,
|
|
5845
|
+
icon: e.icon,
|
|
5846
|
+
shortcut: e.shortcut,
|
|
5847
|
+
dialog: e.dialog,
|
|
5848
|
+
run: e.onClick ? (api) => e.onClick?.(api) : void 0
|
|
5849
|
+
});
|
|
5850
|
+
}
|
|
5851
|
+
return { ...menu, items };
|
|
5852
|
+
});
|
|
5853
|
+
}
|
|
5854
|
+
function computeVisibleMenus(menus, features, canOpen, ext) {
|
|
5855
|
+
return withMenuExtensions(menus, ext).map((menu) => ({
|
|
5856
|
+
...menu,
|
|
5857
|
+
items: filterItems(menu.items, features, canOpen)
|
|
5858
|
+
})).filter((menu) => featureOn(menu.feature, features) && menu.items.length > 0);
|
|
5859
|
+
}
|
|
5860
|
+
|
|
2704
5861
|
// src/chrome/MenuBar.tsx
|
|
2705
|
-
import { jsx as
|
|
5862
|
+
import { jsx as jsx24, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
2706
5863
|
function fmtShortcut(shortcut) {
|
|
2707
5864
|
const isMac = typeof navigator !== "undefined" && /Mac|iPhone|iPad/i.test(navigator.platform || "");
|
|
2708
5865
|
if (!isMac) return shortcut;
|
|
@@ -2722,14 +5879,14 @@ function fmtShortcut(shortcut) {
|
|
|
2722
5879
|
function fu(api) {
|
|
2723
5880
|
return api.univer;
|
|
2724
5881
|
}
|
|
2725
|
-
function
|
|
5882
|
+
function activeRange12(api) {
|
|
2726
5883
|
return fu(api).getActiveWorkbook()?.getActiveSheet()?.getActiveRange() ?? null;
|
|
2727
5884
|
}
|
|
2728
|
-
function
|
|
5885
|
+
function activeSheet3(api) {
|
|
2729
5886
|
return fu(api).getActiveWorkbook()?.getActiveSheet() ?? null;
|
|
2730
5887
|
}
|
|
2731
5888
|
function selectionA1(api) {
|
|
2732
|
-
const range =
|
|
5889
|
+
const range = activeRange12(api);
|
|
2733
5890
|
if (!range) return null;
|
|
2734
5891
|
try {
|
|
2735
5892
|
return range.getA1Notation();
|
|
@@ -2744,38 +5901,38 @@ var copy = (api) => void api.executeCommand("univer.command.copy");
|
|
|
2744
5901
|
var paste = (api) => void api.executeCommand("univer.command.paste");
|
|
2745
5902
|
var pasteFormattingOnly = (api) => void api.executeCommand("sheet.command.paste-format");
|
|
2746
5903
|
var insertRowAbove = (api) => {
|
|
2747
|
-
const range =
|
|
2748
|
-
const sheet =
|
|
5904
|
+
const range = activeRange12(api);
|
|
5905
|
+
const sheet = activeSheet3(api);
|
|
2749
5906
|
if (!range || !sheet) return;
|
|
2750
5907
|
sheet.insertRowBefore(range.getRow());
|
|
2751
5908
|
};
|
|
2752
5909
|
var insertRowBelow = (api) => {
|
|
2753
|
-
const range =
|
|
2754
|
-
const sheet =
|
|
5910
|
+
const range = activeRange12(api);
|
|
5911
|
+
const sheet = activeSheet3(api);
|
|
2755
5912
|
if (!range || !sheet) return;
|
|
2756
5913
|
sheet.insertRowAfter(range.getRow() + range.getHeight() - 1);
|
|
2757
5914
|
};
|
|
2758
5915
|
var insertColumnLeft = (api) => {
|
|
2759
|
-
const range =
|
|
2760
|
-
const sheet =
|
|
5916
|
+
const range = activeRange12(api);
|
|
5917
|
+
const sheet = activeSheet3(api);
|
|
2761
5918
|
if (!range || !sheet) return;
|
|
2762
5919
|
sheet.insertColumnBefore(range.getColumn());
|
|
2763
5920
|
};
|
|
2764
5921
|
var insertColumnRight = (api) => {
|
|
2765
|
-
const range =
|
|
2766
|
-
const sheet =
|
|
5922
|
+
const range = activeRange12(api);
|
|
5923
|
+
const sheet = activeSheet3(api);
|
|
2767
5924
|
if (!range || !sheet) return;
|
|
2768
5925
|
sheet.insertColumnAfter(range.getColumn() + range.getWidth() - 1);
|
|
2769
5926
|
};
|
|
2770
5927
|
var deleteSelectedRow = (api) => {
|
|
2771
|
-
const range =
|
|
2772
|
-
const sheet =
|
|
5928
|
+
const range = activeRange12(api);
|
|
5929
|
+
const sheet = activeSheet3(api);
|
|
2773
5930
|
if (!range || !sheet) return;
|
|
2774
5931
|
sheet.deleteRows(range.getRow(), range.getHeight());
|
|
2775
5932
|
};
|
|
2776
5933
|
var deleteSelectedColumn = (api) => {
|
|
2777
|
-
const range =
|
|
2778
|
-
const sheet =
|
|
5934
|
+
const range = activeRange12(api);
|
|
5935
|
+
const sheet = activeSheet3(api);
|
|
2779
5936
|
if (!range || !sheet) return;
|
|
2780
5937
|
sheet.deleteColumns(range.getColumn(), range.getWidth());
|
|
2781
5938
|
};
|
|
@@ -2786,7 +5943,7 @@ var insertImage = (api) => void api.executeCommand("sheet.command.insert-float-i
|
|
|
2786
5943
|
var insertHyperlink = (api) => void api.executeCommand("sheet.operation.insert-hyper-link");
|
|
2787
5944
|
var insertComment = (api) => void api.executeCommand("sheet.operation.show-comment-modal");
|
|
2788
5945
|
var insertTodayDate = (api) => {
|
|
2789
|
-
const range =
|
|
5946
|
+
const range = activeRange12(api);
|
|
2790
5947
|
if (!range) return;
|
|
2791
5948
|
const today = /* @__PURE__ */ new Date();
|
|
2792
5949
|
const pad = (n) => n.toString().padStart(2, "0");
|
|
@@ -2794,7 +5951,7 @@ var insertTodayDate = (api) => {
|
|
|
2794
5951
|
range.setValue({ v });
|
|
2795
5952
|
};
|
|
2796
5953
|
var insertCurrentTime = (api) => {
|
|
2797
|
-
const range =
|
|
5954
|
+
const range = activeRange12(api);
|
|
2798
5955
|
if (!range) return;
|
|
2799
5956
|
const now = /* @__PURE__ */ new Date();
|
|
2800
5957
|
const pad = (n) => n.toString().padStart(2, "0");
|
|
@@ -2803,8 +5960,8 @@ var insertCurrentTime = (api) => {
|
|
|
2803
5960
|
};
|
|
2804
5961
|
var insertTable = (api) => {
|
|
2805
5962
|
void (async () => {
|
|
2806
|
-
const range =
|
|
2807
|
-
const sheet =
|
|
5963
|
+
const range = activeRange12(api);
|
|
5964
|
+
const sheet = activeSheet3(api);
|
|
2808
5965
|
const wb = fu(api).getActiveWorkbook();
|
|
2809
5966
|
if (!range || !sheet || !wb) return;
|
|
2810
5967
|
await ensurePluginByName("table");
|
|
@@ -2821,16 +5978,16 @@ var insertTable = (api) => {
|
|
|
2821
5978
|
})();
|
|
2822
5979
|
};
|
|
2823
5980
|
var autoFitColumns = (api) => {
|
|
2824
|
-
const range =
|
|
2825
|
-
const sheet =
|
|
5981
|
+
const range = activeRange12(api);
|
|
5982
|
+
const sheet = activeSheet3(api);
|
|
2826
5983
|
if (!range || !sheet) return;
|
|
2827
5984
|
const withAutoWidth = sheet;
|
|
2828
5985
|
withAutoWidth.setColumnAutoWidth?.(range.getColumn(), range.getWidth());
|
|
2829
5986
|
};
|
|
2830
5987
|
var AUTO_FIT_ROW_CAP = 500;
|
|
2831
5988
|
var autoFitRows = (api) => {
|
|
2832
|
-
const range =
|
|
2833
|
-
const sheet =
|
|
5989
|
+
const range = activeRange12(api);
|
|
5990
|
+
const sheet = activeSheet3(api);
|
|
2834
5991
|
if (!range || !sheet) return;
|
|
2835
5992
|
const start = range.getRow();
|
|
2836
5993
|
const count = Math.min(range.getHeight(), AUTO_FIT_ROW_CAP);
|
|
@@ -2869,14 +6026,14 @@ var NUM_FORMAT_SHORTCUT = {
|
|
|
2869
6026
|
scientific: "Ctrl+Shift+6"
|
|
2870
6027
|
};
|
|
2871
6028
|
function setNumberFormatByKey(api, key) {
|
|
2872
|
-
const range =
|
|
6029
|
+
const range = activeRange12(api);
|
|
2873
6030
|
range?.setNumberFormat?.(NUMBER_FORMAT_PATTERNS3[key]);
|
|
2874
6031
|
}
|
|
2875
6032
|
var increaseDecimal = (api) => void api.executeCommand("sheet.command.numfmt.add.decimal.command");
|
|
2876
6033
|
var decreaseDecimal = (api) => void api.executeCommand("sheet.command.numfmt.subtract.decimal.command");
|
|
2877
6034
|
var clearFormat = (api) => void api.executeCommand("sheet.command.clear-selection-format");
|
|
2878
6035
|
function applyBorders(api, choice) {
|
|
2879
|
-
const range =
|
|
6036
|
+
const range = activeRange12(api);
|
|
2880
6037
|
if (!range) return;
|
|
2881
6038
|
const type = choice === "all" ? BorderType2.ALL : choice === "outside" ? BorderType2.OUTSIDE : BorderType2.NONE;
|
|
2882
6039
|
const style = choice === "none" ? BorderStyleTypes2.NONE : BorderStyleTypes2.THIN;
|
|
@@ -2884,8 +6041,8 @@ function applyBorders(api, choice) {
|
|
|
2884
6041
|
}
|
|
2885
6042
|
function rowSpan(api) {
|
|
2886
6043
|
const wb = fu(api).getActiveWorkbook();
|
|
2887
|
-
const sheet =
|
|
2888
|
-
const range =
|
|
6044
|
+
const sheet = activeSheet3(api);
|
|
6045
|
+
const range = activeRange12(api);
|
|
2889
6046
|
if (!wb || !sheet || !range) return null;
|
|
2890
6047
|
const startRow = range.getRow();
|
|
2891
6048
|
const endRow = startRow + range.getHeight() - 1;
|
|
@@ -2894,8 +6051,8 @@ function rowSpan(api) {
|
|
|
2894
6051
|
}
|
|
2895
6052
|
function colSpan(api) {
|
|
2896
6053
|
const wb = fu(api).getActiveWorkbook();
|
|
2897
|
-
const sheet =
|
|
2898
|
-
const range =
|
|
6054
|
+
const sheet = activeSheet3(api);
|
|
6055
|
+
const range = activeRange12(api);
|
|
2899
6056
|
if (!wb || !sheet || !range) return null;
|
|
2900
6057
|
const startColumn = range.getColumn();
|
|
2901
6058
|
const endColumn = startColumn + range.getWidth() - 1;
|
|
@@ -2971,16 +6128,16 @@ var unhideSelectedColumns = (api) => {
|
|
|
2971
6128
|
});
|
|
2972
6129
|
};
|
|
2973
6130
|
var freezeFirstRow = (api) => {
|
|
2974
|
-
|
|
6131
|
+
activeSheet3(api)?.setFrozenRows(1);
|
|
2975
6132
|
};
|
|
2976
6133
|
var freezeFirstColumn = (api) => {
|
|
2977
|
-
|
|
6134
|
+
activeSheet3(api)?.setFrozenColumns(1);
|
|
2978
6135
|
};
|
|
2979
6136
|
var freezeAtSelection = (api) => void api.executeCommand("sheet.command.set-selection-frozen");
|
|
2980
6137
|
var unfreezePanes = (api) => void api.executeCommand("sheet.command.cancel-frozen");
|
|
2981
6138
|
var toggleGridlines = (api) => {
|
|
2982
6139
|
const wb = fu(api).getActiveWorkbook();
|
|
2983
|
-
const sheet =
|
|
6140
|
+
const sheet = activeSheet3(api);
|
|
2984
6141
|
if (!wb || !sheet) return;
|
|
2985
6142
|
void api.executeCommand("sheet.command.toggle-gridlines", {
|
|
2986
6143
|
unitId: wb.getId(),
|
|
@@ -2990,7 +6147,7 @@ var toggleGridlines = (api) => {
|
|
|
2990
6147
|
};
|
|
2991
6148
|
var toggleCommentPanel = (api) => void api.executeCommand("sheet.operation.toggle-comment-panel");
|
|
2992
6149
|
var jumpToFirstCell = (api) => {
|
|
2993
|
-
|
|
6150
|
+
activeSheet3(api)?.getRange(0, 0).activate();
|
|
2994
6151
|
};
|
|
2995
6152
|
var switchToPreviousSheet = (api) => switchSheetByDelta(api, -1);
|
|
2996
6153
|
var switchToNextSheet = (api) => switchSheetByDelta(api, 1);
|
|
@@ -3010,7 +6167,7 @@ var showFormulas = (api) => void api.executeCommand("sheet.command.set-show-form
|
|
|
3010
6167
|
var sortAsc = (api) => sortRange(api, true);
|
|
3011
6168
|
var sortDesc = (api) => sortRange(api, false);
|
|
3012
6169
|
function sortRange(api, ascending) {
|
|
3013
|
-
const range =
|
|
6170
|
+
const range = activeRange12(api);
|
|
3014
6171
|
if (!range) return;
|
|
3015
6172
|
const withSort = range;
|
|
3016
6173
|
withSort.sort?.({ column: range.getColumn(), ascending });
|
|
@@ -3019,8 +6176,8 @@ var toggleFilter = (api) => {
|
|
|
3019
6176
|
void (async () => {
|
|
3020
6177
|
await ensurePluginByName("filter");
|
|
3021
6178
|
const wb = fu(api).getActiveWorkbook();
|
|
3022
|
-
const sheet =
|
|
3023
|
-
const range =
|
|
6179
|
+
const sheet = activeSheet3(api);
|
|
6180
|
+
const range = activeRange12(api);
|
|
3024
6181
|
if (!wb || !sheet || !range) return;
|
|
3025
6182
|
const sheetWithFilter = sheet;
|
|
3026
6183
|
if (sheetWithFilter.getFilter?.()) {
|
|
@@ -3058,7 +6215,8 @@ var MENUS = [
|
|
|
3058
6215
|
id: "about",
|
|
3059
6216
|
label: "About casual sheets",
|
|
3060
6217
|
icon: "help_outline",
|
|
3061
|
-
dialog: "about"
|
|
6218
|
+
dialog: "about",
|
|
6219
|
+
feature: "branding"
|
|
3062
6220
|
}
|
|
3063
6221
|
]
|
|
3064
6222
|
},
|
|
@@ -3606,6 +6764,12 @@ var MENUS = [
|
|
|
3606
6764
|
{
|
|
3607
6765
|
id: "help",
|
|
3608
6766
|
label: "Help",
|
|
6767
|
+
// Whole-menu gate: an embedded host passes `features={{ help: false }}` to
|
|
6768
|
+
// drop the Help menu entirely (no editor-branded surface). Individual
|
|
6769
|
+
// branding links below also carry `feature: 'branding'` so a host that
|
|
6770
|
+
// keeps Help (for keyboard shortcuts) can still hide the About / GitHub
|
|
6771
|
+
// links with `features={{ branding: false }}`.
|
|
6772
|
+
feature: "help",
|
|
3609
6773
|
items: [
|
|
3610
6774
|
{
|
|
3611
6775
|
kind: "item",
|
|
@@ -3615,14 +6779,22 @@ var MENUS = [
|
|
|
3615
6779
|
shortcut: "Ctrl+/",
|
|
3616
6780
|
dialog: "keyboard-shortcuts"
|
|
3617
6781
|
},
|
|
3618
|
-
{ kind: "separator", id: "sep-help" },
|
|
3619
|
-
{
|
|
6782
|
+
{ kind: "separator", id: "sep-help", feature: "branding" },
|
|
6783
|
+
{
|
|
6784
|
+
kind: "item",
|
|
6785
|
+
id: "about",
|
|
6786
|
+
label: "About casual sheets",
|
|
6787
|
+
icon: "info",
|
|
6788
|
+
dialog: "about",
|
|
6789
|
+
feature: "branding"
|
|
6790
|
+
},
|
|
3620
6791
|
{
|
|
3621
6792
|
kind: "item",
|
|
3622
6793
|
id: "github",
|
|
3623
6794
|
label: "View on GitHub",
|
|
3624
6795
|
icon: "open_in_new",
|
|
3625
|
-
run: () => openExternal("https://github.com/CasualOffice/sheets")
|
|
6796
|
+
run: () => openExternal("https://github.com/CasualOffice/sheets"),
|
|
6797
|
+
feature: "branding"
|
|
3626
6798
|
}
|
|
3627
6799
|
]
|
|
3628
6800
|
}
|
|
@@ -3709,64 +6881,10 @@ var SUBMENU_PANEL_STYLE = {
|
|
|
3709
6881
|
boxShadow: "0 6px 20px rgba(0,0,0,0.16)",
|
|
3710
6882
|
zIndex: 1001
|
|
3711
6883
|
};
|
|
3712
|
-
function featureOn(feature, features) {
|
|
3713
|
-
if (!feature) return true;
|
|
3714
|
-
return features[feature] !== false;
|
|
3715
|
-
}
|
|
3716
|
-
function keepItem(item, features, canOpen) {
|
|
3717
|
-
if (!featureOn(item.feature, features)) return null;
|
|
3718
|
-
if (item.kind === "separator") return item;
|
|
3719
|
-
if (item.kind === "submenu") {
|
|
3720
|
-
const items = filterItems(item.items, features, canOpen);
|
|
3721
|
-
if (items.length === 0) return null;
|
|
3722
|
-
return { ...item, items };
|
|
3723
|
-
}
|
|
3724
|
-
if (item.dialog && !canOpen(item.dialog)) return null;
|
|
3725
|
-
return item;
|
|
3726
|
-
}
|
|
3727
|
-
function filterItems(items, features, canOpen) {
|
|
3728
|
-
const kept = items.map((i) => keepItem(i, features, canOpen)).filter((i) => i !== null);
|
|
3729
|
-
const out = [];
|
|
3730
|
-
for (const item of kept) {
|
|
3731
|
-
if (item.kind === "separator") {
|
|
3732
|
-
if (out.length === 0) continue;
|
|
3733
|
-
if (out[out.length - 1].kind === "separator") continue;
|
|
3734
|
-
}
|
|
3735
|
-
out.push(item);
|
|
3736
|
-
}
|
|
3737
|
-
while (out.length > 0 && out[out.length - 1].kind === "separator") out.pop();
|
|
3738
|
-
return out;
|
|
3739
|
-
}
|
|
3740
|
-
function withMenuExtensions(menus, ext) {
|
|
3741
|
-
if (!ext || ext.length === 0) return menus;
|
|
3742
|
-
const byMenu = /* @__PURE__ */ new Map();
|
|
3743
|
-
for (const e of ext) {
|
|
3744
|
-
const list = byMenu.get(e.menu) ?? [];
|
|
3745
|
-
list.push(e);
|
|
3746
|
-
byMenu.set(e.menu, list);
|
|
3747
|
-
}
|
|
3748
|
-
return menus.map((menu) => {
|
|
3749
|
-
const extras = byMenu.get(menu.id);
|
|
3750
|
-
if (!extras || extras.length === 0) return menu;
|
|
3751
|
-
const items = [...menu.items, { kind: "separator", id: `ext-sep-${menu.id}` }];
|
|
3752
|
-
for (const e of extras) {
|
|
3753
|
-
items.push({
|
|
3754
|
-
kind: "item",
|
|
3755
|
-
id: `ext-${e.id}`,
|
|
3756
|
-
label: e.label,
|
|
3757
|
-
icon: e.icon,
|
|
3758
|
-
shortcut: e.shortcut,
|
|
3759
|
-
dialog: e.dialog,
|
|
3760
|
-
run: e.onClick ? (api) => e.onClick?.(api) : void 0
|
|
3761
|
-
});
|
|
3762
|
-
}
|
|
3763
|
-
return { ...menu, items };
|
|
3764
|
-
});
|
|
3765
|
-
}
|
|
3766
6884
|
function MenuBar({ api, features = {}, extensions }) {
|
|
3767
6885
|
const dialogs = useDialogs();
|
|
3768
|
-
const [open, setOpen] =
|
|
3769
|
-
const [openSubmenu, setOpenSubmenu] =
|
|
6886
|
+
const [open, setOpen] = useState22(null);
|
|
6887
|
+
const [openSubmenu, setOpenSubmenu] = useState22(null);
|
|
3770
6888
|
const rootRef = useRef7(null);
|
|
3771
6889
|
useEffect10(() => {
|
|
3772
6890
|
ensureChromeFonts();
|
|
@@ -3806,25 +6924,21 @@ function MenuBar({ api, features = {}, extensions }) {
|
|
|
3806
6924
|
}
|
|
3807
6925
|
item.run?.(api);
|
|
3808
6926
|
};
|
|
3809
|
-
const
|
|
3810
|
-
const visibleMenus = baseMenus.map((menu) => ({
|
|
3811
|
-
...menu,
|
|
3812
|
-
items: filterItems(menu.items, features, dialogs.canOpen)
|
|
3813
|
-
})).filter((menu) => featureOn(menu.feature, features) && menu.items.length > 0);
|
|
6927
|
+
const visibleMenus = computeVisibleMenus(MENUS, features, dialogs.canOpen, extensions?.menu);
|
|
3814
6928
|
const renderItems = (items) => items.map((item) => {
|
|
3815
6929
|
if (item.kind === "separator") {
|
|
3816
|
-
return /* @__PURE__ */
|
|
6930
|
+
return /* @__PURE__ */ jsx24("div", { style: SEPARATOR_STYLE, role: "separator", "aria-hidden": true }, item.id);
|
|
3817
6931
|
}
|
|
3818
6932
|
if (item.kind === "submenu") {
|
|
3819
6933
|
const isSubOpen = openSubmenu === item.id;
|
|
3820
|
-
return /* @__PURE__ */
|
|
6934
|
+
return /* @__PURE__ */ jsxs23(
|
|
3821
6935
|
"div",
|
|
3822
6936
|
{
|
|
3823
6937
|
style: { position: "relative" },
|
|
3824
6938
|
onMouseEnter: () => setOpenSubmenu(item.id),
|
|
3825
6939
|
onMouseLeave: () => setOpenSubmenu((cur) => cur === item.id ? null : cur),
|
|
3826
6940
|
children: [
|
|
3827
|
-
/* @__PURE__ */
|
|
6941
|
+
/* @__PURE__ */ jsxs23(
|
|
3828
6942
|
"button",
|
|
3829
6943
|
{
|
|
3830
6944
|
type: "button",
|
|
@@ -3836,19 +6950,19 @@ function MenuBar({ api, features = {}, extensions }) {
|
|
|
3836
6950
|
style: { ...ITEM_STYLE3, opacity: api ? 1 : 0.5 },
|
|
3837
6951
|
onMouseDown: (e) => e.preventDefault(),
|
|
3838
6952
|
children: [
|
|
3839
|
-
item.icon ? /* @__PURE__ */
|
|
3840
|
-
/* @__PURE__ */
|
|
3841
|
-
/* @__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" } })
|
|
3842
6956
|
]
|
|
3843
6957
|
}
|
|
3844
6958
|
),
|
|
3845
|
-
isSubOpen && /* @__PURE__ */
|
|
6959
|
+
isSubOpen && /* @__PURE__ */ jsx24("div", { style: SUBMENU_PANEL_STYLE, role: "menu", "aria-label": item.label, children: renderItems(item.items) })
|
|
3846
6960
|
]
|
|
3847
6961
|
},
|
|
3848
6962
|
item.id
|
|
3849
6963
|
);
|
|
3850
6964
|
}
|
|
3851
|
-
return /* @__PURE__ */
|
|
6965
|
+
return /* @__PURE__ */ jsxs23(
|
|
3852
6966
|
"button",
|
|
3853
6967
|
{
|
|
3854
6968
|
type: "button",
|
|
@@ -3868,15 +6982,15 @@ function MenuBar({ api, features = {}, extensions }) {
|
|
|
3868
6982
|
e.currentTarget.style.background = "transparent";
|
|
3869
6983
|
},
|
|
3870
6984
|
children: [
|
|
3871
|
-
item.icon ? /* @__PURE__ */
|
|
3872
|
-
/* @__PURE__ */
|
|
3873
|
-
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) })
|
|
3874
6988
|
]
|
|
3875
6989
|
},
|
|
3876
6990
|
item.id
|
|
3877
6991
|
);
|
|
3878
6992
|
});
|
|
3879
|
-
return /* @__PURE__ */
|
|
6993
|
+
return /* @__PURE__ */ jsx24(
|
|
3880
6994
|
"div",
|
|
3881
6995
|
{
|
|
3882
6996
|
ref: rootRef,
|
|
@@ -3886,8 +7000,8 @@ function MenuBar({ api, features = {}, extensions }) {
|
|
|
3886
7000
|
"aria-label": "Menu bar",
|
|
3887
7001
|
children: visibleMenus.map((menu) => {
|
|
3888
7002
|
const isOpen = open === menu.id;
|
|
3889
|
-
return /* @__PURE__ */
|
|
3890
|
-
/* @__PURE__ */
|
|
7003
|
+
return /* @__PURE__ */ jsxs23("div", { style: { position: "relative" }, children: [
|
|
7004
|
+
/* @__PURE__ */ jsx24(
|
|
3891
7005
|
"button",
|
|
3892
7006
|
{
|
|
3893
7007
|
type: "button",
|
|
@@ -3918,7 +7032,7 @@ function MenuBar({ api, features = {}, extensions }) {
|
|
|
3918
7032
|
children: menu.label
|
|
3919
7033
|
}
|
|
3920
7034
|
),
|
|
3921
|
-
isOpen && /* @__PURE__ */
|
|
7035
|
+
isOpen && /* @__PURE__ */ jsx24("div", { style: DROPDOWN_STYLE, role: "menu", "aria-label": menu.label, children: renderItems(menu.items) })
|
|
3922
7036
|
] }, menu.id);
|
|
3923
7037
|
})
|
|
3924
7038
|
}
|
|
@@ -3926,9 +7040,9 @@ function MenuBar({ api, features = {}, extensions }) {
|
|
|
3926
7040
|
}
|
|
3927
7041
|
|
|
3928
7042
|
// src/chrome/SheetTabs.tsx
|
|
3929
|
-
import { useEffect as useEffect11, useState as
|
|
7043
|
+
import { useEffect as useEffect11, useState as useState23 } from "react";
|
|
3930
7044
|
import { createPortal as createPortal2 } from "react-dom";
|
|
3931
|
-
import { jsx as
|
|
7045
|
+
import { jsx as jsx25, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
3932
7046
|
var EMPTY = { tabs: [], activeId: null };
|
|
3933
7047
|
var SHEET_LIST_MUTATIONS = /* @__PURE__ */ new Set([
|
|
3934
7048
|
"sheet.mutation.insert-sheet",
|
|
@@ -3961,7 +7075,7 @@ var BAR_STYLE5 = {
|
|
|
3961
7075
|
overflowX: "auto",
|
|
3962
7076
|
overflowY: "hidden"
|
|
3963
7077
|
};
|
|
3964
|
-
var
|
|
7078
|
+
var ADD_BTN_STYLE2 = {
|
|
3965
7079
|
flex: "0 0 auto",
|
|
3966
7080
|
width: 28,
|
|
3967
7081
|
border: "none",
|
|
@@ -4009,10 +7123,10 @@ var MENU_ITEM_STYLE2 = {
|
|
|
4009
7123
|
cursor: "pointer"
|
|
4010
7124
|
};
|
|
4011
7125
|
function SheetTabs({ api }) {
|
|
4012
|
-
const [{ tabs, activeId }, setSnapshot] =
|
|
4013
|
-
const [renaming, setRenaming] =
|
|
4014
|
-
const [draft, setDraft] =
|
|
4015
|
-
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);
|
|
4016
7130
|
useEffect11(() => {
|
|
4017
7131
|
if (!api) return;
|
|
4018
7132
|
const refresh = () => setSnapshot(readSheets(api));
|
|
@@ -4052,7 +7166,7 @@ function SheetTabs({ api }) {
|
|
|
4052
7166
|
};
|
|
4053
7167
|
}, [menu]);
|
|
4054
7168
|
if (!api) {
|
|
4055
|
-
return /* @__PURE__ */
|
|
7169
|
+
return /* @__PURE__ */ jsx25("div", { style: BAR_STYLE5, "data-testid": "casual-sheets-tabs" });
|
|
4056
7170
|
}
|
|
4057
7171
|
const visible = tabs.filter((t) => !t.hidden);
|
|
4058
7172
|
const wb = () => api.univer.getActiveWorkbook();
|
|
@@ -4092,11 +7206,11 @@ function SheetTabs({ api }) {
|
|
|
4092
7206
|
setRenaming(null);
|
|
4093
7207
|
}
|
|
4094
7208
|
};
|
|
4095
|
-
return /* @__PURE__ */
|
|
7209
|
+
return /* @__PURE__ */ jsxs24("div", { style: BAR_STYLE5, "data-testid": "casual-sheets-tabs", role: "tablist", children: [
|
|
4096
7210
|
visible.map((tab) => {
|
|
4097
7211
|
const active = tab.id === activeId;
|
|
4098
7212
|
if (renaming === tab.id) {
|
|
4099
|
-
return /* @__PURE__ */
|
|
7213
|
+
return /* @__PURE__ */ jsx25(
|
|
4100
7214
|
"input",
|
|
4101
7215
|
{
|
|
4102
7216
|
autoFocus: true,
|
|
@@ -4110,7 +7224,7 @@ function SheetTabs({ api }) {
|
|
|
4110
7224
|
tab.id
|
|
4111
7225
|
);
|
|
4112
7226
|
}
|
|
4113
|
-
return /* @__PURE__ */
|
|
7227
|
+
return /* @__PURE__ */ jsx25(
|
|
4114
7228
|
"button",
|
|
4115
7229
|
{
|
|
4116
7230
|
type: "button",
|
|
@@ -4149,11 +7263,11 @@ function SheetTabs({ api }) {
|
|
|
4149
7263
|
tab.id
|
|
4150
7264
|
);
|
|
4151
7265
|
}),
|
|
4152
|
-
/* @__PURE__ */
|
|
7266
|
+
/* @__PURE__ */ jsx25(
|
|
4153
7267
|
"button",
|
|
4154
7268
|
{
|
|
4155
7269
|
type: "button",
|
|
4156
|
-
style:
|
|
7270
|
+
style: ADD_BTN_STYLE2,
|
|
4157
7271
|
"aria-label": "Add sheet",
|
|
4158
7272
|
title: "Add sheet",
|
|
4159
7273
|
"data-testid": "cs-tab-add",
|
|
@@ -4165,7 +7279,7 @@ function SheetTabs({ api }) {
|
|
|
4165
7279
|
}
|
|
4166
7280
|
),
|
|
4167
7281
|
menu && createPortal2(
|
|
4168
|
-
/* @__PURE__ */
|
|
7282
|
+
/* @__PURE__ */ jsxs24(
|
|
4169
7283
|
"div",
|
|
4170
7284
|
{
|
|
4171
7285
|
style: { ...MENU_STYLE2, left: menu.x, top: menu.y, transform: "translateY(-100%)" },
|
|
@@ -4173,7 +7287,7 @@ function SheetTabs({ api }) {
|
|
|
4173
7287
|
role: "menu",
|
|
4174
7288
|
onPointerDown: (e) => e.stopPropagation(),
|
|
4175
7289
|
children: [
|
|
4176
|
-
/* @__PURE__ */
|
|
7290
|
+
/* @__PURE__ */ jsx25(
|
|
4177
7291
|
"button",
|
|
4178
7292
|
{
|
|
4179
7293
|
type: "button",
|
|
@@ -4188,7 +7302,7 @@ function SheetTabs({ api }) {
|
|
|
4188
7302
|
children: "Rename"
|
|
4189
7303
|
}
|
|
4190
7304
|
),
|
|
4191
|
-
/* @__PURE__ */
|
|
7305
|
+
/* @__PURE__ */ jsx25(
|
|
4192
7306
|
"button",
|
|
4193
7307
|
{
|
|
4194
7308
|
type: "button",
|
|
@@ -4217,11 +7331,11 @@ function SheetTabs({ api }) {
|
|
|
4217
7331
|
// src/chrome/FindReplace.tsx
|
|
4218
7332
|
import {
|
|
4219
7333
|
useEffect as useEffect12,
|
|
4220
|
-
useMemo as
|
|
7334
|
+
useMemo as useMemo16,
|
|
4221
7335
|
useRef as useRef8,
|
|
4222
|
-
useState as
|
|
7336
|
+
useState as useState24
|
|
4223
7337
|
} from "react";
|
|
4224
|
-
import { jsx as
|
|
7338
|
+
import { jsx as jsx26, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
4225
7339
|
function findMatches(api, query, matchCase) {
|
|
4226
7340
|
if (!query) return [];
|
|
4227
7341
|
const snap = api.getSnapshot();
|
|
@@ -4259,7 +7373,7 @@ var WRAP_STYLE = {
|
|
|
4259
7373
|
fontSize: 13,
|
|
4260
7374
|
color: "var(--cs-chrome-fg, #201f1e)"
|
|
4261
7375
|
};
|
|
4262
|
-
var
|
|
7376
|
+
var ROW_STYLE5 = { display: "flex", alignItems: "center", gap: 6, marginBottom: 6 };
|
|
4263
7377
|
var INPUT_STYLE2 = {
|
|
4264
7378
|
flex: "1 1 auto",
|
|
4265
7379
|
height: 26,
|
|
@@ -4297,12 +7411,12 @@ var ICON_BTN_STYLE = {
|
|
|
4297
7411
|
padding: 0
|
|
4298
7412
|
};
|
|
4299
7413
|
function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
4300
|
-
const [open, setOpen] =
|
|
4301
|
-
const [showReplace, setShowReplace] =
|
|
4302
|
-
const [query, setQuery] =
|
|
4303
|
-
const [replaceText, setReplaceText] =
|
|
4304
|
-
const [matchCase, setMatchCase] =
|
|
4305
|
-
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);
|
|
4306
7420
|
const inputRef = useRef8(null);
|
|
4307
7421
|
useEffect12(() => {
|
|
4308
7422
|
const onKey = (e) => {
|
|
@@ -4331,7 +7445,7 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
|
4331
7445
|
setOpen(true);
|
|
4332
7446
|
requestAnimationFrame(() => inputRef.current?.focus());
|
|
4333
7447
|
}, [openSignal, openInReplaceMode]);
|
|
4334
|
-
const matches =
|
|
7448
|
+
const matches = useMemo16(
|
|
4335
7449
|
() => api && open && query ? findMatches(api, query, matchCase) : [],
|
|
4336
7450
|
[api, open, query, matchCase]
|
|
4337
7451
|
);
|
|
@@ -4381,7 +7495,7 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
|
4381
7495
|
};
|
|
4382
7496
|
const count = matches.length;
|
|
4383
7497
|
const position = count === 0 ? 0 : Math.min(idx, count - 1) + 1;
|
|
4384
|
-
return /* @__PURE__ */
|
|
7498
|
+
return /* @__PURE__ */ jsxs25(
|
|
4385
7499
|
"div",
|
|
4386
7500
|
{
|
|
4387
7501
|
style: WRAP_STYLE,
|
|
@@ -4389,8 +7503,8 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
|
4389
7503
|
role: "dialog",
|
|
4390
7504
|
"aria-label": "Find and replace",
|
|
4391
7505
|
children: [
|
|
4392
|
-
/* @__PURE__ */
|
|
4393
|
-
/* @__PURE__ */
|
|
7506
|
+
/* @__PURE__ */ jsxs25("div", { style: ROW_STYLE5, children: [
|
|
7507
|
+
/* @__PURE__ */ jsx26(
|
|
4394
7508
|
"input",
|
|
4395
7509
|
{
|
|
4396
7510
|
ref: inputRef,
|
|
@@ -4404,7 +7518,7 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
|
4404
7518
|
onKeyDown: onInputKey
|
|
4405
7519
|
}
|
|
4406
7520
|
),
|
|
4407
|
-
/* @__PURE__ */
|
|
7521
|
+
/* @__PURE__ */ jsxs25(
|
|
4408
7522
|
"span",
|
|
4409
7523
|
{
|
|
4410
7524
|
style: { minWidth: 54, textAlign: "center", color: "var(--cs-chrome-muted, #6b7280)" },
|
|
@@ -4416,7 +7530,7 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
|
4416
7530
|
]
|
|
4417
7531
|
}
|
|
4418
7532
|
),
|
|
4419
|
-
/* @__PURE__ */
|
|
7533
|
+
/* @__PURE__ */ jsx26(
|
|
4420
7534
|
"button",
|
|
4421
7535
|
{
|
|
4422
7536
|
type: "button",
|
|
@@ -4427,10 +7541,10 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
|
4427
7541
|
e.preventDefault();
|
|
4428
7542
|
go(-1);
|
|
4429
7543
|
},
|
|
4430
|
-
children: /* @__PURE__ */
|
|
7544
|
+
children: /* @__PURE__ */ jsx26(Icon, { name: "keyboard_arrow_up", size: 18 })
|
|
4431
7545
|
}
|
|
4432
7546
|
),
|
|
4433
|
-
/* @__PURE__ */
|
|
7547
|
+
/* @__PURE__ */ jsx26(
|
|
4434
7548
|
"button",
|
|
4435
7549
|
{
|
|
4436
7550
|
type: "button",
|
|
@@ -4441,10 +7555,10 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
|
4441
7555
|
e.preventDefault();
|
|
4442
7556
|
go(1);
|
|
4443
7557
|
},
|
|
4444
|
-
children: /* @__PURE__ */
|
|
7558
|
+
children: /* @__PURE__ */ jsx26(Icon, { name: "keyboard_arrow_down", size: 18 })
|
|
4445
7559
|
}
|
|
4446
7560
|
),
|
|
4447
|
-
/* @__PURE__ */
|
|
7561
|
+
/* @__PURE__ */ jsx26(
|
|
4448
7562
|
"button",
|
|
4449
7563
|
{
|
|
4450
7564
|
type: "button",
|
|
@@ -4455,12 +7569,12 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
|
4455
7569
|
e.preventDefault();
|
|
4456
7570
|
setOpen(false);
|
|
4457
7571
|
},
|
|
4458
|
-
children: /* @__PURE__ */
|
|
7572
|
+
children: /* @__PURE__ */ jsx26(Icon, { name: "close", size: 18 })
|
|
4459
7573
|
}
|
|
4460
7574
|
)
|
|
4461
7575
|
] }),
|
|
4462
|
-
showReplace && /* @__PURE__ */
|
|
4463
|
-
/* @__PURE__ */
|
|
7576
|
+
showReplace && /* @__PURE__ */ jsxs25("div", { style: ROW_STYLE5, children: [
|
|
7577
|
+
/* @__PURE__ */ jsx26(
|
|
4464
7578
|
"input",
|
|
4465
7579
|
{
|
|
4466
7580
|
type: "text",
|
|
@@ -4472,7 +7586,7 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
|
4472
7586
|
onChange: (e) => setReplaceText(e.target.value)
|
|
4473
7587
|
}
|
|
4474
7588
|
),
|
|
4475
|
-
/* @__PURE__ */
|
|
7589
|
+
/* @__PURE__ */ jsx26(
|
|
4476
7590
|
"button",
|
|
4477
7591
|
{
|
|
4478
7592
|
type: "button",
|
|
@@ -4485,7 +7599,7 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
|
4485
7599
|
children: "Replace"
|
|
4486
7600
|
}
|
|
4487
7601
|
),
|
|
4488
|
-
/* @__PURE__ */
|
|
7602
|
+
/* @__PURE__ */ jsx26(
|
|
4489
7603
|
"button",
|
|
4490
7604
|
{
|
|
4491
7605
|
type: "button",
|
|
@@ -4499,9 +7613,9 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
|
4499
7613
|
}
|
|
4500
7614
|
)
|
|
4501
7615
|
] }),
|
|
4502
|
-
/* @__PURE__ */
|
|
4503
|
-
/* @__PURE__ */
|
|
4504
|
-
/* @__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(
|
|
4505
7619
|
"input",
|
|
4506
7620
|
{
|
|
4507
7621
|
type: "checkbox",
|
|
@@ -4512,7 +7626,7 @@ function FindReplace({ api, openSignal, openInReplaceMode }) {
|
|
|
4512
7626
|
),
|
|
4513
7627
|
"Match case"
|
|
4514
7628
|
] }),
|
|
4515
|
-
!showReplace && /* @__PURE__ */
|
|
7629
|
+
!showReplace && /* @__PURE__ */ jsx26(
|
|
4516
7630
|
"button",
|
|
4517
7631
|
{
|
|
4518
7632
|
type: "button",
|
|
@@ -4556,8 +7670,8 @@ function replaceInString(text, query, replacement, matchCase) {
|
|
|
4556
7670
|
}
|
|
4557
7671
|
|
|
4558
7672
|
// src/chrome/ChromeTop.tsx
|
|
4559
|
-
import { useCallback as
|
|
4560
|
-
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";
|
|
4561
7675
|
function ChromeTop({
|
|
4562
7676
|
api,
|
|
4563
7677
|
features,
|
|
@@ -4565,13 +7679,13 @@ function ChromeTop({
|
|
|
4565
7679
|
hostOwnedDialogs,
|
|
4566
7680
|
extensions
|
|
4567
7681
|
}) {
|
|
4568
|
-
const [findSignal, setFindSignal] =
|
|
4569
|
-
const [findReplaceMode, setFindReplaceMode] =
|
|
4570
|
-
const openFindReplace =
|
|
7682
|
+
const [findSignal, setFindSignal] = useState25(0);
|
|
7683
|
+
const [findReplaceMode, setFindReplaceMode] = useState25(false);
|
|
7684
|
+
const openFindReplace = useCallback3((replaceMode) => {
|
|
4571
7685
|
setFindReplaceMode(replaceMode);
|
|
4572
7686
|
setFindSignal((n) => n + 1);
|
|
4573
7687
|
}, []);
|
|
4574
|
-
return /* @__PURE__ */
|
|
7688
|
+
return /* @__PURE__ */ jsxs26(
|
|
4575
7689
|
DialogProvider,
|
|
4576
7690
|
{
|
|
4577
7691
|
api,
|
|
@@ -4580,10 +7694,10 @@ function ChromeTop({
|
|
|
4580
7694
|
hostOwnedDialogs,
|
|
4581
7695
|
onOpenFindReplace: openFindReplace,
|
|
4582
7696
|
children: [
|
|
4583
|
-
/* @__PURE__ */
|
|
4584
|
-
/* @__PURE__ */
|
|
4585
|
-
/* @__PURE__ */
|
|
4586
|
-
/* @__PURE__ */
|
|
7697
|
+
/* @__PURE__ */ jsx27(MenuBar, { api, features, extensions }),
|
|
7698
|
+
/* @__PURE__ */ jsx27(Toolbar, { api, features, extensions }),
|
|
7699
|
+
/* @__PURE__ */ jsx27(FormulaBar, { api }),
|
|
7700
|
+
/* @__PURE__ */ jsx27(
|
|
4587
7701
|
FindReplace,
|
|
4588
7702
|
{
|
|
4589
7703
|
api,
|
|
@@ -4597,11 +7711,11 @@ function ChromeTop({
|
|
|
4597
7711
|
}
|
|
4598
7712
|
|
|
4599
7713
|
// src/chrome/ChromeBottom.tsx
|
|
4600
|
-
import { Fragment as
|
|
7714
|
+
import { Fragment as Fragment16, jsx as jsx28, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
4601
7715
|
function ChromeBottom({ api }) {
|
|
4602
|
-
return /* @__PURE__ */
|
|
4603
|
-
/* @__PURE__ */
|
|
4604
|
-
/* @__PURE__ */
|
|
7716
|
+
return /* @__PURE__ */ jsxs27(Fragment16, { children: [
|
|
7717
|
+
/* @__PURE__ */ jsx28(SheetTabs, { api }),
|
|
7718
|
+
/* @__PURE__ */ jsx28(StatusBar, { api })
|
|
4605
7719
|
] });
|
|
4606
7720
|
}
|
|
4607
7721
|
export {
|