@underverse-ui/underverse 2.0.28 → 2.0.30
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/api-reference.json +1 -1
- package/dist/{EmojiPicker-NV3GQYWI.js → EmojiPicker-ISJ6RXYG.js} +4 -4
- package/dist/{chunk-Y4X6CAZY.js → chunk-2P7TSXEE.js} +82 -25
- package/dist/chunk-2P7TSXEE.js.map +1 -0
- package/dist/{chunk-KTOXCD6J.js → chunk-37MHVJMV.js} +1078 -90
- package/dist/chunk-37MHVJMV.js.map +1 -0
- package/dist/{chunk-QYFXYDBK.js → chunk-57BKIC6J.js} +1306 -888
- package/dist/chunk-57BKIC6J.js.map +1 -0
- package/dist/{chunk-LRIRY4DG.js → chunk-5W63IHIN.js} +11 -3
- package/dist/chunk-5W63IHIN.js.map +1 -0
- package/dist/{chunk-F7MX3XXK.js → chunk-S4UHFRL3.js} +3 -3
- package/dist/{chunk-A4GUPKHM.js → chunk-ZP7F4ZD7.js} +63 -43
- package/dist/chunk-ZP7F4ZD7.js.map +1 -0
- package/dist/index.cjs +3275 -1628
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +55 -0
- package/dist/index.d.ts +55 -0
- package/dist/index.js +23 -10
- package/dist/index.js.map +1 -1
- package/dist/{menu-bar-CFIWMBAA.js → menu-bar-DPCNNUX5.js} +227 -54
- package/dist/menu-bar-DPCNNUX5.js.map +1 -0
- package/dist/ueditor.cjs +9803 -8169
- package/dist/ueditor.cjs.map +1 -1
- package/dist/ueditor.d.cts +10 -0
- package/dist/ueditor.d.ts +10 -0
- package/dist/ueditor.js +4 -4
- package/package.json +2 -1
- package/dist/chunk-A4GUPKHM.js.map +0 -1
- package/dist/chunk-KTOXCD6J.js.map +0 -1
- package/dist/chunk-LRIRY4DG.js.map +0 -1
- package/dist/chunk-QYFXYDBK.js.map +0 -1
- package/dist/chunk-Y4X6CAZY.js.map +0 -1
- package/dist/menu-bar-CFIWMBAA.js.map +0 -1
- /package/dist/{EmojiPicker-NV3GQYWI.js.map → EmojiPicker-ISJ6RXYG.js.map} +0 -0
- /package/dist/{chunk-F7MX3XXK.js.map → chunk-S4UHFRL3.js.map} +0 -0
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
setRefValue,
|
|
12
12
|
useSmartTranslations,
|
|
13
13
|
useUEditorPortalContainerGetter
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-ZP7F4ZD7.js";
|
|
15
15
|
|
|
16
16
|
// src/contexts/UnderverseConfigContext.tsx
|
|
17
17
|
import * as React from "react";
|
|
@@ -834,6 +834,8 @@ var DropdownMenu = ({
|
|
|
834
834
|
} else if (e.key === "Escape" && (isInMenu || isOnTrigger)) {
|
|
835
835
|
e.preventDefault();
|
|
836
836
|
closeMenu();
|
|
837
|
+
const focusTarget = triggerEl.matches("button,a,input,select,textarea,[tabindex]") ? triggerEl : triggerEl.querySelector("button,a,input,select,textarea,[tabindex]");
|
|
838
|
+
focusTarget?.focus();
|
|
837
839
|
}
|
|
838
840
|
};
|
|
839
841
|
const menuDocument = menuRef.current?.ownerDocument;
|
|
@@ -900,7 +902,7 @@ var DropdownMenu = ({
|
|
|
900
902
|
item.destructive && "text-destructive hover:bg-destructive/10 focus:bg-destructive/10"
|
|
901
903
|
),
|
|
902
904
|
children: [
|
|
903
|
-
IconComponent && /* @__PURE__ */ jsx3(IconComponent, { className: "h-4 w-4" }),
|
|
905
|
+
IconComponent && /* @__PURE__ */ jsx3(IconComponent, { "aria-hidden": "true", className: "h-4 w-4" }),
|
|
904
906
|
item.label
|
|
905
907
|
]
|
|
906
908
|
},
|
|
@@ -928,11 +930,11 @@ var DropdownMenu = ({
|
|
|
928
930
|
else callback(0);
|
|
929
931
|
};
|
|
930
932
|
if (!disabled) {
|
|
931
|
-
if (event.key === "ArrowDown") {
|
|
933
|
+
if (!event.altKey && !event.ctrlKey && !event.metaKey && event.key === "ArrowDown") {
|
|
932
934
|
event.preventDefault();
|
|
933
935
|
setOpen(true);
|
|
934
936
|
scheduleFrame(() => focusMenuItem(0));
|
|
935
|
-
} else if (event.key === "ArrowUp") {
|
|
937
|
+
} else if (!event.altKey && !event.ctrlKey && !event.metaKey && event.key === "ArrowUp") {
|
|
936
938
|
event.preventDefault();
|
|
937
939
|
setOpen(true);
|
|
938
940
|
scheduleFrame(() => {
|
|
@@ -1011,6 +1013,8 @@ var DropdownMenuItem = ({
|
|
|
1011
1013
|
return /* @__PURE__ */ jsxs2(
|
|
1012
1014
|
"button",
|
|
1013
1015
|
{
|
|
1016
|
+
type: "button",
|
|
1017
|
+
role: "menuitem",
|
|
1014
1018
|
onClick: () => {
|
|
1015
1019
|
onClick?.();
|
|
1016
1020
|
if (shouldCloseOnSelect) {
|
|
@@ -1032,19 +1036,19 @@ var DropdownMenuItem = ({
|
|
|
1032
1036
|
className
|
|
1033
1037
|
),
|
|
1034
1038
|
children: [
|
|
1035
|
-
Icon && /* @__PURE__ */ jsx3(Icon, { className: cn("w-4 h-4 shrink-0", active ? "text-primary" : "opacity-60 group-hover:opacity-100") }),
|
|
1039
|
+
Icon && /* @__PURE__ */ jsx3(Icon, { "aria-hidden": "true", className: cn("w-4 h-4 shrink-0", active ? "text-primary" : "opacity-60 group-hover:opacity-100") }),
|
|
1036
1040
|
/* @__PURE__ */ jsxs2("div", { className: "flex-1 text-left", children: [
|
|
1037
1041
|
label && /* @__PURE__ */ jsx3("div", { className: cn("font-medium", description && "leading-tight"), children: label }),
|
|
1038
1042
|
description && /* @__PURE__ */ jsx3("div", { className: "text-xs text-muted-foreground", children: description }),
|
|
1039
1043
|
children
|
|
1040
1044
|
] }),
|
|
1041
1045
|
shortcut && /* @__PURE__ */ jsx3("span", { className: "ml-2 text-xs text-muted-foreground opacity-60", children: shortcut }),
|
|
1042
|
-
active && /* @__PURE__ */ jsx3("svg", { className: "w-4 h-4 text-primary shrink-0", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx3("polyline", { points: "20 6 9 17 4 12" }) })
|
|
1046
|
+
active && /* @__PURE__ */ jsx3("svg", { "aria-hidden": "true", className: "w-4 h-4 text-primary shrink-0", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx3("polyline", { points: "20 6 9 17 4 12" }) })
|
|
1043
1047
|
]
|
|
1044
1048
|
}
|
|
1045
1049
|
);
|
|
1046
1050
|
};
|
|
1047
|
-
var DropdownMenuSeparator = ({ className }) => /* @__PURE__ */ jsx3("div", { className: cn("h-px bg-border my-1", className) });
|
|
1051
|
+
var DropdownMenuSeparator = ({ className }) => /* @__PURE__ */ jsx3("div", { role: "separator", className: cn("h-px bg-border my-1", className) });
|
|
1048
1052
|
var DropdownMenuSub = ({ label, icon: Icon, disabled, borderMode, getPortalContainer, children }) => {
|
|
1049
1053
|
const globalConfig = useUnderverseUIConfig();
|
|
1050
1054
|
const resolvedBorderMode = borderMode ?? globalConfig.dropdownMenu?.borderMode ?? globalConfig.borderMode;
|
|
@@ -1055,6 +1059,9 @@ var DropdownMenuSub = ({ label, icon: Icon, disabled, borderMode, getPortalConta
|
|
|
1055
1059
|
"button",
|
|
1056
1060
|
{
|
|
1057
1061
|
type: "button",
|
|
1062
|
+
role: "menuitem",
|
|
1063
|
+
"data-dropdown-menu-item": "",
|
|
1064
|
+
tabIndex: -1,
|
|
1058
1065
|
disabled,
|
|
1059
1066
|
onMouseDown: (e) => e.preventDefault(),
|
|
1060
1067
|
className: cn(
|
|
@@ -1065,9 +1072,9 @@ var DropdownMenuSub = ({ label, icon: Icon, disabled, borderMode, getPortalConta
|
|
|
1065
1072
|
"disabled:opacity-50 disabled:cursor-not-allowed"
|
|
1066
1073
|
),
|
|
1067
1074
|
children: [
|
|
1068
|
-
Icon && /* @__PURE__ */ jsx3(Icon, { className: "w-4 h-4 shrink-0 opacity-60" }),
|
|
1075
|
+
Icon && /* @__PURE__ */ jsx3(Icon, { "aria-hidden": "true", className: "w-4 h-4 shrink-0 opacity-60" }),
|
|
1069
1076
|
/* @__PURE__ */ jsx3("span", { className: "flex-1 text-left", children: label }),
|
|
1070
|
-
/* @__PURE__ */ jsx3(ChevronRight, { className: "w-3 h-3 opacity-50" })
|
|
1077
|
+
/* @__PURE__ */ jsx3(ChevronRight, { "aria-hidden": "true", className: "w-3 h-3 opacity-50" })
|
|
1071
1078
|
]
|
|
1072
1079
|
}
|
|
1073
1080
|
),
|
|
@@ -1170,6 +1177,7 @@ function isDataFileUrl(value) {
|
|
|
1170
1177
|
function isSafeUEditorUrl(raw, kind) {
|
|
1171
1178
|
const value = normalizeUrlInput(raw);
|
|
1172
1179
|
if (!value) return false;
|
|
1180
|
+
if (/[<>'"`]/.test(value)) return false;
|
|
1173
1181
|
if (kind === "image" && isDataImageUrl(value)) return true;
|
|
1174
1182
|
if (kind === "file" && isDataFileUrl(value)) return true;
|
|
1175
1183
|
if (isProtocolRelativeUrl(value)) return false;
|
|
@@ -1194,10 +1202,251 @@ function sanitizeUEditorUrl(raw, kind) {
|
|
|
1194
1202
|
return "";
|
|
1195
1203
|
}
|
|
1196
1204
|
|
|
1205
|
+
// src/components/UEditor/async-insertion-position.ts
|
|
1206
|
+
function trackEditorInsertionPosition(editor, initialPosition = editor.state.selection.from) {
|
|
1207
|
+
let position = initialPosition;
|
|
1208
|
+
const mapPosition = ({ transaction }) => {
|
|
1209
|
+
position = transaction.mapping.map(position, 1);
|
|
1210
|
+
};
|
|
1211
|
+
editor.on("transaction", mapPosition);
|
|
1212
|
+
return {
|
|
1213
|
+
get current() {
|
|
1214
|
+
return position;
|
|
1215
|
+
},
|
|
1216
|
+
stop() {
|
|
1217
|
+
editor.off("transaction", mapPosition);
|
|
1218
|
+
}
|
|
1219
|
+
};
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
// src/components/UEditor/image-file-upload.ts
|
|
1223
|
+
function fileToDataUrl(file) {
|
|
1224
|
+
return new Promise((resolve, reject) => {
|
|
1225
|
+
const reader = new FileReader();
|
|
1226
|
+
reader.onload = () => resolve(String(reader.result ?? ""));
|
|
1227
|
+
reader.onerror = () => reject(reader.error ?? new Error("Failed to read image file."));
|
|
1228
|
+
reader.readAsDataURL(file);
|
|
1229
|
+
});
|
|
1230
|
+
}
|
|
1231
|
+
async function resolveImageSource(file, options) {
|
|
1232
|
+
if (options.insertMode === "upload") {
|
|
1233
|
+
if (!options.upload) {
|
|
1234
|
+
if (!options.fallbackToDataUrl) {
|
|
1235
|
+
throw new Error("Image upload handler is not configured.");
|
|
1236
|
+
}
|
|
1237
|
+
} else {
|
|
1238
|
+
try {
|
|
1239
|
+
const uploadedUrl = await options.upload(file);
|
|
1240
|
+
const safeUploadedUrl = sanitizeUEditorUrl(uploadedUrl, "image");
|
|
1241
|
+
if (safeUploadedUrl) return safeUploadedUrl;
|
|
1242
|
+
if (!options.fallbackToDataUrl) {
|
|
1243
|
+
throw new Error("Image upload returned an invalid URL.");
|
|
1244
|
+
}
|
|
1245
|
+
} catch (error) {
|
|
1246
|
+
if (!options.fallbackToDataUrl) throw error;
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
const dataUrl = await fileToDataUrl(file);
|
|
1251
|
+
const safeDataUrl = sanitizeUEditorUrl(dataUrl, "image");
|
|
1252
|
+
if (!safeDataUrl) throw new Error("Image file could not be converted to a safe data URL.");
|
|
1253
|
+
return safeDataUrl;
|
|
1254
|
+
}
|
|
1255
|
+
async function resolveUEditorImageFiles(files, options) {
|
|
1256
|
+
let hadError = false;
|
|
1257
|
+
const reportError = (error, file) => {
|
|
1258
|
+
hadError = true;
|
|
1259
|
+
options.onError?.(error, file);
|
|
1260
|
+
};
|
|
1261
|
+
const validFiles = files.filter((file) => {
|
|
1262
|
+
if (!file.type.startsWith("image/") || options.allowedMimeTypes.length > 0 && !options.allowedMimeTypes.includes(file.type)) {
|
|
1263
|
+
reportError(new Error(`Unsupported image type: ${file.type || "unknown"}.`), file);
|
|
1264
|
+
return false;
|
|
1265
|
+
}
|
|
1266
|
+
if (file.size > options.maxFileSize) {
|
|
1267
|
+
reportError(new Error(`Image exceeds the ${options.maxFileSize}-byte size limit.`), file);
|
|
1268
|
+
return false;
|
|
1269
|
+
}
|
|
1270
|
+
return true;
|
|
1271
|
+
});
|
|
1272
|
+
const results = await Promise.all(validFiles.map(async (file) => {
|
|
1273
|
+
try {
|
|
1274
|
+
return { file, src: await resolveImageSource(file, options) };
|
|
1275
|
+
} catch (error) {
|
|
1276
|
+
reportError(error instanceof Error ? error : new Error("Image upload failed."), file);
|
|
1277
|
+
return null;
|
|
1278
|
+
}
|
|
1279
|
+
}));
|
|
1280
|
+
return {
|
|
1281
|
+
images: results.filter((image) => image !== null),
|
|
1282
|
+
hadError
|
|
1283
|
+
};
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1197
1286
|
// src/components/UEditor/clipboard-images.ts
|
|
1198
1287
|
import { Extension } from "@tiptap/core";
|
|
1199
1288
|
import { Plugin } from "@tiptap/pm/state";
|
|
1200
1289
|
|
|
1290
|
+
// src/components/UEditor/table-width-model.ts
|
|
1291
|
+
var TABLE_WIDTH_BASIS_POINTS = 1e4;
|
|
1292
|
+
var DEFAULT_RESPONSIVE_TABLE_WIDTH_BP = TABLE_WIDTH_BASIS_POINTS;
|
|
1293
|
+
var MAX_RESPONSIVE_TABLE_WIDTH_BP = 1e5;
|
|
1294
|
+
function positiveNumber(value, fallback) {
|
|
1295
|
+
const number = Number(value);
|
|
1296
|
+
return Number.isFinite(number) && number > 0 ? number : fallback;
|
|
1297
|
+
}
|
|
1298
|
+
function clampTableBasisPoints(value, fallback = TABLE_WIDTH_BASIS_POINTS, maximum = TABLE_WIDTH_BASIS_POINTS) {
|
|
1299
|
+
const number = Number(value);
|
|
1300
|
+
if (!Number.isFinite(number)) return fallback;
|
|
1301
|
+
return Math.min(maximum, Math.max(0, Math.round(number)));
|
|
1302
|
+
}
|
|
1303
|
+
function clampResponsiveTableWidthBp(value, fallback = DEFAULT_RESPONSIVE_TABLE_WIDTH_BP) {
|
|
1304
|
+
return Math.max(1, clampTableBasisPoints(value, fallback, MAX_RESPONSIVE_TABLE_WIDTH_BP));
|
|
1305
|
+
}
|
|
1306
|
+
function normalizeTableWidthMode(value) {
|
|
1307
|
+
return value === "responsive" || value === "full" ? "responsive" : "fixed";
|
|
1308
|
+
}
|
|
1309
|
+
function parsePercentageToBasisPoints(value, maximum = MAX_RESPONSIVE_TABLE_WIDTH_BP) {
|
|
1310
|
+
if (!value) return null;
|
|
1311
|
+
const match = value.trim().match(/^(-?\d+(?:\.\d+)?)%$/);
|
|
1312
|
+
if (!match) return null;
|
|
1313
|
+
const percentage = Number.parseFloat(match[1]);
|
|
1314
|
+
if (!Number.isFinite(percentage)) return null;
|
|
1315
|
+
return clampTableBasisPoints(percentage * 100, TABLE_WIDTH_BASIS_POINTS, maximum);
|
|
1316
|
+
}
|
|
1317
|
+
function formatBasisPointsAsPercentage(value) {
|
|
1318
|
+
const percentage = clampTableBasisPoints(value, 0, MAX_RESPONSIVE_TABLE_WIDTH_BP) / 100;
|
|
1319
|
+
return `${Number.parseFloat(percentage.toFixed(2))}%`;
|
|
1320
|
+
}
|
|
1321
|
+
function parseColumnRatios(value) {
|
|
1322
|
+
const parts = Array.isArray(value) ? value : typeof value === "string" ? value.split(",") : [];
|
|
1323
|
+
const ratios = parts.map((part) => Number(part));
|
|
1324
|
+
return ratios.length > 0 && ratios.every((ratio) => Number.isFinite(ratio) && ratio > 0) ? ratios : null;
|
|
1325
|
+
}
|
|
1326
|
+
function normalizeColumnRatios(values, columnCount) {
|
|
1327
|
+
if (columnCount <= 0) return [];
|
|
1328
|
+
const source = values?.length === columnCount ? values.map((value) => positiveNumber(value, 1)) : Array.from({ length: columnCount }, () => 1);
|
|
1329
|
+
const sourceTotal = source.reduce((sum, value) => sum + value, 0);
|
|
1330
|
+
const normalized = source.map((value) => Math.max(1, Math.round(value / sourceTotal * TABLE_WIDTH_BASIS_POINTS)));
|
|
1331
|
+
let difference = TABLE_WIDTH_BASIS_POINTS - normalized.reduce((sum, value) => sum + value, 0);
|
|
1332
|
+
while (difference !== 0) {
|
|
1333
|
+
let changed = false;
|
|
1334
|
+
for (let index = normalized.length - 1; index >= 0 && difference !== 0; index -= 1) {
|
|
1335
|
+
if (difference < 0 && normalized[index] <= 1) continue;
|
|
1336
|
+
normalized[index] += difference > 0 ? 1 : -1;
|
|
1337
|
+
difference += difference > 0 ? -1 : 1;
|
|
1338
|
+
changed = true;
|
|
1339
|
+
}
|
|
1340
|
+
if (!changed) break;
|
|
1341
|
+
}
|
|
1342
|
+
return normalized;
|
|
1343
|
+
}
|
|
1344
|
+
function getLogicalTableColumnCount(table) {
|
|
1345
|
+
const firstRow = table.firstChild;
|
|
1346
|
+
if (!firstRow) return 0;
|
|
1347
|
+
let count = 0;
|
|
1348
|
+
firstRow.forEach((cell) => {
|
|
1349
|
+
count += Math.max(1, Number(cell.attrs.colspan) || 1);
|
|
1350
|
+
});
|
|
1351
|
+
return count;
|
|
1352
|
+
}
|
|
1353
|
+
function getLegacyTableColumnWeights(table, fallback = 100) {
|
|
1354
|
+
const weights = [];
|
|
1355
|
+
const firstRow = table.firstChild;
|
|
1356
|
+
if (!firstRow) return weights;
|
|
1357
|
+
firstRow.forEach((cell) => {
|
|
1358
|
+
const colspan = Math.max(1, Number(cell.attrs.colspan) || 1);
|
|
1359
|
+
const colwidth = Array.isArray(cell.attrs.colwidth) ? cell.attrs.colwidth : [];
|
|
1360
|
+
for (let index = 0; index < colspan; index += 1) {
|
|
1361
|
+
weights.push(positiveNumber(colwidth[index], fallback));
|
|
1362
|
+
}
|
|
1363
|
+
});
|
|
1364
|
+
return weights;
|
|
1365
|
+
}
|
|
1366
|
+
function getTableColumnRatios(table) {
|
|
1367
|
+
const columnCount = getLogicalTableColumnCount(table);
|
|
1368
|
+
const stored = parseColumnRatios(table.attrs.columnRatios);
|
|
1369
|
+
return normalizeColumnRatios(
|
|
1370
|
+
stored?.length === columnCount ? stored : getLegacyTableColumnWeights(table),
|
|
1371
|
+
columnCount
|
|
1372
|
+
);
|
|
1373
|
+
}
|
|
1374
|
+
function getResponsiveTableWidthBp(table) {
|
|
1375
|
+
return clampResponsiveTableWidthBp(table.attrs.widthBp);
|
|
1376
|
+
}
|
|
1377
|
+
function getResponsiveTableOffsetBp(table) {
|
|
1378
|
+
const width = getResponsiveTableWidthBp(table);
|
|
1379
|
+
return resolveResponsiveTableOffsetBp(width, null, table.attrs.offsetBp);
|
|
1380
|
+
}
|
|
1381
|
+
function resolveResponsiveTableOffsetBp(widthBp, tableAlign, fallbackOffsetBp = 0) {
|
|
1382
|
+
const availableGap = Math.max(
|
|
1383
|
+
0,
|
|
1384
|
+
TABLE_WIDTH_BASIS_POINTS - clampResponsiveTableWidthBp(widthBp)
|
|
1385
|
+
);
|
|
1386
|
+
if (tableAlign === "center") return Math.round(availableGap / 2);
|
|
1387
|
+
if (tableAlign === "right") return availableGap;
|
|
1388
|
+
if (tableAlign === "left") return 0;
|
|
1389
|
+
return Math.min(availableGap, clampTableBasisPoints(fallbackOffsetBp, 0));
|
|
1390
|
+
}
|
|
1391
|
+
function insertResponsiveColumnRatio(ratios, insertIndex, sourceIndex) {
|
|
1392
|
+
if (ratios.length === 0) return [TABLE_WIDTH_BASIS_POINTS];
|
|
1393
|
+
const safeSourceIndex = Math.max(0, Math.min(sourceIndex, ratios.length - 1));
|
|
1394
|
+
const next = [...ratios];
|
|
1395
|
+
const sourceRatio = next[safeSourceIndex];
|
|
1396
|
+
next.splice(Math.max(0, Math.min(insertIndex, next.length)), 0, sourceRatio);
|
|
1397
|
+
return normalizeColumnRatios(next, next.length);
|
|
1398
|
+
}
|
|
1399
|
+
function deleteResponsiveColumnRatio(ratios, deleteIndex) {
|
|
1400
|
+
if (ratios.length <= 1) return [];
|
|
1401
|
+
const next = ratios.filter((_, index) => index !== deleteIndex);
|
|
1402
|
+
return normalizeColumnRatios(next, next.length);
|
|
1403
|
+
}
|
|
1404
|
+
function moveResponsiveColumnRatio(ratios, from, to) {
|
|
1405
|
+
if (from === to || from < 0 || from >= ratios.length || to < 0 || to >= ratios.length) return [...ratios];
|
|
1406
|
+
const next = [...ratios];
|
|
1407
|
+
const [moved] = next.splice(from, 1);
|
|
1408
|
+
next.splice(to, 0, moved);
|
|
1409
|
+
return normalizeColumnRatios(next, next.length);
|
|
1410
|
+
}
|
|
1411
|
+
function insertResponsiveColumnLayout(widthBp, ratios, insertIndex, sourceIndex) {
|
|
1412
|
+
if (ratios.length === 0) {
|
|
1413
|
+
return {
|
|
1414
|
+
widthBp: clampResponsiveTableWidthBp(widthBp),
|
|
1415
|
+
columnRatios: [TABLE_WIDTH_BASIS_POINTS]
|
|
1416
|
+
};
|
|
1417
|
+
}
|
|
1418
|
+
const normalized = normalizeColumnRatios(ratios, ratios.length);
|
|
1419
|
+
const safeSourceIndex = Math.max(0, Math.min(sourceIndex, normalized.length - 1));
|
|
1420
|
+
const sourceRatio = normalized[safeSourceIndex];
|
|
1421
|
+
return {
|
|
1422
|
+
// Growing by the source column's share preserves every existing column's
|
|
1423
|
+
// rendered width instead of squeezing the whole table back into 100%.
|
|
1424
|
+
widthBp: clampResponsiveTableWidthBp(
|
|
1425
|
+
clampResponsiveTableWidthBp(widthBp) * (TABLE_WIDTH_BASIS_POINTS + sourceRatio) / TABLE_WIDTH_BASIS_POINTS
|
|
1426
|
+
),
|
|
1427
|
+
columnRatios: insertResponsiveColumnRatio(normalized, insertIndex, safeSourceIndex)
|
|
1428
|
+
};
|
|
1429
|
+
}
|
|
1430
|
+
function deleteResponsiveColumnLayout(widthBp, ratios, deleteIndex) {
|
|
1431
|
+
const normalized = normalizeColumnRatios(ratios, ratios.length);
|
|
1432
|
+
if (normalized.length <= 1 || deleteIndex < 0 || deleteIndex >= normalized.length) {
|
|
1433
|
+
return {
|
|
1434
|
+
widthBp: clampResponsiveTableWidthBp(widthBp),
|
|
1435
|
+
columnRatios: normalized.length <= 1 ? [] : normalized
|
|
1436
|
+
};
|
|
1437
|
+
}
|
|
1438
|
+
const remainingRatio = TABLE_WIDTH_BASIS_POINTS - normalized[deleteIndex];
|
|
1439
|
+
const nextWidthBp = clampResponsiveTableWidthBp(
|
|
1440
|
+
clampResponsiveTableWidthBp(widthBp) * remainingRatio / TABLE_WIDTH_BASIS_POINTS
|
|
1441
|
+
);
|
|
1442
|
+
return {
|
|
1443
|
+
// Deletion is the inverse operation: remaining columns keep their rendered
|
|
1444
|
+
// widths while the table gives the removed column's space back.
|
|
1445
|
+
widthBp: Math.abs(nextWidthBp - DEFAULT_RESPONSIVE_TABLE_WIDTH_BP) <= 1 ? DEFAULT_RESPONSIVE_TABLE_WIDTH_BP : nextWidthBp,
|
|
1446
|
+
columnRatios: deleteResponsiveColumnRatio(normalized, deleteIndex)
|
|
1447
|
+
};
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1201
1450
|
// src/components/UEditor/clipboard-tables.ts
|
|
1202
1451
|
var DEFAULT_HTML_TABLE_CELL_BACKGROUND_COLOR = "#ffffff";
|
|
1203
1452
|
var DEFAULT_HTML_TABLE_TEXT_COLOR = "#000000";
|
|
@@ -1829,13 +2078,34 @@ function createNormalizedRowContent(row, columnCount, fillerCellAttrs) {
|
|
|
1829
2078
|
}
|
|
1830
2079
|
return content;
|
|
1831
2080
|
}
|
|
1832
|
-
function createTableContent(rows, minColumnCount = 1, fillerCellAttrs) {
|
|
2081
|
+
function createTableContent(rows, minColumnCount = 1, fillerCellAttrs, layout) {
|
|
1833
2082
|
const tableRows = rows.filter((row) => row.cells.length > 0);
|
|
1834
2083
|
if (tableRows.length === 0) return null;
|
|
1835
2084
|
const { positionedRows, columnCount } = normalizeTableRows(tableRows);
|
|
1836
2085
|
if (columnCount < minColumnCount) return null;
|
|
2086
|
+
const inferredColumnWeights = Array.from({ length: columnCount }, () => 100);
|
|
2087
|
+
positionedRows.forEach((row) => {
|
|
2088
|
+
row.cells.forEach(({ cell, colspan, startColumn }) => {
|
|
2089
|
+
const width = colspan === 1 ? cell.attrs?.colwidth?.[0] : null;
|
|
2090
|
+
if (typeof width === "number" && Number.isFinite(width) && width > 0) {
|
|
2091
|
+
inferredColumnWeights[startColumn] = width;
|
|
2092
|
+
}
|
|
2093
|
+
});
|
|
2094
|
+
});
|
|
2095
|
+
const columnRatios = normalizeColumnRatios(
|
|
2096
|
+
layout?.columnRatios?.length === columnCount ? layout.columnRatios : inferredColumnWeights,
|
|
2097
|
+
columnCount
|
|
2098
|
+
);
|
|
2099
|
+
const widthBp = clampResponsiveTableWidthBp(layout?.widthBp, DEFAULT_RESPONSIVE_TABLE_WIDTH_BP);
|
|
2100
|
+
const offsetBp = resolveResponsiveTableOffsetBp(widthBp, null, layout?.offsetBp);
|
|
1837
2101
|
return {
|
|
1838
2102
|
type: "table",
|
|
2103
|
+
attrs: {
|
|
2104
|
+
widthMode: "responsive",
|
|
2105
|
+
widthBp,
|
|
2106
|
+
offsetBp,
|
|
2107
|
+
columnRatios
|
|
2108
|
+
},
|
|
1839
2109
|
content: positionedRows.map((row) => ({
|
|
1840
2110
|
type: "tableRow",
|
|
1841
2111
|
...row.attrs ? { attrs: row.attrs } : {},
|
|
@@ -1856,9 +2126,22 @@ function getClipboardTableContent(dataTransfer) {
|
|
|
1856
2126
|
if (tables.length !== 1 || hasMeaningfulContentOutsideTable(sourceBody)) return null;
|
|
1857
2127
|
const table = tables[0];
|
|
1858
2128
|
if (!(table instanceof HTMLTableElement)) return null;
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
2129
|
+
const storedWidthValue = table.getAttribute("data-table-width-bp");
|
|
2130
|
+
const storedOffsetValue = table.getAttribute("data-table-offset-bp");
|
|
2131
|
+
const storedWidthBp = Number(storedWidthValue);
|
|
2132
|
+
const storedOffsetBp = Number(storedOffsetValue);
|
|
2133
|
+
const widthBp = storedWidthValue !== null && Number.isFinite(storedWidthBp) && storedWidthBp > 0 ? storedWidthBp : parsePercentageToBasisPoints(table.getAttribute("data-table-width") ?? table.style.width) ?? DEFAULT_RESPONSIVE_TABLE_WIDTH_BP;
|
|
2134
|
+
const offsetBp = storedOffsetValue !== null && Number.isFinite(storedOffsetBp) && storedOffsetBp >= 0 ? storedOffsetBp : parsePercentageToBasisPoints(table.getAttribute("data-table-offset") ?? table.style.marginLeft) ?? 0;
|
|
2135
|
+
return createTableContent(
|
|
2136
|
+
getHtmlTableRows(table, styleMap),
|
|
2137
|
+
1,
|
|
2138
|
+
{ backgroundColor: DEFAULT_HTML_TABLE_CELL_BACKGROUND_COLOR },
|
|
2139
|
+
{
|
|
2140
|
+
widthBp,
|
|
2141
|
+
offsetBp,
|
|
2142
|
+
columnRatios: parseColumnRatios(table.getAttribute("data-table-column-ratios"))
|
|
2143
|
+
}
|
|
2144
|
+
);
|
|
1862
2145
|
}
|
|
1863
2146
|
function parseClipboardTsvRows(text) {
|
|
1864
2147
|
const rows = [];
|
|
@@ -1944,26 +2227,6 @@ function getImageFiles(dataTransfer) {
|
|
|
1944
2227
|
}
|
|
1945
2228
|
return Array.from(byKey.values());
|
|
1946
2229
|
}
|
|
1947
|
-
function fileToDataUrl(file) {
|
|
1948
|
-
return new Promise((resolve, reject) => {
|
|
1949
|
-
const reader = new FileReader();
|
|
1950
|
-
reader.onload = () => resolve(String(reader.result ?? ""));
|
|
1951
|
-
reader.onerror = () => reject(reader.error ?? new Error("Failed to read image file"));
|
|
1952
|
-
reader.readAsDataURL(file);
|
|
1953
|
-
});
|
|
1954
|
-
}
|
|
1955
|
-
async function resolveImageSrc(file, options) {
|
|
1956
|
-
if (options.insertMode === "upload" && options.upload) {
|
|
1957
|
-
try {
|
|
1958
|
-
const result = await options.upload(file);
|
|
1959
|
-
const src = typeof result === "string" ? sanitizeUEditorUrl(result, "image") : "";
|
|
1960
|
-
if (src) return src;
|
|
1961
|
-
} catch (err) {
|
|
1962
|
-
if (!options.fallbackToDataUrl) throw err;
|
|
1963
|
-
}
|
|
1964
|
-
}
|
|
1965
|
-
return fileToDataUrl(file);
|
|
1966
|
-
}
|
|
1967
2230
|
var ClipboardImages = Extension.create({
|
|
1968
2231
|
name: "clipboardImages",
|
|
1969
2232
|
addOptions() {
|
|
@@ -1972,25 +2235,32 @@ var ClipboardImages = Extension.create({
|
|
|
1972
2235
|
allowedMimeTypes: DEFAULT_UEDITOR_IMAGE_MIME_TYPES,
|
|
1973
2236
|
upload: void 0,
|
|
1974
2237
|
fallbackToDataUrl: true,
|
|
1975
|
-
insertMode: "base64"
|
|
2238
|
+
insertMode: "base64",
|
|
2239
|
+
onError: void 0
|
|
1976
2240
|
};
|
|
1977
2241
|
},
|
|
1978
2242
|
addProseMirrorPlugins() {
|
|
1979
2243
|
const editor = this.editor;
|
|
1980
2244
|
const options = this.options;
|
|
1981
2245
|
const insertFiles = async (files, selectionPos) => {
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
2246
|
+
const trackedPosition = trackEditorInsertionPosition(editor, selectionPos);
|
|
2247
|
+
try {
|
|
2248
|
+
const { images } = await resolveUEditorImageFiles(files, {
|
|
2249
|
+
maxFileSize: options.maxFileSize,
|
|
2250
|
+
allowedMimeTypes: options.allowedMimeTypes,
|
|
2251
|
+
upload: options.upload,
|
|
2252
|
+
fallbackToDataUrl: options.fallbackToDataUrl,
|
|
2253
|
+
insertMode: options.insertMode,
|
|
2254
|
+
onError: options.onError
|
|
2255
|
+
});
|
|
2256
|
+
if (editor.isDestroyed || images.length === 0) return;
|
|
2257
|
+
const content = images.map((image) => ({
|
|
2258
|
+
type: "image",
|
|
2259
|
+
attrs: { src: image.src, alt: image.file.name }
|
|
2260
|
+
}));
|
|
2261
|
+
editor.commands.insertContentAt(trackedPosition.current, content, { updateSelection: false });
|
|
2262
|
+
} finally {
|
|
2263
|
+
trackedPosition.stop();
|
|
1994
2264
|
}
|
|
1995
2265
|
};
|
|
1996
2266
|
return [
|
|
@@ -2019,7 +2289,8 @@ var ClipboardImages = Extension.create({
|
|
|
2019
2289
|
},
|
|
2020
2290
|
handleDrop: (view, event, _slice, moved) => {
|
|
2021
2291
|
if (moved) return false;
|
|
2022
|
-
|
|
2292
|
+
const DragEventCtor = view.dom.ownerDocument.defaultView?.DragEvent;
|
|
2293
|
+
if (!DragEventCtor || !(event instanceof DragEventCtor)) return false;
|
|
2023
2294
|
const files = getImageFiles(event.dataTransfer);
|
|
2024
2295
|
if (files.length === 0) return false;
|
|
2025
2296
|
const pos = view.posAtCoords({ left: event.clientX, top: event.clientY })?.pos;
|
|
@@ -2049,6 +2320,7 @@ var LinkInput = ({
|
|
|
2049
2320
|
const [url, setUrl] = useState3(initialUrl);
|
|
2050
2321
|
const [error, setError] = useState3("");
|
|
2051
2322
|
const inputRef = useRef2(null);
|
|
2323
|
+
const inputId = useId();
|
|
2052
2324
|
const errorId = useId();
|
|
2053
2325
|
useEffect3(() => {
|
|
2054
2326
|
inputRef.current?.focus();
|
|
@@ -2065,12 +2337,17 @@ var LinkInput = ({
|
|
|
2065
2337
|
onSubmit(normalized);
|
|
2066
2338
|
};
|
|
2067
2339
|
return /* @__PURE__ */ jsxs3("form", { onSubmit: handleSubmit, className: "p-2", children: [
|
|
2340
|
+
/* @__PURE__ */ jsx4("label", { htmlFor: inputId, className: "sr-only", children: t("toolbar.link") }),
|
|
2068
2341
|
/* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", children: [
|
|
2069
2342
|
/* @__PURE__ */ jsx4(
|
|
2070
2343
|
"input",
|
|
2071
2344
|
{
|
|
2072
2345
|
ref: inputRef,
|
|
2346
|
+
id: inputId,
|
|
2073
2347
|
type: "text",
|
|
2348
|
+
inputMode: "url",
|
|
2349
|
+
name: "link-url",
|
|
2350
|
+
autoComplete: "off",
|
|
2074
2351
|
value: url,
|
|
2075
2352
|
onChange: (e) => {
|
|
2076
2353
|
setUrl(e.target.value);
|
|
@@ -2082,8 +2359,8 @@ var LinkInput = ({
|
|
|
2082
2359
|
className: `flex-1 rounded-lg bg-muted/50 px-3 py-2 text-sm ${formControlOutlineClass} aria-invalid:border-destructive`
|
|
2083
2360
|
}
|
|
2084
2361
|
),
|
|
2085
|
-
/* @__PURE__ */ jsx4("button", { type: "submit", className: "p-2 rounded-lg bg-primary text-primary-foreground hover:bg-primary/90 transition-colors", children: /* @__PURE__ */ jsx4(Check, { className: "w-4 h-4" }) }),
|
|
2086
|
-
/* @__PURE__ */ jsx4("button", { type: "button", onClick: onCancel, className: "p-2 rounded-lg hover:bg-muted transition-colors text-muted-foreground", children: /* @__PURE__ */ jsx4(X, { className: "w-4 h-4" }) })
|
|
2362
|
+
/* @__PURE__ */ jsx4("button", { type: "submit", "aria-label": t("toolbar.link"), className: "p-2 rounded-lg bg-primary text-primary-foreground hover:bg-primary/90 transition-colors", children: /* @__PURE__ */ jsx4(Check, { "aria-hidden": "true", className: "w-4 h-4" }) }),
|
|
2363
|
+
/* @__PURE__ */ jsx4("button", { type: "button", "aria-label": t("imageInput.cancelBtn"), onClick: onCancel, className: "p-2 rounded-lg hover:bg-muted transition-colors text-muted-foreground", children: /* @__PURE__ */ jsx4(X, { "aria-hidden": "true", className: "w-4 h-4" }) })
|
|
2087
2364
|
] }),
|
|
2088
2365
|
error ? /* @__PURE__ */ jsx4("p", { id: errorId, role: "alert", className: "mt-1.5 px-1 text-xs text-destructive", children: error }) : null
|
|
2089
2366
|
] });
|
|
@@ -2092,7 +2369,11 @@ var ImageInput = ({ onSubmit, onCancel }) => {
|
|
|
2092
2369
|
const t = useSmartTranslations("UEditor");
|
|
2093
2370
|
const [url, setUrl] = useState3("");
|
|
2094
2371
|
const [alt, setAlt] = useState3("");
|
|
2372
|
+
const [error, setError] = useState3("");
|
|
2095
2373
|
const inputRef = useRef2(null);
|
|
2374
|
+
const urlId = useId();
|
|
2375
|
+
const altId = useId();
|
|
2376
|
+
const errorId = useId();
|
|
2096
2377
|
useEffect3(() => {
|
|
2097
2378
|
inputRef.current?.focus();
|
|
2098
2379
|
}, []);
|
|
@@ -2100,30 +2381,45 @@ var ImageInput = ({ onSubmit, onCancel }) => {
|
|
|
2100
2381
|
e.preventDefault();
|
|
2101
2382
|
const safeUrl = sanitizeUEditorUrl(url, "image");
|
|
2102
2383
|
if (safeUrl) {
|
|
2384
|
+
setError("");
|
|
2103
2385
|
onSubmit(safeUrl, alt);
|
|
2386
|
+
} else {
|
|
2387
|
+
setError(t("imageInput.invalid"));
|
|
2104
2388
|
}
|
|
2105
2389
|
};
|
|
2106
2390
|
return /* @__PURE__ */ jsxs3("form", { onSubmit: handleSubmit, className: "p-3 space-y-3", children: [
|
|
2107
2391
|
/* @__PURE__ */ jsxs3("div", { children: [
|
|
2108
|
-
/* @__PURE__ */ jsx4("label", { className: "text-xs font-medium text-muted-foreground", children: t("imageInput.urlLabel") }),
|
|
2392
|
+
/* @__PURE__ */ jsx4("label", { htmlFor: urlId, className: "text-xs font-medium text-muted-foreground", children: t("imageInput.urlLabel") }),
|
|
2109
2393
|
/* @__PURE__ */ jsx4(
|
|
2110
2394
|
"input",
|
|
2111
2395
|
{
|
|
2112
2396
|
ref: inputRef,
|
|
2397
|
+
id: urlId,
|
|
2113
2398
|
type: "text",
|
|
2399
|
+
inputMode: "url",
|
|
2400
|
+
name: "image-url",
|
|
2401
|
+
autoComplete: "off",
|
|
2114
2402
|
value: url,
|
|
2115
|
-
onChange: (e) =>
|
|
2403
|
+
onChange: (e) => {
|
|
2404
|
+
setUrl(e.target.value);
|
|
2405
|
+
if (error) setError("");
|
|
2406
|
+
},
|
|
2116
2407
|
placeholder: t("imageInput.urlPlaceholder"),
|
|
2408
|
+
"aria-invalid": Boolean(error),
|
|
2409
|
+
"aria-describedby": error ? errorId : void 0,
|
|
2117
2410
|
className: `mt-1 w-full rounded-lg bg-muted/50 px-3 py-2 text-sm ${formControlOutlineClass}`
|
|
2118
2411
|
}
|
|
2119
2412
|
)
|
|
2120
2413
|
] }),
|
|
2121
2414
|
/* @__PURE__ */ jsxs3("div", { children: [
|
|
2122
|
-
/* @__PURE__ */ jsx4("label", { className: "text-xs font-medium text-muted-foreground", children: t("imageInput.altLabel") }),
|
|
2415
|
+
/* @__PURE__ */ jsx4("label", { htmlFor: altId, className: "text-xs font-medium text-muted-foreground", children: t("imageInput.altLabel") }),
|
|
2123
2416
|
/* @__PURE__ */ jsx4(
|
|
2124
2417
|
"input",
|
|
2125
2418
|
{
|
|
2419
|
+
id: altId,
|
|
2126
2420
|
type: "text",
|
|
2421
|
+
name: "image-alt",
|
|
2422
|
+
autoComplete: "off",
|
|
2127
2423
|
value: alt,
|
|
2128
2424
|
onChange: (e) => setAlt(e.target.value),
|
|
2129
2425
|
placeholder: t("imageInput.altPlaceholder"),
|
|
@@ -2131,6 +2427,7 @@ var ImageInput = ({ onSubmit, onCancel }) => {
|
|
|
2131
2427
|
}
|
|
2132
2428
|
)
|
|
2133
2429
|
] }),
|
|
2430
|
+
error ? /* @__PURE__ */ jsx4("p", { id: errorId, role: "alert", className: "text-xs text-destructive", children: error }) : null,
|
|
2134
2431
|
/* @__PURE__ */ jsxs3("div", { className: "flex gap-2", children: [
|
|
2135
2432
|
/* @__PURE__ */ jsx4(
|
|
2136
2433
|
"button",
|
|
@@ -2986,10 +3283,14 @@ function findTableNodeInfoFromState(state, anchorPos) {
|
|
|
2986
3283
|
function applyTableAlignment(editor, tableAlign, anchorPos) {
|
|
2987
3284
|
const tableInfo = findTableNodeInfoFromState(editor.state, anchorPos);
|
|
2988
3285
|
if (!tableInfo) return false;
|
|
3286
|
+
const responsive = normalizeTableWidthMode(tableInfo.node.attrs.widthMode) === "responsive";
|
|
3287
|
+
const widthBp = getResponsiveTableWidthBp(tableInfo.node);
|
|
3288
|
+
const offsetBp = resolveResponsiveTableOffsetBp(widthBp, tableAlign);
|
|
2989
3289
|
editor.view.dispatch(
|
|
2990
3290
|
editor.state.tr.setNodeMarkup(tableInfo.pos, tableInfo.node.type, {
|
|
2991
3291
|
...tableInfo.node.attrs,
|
|
2992
|
-
textAlign: tableAlign
|
|
3292
|
+
textAlign: tableAlign,
|
|
3293
|
+
...responsive ? { offsetBp } : null
|
|
2993
3294
|
})
|
|
2994
3295
|
);
|
|
2995
3296
|
const domAtTable = editor.view.domAtPos(Math.min(tableInfo.pos + 1, editor.state.doc.content.size)).node;
|
|
@@ -3006,8 +3307,8 @@ function applyTableAlignment(editor, tableAlign, anchorPos) {
|
|
|
3006
3307
|
}
|
|
3007
3308
|
if (tableAlign) {
|
|
3008
3309
|
tableElement.setAttribute("data-table-align", tableAlign);
|
|
3009
|
-
tableElement.style.marginLeft = tableAlign === "center" || tableAlign === "right" ? "auto" : "0";
|
|
3010
|
-
tableElement.style.marginRight = tableAlign === "center" ? "auto" : tableAlign === "right" ? "0" : "auto";
|
|
3310
|
+
tableElement.style.marginLeft = responsive ? formatBasisPointsAsPercentage(offsetBp) : tableAlign === "center" || tableAlign === "right" ? "auto" : "0";
|
|
3311
|
+
tableElement.style.marginRight = responsive ? "auto" : tableAlign === "center" ? "auto" : tableAlign === "right" ? "0" : "auto";
|
|
3011
3312
|
} else {
|
|
3012
3313
|
tableElement.removeAttribute("data-table-align");
|
|
3013
3314
|
tableElement.style.removeProperty("margin-left");
|
|
@@ -3020,6 +3321,609 @@ function applyTableAlignment(editor, tableAlign, anchorPos) {
|
|
|
3020
3321
|
// src/components/UEditor/table-cell-commands.ts
|
|
3021
3322
|
import { TextSelection as TextSelection3 } from "@tiptap/pm/state";
|
|
3022
3323
|
import { selectedRect, TableMap } from "@tiptap/pm/tables";
|
|
3324
|
+
|
|
3325
|
+
// src/components/UEditor/table-formula.ts
|
|
3326
|
+
var CELL_ADDRESS_RE = /^(\$?)([A-Z]+)(\$?)([1-9]\d*)$/i;
|
|
3327
|
+
var CELL_RANGE_RE = /^(\$?[A-Z]+\$?[1-9]\d*):(\$?[A-Z]+\$?[1-9]\d*)$/i;
|
|
3328
|
+
var SUPPORTED_FUNCTIONS = /* @__PURE__ */ new Set(["SUM", "AVG", "MIN", "MAX", "COUNT"]);
|
|
3329
|
+
function columnNameToIndex(columnName) {
|
|
3330
|
+
const normalized = columnName.trim().toUpperCase();
|
|
3331
|
+
if (!/^[A-Z]+$/.test(normalized)) {
|
|
3332
|
+
return -1;
|
|
3333
|
+
}
|
|
3334
|
+
let index = 0;
|
|
3335
|
+
for (const char of normalized) {
|
|
3336
|
+
index = index * 26 + char.charCodeAt(0) - 64;
|
|
3337
|
+
}
|
|
3338
|
+
return index - 1;
|
|
3339
|
+
}
|
|
3340
|
+
function indexToColumnName(index) {
|
|
3341
|
+
if (!Number.isInteger(index) || index < 0) {
|
|
3342
|
+
return "";
|
|
3343
|
+
}
|
|
3344
|
+
let value = index + 1;
|
|
3345
|
+
let name = "";
|
|
3346
|
+
while (value > 0) {
|
|
3347
|
+
const remainder = (value - 1) % 26;
|
|
3348
|
+
name = String.fromCharCode(65 + remainder) + name;
|
|
3349
|
+
value = Math.floor((value - 1) / 26);
|
|
3350
|
+
}
|
|
3351
|
+
return name;
|
|
3352
|
+
}
|
|
3353
|
+
function parseTableCellAddress(input) {
|
|
3354
|
+
const match = input.trim().match(CELL_ADDRESS_RE);
|
|
3355
|
+
if (!match) {
|
|
3356
|
+
return null;
|
|
3357
|
+
}
|
|
3358
|
+
const column = columnNameToIndex(match[2] ?? "");
|
|
3359
|
+
const row = Number.parseInt(match[4] ?? "", 10) - 1;
|
|
3360
|
+
if (column < 0 || row < 0) {
|
|
3361
|
+
return null;
|
|
3362
|
+
}
|
|
3363
|
+
return {
|
|
3364
|
+
column,
|
|
3365
|
+
row,
|
|
3366
|
+
label: `${indexToColumnName(column)}${row + 1}`
|
|
3367
|
+
};
|
|
3368
|
+
}
|
|
3369
|
+
function parseTableCellRange(input) {
|
|
3370
|
+
const match = input.trim().match(CELL_RANGE_RE);
|
|
3371
|
+
if (!match) {
|
|
3372
|
+
return null;
|
|
3373
|
+
}
|
|
3374
|
+
const from = parseTableCellAddress(match[1] ?? "");
|
|
3375
|
+
const to = parseTableCellAddress(match[2] ?? "");
|
|
3376
|
+
if (!from || !to) {
|
|
3377
|
+
return null;
|
|
3378
|
+
}
|
|
3379
|
+
return { from, to };
|
|
3380
|
+
}
|
|
3381
|
+
function getTableCellRangeLabels(range) {
|
|
3382
|
+
const startColumn = Math.min(range.from.column, range.to.column);
|
|
3383
|
+
const endColumn = Math.max(range.from.column, range.to.column);
|
|
3384
|
+
const startRow = Math.min(range.from.row, range.to.row);
|
|
3385
|
+
const endRow = Math.max(range.from.row, range.to.row);
|
|
3386
|
+
const labels = [];
|
|
3387
|
+
for (let row = startRow; row <= endRow; row += 1) {
|
|
3388
|
+
for (let column = startColumn; column <= endColumn; column += 1) {
|
|
3389
|
+
labels.push(`${indexToColumnName(column)}${row + 1}`);
|
|
3390
|
+
}
|
|
3391
|
+
}
|
|
3392
|
+
return labels;
|
|
3393
|
+
}
|
|
3394
|
+
function normalizeTableFormula(formula) {
|
|
3395
|
+
return formula.trim().replace(/^=/, "").trim();
|
|
3396
|
+
}
|
|
3397
|
+
function isDraftTableFormula(formula) {
|
|
3398
|
+
const normalized = normalizeTableFormula(formula);
|
|
3399
|
+
if (!normalized) return true;
|
|
3400
|
+
let depth = 0;
|
|
3401
|
+
for (const char of normalized) {
|
|
3402
|
+
if (char === "(") depth += 1;
|
|
3403
|
+
if (char === ")") depth -= 1;
|
|
3404
|
+
if (depth < 0) return false;
|
|
3405
|
+
}
|
|
3406
|
+
if (depth > 0) return true;
|
|
3407
|
+
if (/^[A-Z]+\(\s*\)$/i.test(normalized)) return true;
|
|
3408
|
+
if (/[+\-*/,(]\s*$/.test(normalized)) return true;
|
|
3409
|
+
return false;
|
|
3410
|
+
}
|
|
3411
|
+
function formatFormulaError(error) {
|
|
3412
|
+
return `#${error.toUpperCase()}`;
|
|
3413
|
+
}
|
|
3414
|
+
function normalizeTableNumberFormat(format) {
|
|
3415
|
+
return format === "text" || format === "number" || format === "currency" || format === "percent" || format === "date" ? format : "text";
|
|
3416
|
+
}
|
|
3417
|
+
function formatTableFormulaDisplayValue(value, numberFormat) {
|
|
3418
|
+
const stringValue = String(value);
|
|
3419
|
+
if (stringValue.startsWith("#")) {
|
|
3420
|
+
return stringValue;
|
|
3421
|
+
}
|
|
3422
|
+
const normalizedFormat = normalizeTableNumberFormat(numberFormat);
|
|
3423
|
+
if (normalizedFormat === "text") {
|
|
3424
|
+
return stringValue;
|
|
3425
|
+
}
|
|
3426
|
+
const numericValue = typeof value === "number" ? value : Number.parseFloat(stringValue.replace(/,/g, ""));
|
|
3427
|
+
if (!Number.isFinite(numericValue)) {
|
|
3428
|
+
return stringValue;
|
|
3429
|
+
}
|
|
3430
|
+
if (normalizedFormat === "number") {
|
|
3431
|
+
return new Intl.NumberFormat("en-US", { maximumFractionDigits: 6 }).format(numericValue);
|
|
3432
|
+
}
|
|
3433
|
+
if (normalizedFormat === "currency") {
|
|
3434
|
+
return new Intl.NumberFormat("en-US", {
|
|
3435
|
+
style: "currency",
|
|
3436
|
+
currency: "USD",
|
|
3437
|
+
maximumFractionDigits: 2
|
|
3438
|
+
}).format(numericValue);
|
|
3439
|
+
}
|
|
3440
|
+
if (normalizedFormat === "percent") {
|
|
3441
|
+
return new Intl.NumberFormat("en-US", {
|
|
3442
|
+
style: "percent",
|
|
3443
|
+
maximumFractionDigits: 2
|
|
3444
|
+
}).format(numericValue);
|
|
3445
|
+
}
|
|
3446
|
+
const excelEpochMs = Date.UTC(1899, 11, 30);
|
|
3447
|
+
const date = new Date(excelEpochMs + numericValue * 24 * 60 * 60 * 1e3);
|
|
3448
|
+
if (Number.isNaN(date.getTime())) {
|
|
3449
|
+
return stringValue;
|
|
3450
|
+
}
|
|
3451
|
+
return new Intl.DateTimeFormat("en-US", {
|
|
3452
|
+
year: "numeric",
|
|
3453
|
+
month: "2-digit",
|
|
3454
|
+
day: "2-digit",
|
|
3455
|
+
timeZone: "UTC"
|
|
3456
|
+
}).format(date);
|
|
3457
|
+
}
|
|
3458
|
+
function getTableFormulaReferences(formula) {
|
|
3459
|
+
const normalized = normalizeTableFormula(formula);
|
|
3460
|
+
if (!normalized) return [];
|
|
3461
|
+
const tokens = tokenizeFormula(normalized);
|
|
3462
|
+
if (!tokens) return [];
|
|
3463
|
+
const references = /* @__PURE__ */ new Set();
|
|
3464
|
+
for (const token of tokens) {
|
|
3465
|
+
if (token.type === "cell") {
|
|
3466
|
+
const address = parseTableCellAddress(token.value);
|
|
3467
|
+
if (address) references.add(address.label);
|
|
3468
|
+
} else if (token.type === "range") {
|
|
3469
|
+
const range = parseTableCellRange(token.value);
|
|
3470
|
+
if (!range) continue;
|
|
3471
|
+
for (const label of getTableCellRangeLabels(range)) {
|
|
3472
|
+
references.add(label);
|
|
3473
|
+
}
|
|
3474
|
+
}
|
|
3475
|
+
}
|
|
3476
|
+
return Array.from(references);
|
|
3477
|
+
}
|
|
3478
|
+
function buildTableFormulaDependencyGraph(cells) {
|
|
3479
|
+
const dependencies = /* @__PURE__ */ new Map();
|
|
3480
|
+
const dependents = /* @__PURE__ */ new Map();
|
|
3481
|
+
const formulas = /* @__PURE__ */ new Map();
|
|
3482
|
+
for (const cell of cells) {
|
|
3483
|
+
const label = cell.label.toUpperCase();
|
|
3484
|
+
formulas.set(label, cell.formula);
|
|
3485
|
+
dependencies.set(label, new Set(getTableFormulaReferences(cell.formula)));
|
|
3486
|
+
if (!dependents.has(label)) {
|
|
3487
|
+
dependents.set(label, /* @__PURE__ */ new Set());
|
|
3488
|
+
}
|
|
3489
|
+
}
|
|
3490
|
+
for (const [label, refs] of dependencies) {
|
|
3491
|
+
for (const ref of refs) {
|
|
3492
|
+
if (!dependents.has(ref)) {
|
|
3493
|
+
dependents.set(ref, /* @__PURE__ */ new Set());
|
|
3494
|
+
}
|
|
3495
|
+
dependents.get(ref)?.add(label);
|
|
3496
|
+
}
|
|
3497
|
+
}
|
|
3498
|
+
return { dependencies, dependents, formulas };
|
|
3499
|
+
}
|
|
3500
|
+
function getTableFormulaCircularReferences(graph) {
|
|
3501
|
+
const circular = /* @__PURE__ */ new Set();
|
|
3502
|
+
const visited = /* @__PURE__ */ new Set();
|
|
3503
|
+
const finishOrder = [];
|
|
3504
|
+
for (const startLabel of graph.formulas.keys()) {
|
|
3505
|
+
if (visited.has(startLabel)) continue;
|
|
3506
|
+
visited.add(startLabel);
|
|
3507
|
+
const stack = [
|
|
3508
|
+
{
|
|
3509
|
+
label: startLabel,
|
|
3510
|
+
references: Array.from(graph.dependencies.get(startLabel) ?? []).filter((ref) => graph.formulas.has(ref)),
|
|
3511
|
+
nextIndex: 0
|
|
3512
|
+
}
|
|
3513
|
+
];
|
|
3514
|
+
while (stack.length > 0) {
|
|
3515
|
+
const frame = stack[stack.length - 1];
|
|
3516
|
+
if (!frame) break;
|
|
3517
|
+
const reference = frame.references[frame.nextIndex];
|
|
3518
|
+
if (reference) {
|
|
3519
|
+
frame.nextIndex += 1;
|
|
3520
|
+
if (!visited.has(reference)) {
|
|
3521
|
+
visited.add(reference);
|
|
3522
|
+
stack.push({
|
|
3523
|
+
label: reference,
|
|
3524
|
+
references: Array.from(graph.dependencies.get(reference) ?? []).filter((ref) => graph.formulas.has(ref)),
|
|
3525
|
+
nextIndex: 0
|
|
3526
|
+
});
|
|
3527
|
+
}
|
|
3528
|
+
continue;
|
|
3529
|
+
}
|
|
3530
|
+
stack.pop();
|
|
3531
|
+
finishOrder.push(frame.label);
|
|
3532
|
+
}
|
|
3533
|
+
}
|
|
3534
|
+
const assigned = /* @__PURE__ */ new Set();
|
|
3535
|
+
for (let index = finishOrder.length - 1; index >= 0; index -= 1) {
|
|
3536
|
+
const startLabel = finishOrder[index];
|
|
3537
|
+
if (!startLabel || assigned.has(startLabel)) continue;
|
|
3538
|
+
const component = [];
|
|
3539
|
+
const stack = [startLabel];
|
|
3540
|
+
assigned.add(startLabel);
|
|
3541
|
+
while (stack.length > 0) {
|
|
3542
|
+
const label = stack.pop();
|
|
3543
|
+
if (!label) continue;
|
|
3544
|
+
component.push(label);
|
|
3545
|
+
for (const dependent of graph.dependents.get(label) ?? []) {
|
|
3546
|
+
if (!graph.formulas.has(dependent) || assigned.has(dependent)) continue;
|
|
3547
|
+
assigned.add(dependent);
|
|
3548
|
+
stack.push(dependent);
|
|
3549
|
+
}
|
|
3550
|
+
}
|
|
3551
|
+
if (component.length > 1) {
|
|
3552
|
+
for (const label of component) circular.add(label);
|
|
3553
|
+
} else {
|
|
3554
|
+
const label = component[0];
|
|
3555
|
+
if (label && graph.dependencies.get(label)?.has(label)) circular.add(label);
|
|
3556
|
+
}
|
|
3557
|
+
}
|
|
3558
|
+
return circular;
|
|
3559
|
+
}
|
|
3560
|
+
function getTableFormulaRecalculationOrder(graph) {
|
|
3561
|
+
const circular = getTableFormulaCircularReferences(graph);
|
|
3562
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
3563
|
+
const visited = /* @__PURE__ */ new Set();
|
|
3564
|
+
const order = [];
|
|
3565
|
+
for (const startLabel of graph.formulas.keys()) {
|
|
3566
|
+
if (visited.has(startLabel) || circular.has(startLabel)) continue;
|
|
3567
|
+
const stack = [];
|
|
3568
|
+
const push = (label) => {
|
|
3569
|
+
visiting.add(label);
|
|
3570
|
+
stack.push({
|
|
3571
|
+
label,
|
|
3572
|
+
references: Array.from(graph.dependencies.get(label) ?? []).filter((ref) => graph.formulas.has(ref) && !circular.has(ref)),
|
|
3573
|
+
nextIndex: 0
|
|
3574
|
+
});
|
|
3575
|
+
};
|
|
3576
|
+
push(startLabel);
|
|
3577
|
+
while (stack.length > 0) {
|
|
3578
|
+
const frame = stack[stack.length - 1];
|
|
3579
|
+
if (!frame) break;
|
|
3580
|
+
const reference = frame.references[frame.nextIndex];
|
|
3581
|
+
if (reference) {
|
|
3582
|
+
frame.nextIndex += 1;
|
|
3583
|
+
if (!visited.has(reference) && !visiting.has(reference)) {
|
|
3584
|
+
push(reference);
|
|
3585
|
+
}
|
|
3586
|
+
continue;
|
|
3587
|
+
}
|
|
3588
|
+
stack.pop();
|
|
3589
|
+
visiting.delete(frame.label);
|
|
3590
|
+
visited.add(frame.label);
|
|
3591
|
+
order.push(frame.label);
|
|
3592
|
+
}
|
|
3593
|
+
}
|
|
3594
|
+
return { order, circular };
|
|
3595
|
+
}
|
|
3596
|
+
function getAffectedTableFormulaLabels(graph, changedLabels) {
|
|
3597
|
+
const affected = /* @__PURE__ */ new Set();
|
|
3598
|
+
const queue = Array.from(changedLabels, (label) => label.toUpperCase());
|
|
3599
|
+
for (const label of queue) {
|
|
3600
|
+
if (graph.formulas.has(label)) {
|
|
3601
|
+
affected.add(label);
|
|
3602
|
+
}
|
|
3603
|
+
}
|
|
3604
|
+
for (let index = 0; index < queue.length; index += 1) {
|
|
3605
|
+
const label = queue[index];
|
|
3606
|
+
if (!label) continue;
|
|
3607
|
+
for (const dependent of graph.dependents.get(label) ?? []) {
|
|
3608
|
+
if (affected.has(dependent)) continue;
|
|
3609
|
+
affected.add(dependent);
|
|
3610
|
+
queue.push(dependent);
|
|
3611
|
+
}
|
|
3612
|
+
}
|
|
3613
|
+
return affected;
|
|
3614
|
+
}
|
|
3615
|
+
function evaluateBasicTableFormula(formula, getCellValue) {
|
|
3616
|
+
const normalized = normalizeTableFormula(formula);
|
|
3617
|
+
if (!normalized) {
|
|
3618
|
+
return { value: null, error: "empty" };
|
|
3619
|
+
}
|
|
3620
|
+
if (normalized.includes("#REF!")) {
|
|
3621
|
+
return { value: null, error: "invalid-reference" };
|
|
3622
|
+
}
|
|
3623
|
+
const tokens = tokenizeFormula(normalized);
|
|
3624
|
+
if (!tokens) {
|
|
3625
|
+
return { value: null, error: "invalid-formula" };
|
|
3626
|
+
}
|
|
3627
|
+
const parser = new FormulaParser(tokens, getCellValue);
|
|
3628
|
+
const result = parser.parseExpression();
|
|
3629
|
+
if (result.error) {
|
|
3630
|
+
return result;
|
|
3631
|
+
}
|
|
3632
|
+
if (!parser.isComplete()) {
|
|
3633
|
+
return { value: null, error: "invalid-formula" };
|
|
3634
|
+
}
|
|
3635
|
+
return result;
|
|
3636
|
+
}
|
|
3637
|
+
function tokenizeFormula(formula) {
|
|
3638
|
+
const tokens = [];
|
|
3639
|
+
let index = 0;
|
|
3640
|
+
while (index < formula.length) {
|
|
3641
|
+
const char = formula[index];
|
|
3642
|
+
if (!char) break;
|
|
3643
|
+
if (/\s/.test(char)) {
|
|
3644
|
+
index += 1;
|
|
3645
|
+
continue;
|
|
3646
|
+
}
|
|
3647
|
+
if (char === "," || char === "+" || char === "-" || char === "*" || char === "/" || char === "(" || char === ")") {
|
|
3648
|
+
if (char === ",") tokens.push({ type: "comma", value: char });
|
|
3649
|
+
else if (char === "(" || char === ")") tokens.push({ type: "paren", value: char });
|
|
3650
|
+
else tokens.push({ type: "operator", value: char });
|
|
3651
|
+
index += 1;
|
|
3652
|
+
continue;
|
|
3653
|
+
}
|
|
3654
|
+
const numberMatch = formula.slice(index).match(/^(?:\d+(?:\.\d*)?|\.\d+)/);
|
|
3655
|
+
if (numberMatch?.[0]) {
|
|
3656
|
+
const value = Number.parseFloat(numberMatch[0]);
|
|
3657
|
+
if (!Number.isFinite(value)) return null;
|
|
3658
|
+
tokens.push({ type: "number", value });
|
|
3659
|
+
index += numberMatch[0].length;
|
|
3660
|
+
continue;
|
|
3661
|
+
}
|
|
3662
|
+
const identifierMatch = formula.slice(index).match(/^\$?[A-Z]+\$?[1-9]\d*(?::\$?[A-Z]+\$?[1-9]\d*)?|^[A-Z]+/i);
|
|
3663
|
+
if (identifierMatch?.[0]) {
|
|
3664
|
+
const value = identifierMatch[0].toUpperCase();
|
|
3665
|
+
if (CELL_RANGE_RE.test(value)) tokens.push({ type: "range", value });
|
|
3666
|
+
else if (parseTableCellAddress(value)) tokens.push({ type: "cell", value });
|
|
3667
|
+
else if (SUPPORTED_FUNCTIONS.has(value)) tokens.push({ type: "function", value });
|
|
3668
|
+
else return null;
|
|
3669
|
+
index += identifierMatch[0].length;
|
|
3670
|
+
continue;
|
|
3671
|
+
}
|
|
3672
|
+
return null;
|
|
3673
|
+
}
|
|
3674
|
+
return tokens;
|
|
3675
|
+
}
|
|
3676
|
+
function toFiniteFormulaResult(value) {
|
|
3677
|
+
return Number.isFinite(value) ? { value, error: null } : { value: null, error: "invalid-formula" };
|
|
3678
|
+
}
|
|
3679
|
+
function parseTableCellNumericValue(value) {
|
|
3680
|
+
if (typeof value === "number") {
|
|
3681
|
+
return Number.isFinite(value) ? value : null;
|
|
3682
|
+
}
|
|
3683
|
+
let normalized = String(value ?? "").trim();
|
|
3684
|
+
if (!normalized || normalized.startsWith("#")) return null;
|
|
3685
|
+
const isPercent = normalized.endsWith("%");
|
|
3686
|
+
if (isPercent) normalized = normalized.slice(0, -1).trim();
|
|
3687
|
+
const currencyMatch = normalized.match(/^([+-]?)\$(.+)$/);
|
|
3688
|
+
if (currencyMatch) {
|
|
3689
|
+
normalized = `${currencyMatch[1] ?? ""}${currencyMatch[2] ?? ""}`.trim();
|
|
3690
|
+
}
|
|
3691
|
+
const groupedNumber = /^[+-]?\d{1,3}(?:,\d{3})+(?:\.\d*)?(?:[eE][+-]?\d+)?$/;
|
|
3692
|
+
const plainNumber = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/;
|
|
3693
|
+
if (!groupedNumber.test(normalized) && !plainNumber.test(normalized)) return null;
|
|
3694
|
+
const parsed = Number(normalized.replace(/,/g, ""));
|
|
3695
|
+
if (!Number.isFinite(parsed)) return null;
|
|
3696
|
+
return isPercent ? parsed / 100 : parsed;
|
|
3697
|
+
}
|
|
3698
|
+
var FormulaParser = class {
|
|
3699
|
+
constructor(tokens, getCellValue) {
|
|
3700
|
+
this.tokens = tokens;
|
|
3701
|
+
this.getCellValue = getCellValue;
|
|
3702
|
+
this.index = 0;
|
|
3703
|
+
}
|
|
3704
|
+
isComplete() {
|
|
3705
|
+
return this.index >= this.tokens.length;
|
|
3706
|
+
}
|
|
3707
|
+
parseExpression() {
|
|
3708
|
+
let left = this.parseTerm();
|
|
3709
|
+
while (!left.error) {
|
|
3710
|
+
const operator = this.peekOperator(["+", "-"]);
|
|
3711
|
+
if (!operator) break;
|
|
3712
|
+
this.index += 1;
|
|
3713
|
+
const right = this.parseTerm();
|
|
3714
|
+
if (right.error) return right;
|
|
3715
|
+
left = toFiniteFormulaResult(operator.value === "+" ? left.value + right.value : left.value - right.value);
|
|
3716
|
+
}
|
|
3717
|
+
return left;
|
|
3718
|
+
}
|
|
3719
|
+
parseTerm() {
|
|
3720
|
+
let left = this.parseFactor();
|
|
3721
|
+
while (!left.error) {
|
|
3722
|
+
const operator = this.peekOperator(["*", "/"]);
|
|
3723
|
+
if (!operator) break;
|
|
3724
|
+
this.index += 1;
|
|
3725
|
+
const right = this.parseFactor();
|
|
3726
|
+
if (right.error) return right;
|
|
3727
|
+
if (operator.value === "/" && right.value === 0) {
|
|
3728
|
+
return { value: null, error: "division-by-zero" };
|
|
3729
|
+
}
|
|
3730
|
+
left = toFiniteFormulaResult(operator.value === "*" ? left.value * right.value : left.value / right.value);
|
|
3731
|
+
}
|
|
3732
|
+
return left;
|
|
3733
|
+
}
|
|
3734
|
+
parseFactor() {
|
|
3735
|
+
const token = this.tokens[this.index];
|
|
3736
|
+
if (!token) {
|
|
3737
|
+
return { value: null, error: "invalid-formula" };
|
|
3738
|
+
}
|
|
3739
|
+
if (token.type === "operator" && (token.value === "-" || token.value === "+")) {
|
|
3740
|
+
this.index += 1;
|
|
3741
|
+
const value = this.parseFactor();
|
|
3742
|
+
if (value.error) return value;
|
|
3743
|
+
return toFiniteFormulaResult(token.value === "-" ? -value.value : value.value);
|
|
3744
|
+
}
|
|
3745
|
+
if (token.type === "number") {
|
|
3746
|
+
this.index += 1;
|
|
3747
|
+
return { value: token.value, error: null };
|
|
3748
|
+
}
|
|
3749
|
+
if (token.type === "cell") {
|
|
3750
|
+
this.index += 1;
|
|
3751
|
+
return this.readCellNumber(token.value);
|
|
3752
|
+
}
|
|
3753
|
+
if (token.type === "function") {
|
|
3754
|
+
return this.parseFunction(token.value);
|
|
3755
|
+
}
|
|
3756
|
+
if (token.type === "paren" && token.value === "(") {
|
|
3757
|
+
this.index += 1;
|
|
3758
|
+
const value = this.parseExpression();
|
|
3759
|
+
if (value.error) return value;
|
|
3760
|
+
if (!this.consumeParen(")")) {
|
|
3761
|
+
return { value: null, error: "invalid-formula" };
|
|
3762
|
+
}
|
|
3763
|
+
return value;
|
|
3764
|
+
}
|
|
3765
|
+
return { value: null, error: "invalid-formula" };
|
|
3766
|
+
}
|
|
3767
|
+
parseFunction(name) {
|
|
3768
|
+
this.index += 1;
|
|
3769
|
+
if (!this.consumeParen("(")) {
|
|
3770
|
+
return { value: null, error: "invalid-formula" };
|
|
3771
|
+
}
|
|
3772
|
+
const values = [];
|
|
3773
|
+
while (true) {
|
|
3774
|
+
const token = this.tokens[this.index];
|
|
3775
|
+
if (!token) {
|
|
3776
|
+
return { value: null, error: "invalid-formula" };
|
|
3777
|
+
}
|
|
3778
|
+
if (token.type === "range") {
|
|
3779
|
+
this.index += 1;
|
|
3780
|
+
const range = parseTableCellRange(token.value);
|
|
3781
|
+
if (!range) return { value: null, error: "invalid-reference" };
|
|
3782
|
+
for (const label of getTableCellRangeLabels(range)) {
|
|
3783
|
+
if (name === "COUNT") {
|
|
3784
|
+
const cellValue2 = this.readOptionalCellNumber(label);
|
|
3785
|
+
if (cellValue2 != null) values.push(cellValue2);
|
|
3786
|
+
continue;
|
|
3787
|
+
}
|
|
3788
|
+
const cellValue = this.readOptionalCellNumber(label);
|
|
3789
|
+
if (cellValue != null) values.push(cellValue);
|
|
3790
|
+
}
|
|
3791
|
+
} else if (token.type === "cell") {
|
|
3792
|
+
this.index += 1;
|
|
3793
|
+
const cellValue = this.readOptionalCellNumber(token.value);
|
|
3794
|
+
if (cellValue != null) values.push(cellValue);
|
|
3795
|
+
} else {
|
|
3796
|
+
const value = this.parseExpression();
|
|
3797
|
+
if (value.error) return value;
|
|
3798
|
+
values.push(value.value);
|
|
3799
|
+
}
|
|
3800
|
+
if (this.consumeComma()) {
|
|
3801
|
+
continue;
|
|
3802
|
+
}
|
|
3803
|
+
if (this.consumeParen(")")) {
|
|
3804
|
+
break;
|
|
3805
|
+
}
|
|
3806
|
+
return { value: null, error: "invalid-formula" };
|
|
3807
|
+
}
|
|
3808
|
+
if (name === "SUM") return toFiniteFormulaResult(values.reduce((sum, value) => sum + value, 0));
|
|
3809
|
+
if (name === "AVG") {
|
|
3810
|
+
return values.length > 0 ? toFiniteFormulaResult(values.reduce((sum, value) => sum + value, 0) / values.length) : { value: null, error: "division-by-zero" };
|
|
3811
|
+
}
|
|
3812
|
+
if (name === "MIN") return toFiniteFormulaResult(values.length > 0 ? Math.min(...values) : 0);
|
|
3813
|
+
if (name === "MAX") return toFiniteFormulaResult(values.length > 0 ? Math.max(...values) : 0);
|
|
3814
|
+
if (name === "COUNT") return { value: values.length, error: null };
|
|
3815
|
+
return { value: null, error: "invalid-formula" };
|
|
3816
|
+
}
|
|
3817
|
+
readCellNumber(label) {
|
|
3818
|
+
const address = parseTableCellAddress(label);
|
|
3819
|
+
const parsed = address ? parseTableCellNumericValue(this.getCellValue(address.label)) : null;
|
|
3820
|
+
if (parsed == null) {
|
|
3821
|
+
return { value: null, error: "invalid-reference" };
|
|
3822
|
+
}
|
|
3823
|
+
return { value: parsed, error: null };
|
|
3824
|
+
}
|
|
3825
|
+
readOptionalCellNumber(label) {
|
|
3826
|
+
const address = parseTableCellAddress(label);
|
|
3827
|
+
return address ? parseTableCellNumericValue(this.getCellValue(address.label)) : null;
|
|
3828
|
+
}
|
|
3829
|
+
peekOperator(operators) {
|
|
3830
|
+
const token = this.tokens[this.index];
|
|
3831
|
+
return token?.type === "operator" && operators.includes(token.value) ? token : null;
|
|
3832
|
+
}
|
|
3833
|
+
consumeComma() {
|
|
3834
|
+
if (this.tokens[this.index]?.type !== "comma") {
|
|
3835
|
+
return false;
|
|
3836
|
+
}
|
|
3837
|
+
this.index += 1;
|
|
3838
|
+
return true;
|
|
3839
|
+
}
|
|
3840
|
+
consumeParen(value) {
|
|
3841
|
+
const token = this.tokens[this.index];
|
|
3842
|
+
if (token?.type !== "paren" || token.value !== value) {
|
|
3843
|
+
return false;
|
|
3844
|
+
}
|
|
3845
|
+
this.index += 1;
|
|
3846
|
+
return true;
|
|
3847
|
+
}
|
|
3848
|
+
};
|
|
3849
|
+
|
|
3850
|
+
// src/components/UEditor/table-formula-references.ts
|
|
3851
|
+
var CELL_REFERENCE_RE = /(^|[^A-Z0-9_])(\$?)([A-Z]+)(\$?)([1-9]\d*)\b/gi;
|
|
3852
|
+
function rewriteTableFormulaReferences(formula, mapCoordinate) {
|
|
3853
|
+
return formula.replace(CELL_REFERENCE_RE, (_match, prefix, columnMarker, columnName, rowMarker, rowNumber) => {
|
|
3854
|
+
const column = columnNameToIndex(columnName);
|
|
3855
|
+
const row = Number.parseInt(rowNumber, 10) - 1;
|
|
3856
|
+
if (column < 0 || row < 0) return _match;
|
|
3857
|
+
const mapped = mapCoordinate({
|
|
3858
|
+
column,
|
|
3859
|
+
row,
|
|
3860
|
+
columnAbsolute: columnMarker === "$",
|
|
3861
|
+
rowAbsolute: rowMarker === "$"
|
|
3862
|
+
});
|
|
3863
|
+
if (!mapped) return `${prefix}#REF!`;
|
|
3864
|
+
return `${prefix}${columnMarker}${indexToColumnName(mapped.column)}${rowMarker}${mapped.row + 1}`;
|
|
3865
|
+
});
|
|
3866
|
+
}
|
|
3867
|
+
function rewriteNodeFormulas(node, mapCoordinate) {
|
|
3868
|
+
if (node.type.name === "tableCell" || node.type.name === "tableHeader") {
|
|
3869
|
+
const formula = typeof node.attrs.formula === "string" ? node.attrs.formula : "";
|
|
3870
|
+
if (!formula) return node;
|
|
3871
|
+
const nextFormula = rewriteTableFormulaReferences(formula, mapCoordinate);
|
|
3872
|
+
if (nextFormula === formula) return node;
|
|
3873
|
+
return node.type.create({ ...node.attrs, formula: nextFormula, computedValue: null }, node.content, node.marks);
|
|
3874
|
+
}
|
|
3875
|
+
if (node.childCount === 0) return node;
|
|
3876
|
+
const children = [];
|
|
3877
|
+
let changed = false;
|
|
3878
|
+
node.forEach((child) => {
|
|
3879
|
+
const nextChild = rewriteNodeFormulas(child, mapCoordinate);
|
|
3880
|
+
children.push(nextChild);
|
|
3881
|
+
if (nextChild !== child) changed = true;
|
|
3882
|
+
});
|
|
3883
|
+
return changed ? node.type.create(node.attrs, children, node.marks) : node;
|
|
3884
|
+
}
|
|
3885
|
+
function rewriteTableNodeFormulaReferences(tableNode, mapCoordinate) {
|
|
3886
|
+
return rewriteNodeFormulas(tableNode, mapCoordinate);
|
|
3887
|
+
}
|
|
3888
|
+
function rewriteTableNodeFormulaReferencesForInsertion(tableNode, axis, index, count = 1) {
|
|
3889
|
+
return rewriteTableNodeFormulaReferences(tableNode, (coordinate) => {
|
|
3890
|
+
const current = coordinate[axis];
|
|
3891
|
+
return current >= index ? { ...coordinate, [axis]: current + count } : coordinate;
|
|
3892
|
+
});
|
|
3893
|
+
}
|
|
3894
|
+
function rewriteTableNodeFormulaReferencesForDeletion(tableNode, axis, index, count = 1) {
|
|
3895
|
+
const end = index + count;
|
|
3896
|
+
return rewriteTableNodeFormulaReferences(tableNode, (coordinate) => {
|
|
3897
|
+
const current = coordinate[axis];
|
|
3898
|
+
if (current >= index && current < end) return null;
|
|
3899
|
+
return current >= end ? { ...coordinate, [axis]: current - count } : coordinate;
|
|
3900
|
+
});
|
|
3901
|
+
}
|
|
3902
|
+
function rewriteTableNodeFormulaReferencesForMove(tableNode, axis, from, to) {
|
|
3903
|
+
if (from === to) return tableNode;
|
|
3904
|
+
return rewriteTableNodeFormulaReferences(tableNode, (coordinate) => {
|
|
3905
|
+
const current = coordinate[axis];
|
|
3906
|
+
if (current === from) return { ...coordinate, [axis]: to };
|
|
3907
|
+
if (from < to && current > from && current <= to) {
|
|
3908
|
+
return { ...coordinate, [axis]: current - 1 };
|
|
3909
|
+
}
|
|
3910
|
+
if (from > to && current >= to && current < from) {
|
|
3911
|
+
return { ...coordinate, [axis]: current + 1 };
|
|
3912
|
+
}
|
|
3913
|
+
return coordinate;
|
|
3914
|
+
});
|
|
3915
|
+
}
|
|
3916
|
+
function shiftCopiedTableNodeFormulaReferences(node, axis, amount) {
|
|
3917
|
+
return rewriteNodeFormulas(node, (coordinate) => {
|
|
3918
|
+
const isAbsolute = axis === "column" ? coordinate.columnAbsolute : coordinate.rowAbsolute;
|
|
3919
|
+
return {
|
|
3920
|
+
...coordinate,
|
|
3921
|
+
[axis]: isAbsolute ? coordinate[axis] : Math.max(0, coordinate[axis] + amount)
|
|
3922
|
+
};
|
|
3923
|
+
});
|
|
3924
|
+
}
|
|
3925
|
+
|
|
3926
|
+
// src/components/UEditor/table-cell-commands.ts
|
|
3023
3927
|
function getCellSelectionPositions(selection) {
|
|
3024
3928
|
const value = selection;
|
|
3025
3929
|
const anchor = value.$anchorCell?.pos;
|
|
@@ -3062,7 +3966,11 @@ function collectChildren(node) {
|
|
|
3062
3966
|
return children;
|
|
3063
3967
|
}
|
|
3064
3968
|
function createEmptyCellNode(cellNode) {
|
|
3065
|
-
return cellNode.type.createAndFill(
|
|
3969
|
+
return cellNode.type.createAndFill({
|
|
3970
|
+
...cellNode.attrs,
|
|
3971
|
+
formula: null,
|
|
3972
|
+
computedValue: null
|
|
3973
|
+
}) ?? cellNode;
|
|
3066
3974
|
}
|
|
3067
3975
|
function createCellCopyForColumnDuplicate(cellNode) {
|
|
3068
3976
|
return cellNode.type.create(cellNode.attrs, cellNode.content);
|
|
@@ -3222,11 +4130,16 @@ function replaceTableAtCellPos(editor, cellPos, updateTable) {
|
|
|
3222
4130
|
}
|
|
3223
4131
|
function duplicateTableRowAt(editor, rowIndex, cellPos) {
|
|
3224
4132
|
return replaceTableAtCellPos(editor, cellPos, (tableNode) => {
|
|
3225
|
-
const
|
|
4133
|
+
const tableWithShiftedReferences = rewriteTableNodeFormulaReferencesForInsertion(
|
|
4134
|
+
tableNode,
|
|
4135
|
+
"row",
|
|
4136
|
+
rowIndex + 1
|
|
4137
|
+
);
|
|
4138
|
+
const rows = collectChildren(tableWithShiftedReferences);
|
|
3226
4139
|
const rowNode = rows[rowIndex];
|
|
3227
4140
|
if (!rowNode) return null;
|
|
3228
|
-
rows.splice(rowIndex + 1, 0,
|
|
3229
|
-
return
|
|
4141
|
+
rows.splice(rowIndex + 1, 0, shiftCopiedTableNodeFormulaReferences(rowNode, "row", 1));
|
|
4142
|
+
return tableWithShiftedReferences.type.create(tableWithShiftedReferences.attrs, rows);
|
|
3230
4143
|
});
|
|
3231
4144
|
}
|
|
3232
4145
|
function clearTableRowAt(editor, rowIndex, cellPos) {
|
|
@@ -3247,9 +4160,14 @@ function clearTableRowAt(editor, rowIndex, cellPos) {
|
|
|
3247
4160
|
}
|
|
3248
4161
|
function duplicateTableColumnAt(editor, columnIndex, cellPos) {
|
|
3249
4162
|
return replaceTableAtCellPos(editor, cellPos, (tableNode) => {
|
|
3250
|
-
const
|
|
4163
|
+
const tableWithShiftedReferences = rewriteTableNodeFormulaReferencesForInsertion(
|
|
4164
|
+
tableNode,
|
|
4165
|
+
"column",
|
|
4166
|
+
columnIndex + 1
|
|
4167
|
+
);
|
|
4168
|
+
const map = TableMap.get(tableWithShiftedReferences);
|
|
3251
4169
|
if (columnIndex < 0 || columnIndex >= map.width) return null;
|
|
3252
|
-
const rows = getTableRows(
|
|
4170
|
+
const rows = getTableRows(tableWithShiftedReferences).map((rowInfo, rowIndex) => {
|
|
3253
4171
|
const cells = collectChildren(rowInfo.node);
|
|
3254
4172
|
const sourceCell = rowInfo.cells.find((entry) => {
|
|
3255
4173
|
const rect = safeFindCell(map, entry.relativePos);
|
|
@@ -3261,10 +4179,31 @@ function duplicateTableColumnAt(editor, columnIndex, cellPos) {
|
|
|
3261
4179
|
cells[sourceCell.index] = createCellWithDuplicatedLogicalColumn(sourceCell.node, columnIndex - sourceRect.left);
|
|
3262
4180
|
return rowInfo.node.type.create(rowInfo.node.attrs, cells);
|
|
3263
4181
|
}
|
|
3264
|
-
cells.splice(
|
|
4182
|
+
cells.splice(
|
|
4183
|
+
sourceCell.index + 1,
|
|
4184
|
+
0,
|
|
4185
|
+
shiftCopiedTableNodeFormulaReferences(createCellCopyForColumnDuplicate(sourceCell.node), "column", 1)
|
|
4186
|
+
);
|
|
3265
4187
|
return rowInfo.node.type.create(rowInfo.node.attrs, cells);
|
|
3266
4188
|
});
|
|
3267
|
-
|
|
4189
|
+
const responsiveLayout = normalizeTableWidthMode(tableWithShiftedReferences.attrs.widthMode) === "responsive" ? insertResponsiveColumnLayout(
|
|
4190
|
+
getResponsiveTableWidthBp(tableWithShiftedReferences),
|
|
4191
|
+
getTableColumnRatios(tableWithShiftedReferences),
|
|
4192
|
+
columnIndex + 1,
|
|
4193
|
+
columnIndex
|
|
4194
|
+
) : null;
|
|
4195
|
+
const tableAttrs = responsiveLayout ? {
|
|
4196
|
+
...tableWithShiftedReferences.attrs,
|
|
4197
|
+
widthMode: "responsive",
|
|
4198
|
+
widthBp: responsiveLayout.widthBp,
|
|
4199
|
+
offsetBp: resolveResponsiveTableOffsetBp(
|
|
4200
|
+
responsiveLayout.widthBp,
|
|
4201
|
+
tableWithShiftedReferences.attrs.textAlign,
|
|
4202
|
+
tableWithShiftedReferences.attrs.offsetBp
|
|
4203
|
+
),
|
|
4204
|
+
columnRatios: responsiveLayout.columnRatios
|
|
4205
|
+
} : tableWithShiftedReferences.attrs;
|
|
4206
|
+
return tableWithShiftedReferences.type.create(tableAttrs, rows);
|
|
3268
4207
|
});
|
|
3269
4208
|
}
|
|
3270
4209
|
function clearTableColumnAt(editor, columnIndex, cellPos) {
|
|
@@ -3500,14 +4439,6 @@ function useSharedEditorUiRenderState(editor) {
|
|
|
3500
4439
|
}
|
|
3501
4440
|
return context.value;
|
|
3502
4441
|
}
|
|
3503
|
-
function fileToDataUrl2(file) {
|
|
3504
|
-
return new Promise((resolve, reject) => {
|
|
3505
|
-
const reader = new FileReader();
|
|
3506
|
-
reader.onload = () => resolve(String(reader.result ?? ""));
|
|
3507
|
-
reader.onerror = () => reject(reader.error ?? new Error("Failed to read image file"));
|
|
3508
|
-
reader.readAsDataURL(file);
|
|
3509
|
-
});
|
|
3510
|
-
}
|
|
3511
4442
|
function formatTableInsertLabel(template, rows, cols) {
|
|
3512
4443
|
return template.replace("{rows}", String(rows)).replace("{cols}", String(cols));
|
|
3513
4444
|
}
|
|
@@ -3553,7 +4484,7 @@ var TableInsertGrid = ({
|
|
|
3553
4484
|
const maxCols = 8;
|
|
3554
4485
|
return /* @__PURE__ */ jsxs5("div", { className: "mb-2 rounded-xl border border-border/60 bg-muted/20 p-2", children: [
|
|
3555
4486
|
/* @__PURE__ */ jsx7("div", { className: "mb-2 text-sm font-medium text-foreground", children: formatTableInsertLabel(previewTemplate, selection.rows, selection.cols) }),
|
|
3556
|
-
/* @__PURE__ */ jsx7("div", { className: "grid grid-cols-8 gap-1", children: Array.from({ length: maxRows }).map(
|
|
4487
|
+
/* @__PURE__ */ jsx7("div", { className: "grid grid-cols-8 gap-1", role: "grid", "aria-label": insertLabel, children: Array.from({ length: maxRows }).map(
|
|
3557
4488
|
(_, rowIndex) => Array.from({ length: maxCols }).map((__, colIndex) => {
|
|
3558
4489
|
const rows = rowIndex + 1;
|
|
3559
4490
|
const cols = colIndex + 1;
|
|
@@ -3562,10 +4493,22 @@ var TableInsertGrid = ({
|
|
|
3562
4493
|
"button",
|
|
3563
4494
|
{
|
|
3564
4495
|
type: "button",
|
|
4496
|
+
role: "gridcell",
|
|
4497
|
+
tabIndex: rows === selection.rows && cols === selection.cols ? 0 : -1,
|
|
4498
|
+
"aria-selected": rows === selection.rows && cols === selection.cols,
|
|
4499
|
+
"data-table-grid-cell": `${rows}-${cols}`,
|
|
3565
4500
|
"aria-label": formatTableInsertLabel(previewTemplate, rows, cols),
|
|
3566
4501
|
onMouseDown: (e) => e.preventDefault(),
|
|
3567
4502
|
onMouseEnter: () => setSelection({ rows, cols }),
|
|
3568
4503
|
onFocus: () => setSelection({ rows, cols }),
|
|
4504
|
+
onKeyDown: (event) => {
|
|
4505
|
+
const nextRows = event.key === "ArrowUp" ? Math.max(1, rows - 1) : event.key === "ArrowDown" ? Math.min(maxRows, rows + 1) : rows;
|
|
4506
|
+
const nextCols = event.key === "ArrowLeft" ? Math.max(1, cols - 1) : event.key === "ArrowRight" ? Math.min(maxCols, cols + 1) : cols;
|
|
4507
|
+
if (nextRows === rows && nextCols === cols) return;
|
|
4508
|
+
event.preventDefault();
|
|
4509
|
+
setSelection({ rows: nextRows, cols: nextCols });
|
|
4510
|
+
event.currentTarget.parentElement?.querySelector(`[data-table-grid-cell="${nextRows}-${nextCols}"]`)?.focus();
|
|
4511
|
+
},
|
|
3569
4512
|
onClick: () => onInsert(rows, cols),
|
|
3570
4513
|
className: cn(
|
|
3571
4514
|
"h-5 w-5 rounded-sm border transition-colors",
|
|
@@ -3597,6 +4540,8 @@ var EditorToolbar = ({
|
|
|
3597
4540
|
imageInsertMode = "base64",
|
|
3598
4541
|
maxImageFileSize = DEFAULT_UEDITOR_IMAGE_MAX_FILE_SIZE,
|
|
3599
4542
|
allowedImageMimeTypes = DEFAULT_UEDITOR_IMAGE_MIME_TYPES,
|
|
4543
|
+
onImageUploadError,
|
|
4544
|
+
fallbackToDataUrl = true,
|
|
3600
4545
|
fontFamilies,
|
|
3601
4546
|
defaultFontFamily = "Inter",
|
|
3602
4547
|
fontSizes,
|
|
@@ -3655,28 +4600,36 @@ var EditorToolbar = ({
|
|
|
3655
4600
|
const applyFontSizeDraft = () => {
|
|
3656
4601
|
const parsed = Number.parseFloat(fontSizeDraft);
|
|
3657
4602
|
if (!Number.isFinite(parsed)) return;
|
|
3658
|
-
|
|
3659
|
-
|
|
4603
|
+
const clamped = Math.min(512, Math.max(8, parsed));
|
|
4604
|
+
editor.chain().focus().setFontSize(`${clamped}px`).run();
|
|
4605
|
+
setFontSizeDraft(String(clamped));
|
|
3660
4606
|
};
|
|
3661
4607
|
const insertImageFiles = async (files) => {
|
|
3662
4608
|
if (files.length === 0) return;
|
|
3663
4609
|
setIsUploadingImage(true);
|
|
3664
4610
|
setImageUploadError(null);
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
4611
|
+
const trackedPosition = trackEditorInsertionPosition(editor);
|
|
4612
|
+
try {
|
|
4613
|
+
const { images, hadError } = await resolveUEditorImageFiles(files, {
|
|
4614
|
+
maxFileSize: maxImageFileSize,
|
|
4615
|
+
allowedMimeTypes: allowedImageMimeTypes,
|
|
4616
|
+
upload: uploadImage,
|
|
4617
|
+
fallbackToDataUrl,
|
|
4618
|
+
insertMode: imageInsertMode,
|
|
4619
|
+
onError: onImageUploadError
|
|
4620
|
+
});
|
|
4621
|
+
if (hadError) setImageUploadError(t("imageInput.uploadError"));
|
|
4622
|
+
if (!editor.isDestroyed && images.length > 0) {
|
|
4623
|
+
const content = images.map((image) => ({
|
|
4624
|
+
type: "image",
|
|
4625
|
+
attrs: { src: image.src, alt: image.file.name }
|
|
4626
|
+
}));
|
|
4627
|
+
editor.commands.insertContentAt(trackedPosition.current, content, { updateSelection: false });
|
|
3677
4628
|
}
|
|
4629
|
+
} finally {
|
|
4630
|
+
trackedPosition.stop();
|
|
4631
|
+
setIsUploadingImage(false);
|
|
3678
4632
|
}
|
|
3679
|
-
setIsUploadingImage(false);
|
|
3680
4633
|
};
|
|
3681
4634
|
if (variant === "minimal") {
|
|
3682
4635
|
return /* @__PURE__ */ jsxs5("div", { className: "flex flex-wrap items-center gap-0.5 border-b border-border/35 bg-muted/30 p-1.5", children: [
|
|
@@ -3831,6 +4784,8 @@ var EditorToolbar = ({
|
|
|
3831
4784
|
"input",
|
|
3832
4785
|
{
|
|
3833
4786
|
type: "number",
|
|
4787
|
+
min: 8,
|
|
4788
|
+
max: 512,
|
|
3834
4789
|
step: 1,
|
|
3835
4790
|
value: isEditingFontSize ? fontSizeDraft : displayedFontSizeLabel,
|
|
3836
4791
|
onFocus: () => {
|
|
@@ -4380,7 +5335,7 @@ var EditorToolbar = ({
|
|
|
4380
5335
|
{
|
|
4381
5336
|
ref: fileInputRef,
|
|
4382
5337
|
type: "file",
|
|
4383
|
-
accept: "image/*",
|
|
5338
|
+
accept: allowedImageMimeTypes.length > 0 ? allowedImageMimeTypes.join(",") : "image/*",
|
|
4384
5339
|
multiple: true,
|
|
4385
5340
|
className: "hidden",
|
|
4386
5341
|
onChange: (e) => {
|
|
@@ -4769,6 +5724,25 @@ export {
|
|
|
4769
5724
|
DropdownMenu_default,
|
|
4770
5725
|
isSafeUEditorUrl,
|
|
4771
5726
|
sanitizeUEditorUrl,
|
|
5727
|
+
TABLE_WIDTH_BASIS_POINTS,
|
|
5728
|
+
DEFAULT_RESPONSIVE_TABLE_WIDTH_BP,
|
|
5729
|
+
clampResponsiveTableWidthBp,
|
|
5730
|
+
normalizeTableWidthMode,
|
|
5731
|
+
parsePercentageToBasisPoints,
|
|
5732
|
+
formatBasisPointsAsPercentage,
|
|
5733
|
+
parseColumnRatios,
|
|
5734
|
+
normalizeColumnRatios,
|
|
5735
|
+
getLogicalTableColumnCount,
|
|
5736
|
+
getLegacyTableColumnWeights,
|
|
5737
|
+
getTableColumnRatios,
|
|
5738
|
+
getResponsiveTableWidthBp,
|
|
5739
|
+
getResponsiveTableOffsetBp,
|
|
5740
|
+
resolveResponsiveTableOffsetBp,
|
|
5741
|
+
moveResponsiveColumnRatio,
|
|
5742
|
+
insertResponsiveColumnLayout,
|
|
5743
|
+
deleteResponsiveColumnLayout,
|
|
5744
|
+
trackEditorInsertionPosition,
|
|
5745
|
+
resolveUEditorImageFiles,
|
|
4772
5746
|
DEFAULT_UEDITOR_IMAGE_MAX_FILE_SIZE,
|
|
4773
5747
|
DEFAULT_UEDITOR_IMAGE_MIME_TYPES,
|
|
4774
5748
|
ClipboardImages,
|
|
@@ -4796,6 +5770,21 @@ export {
|
|
|
4796
5770
|
getRelativeSelectedCellsMetrics,
|
|
4797
5771
|
findTableNodeInfoFromState,
|
|
4798
5772
|
applyTableAlignment,
|
|
5773
|
+
indexToColumnName,
|
|
5774
|
+
parseTableCellRange,
|
|
5775
|
+
getTableCellRangeLabels,
|
|
5776
|
+
normalizeTableFormula,
|
|
5777
|
+
isDraftTableFormula,
|
|
5778
|
+
formatFormulaError,
|
|
5779
|
+
formatTableFormulaDisplayValue,
|
|
5780
|
+
getTableFormulaReferences,
|
|
5781
|
+
buildTableFormulaDependencyGraph,
|
|
5782
|
+
getTableFormulaRecalculationOrder,
|
|
5783
|
+
getAffectedTableFormulaLabels,
|
|
5784
|
+
evaluateBasicTableFormula,
|
|
5785
|
+
rewriteTableNodeFormulaReferencesForInsertion,
|
|
5786
|
+
rewriteTableNodeFormulaReferencesForDeletion,
|
|
5787
|
+
rewriteTableNodeFormulaReferencesForMove,
|
|
4799
5788
|
dispatchTableLayoutChange,
|
|
4800
5789
|
mergeTableCellsPreservingColumnWidths,
|
|
4801
5790
|
runTableCommandAtCellPos,
|
|
@@ -4823,10 +5812,9 @@ export {
|
|
|
4823
5812
|
getTableAnchorPos,
|
|
4824
5813
|
EditorUiRenderStateProvider,
|
|
4825
5814
|
useSharedEditorUiRenderState,
|
|
4826
|
-
fileToDataUrl2 as fileToDataUrl,
|
|
4827
5815
|
ToolbarButton,
|
|
4828
5816
|
TableInsertGrid,
|
|
4829
5817
|
EditorToolbar,
|
|
4830
5818
|
UEDITOR_PROSEMIRROR_CLASS_NAME
|
|
4831
5819
|
};
|
|
4832
|
-
//# sourceMappingURL=chunk-
|
|
5820
|
+
//# sourceMappingURL=chunk-37MHVJMV.js.map
|