@underverse-ui/underverse 1.0.183 → 1.0.185
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/index.cjs +1241 -395
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +40 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +1224 -378
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
package/dist/index.cjs
CHANGED
|
@@ -1086,6 +1086,11 @@ var en_default = {
|
|
|
1086
1086
|
borderColor: "Border Color",
|
|
1087
1087
|
clearBorder: "Clear Border",
|
|
1088
1088
|
formula: "Formula",
|
|
1089
|
+
formulaBar: "Formula bar",
|
|
1090
|
+
formulaError: "Formula error",
|
|
1091
|
+
editFormula: "Edit formula",
|
|
1092
|
+
clearCell: "Clear cell",
|
|
1093
|
+
convertToValue: "Convert to value",
|
|
1089
1094
|
numberFormat: "Number Format",
|
|
1090
1095
|
formatText: "Text",
|
|
1091
1096
|
formatDate: "Date",
|
|
@@ -1508,6 +1513,11 @@ var vi_default = {
|
|
|
1508
1513
|
borderColor: "M\xE0u vi\u1EC1n",
|
|
1509
1514
|
clearBorder: "X\xF3a vi\u1EC1n",
|
|
1510
1515
|
formula: "C\xF4ng th\u1EE9c",
|
|
1516
|
+
formulaBar: "Thanh c\xF4ng th\u1EE9c",
|
|
1517
|
+
formulaError: "L\u1ED7i c\xF4ng th\u1EE9c",
|
|
1518
|
+
editFormula: "S\u1EEDa c\xF4ng th\u1EE9c",
|
|
1519
|
+
clearCell: "X\xF3a n\u1ED9i dung \xF4",
|
|
1520
|
+
convertToValue: "Chuy\u1EC3n th\xE0nh gi\xE1 tr\u1ECB",
|
|
1511
1521
|
numberFormat: "\u0110\u1ECBnh d\u1EA1ng s\u1ED1",
|
|
1512
1522
|
formatText: "Ch\u1EEF",
|
|
1513
1523
|
formatDate: "Ng\xE0y",
|
|
@@ -1930,6 +1940,11 @@ var ko_default = {
|
|
|
1930
1940
|
borderColor: "\uD14C\uB450\uB9AC \uC0C9\uC0C1",
|
|
1931
1941
|
clearBorder: "\uD14C\uB450\uB9AC \uC9C0\uC6B0\uAE30",
|
|
1932
1942
|
formula: "\uC218\uC2DD",
|
|
1943
|
+
formulaBar: "\uC218\uC2DD \uC785\uB825\uC904",
|
|
1944
|
+
formulaError: "\uC218\uC2DD \uC624\uB958",
|
|
1945
|
+
editFormula: "\uC218\uC2DD \uD3B8\uC9D1",
|
|
1946
|
+
clearCell: "\uC140 \uB0B4\uC6A9 \uC0AD\uC81C",
|
|
1947
|
+
convertToValue: "\uAC12\uC73C\uB85C \uBCC0\uD658",
|
|
1933
1948
|
numberFormat: "\uD45C\uC2DC \uD615\uC2DD",
|
|
1934
1949
|
formatText: "\uD14D\uC2A4\uD2B8",
|
|
1935
1950
|
formatDate: "\uB0A0\uC9DC",
|
|
@@ -2352,6 +2367,11 @@ var ja_default = {
|
|
|
2352
2367
|
borderColor: "\u67A0\u7DDA\u306E\u8272",
|
|
2353
2368
|
clearBorder: "\u67A0\u7DDA\u3092\u30AF\u30EA\u30A2",
|
|
2354
2369
|
formula: "\u6570\u5F0F",
|
|
2370
|
+
formulaBar: "\u6570\u5F0F\u30D0\u30FC",
|
|
2371
|
+
formulaError: "\u6570\u5F0F\u30A8\u30E9\u30FC",
|
|
2372
|
+
editFormula: "\u6570\u5F0F\u3092\u7DE8\u96C6",
|
|
2373
|
+
clearCell: "\u30BB\u30EB\u3092\u30AF\u30EA\u30A2",
|
|
2374
|
+
convertToValue: "\u5024\u306B\u5909\u63DB",
|
|
2355
2375
|
numberFormat: "\u8868\u793A\u5F62\u5F0F",
|
|
2356
2376
|
formatText: "\u30C6\u30AD\u30B9\u30C8",
|
|
2357
2377
|
formatDate: "\u65E5\u4ED8",
|
|
@@ -27548,8 +27568,8 @@ function useLocale2() {
|
|
|
27548
27568
|
}
|
|
27549
27569
|
|
|
27550
27570
|
// src/components/UEditor/UEditor.tsx
|
|
27551
|
-
var
|
|
27552
|
-
var
|
|
27571
|
+
var import_react82 = __toESM(require("react"), 1);
|
|
27572
|
+
var import_react83 = require("@tiptap/react");
|
|
27553
27573
|
|
|
27554
27574
|
// src/components/UEditor/extensions.ts
|
|
27555
27575
|
var import_core16 = require("@tiptap/core");
|
|
@@ -27711,6 +27731,54 @@ var import_react49 = require("@tiptap/react");
|
|
|
27711
27731
|
var import_react47 = __toESM(require("react"), 1);
|
|
27712
27732
|
var import_react48 = require("@tiptap/react");
|
|
27713
27733
|
var import_lucide_react43 = require("lucide-react");
|
|
27734
|
+
|
|
27735
|
+
// src/components/UEditor/url-safety.ts
|
|
27736
|
+
var LINK_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:", "mailto:", "tel:"]);
|
|
27737
|
+
var IMAGE_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:"]);
|
|
27738
|
+
var FILE_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:", "blob:"]);
|
|
27739
|
+
function normalizeUrlInput(raw) {
|
|
27740
|
+
return raw.trim().replace(/[\u0000-\u001F\u007F\s]+/g, "");
|
|
27741
|
+
}
|
|
27742
|
+
function isProtocolRelativeUrl(value) {
|
|
27743
|
+
return value.startsWith("//");
|
|
27744
|
+
}
|
|
27745
|
+
function isRelativeUrl(value) {
|
|
27746
|
+
return value.startsWith("/") || value.startsWith("./") || value.startsWith("../") || value.startsWith("#");
|
|
27747
|
+
}
|
|
27748
|
+
function isDataImageUrl(value) {
|
|
27749
|
+
return /^data:image\/(?:png|jpe?g|gif|webp|svg\+xml|bmp|x-icon|avif);base64,/i.test(value);
|
|
27750
|
+
}
|
|
27751
|
+
function isDataFileUrl(value) {
|
|
27752
|
+
return /^data:[a-z0-9][a-z0-9!#$&^_.+-]*\/[a-z0-9][a-z0-9!#$&^_.+-]*(?:;[a-z0-9!#$&^_.+-]+=[^;,]*)*;base64,[a-z0-9+/]*={0,2}$/i.test(value);
|
|
27753
|
+
}
|
|
27754
|
+
function isSafeUEditorUrl(raw, kind) {
|
|
27755
|
+
const value = normalizeUrlInput(raw);
|
|
27756
|
+
if (!value) return false;
|
|
27757
|
+
if (kind === "image" && isDataImageUrl(value)) return true;
|
|
27758
|
+
if (kind === "file" && isDataFileUrl(value)) return true;
|
|
27759
|
+
if (isProtocolRelativeUrl(value)) return false;
|
|
27760
|
+
if (isRelativeUrl(value)) return true;
|
|
27761
|
+
try {
|
|
27762
|
+
const parsed = new URL(value);
|
|
27763
|
+
if (kind === "image") return IMAGE_PROTOCOLS.has(parsed.protocol);
|
|
27764
|
+
if (kind === "file") return FILE_PROTOCOLS.has(parsed.protocol);
|
|
27765
|
+
return LINK_PROTOCOLS.has(parsed.protocol);
|
|
27766
|
+
} catch {
|
|
27767
|
+
return false;
|
|
27768
|
+
}
|
|
27769
|
+
}
|
|
27770
|
+
function sanitizeUEditorUrl(raw, kind) {
|
|
27771
|
+
const value = raw.trim();
|
|
27772
|
+
if (!value) return "";
|
|
27773
|
+
if (isSafeUEditorUrl(value, kind)) return normalizeUrlInput(value);
|
|
27774
|
+
if (kind === "link" && !isProtocolRelativeUrl(value) && !/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(value)) {
|
|
27775
|
+
const withProtocol = `https://${value}`;
|
|
27776
|
+
return isSafeUEditorUrl(withProtocol, kind) ? withProtocol : "";
|
|
27777
|
+
}
|
|
27778
|
+
return "";
|
|
27779
|
+
}
|
|
27780
|
+
|
|
27781
|
+
// src/components/UEditor/BookmarkView.tsx
|
|
27714
27782
|
var import_jsx_runtime80 = require("react/jsx-runtime");
|
|
27715
27783
|
var BookmarkView = ({ node, updateAttributes, selected, editor }) => {
|
|
27716
27784
|
const t = useSmartTranslations("UEditor");
|
|
@@ -27718,17 +27786,25 @@ var BookmarkView = ({ node, updateAttributes, selected, editor }) => {
|
|
|
27718
27786
|
const [loading2, setLoading] = (0, import_react47.useState)(false);
|
|
27719
27787
|
const [error, setError] = (0, import_react47.useState)(false);
|
|
27720
27788
|
const [retryToken, setRetryToken] = (0, import_react47.useState)(0);
|
|
27721
|
-
const
|
|
27789
|
+
const lastFetchKeyRef = import_react47.default.useRef("");
|
|
27722
27790
|
const fetchRequestIdRef = import_react47.default.useRef(0);
|
|
27791
|
+
const safeUrl = sanitizeUEditorUrl(String(url ?? ""), "link");
|
|
27792
|
+
const safeImage = sanitizeUEditorUrl(String(image ?? ""), "image");
|
|
27793
|
+
const clickToOpenNewTabMessage = t("bookmark.clickToOpenNewTab");
|
|
27794
|
+
const clickToOpenLinkMessage = t("bookmark.clickToOpenLink");
|
|
27795
|
+
const noDescriptionMessage = t("bookmark.noDescription");
|
|
27723
27796
|
(0, import_react47.useEffect)(() => {
|
|
27724
27797
|
if (title && retryToken === 0) return;
|
|
27725
|
-
if (
|
|
27726
|
-
|
|
27798
|
+
if (!safeUrl) return;
|
|
27799
|
+
const fetchKey = `${safeUrl}\0${retryToken}`;
|
|
27800
|
+
if (lastFetchKeyRef.current === fetchKey) return;
|
|
27801
|
+
lastFetchKeyRef.current = fetchKey;
|
|
27727
27802
|
const requestId = fetchRequestIdRef.current + 1;
|
|
27728
27803
|
fetchRequestIdRef.current = requestId;
|
|
27804
|
+
let active = true;
|
|
27729
27805
|
const safeUpdateAttributes = (attrs) => {
|
|
27730
27806
|
Promise.resolve().then(() => {
|
|
27731
|
-
if (fetchRequestIdRef.current === requestId && editor && !editor.isDestroyed) {
|
|
27807
|
+
if (active && fetchRequestIdRef.current === requestId && editor && !editor.isDestroyed) {
|
|
27732
27808
|
updateAttributes(attrs);
|
|
27733
27809
|
}
|
|
27734
27810
|
});
|
|
@@ -27737,43 +27813,75 @@ var BookmarkView = ({ node, updateAttributes, selected, editor }) => {
|
|
|
27737
27813
|
const fetchMetadata = extension?.options?.fetchMetadata;
|
|
27738
27814
|
if (!fetchMetadata) {
|
|
27739
27815
|
try {
|
|
27740
|
-
const hostname = new URL(
|
|
27816
|
+
const hostname = new URL(safeUrl).hostname;
|
|
27741
27817
|
safeUpdateAttributes({
|
|
27742
|
-
title: hostname ||
|
|
27743
|
-
description:
|
|
27818
|
+
title: hostname || safeUrl,
|
|
27819
|
+
description: clickToOpenNewTabMessage,
|
|
27744
27820
|
publisher: hostname
|
|
27745
27821
|
});
|
|
27746
27822
|
} catch {
|
|
27747
27823
|
safeUpdateAttributes({
|
|
27748
|
-
title:
|
|
27749
|
-
description:
|
|
27824
|
+
title: safeUrl,
|
|
27825
|
+
description: clickToOpenLinkMessage,
|
|
27750
27826
|
publisher: ""
|
|
27751
27827
|
});
|
|
27752
27828
|
}
|
|
27753
|
-
return
|
|
27829
|
+
return () => {
|
|
27830
|
+
active = false;
|
|
27831
|
+
};
|
|
27754
27832
|
}
|
|
27755
|
-
|
|
27756
|
-
|
|
27757
|
-
|
|
27758
|
-
|
|
27833
|
+
queueMicrotask(() => {
|
|
27834
|
+
if (!active) return;
|
|
27835
|
+
setLoading(true);
|
|
27836
|
+
setError(false);
|
|
27837
|
+
});
|
|
27838
|
+
fetchMetadata(safeUrl).then((meta) => {
|
|
27839
|
+
if (!active || fetchRequestIdRef.current !== requestId) return;
|
|
27840
|
+
const hostname = (() => {
|
|
27841
|
+
try {
|
|
27842
|
+
return new URL(safeUrl).hostname;
|
|
27843
|
+
} catch {
|
|
27844
|
+
return "";
|
|
27845
|
+
}
|
|
27846
|
+
})();
|
|
27759
27847
|
safeUpdateAttributes({
|
|
27760
|
-
title: meta.title ||
|
|
27761
|
-
description: meta.description ||
|
|
27762
|
-
image: meta.image
|
|
27763
|
-
publisher: meta.publisher ||
|
|
27848
|
+
title: meta.title || hostname || safeUrl,
|
|
27849
|
+
description: meta.description || noDescriptionMessage,
|
|
27850
|
+
image: meta.image ? sanitizeUEditorUrl(meta.image, "image") : "",
|
|
27851
|
+
publisher: meta.publisher || hostname
|
|
27764
27852
|
});
|
|
27765
27853
|
setLoading(false);
|
|
27766
27854
|
}).catch(() => {
|
|
27767
|
-
if (fetchRequestIdRef.current !== requestId) return;
|
|
27855
|
+
if (!active || fetchRequestIdRef.current !== requestId) return;
|
|
27768
27856
|
setError(true);
|
|
27769
27857
|
setLoading(false);
|
|
27770
27858
|
});
|
|
27771
|
-
return
|
|
27772
|
-
|
|
27773
|
-
|
|
27774
|
-
|
|
27775
|
-
|
|
27776
|
-
|
|
27859
|
+
return () => {
|
|
27860
|
+
active = false;
|
|
27861
|
+
};
|
|
27862
|
+
}, [
|
|
27863
|
+
safeUrl,
|
|
27864
|
+
title,
|
|
27865
|
+
editor,
|
|
27866
|
+
updateAttributes,
|
|
27867
|
+
retryToken,
|
|
27868
|
+
clickToOpenNewTabMessage,
|
|
27869
|
+
clickToOpenLinkMessage,
|
|
27870
|
+
noDescriptionMessage
|
|
27871
|
+
]);
|
|
27872
|
+
const openLink = () => {
|
|
27873
|
+
if (safeUrl) window.open(safeUrl, "_blank", "noopener,noreferrer");
|
|
27874
|
+
};
|
|
27875
|
+
const handleOpenLink = (event) => {
|
|
27876
|
+
event.preventDefault();
|
|
27877
|
+
event.stopPropagation();
|
|
27878
|
+
openLink();
|
|
27879
|
+
};
|
|
27880
|
+
const handleKeyDown2 = (event) => {
|
|
27881
|
+
if (event.key !== "Enter") return;
|
|
27882
|
+
event.preventDefault();
|
|
27883
|
+
event.stopPropagation();
|
|
27884
|
+
openLink();
|
|
27777
27885
|
};
|
|
27778
27886
|
const handleRetryPreview = (event) => {
|
|
27779
27887
|
event.preventDefault();
|
|
@@ -27782,7 +27890,7 @@ var BookmarkView = ({ node, updateAttributes, selected, editor }) => {
|
|
|
27782
27890
|
};
|
|
27783
27891
|
const displayPublisher = publisher || (() => {
|
|
27784
27892
|
try {
|
|
27785
|
-
return new URL(
|
|
27893
|
+
return safeUrl ? new URL(safeUrl).hostname : "";
|
|
27786
27894
|
} catch {
|
|
27787
27895
|
return "";
|
|
27788
27896
|
}
|
|
@@ -27795,6 +27903,10 @@ var BookmarkView = ({ node, updateAttributes, selected, editor }) => {
|
|
|
27795
27903
|
selected && "ring-2 ring-primary/45 border-primary/20 shadow-sm"
|
|
27796
27904
|
),
|
|
27797
27905
|
onClick: handleOpenLink,
|
|
27906
|
+
onKeyDown: handleKeyDown2,
|
|
27907
|
+
role: "link",
|
|
27908
|
+
tabIndex: safeUrl ? 0 : -1,
|
|
27909
|
+
"aria-disabled": !safeUrl,
|
|
27798
27910
|
children: loading2 ? /* @__PURE__ */ (0, import_jsx_runtime80.jsxs)("div", { className: "flex items-center justify-between p-4 gap-4 animate-pulse", children: [
|
|
27799
27911
|
/* @__PURE__ */ (0, import_jsx_runtime80.jsxs)("div", { className: "flex-1 flex flex-col gap-2", children: [
|
|
27800
27912
|
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)("div", { className: "h-4 w-1/3 bg-muted-foreground/20 rounded" }),
|
|
@@ -27809,11 +27921,11 @@ var BookmarkView = ({ node, updateAttributes, selected, editor }) => {
|
|
|
27809
27921
|
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)(import_lucide_react43.Globe, { className: "h-3 w-3 flex-shrink-0" }),
|
|
27810
27922
|
displayPublisher
|
|
27811
27923
|
] }),
|
|
27812
|
-
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)("h4", { className: "text-sm font-semibold text-foreground line-clamp-1 break-all", children: title || url }),
|
|
27924
|
+
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)("h4", { className: "text-sm font-semibold text-foreground line-clamp-1 break-all", children: title || safeUrl || String(url ?? "") }),
|
|
27813
27925
|
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)("p", { className: "text-xs text-muted-foreground line-clamp-2 leading-relaxed", children: description })
|
|
27814
27926
|
] }),
|
|
27815
27927
|
/* @__PURE__ */ (0, import_jsx_runtime80.jsxs)("span", { className: "text-[11px] text-primary/80 font-mono mt-2 truncate flex items-center gap-1", children: [
|
|
27816
|
-
url,
|
|
27928
|
+
safeUrl || String(url ?? ""),
|
|
27817
27929
|
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)(import_lucide_react43.ExternalLink, { className: "h-3 w-3 flex-shrink-0" })
|
|
27818
27930
|
] }),
|
|
27819
27931
|
error && /* @__PURE__ */ (0, import_jsx_runtime80.jsxs)("div", { className: "mt-2 flex flex-wrap items-center gap-2 text-xs text-destructive", children: [
|
|
@@ -27835,10 +27947,10 @@ var BookmarkView = ({ node, updateAttributes, selected, editor }) => {
|
|
|
27835
27947
|
)
|
|
27836
27948
|
] })
|
|
27837
27949
|
] }),
|
|
27838
|
-
|
|
27950
|
+
safeImage && /* @__PURE__ */ (0, import_jsx_runtime80.jsx)("div", { className: "relative w-full sm:w-44 h-32 sm:h-auto flex-shrink-0 bg-muted overflow-hidden border-t sm:border-t-0 sm:border-l border-border/50", children: /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(
|
|
27839
27951
|
"img",
|
|
27840
27952
|
{
|
|
27841
|
-
src:
|
|
27953
|
+
src: safeImage,
|
|
27842
27954
|
alt: title || "Link preview image",
|
|
27843
27955
|
className: "absolute inset-0 w-full h-full object-cover transition-opacity duration-300",
|
|
27844
27956
|
onError: (e) => {
|
|
@@ -27868,11 +27980,11 @@ var Bookmark = import_core2.Node.create({
|
|
|
27868
27980
|
return {
|
|
27869
27981
|
url: {
|
|
27870
27982
|
default: "",
|
|
27871
|
-
parseHTML: (element) => element.getAttribute("href") || element.getAttribute("data-url") || "",
|
|
27872
|
-
renderHTML: (attributes) =>
|
|
27873
|
-
|
|
27874
|
-
"data-url":
|
|
27875
|
-
}
|
|
27983
|
+
parseHTML: (element) => sanitizeUEditorUrl(element.getAttribute("href") || element.getAttribute("data-url") || "", "link"),
|
|
27984
|
+
renderHTML: (attributes) => {
|
|
27985
|
+
const url = sanitizeUEditorUrl(String(attributes.url ?? ""), "link");
|
|
27986
|
+
return url ? { href: url, "data-url": url } : {};
|
|
27987
|
+
}
|
|
27876
27988
|
},
|
|
27877
27989
|
title: {
|
|
27878
27990
|
default: "",
|
|
@@ -27890,10 +28002,11 @@ var Bookmark = import_core2.Node.create({
|
|
|
27890
28002
|
},
|
|
27891
28003
|
image: {
|
|
27892
28004
|
default: "",
|
|
27893
|
-
parseHTML: (element) => element.getAttribute("data-image") || "",
|
|
27894
|
-
renderHTML: (attributes) =>
|
|
27895
|
-
|
|
27896
|
-
|
|
28005
|
+
parseHTML: (element) => sanitizeUEditorUrl(element.getAttribute("data-image") || "", "image"),
|
|
28006
|
+
renderHTML: (attributes) => {
|
|
28007
|
+
const image = sanitizeUEditorUrl(String(attributes.image ?? ""), "image");
|
|
28008
|
+
return image ? { "data-image": image } : {};
|
|
28009
|
+
}
|
|
27897
28010
|
},
|
|
27898
28011
|
publisher: {
|
|
27899
28012
|
default: "",
|
|
@@ -27926,9 +28039,12 @@ var Bookmark = import_core2.Node.create({
|
|
|
27926
28039
|
addCommands() {
|
|
27927
28040
|
return {
|
|
27928
28041
|
setBookmark: (attributes) => ({ commands }) => {
|
|
28042
|
+
const url = sanitizeUEditorUrl(attributes.url, "link");
|
|
28043
|
+
if (!url) return false;
|
|
28044
|
+
const image = attributes.image ? sanitizeUEditorUrl(attributes.image, "image") : "";
|
|
27929
28045
|
return commands.insertContent({
|
|
27930
28046
|
type: this.name,
|
|
27931
|
-
attrs: attributes
|
|
28047
|
+
attrs: { ...attributes, url, image }
|
|
27932
28048
|
});
|
|
27933
28049
|
}
|
|
27934
28050
|
};
|
|
@@ -27945,7 +28061,6 @@ var Bookmark = import_core2.Node.create({
|
|
|
27945
28061
|
if (!text || !URL_REGEX.test(text)) return false;
|
|
27946
28062
|
const { selection } = view.state;
|
|
27947
28063
|
const parent = selection.$from.parent;
|
|
27948
|
-
console.log("DEBUG handlePaste name:", parent.type.name, "size:", parent.content.size);
|
|
27949
28064
|
const isEmptyParagraph = parent.type.name === "paragraph" && parent.content.size === 0;
|
|
27950
28065
|
if (isEmptyParagraph) {
|
|
27951
28066
|
event.preventDefault();
|
|
@@ -27973,15 +28088,15 @@ var import_react51 = require("@tiptap/react");
|
|
|
27973
28088
|
var import_lucide_react44 = require("lucide-react");
|
|
27974
28089
|
var import_jsx_runtime81 = require("react/jsx-runtime");
|
|
27975
28090
|
function formatBytes(bytes, decimals = 1) {
|
|
27976
|
-
if (!bytes || bytes <= 0) return "0 Bytes";
|
|
28091
|
+
if (!Number.isFinite(bytes) || bytes <= 0) return "0 Bytes";
|
|
27977
28092
|
const k = 1024;
|
|
27978
28093
|
const dm = decimals < 0 ? 0 : decimals;
|
|
27979
|
-
const sizes = ["Bytes", "KB", "MB", "GB"];
|
|
27980
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
28094
|
+
const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB"];
|
|
28095
|
+
const i = Math.min(Math.floor(Math.log(bytes) / Math.log(k)), sizes.length - 1);
|
|
27981
28096
|
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
|
|
27982
28097
|
}
|
|
27983
28098
|
function getFileIcon2(type) {
|
|
27984
|
-
const mime = (type || "").toLowerCase();
|
|
28099
|
+
const mime = String(type || "").toLowerCase();
|
|
27985
28100
|
if (mime.includes("pdf")) return import_lucide_react44.FileText;
|
|
27986
28101
|
if (mime.startsWith("image/")) return import_lucide_react44.Image;
|
|
27987
28102
|
if (mime.startsWith("audio/")) return import_lucide_react44.Music;
|
|
@@ -28007,11 +28122,13 @@ function dataURLtoFile(dataurl, filename, mimeType) {
|
|
|
28007
28122
|
}
|
|
28008
28123
|
}
|
|
28009
28124
|
return new File([bytes], filename, { type: mime });
|
|
28010
|
-
} catch
|
|
28011
|
-
console.error("Error converting data URI to file:", e);
|
|
28125
|
+
} catch {
|
|
28012
28126
|
return null;
|
|
28013
28127
|
}
|
|
28014
28128
|
}
|
|
28129
|
+
function getErrorMessage(error) {
|
|
28130
|
+
return error instanceof Error && error.message ? error.message : "Upload failed";
|
|
28131
|
+
}
|
|
28015
28132
|
var FileCardView = ({ node, selected, editor, updateAttributes }) => {
|
|
28016
28133
|
const t = useSmartTranslations("UEditor");
|
|
28017
28134
|
const { src, fileName, fileSize, fileType } = node.attrs;
|
|
@@ -28019,49 +28136,64 @@ var FileCardView = ({ node, selected, editor, updateAttributes }) => {
|
|
|
28019
28136
|
const [uploadError, setUploadError] = (0, import_react50.useState)(null);
|
|
28020
28137
|
const [retryToken, setRetryToken] = (0, import_react50.useState)(0);
|
|
28021
28138
|
const uploadAttemptedRef = (0, import_react50.useRef)(false);
|
|
28139
|
+
const lastUploadSrcRef = (0, import_react50.useRef)("");
|
|
28140
|
+
const uploadRequestIdRef = (0, import_react50.useRef)(0);
|
|
28141
|
+
const safeSrc = sanitizeUEditorUrl(String(src ?? ""), "file");
|
|
28142
|
+
const extension = editor?.extensionManager?.extensions?.find((item) => item.name === "fileCard");
|
|
28143
|
+
const uploadFn = extension?.options?.upload;
|
|
28022
28144
|
(0, import_react50.useEffect)(() => {
|
|
28023
|
-
if (
|
|
28145
|
+
if (lastUploadSrcRef.current !== safeSrc) {
|
|
28146
|
+
lastUploadSrcRef.current = safeSrc;
|
|
28147
|
+
uploadAttemptedRef.current = false;
|
|
28148
|
+
}
|
|
28149
|
+
if (!safeSrc || !safeSrc.startsWith("data:") || !uploadFn) {
|
|
28024
28150
|
return;
|
|
28025
28151
|
}
|
|
28026
|
-
|
|
28027
|
-
const uploadFn = extension?.options?.upload;
|
|
28028
|
-
if (!uploadFn || uploadAttemptedRef.current || !updateAttributes) {
|
|
28152
|
+
if (uploadAttemptedRef.current || !updateAttributes) {
|
|
28029
28153
|
return;
|
|
28030
28154
|
}
|
|
28031
28155
|
uploadAttemptedRef.current = true;
|
|
28032
28156
|
setUploadError(null);
|
|
28157
|
+
const requestId = uploadRequestIdRef.current + 1;
|
|
28158
|
+
uploadRequestIdRef.current = requestId;
|
|
28159
|
+
let active = true;
|
|
28033
28160
|
const runUpload = async () => {
|
|
28034
28161
|
try {
|
|
28035
|
-
const file = dataURLtoFile(
|
|
28162
|
+
const file = dataURLtoFile(safeSrc, fileName || "file", fileType);
|
|
28036
28163
|
if (!file) {
|
|
28037
28164
|
throw new Error("Failed to parse file data");
|
|
28038
28165
|
}
|
|
28039
28166
|
const resolvedUrl = await uploadFn(file);
|
|
28167
|
+
const nextSrc = sanitizeUEditorUrl(String(resolvedUrl ?? ""), "file");
|
|
28168
|
+
if (!nextSrc) throw new Error("Upload handler returned an unsafe URL");
|
|
28040
28169
|
Promise.resolve().then(() => {
|
|
28041
|
-
if (editor && !editor.isDestroyed) {
|
|
28042
|
-
updateAttributes({ src:
|
|
28170
|
+
if (active && uploadRequestIdRef.current === requestId && editor && !editor.isDestroyed) {
|
|
28171
|
+
updateAttributes({ src: nextSrc });
|
|
28043
28172
|
}
|
|
28044
28173
|
});
|
|
28045
|
-
} catch (
|
|
28046
|
-
|
|
28047
|
-
setUploadError(
|
|
28174
|
+
} catch (error) {
|
|
28175
|
+
if (!active || uploadRequestIdRef.current !== requestId) return;
|
|
28176
|
+
setUploadError(getErrorMessage(error));
|
|
28048
28177
|
uploadAttemptedRef.current = false;
|
|
28049
28178
|
}
|
|
28050
28179
|
};
|
|
28051
28180
|
void runUpload();
|
|
28052
|
-
|
|
28181
|
+
return () => {
|
|
28182
|
+
active = false;
|
|
28183
|
+
};
|
|
28184
|
+
}, [safeSrc, fileName, fileType, editor, updateAttributes, uploadFn, retryToken]);
|
|
28053
28185
|
const handleDownload = (e) => {
|
|
28054
28186
|
e.preventDefault();
|
|
28055
28187
|
e.stopPropagation();
|
|
28056
|
-
if (!
|
|
28188
|
+
if (!safeSrc) return;
|
|
28057
28189
|
const link = document.createElement("a");
|
|
28058
|
-
link.href =
|
|
28190
|
+
link.href = safeSrc;
|
|
28059
28191
|
link.download = fileName || "download";
|
|
28060
28192
|
document.body.appendChild(link);
|
|
28061
28193
|
link.click();
|
|
28062
28194
|
document.body.removeChild(link);
|
|
28063
28195
|
};
|
|
28064
|
-
const
|
|
28196
|
+
const hasInlineUpload = safeSrc.startsWith("data:") && Boolean(uploadFn);
|
|
28065
28197
|
const handleRetryUpload = (event) => {
|
|
28066
28198
|
event.preventDefault();
|
|
28067
28199
|
event.stopPropagation();
|
|
@@ -28084,11 +28216,11 @@ var FileCardView = ({ node, selected, editor, updateAttributes }) => {
|
|
|
28084
28216
|
/* @__PURE__ */ (0, import_jsx_runtime81.jsx)("div", { className: "text-sm font-semibold text-foreground truncate break-all", children: fileName || t("fileCard.unnamed") }),
|
|
28085
28217
|
/* @__PURE__ */ (0, import_jsx_runtime81.jsxs)("div", { className: "text-xs text-muted-foreground flex items-center gap-2 mt-0.5", children: [
|
|
28086
28218
|
fileSize ? /* @__PURE__ */ (0, import_jsx_runtime81.jsx)("span", { children: formatBytes(fileSize) }) : null,
|
|
28087
|
-
|
|
28219
|
+
hasInlineUpload && /* @__PURE__ */ (0, import_jsx_runtime81.jsx)("span", { className: cn(
|
|
28088
28220
|
"font-medium truncate max-w-50",
|
|
28089
28221
|
uploadError ? "text-destructive" : "text-primary animate-pulse"
|
|
28090
28222
|
), children: uploadError ? `${t("fileCard.uploadFailed") || "T\u1EA3i l\xEAn th\u1EA5t b\u1EA1i"}: ${uploadError}` : t("fileCard.uploading") }),
|
|
28091
|
-
|
|
28223
|
+
hasInlineUpload && uploadError && /* @__PURE__ */ (0, import_jsx_runtime81.jsxs)(
|
|
28092
28224
|
"button",
|
|
28093
28225
|
{
|
|
28094
28226
|
type: "button",
|
|
@@ -28103,7 +28235,7 @@ var FileCardView = ({ node, selected, editor, updateAttributes }) => {
|
|
|
28103
28235
|
] })
|
|
28104
28236
|
] })
|
|
28105
28237
|
] }),
|
|
28106
|
-
|
|
28238
|
+
safeSrc && /* @__PURE__ */ (0, import_jsx_runtime81.jsx)(
|
|
28107
28239
|
"button",
|
|
28108
28240
|
{
|
|
28109
28241
|
type: "button",
|
|
@@ -28132,11 +28264,11 @@ var FileCard = import_core3.Node.create({
|
|
|
28132
28264
|
return {
|
|
28133
28265
|
src: {
|
|
28134
28266
|
default: "",
|
|
28135
|
-
parseHTML: (element) => element.getAttribute("href") || element.getAttribute("data-src") || "",
|
|
28136
|
-
renderHTML: (attributes) =>
|
|
28137
|
-
|
|
28138
|
-
"data-src":
|
|
28139
|
-
}
|
|
28267
|
+
parseHTML: (element) => sanitizeUEditorUrl(element.getAttribute("href") || element.getAttribute("data-src") || "", "file"),
|
|
28268
|
+
renderHTML: (attributes) => {
|
|
28269
|
+
const src = sanitizeUEditorUrl(String(attributes.src ?? ""), "file");
|
|
28270
|
+
return src ? { href: src, "data-src": src } : {};
|
|
28271
|
+
}
|
|
28140
28272
|
},
|
|
28141
28273
|
fileName: {
|
|
28142
28274
|
default: "",
|
|
@@ -28148,13 +28280,15 @@ var FileCard = import_core3.Node.create({
|
|
|
28148
28280
|
fileSize: {
|
|
28149
28281
|
default: null,
|
|
28150
28282
|
parseHTML: (element) => {
|
|
28151
|
-
const
|
|
28152
|
-
|
|
28283
|
+
const rawValue = element.getAttribute("data-file-size");
|
|
28284
|
+
const value = rawValue ? Number(rawValue) : Number.NaN;
|
|
28285
|
+
return Number.isFinite(value) && value >= 0 ? value : null;
|
|
28153
28286
|
},
|
|
28154
28287
|
renderHTML: (attributes) => {
|
|
28155
|
-
|
|
28288
|
+
const fileSize = Number(attributes.fileSize);
|
|
28289
|
+
if (!Number.isFinite(fileSize) || fileSize < 0) return {};
|
|
28156
28290
|
return {
|
|
28157
|
-
"data-file-size":
|
|
28291
|
+
"data-file-size": fileSize
|
|
28158
28292
|
};
|
|
28159
28293
|
}
|
|
28160
28294
|
},
|
|
@@ -28189,9 +28323,16 @@ var FileCard = import_core3.Node.create({
|
|
|
28189
28323
|
addCommands() {
|
|
28190
28324
|
return {
|
|
28191
28325
|
setFileCard: (attributes) => ({ commands }) => {
|
|
28326
|
+
const src = sanitizeUEditorUrl(attributes.src, "file");
|
|
28327
|
+
if (!src) return false;
|
|
28328
|
+
const fileSize = Number(attributes.fileSize);
|
|
28192
28329
|
return commands.insertContent({
|
|
28193
28330
|
type: this.name,
|
|
28194
|
-
attrs:
|
|
28331
|
+
attrs: {
|
|
28332
|
+
...attributes,
|
|
28333
|
+
src,
|
|
28334
|
+
fileSize: Number.isFinite(fileSize) && fileSize >= 0 ? fileSize : null
|
|
28335
|
+
}
|
|
28195
28336
|
});
|
|
28196
28337
|
}
|
|
28197
28338
|
};
|
|
@@ -28605,6 +28746,10 @@ function destroyTippyInstance(instance) {
|
|
|
28605
28746
|
if (!instance || instance.state.isDestroyed) return;
|
|
28606
28747
|
instance.destroy();
|
|
28607
28748
|
}
|
|
28749
|
+
function setTippyReferenceClientRect(instance, clientRect) {
|
|
28750
|
+
if (!instance || instance.state.isDestroyed) return;
|
|
28751
|
+
instance.setProps({ getReferenceClientRect: clientRect });
|
|
28752
|
+
}
|
|
28608
28753
|
|
|
28609
28754
|
// src/components/UEditor/slash-command.tsx
|
|
28610
28755
|
var import_jsx_runtime84 = require("react/jsx-runtime");
|
|
@@ -28977,9 +29122,7 @@ var SlashCommand = import_core6.Extension.create({
|
|
|
28977
29122
|
if (!props.clientRect) {
|
|
28978
29123
|
return;
|
|
28979
29124
|
}
|
|
28980
|
-
popup
|
|
28981
|
-
getReferenceClientRect: props.clientRect
|
|
28982
|
-
});
|
|
29125
|
+
setTippyReferenceClientRect(getFirstTippyInstance(popup), props.clientRect);
|
|
28983
29126
|
},
|
|
28984
29127
|
onKeyDown(props) {
|
|
28985
29128
|
if (!props || !props.event) return false;
|
|
@@ -29716,45 +29859,6 @@ function getClipboardTsvTableContent(dataTransfer) {
|
|
|
29716
29859
|
);
|
|
29717
29860
|
}
|
|
29718
29861
|
|
|
29719
|
-
// src/components/UEditor/url-safety.ts
|
|
29720
|
-
var LINK_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:", "mailto:", "tel:"]);
|
|
29721
|
-
var IMAGE_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:"]);
|
|
29722
|
-
function normalizeUrlInput(raw) {
|
|
29723
|
-
return raw.trim().replace(/[\u0000-\u001F\u007F\s]+/g, "");
|
|
29724
|
-
}
|
|
29725
|
-
function isProtocolRelativeUrl(value) {
|
|
29726
|
-
return value.startsWith("//");
|
|
29727
|
-
}
|
|
29728
|
-
function isRelativeUrl(value) {
|
|
29729
|
-
return value.startsWith("/") || value.startsWith("./") || value.startsWith("../") || value.startsWith("#");
|
|
29730
|
-
}
|
|
29731
|
-
function isDataImageUrl(value) {
|
|
29732
|
-
return /^data:image\/(?:png|jpe?g|gif|webp|svg\+xml|bmp|x-icon|avif);base64,/i.test(value);
|
|
29733
|
-
}
|
|
29734
|
-
function isSafeUEditorUrl(raw, kind) {
|
|
29735
|
-
const value = normalizeUrlInput(raw);
|
|
29736
|
-
if (!value) return false;
|
|
29737
|
-
if (kind === "image" && isDataImageUrl(value)) return true;
|
|
29738
|
-
if (isRelativeUrl(value)) return true;
|
|
29739
|
-
if (isProtocolRelativeUrl(value)) return false;
|
|
29740
|
-
try {
|
|
29741
|
-
const parsed = new URL(value);
|
|
29742
|
-
return kind === "image" ? IMAGE_PROTOCOLS.has(parsed.protocol) : LINK_PROTOCOLS.has(parsed.protocol);
|
|
29743
|
-
} catch {
|
|
29744
|
-
return false;
|
|
29745
|
-
}
|
|
29746
|
-
}
|
|
29747
|
-
function sanitizeUEditorUrl(raw, kind) {
|
|
29748
|
-
const value = raw.trim();
|
|
29749
|
-
if (!value) return "";
|
|
29750
|
-
if (isSafeUEditorUrl(value, kind)) return normalizeUrlInput(value);
|
|
29751
|
-
if (kind === "link" && !isProtocolRelativeUrl(value) && !/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(value)) {
|
|
29752
|
-
const withProtocol = `https://${value}`;
|
|
29753
|
-
return isSafeUEditorUrl(withProtocol, kind) ? withProtocol : "";
|
|
29754
|
-
}
|
|
29755
|
-
return "";
|
|
29756
|
-
}
|
|
29757
|
-
|
|
29758
29862
|
// src/components/UEditor/clipboard-images.ts
|
|
29759
29863
|
var DEFAULT_UEDITOR_IMAGE_MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
29760
29864
|
var DEFAULT_UEDITOR_IMAGE_MIME_TYPES = ["image/png", "image/jpeg", "image/webp", "image/gif", "image/svg+xml"];
|
|
@@ -29996,9 +30100,7 @@ var EmojiSuggestion = import_core8.Extension.create({
|
|
|
29996
30100
|
if (!props.clientRect) {
|
|
29997
30101
|
return;
|
|
29998
30102
|
}
|
|
29999
|
-
popup
|
|
30000
|
-
getReferenceClientRect: props.clientRect
|
|
30001
|
-
});
|
|
30103
|
+
setTippyReferenceClientRect(getFirstTippyInstance(popup), props.clientRect);
|
|
30002
30104
|
},
|
|
30003
30105
|
onKeyDown(props) {
|
|
30004
30106
|
if (!props || !props.event) return false;
|
|
@@ -30067,11 +30169,17 @@ function isInTableCell(editor) {
|
|
|
30067
30169
|
}
|
|
30068
30170
|
function buildFormulaSuggestionItems({ query }) {
|
|
30069
30171
|
const normalizedQuery = query.trim().toUpperCase();
|
|
30172
|
+
if (!isFormulaFunctionSuggestionQuery(normalizedQuery)) {
|
|
30173
|
+
return [];
|
|
30174
|
+
}
|
|
30070
30175
|
if (!normalizedQuery) {
|
|
30071
30176
|
return FORMULA_FUNCTIONS;
|
|
30072
30177
|
}
|
|
30073
30178
|
return FORMULA_FUNCTIONS.filter((item) => item.name.startsWith(normalizedQuery));
|
|
30074
30179
|
}
|
|
30180
|
+
function isFormulaFunctionSuggestionQuery(query) {
|
|
30181
|
+
return /^[A-Z]*$/i.test(query.trim());
|
|
30182
|
+
}
|
|
30075
30183
|
var FormulaSuggestionList = (0, import_react62.forwardRef)((props, ref) => {
|
|
30076
30184
|
const t = useSmartTranslations("UEditor");
|
|
30077
30185
|
const [selectedIndex, setSelectedIndex] = import_react62.default.useState(0);
|
|
@@ -30089,7 +30197,7 @@ var FormulaSuggestionList = (0, import_react62.forwardRef)((props, ref) => {
|
|
|
30089
30197
|
setSelectedIndex((prev) => (prev + 1) % props.items.length);
|
|
30090
30198
|
return true;
|
|
30091
30199
|
}
|
|
30092
|
-
if (event.key === "
|
|
30200
|
+
if (event.key === "Tab") {
|
|
30093
30201
|
const item = props.items[selectedIndex];
|
|
30094
30202
|
if (item) {
|
|
30095
30203
|
props.command(item);
|
|
@@ -30142,7 +30250,11 @@ var FormulaSuggestion = import_core9.Extension.create({
|
|
|
30142
30250
|
editor: this.editor,
|
|
30143
30251
|
char: "=",
|
|
30144
30252
|
pluginKey: new import_state4.PluginKey("formulaSuggestion"),
|
|
30145
|
-
allow: ({ editor }) =>
|
|
30253
|
+
allow: ({ editor, range }) => {
|
|
30254
|
+
if (!isInTableCell(editor)) return false;
|
|
30255
|
+
const suggestionText = editor.state.doc.textBetween(range.from, range.to, "", "");
|
|
30256
|
+
return suggestionText.startsWith("=") && isFormulaFunctionSuggestionQuery(suggestionText.slice(1));
|
|
30257
|
+
},
|
|
30146
30258
|
command: ({ editor, range, props }) => {
|
|
30147
30259
|
insertFormulaFunction(editor, range, props);
|
|
30148
30260
|
},
|
|
@@ -30175,9 +30287,7 @@ var FormulaSuggestion = import_core9.Extension.create({
|
|
|
30175
30287
|
if (!props.clientRect) {
|
|
30176
30288
|
return;
|
|
30177
30289
|
}
|
|
30178
|
-
popup
|
|
30179
|
-
getReferenceClientRect: props.clientRect
|
|
30180
|
-
});
|
|
30290
|
+
setTippyReferenceClientRect(getFirstTippyInstance(popup), props.clientRect);
|
|
30181
30291
|
},
|
|
30182
30292
|
onKeyDown(props) {
|
|
30183
30293
|
const popupInstance = getFirstTippyInstance(popup);
|
|
@@ -31919,7 +32029,6 @@ function buildUEditorExtensions({
|
|
|
31919
32029
|
import_extension_gapcursor.default,
|
|
31920
32030
|
import_extension_link.default.configure({
|
|
31921
32031
|
openOnClick: false,
|
|
31922
|
-
protocols: ["http", "https", "mailto", "tel"],
|
|
31923
32032
|
isAllowedUri: (url) => isSafeUEditorUrl(url ?? "", "link"),
|
|
31924
32033
|
HTMLAttributes: {
|
|
31925
32034
|
class: "text-primary underline underline-offset-2 hover:text-primary/80 cursor-pointer"
|
|
@@ -32971,7 +33080,7 @@ var EditorToolbar = ({
|
|
|
32971
33080
|
const [showImageInput, setShowImageInput] = (0, import_react70.useState)(false);
|
|
32972
33081
|
const [showLinkInput, setShowLinkInput] = (0, import_react70.useState)(false);
|
|
32973
33082
|
const [isTableMenuOpen, setIsTableMenuOpen] = (0, import_react70.useState)(false);
|
|
32974
|
-
const
|
|
33083
|
+
const [preservedTableAnchorPos, setPreservedTableAnchorPos] = (0, import_react70.useState)(null);
|
|
32975
33084
|
const fileInputRef = (0, import_react70.useRef)(null);
|
|
32976
33085
|
const [isUploadingImage, setIsUploadingImage] = (0, import_react70.useState)(false);
|
|
32977
33086
|
const [imageUploadError, setImageUploadError] = (0, import_react70.useState)(null);
|
|
@@ -32986,7 +33095,7 @@ var EditorToolbar = ({
|
|
|
32986
33095
|
const tableAlignAttr = tableAttrs.tableAlign ?? tableAttrs.textAlign;
|
|
32987
33096
|
const currentTableAlign = tableAlignAttr === "center" || tableAlignAttr === "right" ? tableAlignAttr : "left";
|
|
32988
33097
|
const isTableSelected = tableInfo !== null;
|
|
32989
|
-
const hasTableContext = isTableSelected ||
|
|
33098
|
+
const hasTableContext = isTableSelected || preservedTableAnchorPos !== null;
|
|
32990
33099
|
const canMergeCells = hasTableContext && editor.can().mergeCells();
|
|
32991
33100
|
const canSplitCell = hasTableContext && editor.can().splitCell();
|
|
32992
33101
|
const currentCellVerticalAlign = normalizeStyleValue(editor.getAttributes("tableCell").verticalAlign || editor.getAttributes("tableHeader").verticalAlign) || "";
|
|
@@ -33011,7 +33120,7 @@ var EditorToolbar = ({
|
|
|
33011
33120
|
const displayedFontFamilyValue = currentFontFamily || defaultFontFamilyValue;
|
|
33012
33121
|
const displayedFontSizeLabel = currentFontSize ? currentFontSizeLabel : "13";
|
|
33013
33122
|
const activeFontSize = currentFontSize || "13px";
|
|
33014
|
-
const tableCommandAnchorPos =
|
|
33123
|
+
const tableCommandAnchorPos = preservedTableAnchorPos ?? tableAnchorPos ?? void 0;
|
|
33015
33124
|
const isMedium = variant === "medium";
|
|
33016
33125
|
const isMediumFull = variant === "medium-full";
|
|
33017
33126
|
const isFull = variant === "default" || variant === "full" || variant === "notion" || !variant;
|
|
@@ -33687,7 +33796,7 @@ var EditorToolbar = ({
|
|
|
33687
33796
|
isOpen: isTableMenuOpen,
|
|
33688
33797
|
onOpenChange: (open) => {
|
|
33689
33798
|
setIsTableMenuOpen(open);
|
|
33690
|
-
|
|
33799
|
+
setPreservedTableAnchorPos(open ? getTableAnchorPos(editor) : null);
|
|
33691
33800
|
},
|
|
33692
33801
|
trigger: /* @__PURE__ */ (0, import_jsx_runtime91.jsxs)(ToolbarButton, { onClick: () => {
|
|
33693
33802
|
}, title: t("toolbar.table"), children: [
|
|
@@ -33704,6 +33813,7 @@ var EditorToolbar = ({
|
|
|
33704
33813
|
onInsert: (rows, cols) => {
|
|
33705
33814
|
editor.chain().focus().insertTable({ rows, cols, withHeaderRow: true }).run();
|
|
33706
33815
|
setIsTableMenuOpen(false);
|
|
33816
|
+
setPreservedTableAnchorPos(null);
|
|
33707
33817
|
}
|
|
33708
33818
|
}
|
|
33709
33819
|
),
|
|
@@ -33894,6 +34004,7 @@ var import_react_dom8 = require("react-dom");
|
|
|
33894
34004
|
var import_lucide_react54 = require("lucide-react");
|
|
33895
34005
|
|
|
33896
34006
|
// src/components/UEditor/table-formula-commands.ts
|
|
34007
|
+
var import_state10 = require("@tiptap/pm/state");
|
|
33897
34008
|
var import_tables5 = require("@tiptap/pm/tables");
|
|
33898
34009
|
|
|
33899
34010
|
// src/components/UEditor/table-formula.ts
|
|
@@ -34071,51 +34182,98 @@ function buildTableFormulaDependencyGraph(cells) {
|
|
|
34071
34182
|
return { dependencies, dependents, formulas };
|
|
34072
34183
|
}
|
|
34073
34184
|
function getTableFormulaCircularReferences(graph) {
|
|
34074
|
-
const visiting = /* @__PURE__ */ new Set();
|
|
34075
|
-
const visited = /* @__PURE__ */ new Set();
|
|
34076
34185
|
const circular = /* @__PURE__ */ new Set();
|
|
34077
|
-
const
|
|
34078
|
-
const
|
|
34079
|
-
|
|
34080
|
-
|
|
34081
|
-
|
|
34082
|
-
|
|
34186
|
+
const visited = /* @__PURE__ */ new Set();
|
|
34187
|
+
const finishOrder = [];
|
|
34188
|
+
for (const startLabel of graph.formulas.keys()) {
|
|
34189
|
+
if (visited.has(startLabel)) continue;
|
|
34190
|
+
visited.add(startLabel);
|
|
34191
|
+
const stack = [
|
|
34192
|
+
{
|
|
34193
|
+
label: startLabel,
|
|
34194
|
+
references: Array.from(graph.dependencies.get(startLabel) ?? []).filter((ref) => graph.formulas.has(ref)),
|
|
34195
|
+
nextIndex: 0
|
|
34083
34196
|
}
|
|
34084
|
-
|
|
34085
|
-
|
|
34086
|
-
|
|
34087
|
-
|
|
34088
|
-
|
|
34089
|
-
|
|
34090
|
-
|
|
34091
|
-
|
|
34197
|
+
];
|
|
34198
|
+
while (stack.length > 0) {
|
|
34199
|
+
const frame = stack[stack.length - 1];
|
|
34200
|
+
if (!frame) break;
|
|
34201
|
+
const reference = frame.references[frame.nextIndex];
|
|
34202
|
+
if (reference) {
|
|
34203
|
+
frame.nextIndex += 1;
|
|
34204
|
+
if (!visited.has(reference)) {
|
|
34205
|
+
visited.add(reference);
|
|
34206
|
+
stack.push({
|
|
34207
|
+
label: reference,
|
|
34208
|
+
references: Array.from(graph.dependencies.get(reference) ?? []).filter((ref) => graph.formulas.has(ref)),
|
|
34209
|
+
nextIndex: 0
|
|
34210
|
+
});
|
|
34211
|
+
}
|
|
34212
|
+
continue;
|
|
34092
34213
|
}
|
|
34214
|
+
stack.pop();
|
|
34215
|
+
finishOrder.push(frame.label);
|
|
34216
|
+
}
|
|
34217
|
+
}
|
|
34218
|
+
const assigned = /* @__PURE__ */ new Set();
|
|
34219
|
+
for (let index = finishOrder.length - 1; index >= 0; index -= 1) {
|
|
34220
|
+
const startLabel = finishOrder[index];
|
|
34221
|
+
if (!startLabel || assigned.has(startLabel)) continue;
|
|
34222
|
+
const component = [];
|
|
34223
|
+
const stack = [startLabel];
|
|
34224
|
+
assigned.add(startLabel);
|
|
34225
|
+
while (stack.length > 0) {
|
|
34226
|
+
const label = stack.pop();
|
|
34227
|
+
if (!label) continue;
|
|
34228
|
+
component.push(label);
|
|
34229
|
+
for (const dependent of graph.dependents.get(label) ?? []) {
|
|
34230
|
+
if (!graph.formulas.has(dependent) || assigned.has(dependent)) continue;
|
|
34231
|
+
assigned.add(dependent);
|
|
34232
|
+
stack.push(dependent);
|
|
34233
|
+
}
|
|
34234
|
+
}
|
|
34235
|
+
if (component.length > 1) {
|
|
34236
|
+
for (const label of component) circular.add(label);
|
|
34237
|
+
} else {
|
|
34238
|
+
const label = component[0];
|
|
34239
|
+
if (label && graph.dependencies.get(label)?.has(label)) circular.add(label);
|
|
34093
34240
|
}
|
|
34094
|
-
stack.pop();
|
|
34095
|
-
visiting.delete(label);
|
|
34096
|
-
visited.add(label);
|
|
34097
|
-
};
|
|
34098
|
-
for (const label of graph.formulas.keys()) {
|
|
34099
|
-
visit(label);
|
|
34100
34241
|
}
|
|
34101
34242
|
return circular;
|
|
34102
34243
|
}
|
|
34103
34244
|
function getTableFormulaRecalculationOrder(graph) {
|
|
34104
34245
|
const circular = getTableFormulaCircularReferences(graph);
|
|
34246
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
34105
34247
|
const visited = /* @__PURE__ */ new Set();
|
|
34106
34248
|
const order = [];
|
|
34107
|
-
const
|
|
34108
|
-
if (visited.has(
|
|
34109
|
-
|
|
34110
|
-
|
|
34111
|
-
|
|
34112
|
-
|
|
34249
|
+
for (const startLabel of graph.formulas.keys()) {
|
|
34250
|
+
if (visited.has(startLabel) || circular.has(startLabel)) continue;
|
|
34251
|
+
const stack = [];
|
|
34252
|
+
const push = (label) => {
|
|
34253
|
+
visiting.add(label);
|
|
34254
|
+
stack.push({
|
|
34255
|
+
label,
|
|
34256
|
+
references: Array.from(graph.dependencies.get(label) ?? []).filter((ref) => graph.formulas.has(ref) && !circular.has(ref)),
|
|
34257
|
+
nextIndex: 0
|
|
34258
|
+
});
|
|
34259
|
+
};
|
|
34260
|
+
push(startLabel);
|
|
34261
|
+
while (stack.length > 0) {
|
|
34262
|
+
const frame = stack[stack.length - 1];
|
|
34263
|
+
if (!frame) break;
|
|
34264
|
+
const reference = frame.references[frame.nextIndex];
|
|
34265
|
+
if (reference) {
|
|
34266
|
+
frame.nextIndex += 1;
|
|
34267
|
+
if (!visited.has(reference) && !visiting.has(reference)) {
|
|
34268
|
+
push(reference);
|
|
34269
|
+
}
|
|
34270
|
+
continue;
|
|
34113
34271
|
}
|
|
34272
|
+
stack.pop();
|
|
34273
|
+
visiting.delete(frame.label);
|
|
34274
|
+
visited.add(frame.label);
|
|
34275
|
+
order.push(frame.label);
|
|
34114
34276
|
}
|
|
34115
|
-
order.push(label);
|
|
34116
|
-
};
|
|
34117
|
-
for (const label of graph.formulas.keys()) {
|
|
34118
|
-
visit(label);
|
|
34119
34277
|
}
|
|
34120
34278
|
return { order, circular };
|
|
34121
34279
|
}
|
|
@@ -34174,9 +34332,11 @@ function tokenizeFormula(formula) {
|
|
|
34174
34332
|
index += 1;
|
|
34175
34333
|
continue;
|
|
34176
34334
|
}
|
|
34177
|
-
const numberMatch = formula.slice(index).match(
|
|
34335
|
+
const numberMatch = formula.slice(index).match(/^(?:\d+(?:\.\d*)?|\.\d+)/);
|
|
34178
34336
|
if (numberMatch?.[0]) {
|
|
34179
|
-
|
|
34337
|
+
const value = Number.parseFloat(numberMatch[0]);
|
|
34338
|
+
if (!Number.isFinite(value)) return null;
|
|
34339
|
+
tokens.push({ type: "number", value });
|
|
34180
34340
|
index += numberMatch[0].length;
|
|
34181
34341
|
continue;
|
|
34182
34342
|
}
|
|
@@ -34194,6 +34354,28 @@ function tokenizeFormula(formula) {
|
|
|
34194
34354
|
}
|
|
34195
34355
|
return tokens;
|
|
34196
34356
|
}
|
|
34357
|
+
function toFiniteFormulaResult(value) {
|
|
34358
|
+
return Number.isFinite(value) ? { value, error: null } : { value: null, error: "invalid-formula" };
|
|
34359
|
+
}
|
|
34360
|
+
function parseTableCellNumericValue(value) {
|
|
34361
|
+
if (typeof value === "number") {
|
|
34362
|
+
return Number.isFinite(value) ? value : null;
|
|
34363
|
+
}
|
|
34364
|
+
let normalized = String(value ?? "").trim();
|
|
34365
|
+
if (!normalized || normalized.startsWith("#")) return null;
|
|
34366
|
+
const isPercent = normalized.endsWith("%");
|
|
34367
|
+
if (isPercent) normalized = normalized.slice(0, -1).trim();
|
|
34368
|
+
const currencyMatch = normalized.match(/^([+-]?)\$(.+)$/);
|
|
34369
|
+
if (currencyMatch) {
|
|
34370
|
+
normalized = `${currencyMatch[1] ?? ""}${currencyMatch[2] ?? ""}`.trim();
|
|
34371
|
+
}
|
|
34372
|
+
const groupedNumber = /^[+-]?\d{1,3}(?:,\d{3})+(?:\.\d*)?(?:[eE][+-]?\d+)?$/;
|
|
34373
|
+
const plainNumber = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/;
|
|
34374
|
+
if (!groupedNumber.test(normalized) && !plainNumber.test(normalized)) return null;
|
|
34375
|
+
const parsed = Number(normalized.replace(/,/g, ""));
|
|
34376
|
+
if (!Number.isFinite(parsed)) return null;
|
|
34377
|
+
return isPercent ? parsed / 100 : parsed;
|
|
34378
|
+
}
|
|
34197
34379
|
var FormulaParser = class {
|
|
34198
34380
|
constructor(tokens, getCellValue) {
|
|
34199
34381
|
this.tokens = tokens;
|
|
@@ -34211,10 +34393,7 @@ var FormulaParser = class {
|
|
|
34211
34393
|
this.index += 1;
|
|
34212
34394
|
const right = this.parseTerm();
|
|
34213
34395
|
if (right.error) return right;
|
|
34214
|
-
left =
|
|
34215
|
-
value: operator.value === "+" ? left.value + right.value : left.value - right.value,
|
|
34216
|
-
error: null
|
|
34217
|
-
};
|
|
34396
|
+
left = toFiniteFormulaResult(operator.value === "+" ? left.value + right.value : left.value - right.value);
|
|
34218
34397
|
}
|
|
34219
34398
|
return left;
|
|
34220
34399
|
}
|
|
@@ -34229,10 +34408,7 @@ var FormulaParser = class {
|
|
|
34229
34408
|
if (operator.value === "/" && right.value === 0) {
|
|
34230
34409
|
return { value: null, error: "division-by-zero" };
|
|
34231
34410
|
}
|
|
34232
|
-
left =
|
|
34233
|
-
value: operator.value === "*" ? left.value * right.value : left.value / right.value,
|
|
34234
|
-
error: null
|
|
34235
|
-
};
|
|
34411
|
+
left = toFiniteFormulaResult(operator.value === "*" ? left.value * right.value : left.value / right.value);
|
|
34236
34412
|
}
|
|
34237
34413
|
return left;
|
|
34238
34414
|
}
|
|
@@ -34241,11 +34417,11 @@ var FormulaParser = class {
|
|
|
34241
34417
|
if (!token) {
|
|
34242
34418
|
return { value: null, error: "invalid-formula" };
|
|
34243
34419
|
}
|
|
34244
|
-
if (token.type === "operator" && token.value === "-") {
|
|
34420
|
+
if (token.type === "operator" && (token.value === "-" || token.value === "+")) {
|
|
34245
34421
|
this.index += 1;
|
|
34246
34422
|
const value = this.parseFactor();
|
|
34247
34423
|
if (value.error) return value;
|
|
34248
|
-
return
|
|
34424
|
+
return toFiniteFormulaResult(token.value === "-" ? -value.value : value.value);
|
|
34249
34425
|
}
|
|
34250
34426
|
if (token.type === "number") {
|
|
34251
34427
|
this.index += 1;
|
|
@@ -34290,11 +34466,10 @@ var FormulaParser = class {
|
|
|
34290
34466
|
if (cellValue2 != null) values.push(cellValue2);
|
|
34291
34467
|
continue;
|
|
34292
34468
|
}
|
|
34293
|
-
const cellValue = this.
|
|
34294
|
-
if (cellValue
|
|
34295
|
-
values.push(cellValue.value);
|
|
34469
|
+
const cellValue = this.readOptionalCellNumber(label);
|
|
34470
|
+
if (cellValue != null) values.push(cellValue);
|
|
34296
34471
|
}
|
|
34297
|
-
} else if (
|
|
34472
|
+
} else if (token.type === "cell") {
|
|
34298
34473
|
this.index += 1;
|
|
34299
34474
|
const cellValue = this.readOptionalCellNumber(token.value);
|
|
34300
34475
|
if (cellValue != null) values.push(cellValue);
|
|
@@ -34311,28 +34486,24 @@ var FormulaParser = class {
|
|
|
34311
34486
|
}
|
|
34312
34487
|
return { value: null, error: "invalid-formula" };
|
|
34313
34488
|
}
|
|
34314
|
-
if (values.
|
|
34315
|
-
|
|
34489
|
+
if (name === "SUM") return toFiniteFormulaResult(values.reduce((sum, value) => sum + value, 0));
|
|
34490
|
+
if (name === "AVG") {
|
|
34491
|
+
return values.length > 0 ? toFiniteFormulaResult(values.reduce((sum, value) => sum + value, 0) / values.length) : { value: null, error: "division-by-zero" };
|
|
34316
34492
|
}
|
|
34317
|
-
if (name === "
|
|
34318
|
-
if (name === "
|
|
34319
|
-
if (name === "MIN") return { value: Math.min(...values), error: null };
|
|
34320
|
-
if (name === "MAX") return { value: Math.max(...values), error: null };
|
|
34493
|
+
if (name === "MIN") return toFiniteFormulaResult(values.length > 0 ? Math.min(...values) : 0);
|
|
34494
|
+
if (name === "MAX") return toFiniteFormulaResult(values.length > 0 ? Math.max(...values) : 0);
|
|
34321
34495
|
if (name === "COUNT") return { value: values.length, error: null };
|
|
34322
34496
|
return { value: null, error: "invalid-formula" };
|
|
34323
34497
|
}
|
|
34324
34498
|
readCellNumber(label) {
|
|
34325
|
-
const
|
|
34326
|
-
|
|
34327
|
-
if (!Number.isFinite(parsed)) {
|
|
34499
|
+
const parsed = parseTableCellNumericValue(this.getCellValue(label));
|
|
34500
|
+
if (parsed == null) {
|
|
34328
34501
|
return { value: null, error: "invalid-reference" };
|
|
34329
34502
|
}
|
|
34330
34503
|
return { value: parsed, error: null };
|
|
34331
34504
|
}
|
|
34332
34505
|
readOptionalCellNumber(label) {
|
|
34333
|
-
|
|
34334
|
-
const parsed = typeof value === "number" ? value : Number.parseFloat(String(value ?? "").trim());
|
|
34335
|
-
return Number.isFinite(parsed) ? parsed : null;
|
|
34506
|
+
return parseTableCellNumericValue(this.getCellValue(label));
|
|
34336
34507
|
}
|
|
34337
34508
|
peekOperator(operators) {
|
|
34338
34509
|
const token = this.tokens[this.index];
|
|
@@ -34397,6 +34568,75 @@ function getSelectionTableCellNode(editor) {
|
|
|
34397
34568
|
}
|
|
34398
34569
|
return null;
|
|
34399
34570
|
}
|
|
34571
|
+
function getSelectionTableCellPos(editor) {
|
|
34572
|
+
const cellSelection = editor.state.selection;
|
|
34573
|
+
const anchorCellPos = cellSelection.$anchorCell?.pos;
|
|
34574
|
+
if (typeof anchorCellPos === "number") {
|
|
34575
|
+
const node = editor.state.doc.nodeAt(anchorCellPos);
|
|
34576
|
+
if (node?.type.name === "tableCell" || node?.type.name === "tableHeader") {
|
|
34577
|
+
return anchorCellPos;
|
|
34578
|
+
}
|
|
34579
|
+
}
|
|
34580
|
+
const { $from } = editor.state.selection;
|
|
34581
|
+
for (let depth = $from.depth; depth > 0; depth -= 1) {
|
|
34582
|
+
const node = $from.node(depth);
|
|
34583
|
+
if (node.type.name === "tableCell" || node.type.name === "tableHeader") {
|
|
34584
|
+
return $from.before(depth);
|
|
34585
|
+
}
|
|
34586
|
+
}
|
|
34587
|
+
return null;
|
|
34588
|
+
}
|
|
34589
|
+
function getSelectionTableCellAnchor(editor) {
|
|
34590
|
+
const cellPos = getSelectionTableCellPos(editor);
|
|
34591
|
+
if (cellPos == null) return null;
|
|
34592
|
+
const node = editor.state.doc.nodeAt(cellPos);
|
|
34593
|
+
if (!node || node.type.name !== "tableCell" && node.type.name !== "tableHeader") return null;
|
|
34594
|
+
const $cell = editor.state.doc.resolve(Math.min(cellPos + 1, editor.state.doc.content.size));
|
|
34595
|
+
for (let depth = $cell.depth; depth > 0; depth -= 1) {
|
|
34596
|
+
const tableNode = $cell.node(depth);
|
|
34597
|
+
if (tableNode.type.name !== "table") continue;
|
|
34598
|
+
const tableStart = $cell.start(depth);
|
|
34599
|
+
const rect = safeFindCell2(import_tables5.TableMap.get(tableNode), cellPos - tableStart);
|
|
34600
|
+
if (!rect) return null;
|
|
34601
|
+
return {
|
|
34602
|
+
cellPos,
|
|
34603
|
+
column: rect.left,
|
|
34604
|
+
label: `${indexToColumnName(rect.left)}${rect.top + 1}`,
|
|
34605
|
+
node,
|
|
34606
|
+
row: rect.top,
|
|
34607
|
+
tablePos: tableStart - 1
|
|
34608
|
+
};
|
|
34609
|
+
}
|
|
34610
|
+
return null;
|
|
34611
|
+
}
|
|
34612
|
+
function restoreSelectionAtTableCell(editor, anchor) {
|
|
34613
|
+
const tableNode = editor.state.doc.nodeAt(anchor.tablePos);
|
|
34614
|
+
if (!tableNode || tableNode.type.name !== "table") return false;
|
|
34615
|
+
const map = import_tables5.TableMap.get(tableNode);
|
|
34616
|
+
if (anchor.row >= map.height || anchor.column >= map.width) return false;
|
|
34617
|
+
const relativeCellPos = map.positionAt(anchor.row, anchor.column, tableNode);
|
|
34618
|
+
const cellPos = anchor.tablePos + 1 + relativeCellPos;
|
|
34619
|
+
const cellNode = editor.state.doc.nodeAt(cellPos);
|
|
34620
|
+
if (!cellNode || cellNode.type.name !== "tableCell" && cellNode.type.name !== "tableHeader") return false;
|
|
34621
|
+
const selectionPos = Math.min(cellPos + 2, editor.state.doc.content.size);
|
|
34622
|
+
editor.view.dispatch(
|
|
34623
|
+
editor.state.tr.setSelection(import_state10.TextSelection.near(editor.state.doc.resolve(selectionPos))).setMeta("addToHistory", false)
|
|
34624
|
+
);
|
|
34625
|
+
return true;
|
|
34626
|
+
}
|
|
34627
|
+
function getSelectedTableFormulaCell(editor) {
|
|
34628
|
+
const selectedCell = getSelectionTableCellAnchor(editor);
|
|
34629
|
+
if (!selectedCell) return null;
|
|
34630
|
+
const formula = typeof selectedCell.node.attrs.formula === "string" ? selectedCell.node.attrs.formula.trim() : "";
|
|
34631
|
+
if (!formula) return null;
|
|
34632
|
+
const computedValue = typeof selectedCell.node.attrs.computedValue === "string" ? selectedCell.node.attrs.computedValue : "";
|
|
34633
|
+
return {
|
|
34634
|
+
...selectedCell,
|
|
34635
|
+
computedValue,
|
|
34636
|
+
formula,
|
|
34637
|
+
formulaState: computedValue.startsWith("#") ? "error" : "computed"
|
|
34638
|
+
};
|
|
34639
|
+
}
|
|
34400
34640
|
function getSelectionTableInfo(editor) {
|
|
34401
34641
|
const { $from } = editor.state.selection;
|
|
34402
34642
|
for (let depth = $from.depth; depth > 0; depth -= 1) {
|
|
@@ -34443,6 +34683,28 @@ function createCellDisplayContent(cellNode, displayValue) {
|
|
|
34443
34683
|
}
|
|
34444
34684
|
return [paragraphType.create(null, cellNode.type.schema.text(displayValue))];
|
|
34445
34685
|
}
|
|
34686
|
+
function removeSelectedTableCellFormula(editor, clearContent) {
|
|
34687
|
+
const selectedCell = getSelectedTableFormulaCell(editor);
|
|
34688
|
+
if (!selectedCell) return false;
|
|
34689
|
+
const nextCell2 = selectedCell.node.type.create(
|
|
34690
|
+
{
|
|
34691
|
+
...selectedCell.node.attrs,
|
|
34692
|
+
formula: null,
|
|
34693
|
+
computedValue: null
|
|
34694
|
+
},
|
|
34695
|
+
clearContent ? createCellDisplayContent(selectedCell.node, "") : selectedCell.node.content,
|
|
34696
|
+
selectedCell.node.marks
|
|
34697
|
+
);
|
|
34698
|
+
let transaction = editor.state.tr.replaceWith(selectedCell.cellPos, selectedCell.cellPos + selectedCell.node.nodeSize, nextCell2).setMeta(UEDITOR_TABLE_FORMULA_RECALCULATE_META, true);
|
|
34699
|
+
const selectionPos = Math.min(selectedCell.cellPos + 2, transaction.doc.content.size);
|
|
34700
|
+
transaction = transaction.setSelection(import_state10.TextSelection.near(transaction.doc.resolve(selectionPos)));
|
|
34701
|
+
editor.view.dispatch(transaction);
|
|
34702
|
+
recalculateActiveTableFormulas(editor);
|
|
34703
|
+
restoreSelectionAtTableCell(editor, selectedCell);
|
|
34704
|
+
editor.view.focus();
|
|
34705
|
+
dispatchTableLayoutChange(editor);
|
|
34706
|
+
return true;
|
|
34707
|
+
}
|
|
34446
34708
|
function buildTableValueMap(tableNode) {
|
|
34447
34709
|
const map = import_tables5.TableMap.get(tableNode);
|
|
34448
34710
|
const values = /* @__PURE__ */ new Map();
|
|
@@ -34465,21 +34727,15 @@ function normalizeFormulaInput(formula) {
|
|
|
34465
34727
|
function setSelectedTableCellFormula(editor, formula) {
|
|
34466
34728
|
const normalized = normalizeFormulaInput(formula);
|
|
34467
34729
|
const { state, view } = editor;
|
|
34730
|
+
const selectionAnchor = getSelectionTableCellAnchor(editor);
|
|
34468
34731
|
if (!normalized) {
|
|
34469
|
-
|
|
34470
|
-
const clearedValue2 = (0, import_tables5.setCellAttr)("computedValue", null)(editor.state, view.dispatch.bind(view));
|
|
34471
|
-
if (clearedFormula || clearedValue2) {
|
|
34472
|
-
recalculateActiveTableFormulas(editor);
|
|
34473
|
-
view.focus();
|
|
34474
|
-
dispatchTableLayoutChange(editor);
|
|
34475
|
-
return true;
|
|
34476
|
-
}
|
|
34477
|
-
return false;
|
|
34732
|
+
return clearSelectedTableCellFormula(editor);
|
|
34478
34733
|
}
|
|
34479
34734
|
const appliedFormula = (0, import_tables5.setCellAttr)("formula", normalized)(state, view.dispatch.bind(view));
|
|
34480
34735
|
const clearedValue = (0, import_tables5.setCellAttr)("computedValue", null)(editor.state, view.dispatch.bind(view));
|
|
34481
34736
|
if (appliedFormula || clearedValue) {
|
|
34482
34737
|
recalculateActiveTableFormulas(editor);
|
|
34738
|
+
if (selectionAnchor) restoreSelectionAtTableCell(editor, selectionAnchor);
|
|
34483
34739
|
view.focus();
|
|
34484
34740
|
dispatchTableLayoutChange(editor);
|
|
34485
34741
|
return true;
|
|
@@ -34487,7 +34743,10 @@ function setSelectedTableCellFormula(editor, formula) {
|
|
|
34487
34743
|
return false;
|
|
34488
34744
|
}
|
|
34489
34745
|
function clearSelectedTableCellFormula(editor) {
|
|
34490
|
-
return
|
|
34746
|
+
return removeSelectedTableCellFormula(editor, true);
|
|
34747
|
+
}
|
|
34748
|
+
function convertSelectedTableCellFormulaToValue(editor) {
|
|
34749
|
+
return removeSelectedTableCellFormula(editor, false);
|
|
34491
34750
|
}
|
|
34492
34751
|
function setSelectedTableCellNumberFormat(editor, numberFormat) {
|
|
34493
34752
|
const value = numberFormat && numberFormat !== "text" ? numberFormat : null;
|
|
@@ -34687,6 +34946,58 @@ function recalculateAllTableFormulas(editor) {
|
|
|
34687
34946
|
|
|
34688
34947
|
// src/components/UEditor/menus.tsx
|
|
34689
34948
|
var import_jsx_runtime92 = require("react/jsx-runtime");
|
|
34949
|
+
var FloatingSlashCommandMenu = ({ editor, onClose }) => {
|
|
34950
|
+
const t = useSmartTranslations("UEditor");
|
|
34951
|
+
const messages = (0, import_react72.useMemo)(() => buildSlashCommandMessages(t), [t]);
|
|
34952
|
+
const items = (0, import_react72.useMemo)(() => buildSlashCommandItems({ query: "", messages }), [messages]);
|
|
34953
|
+
const listRef = (0, import_react72.useRef)(null);
|
|
34954
|
+
(0, import_react72.useEffect)(() => {
|
|
34955
|
+
const handleKeyDown2 = (event) => {
|
|
34956
|
+
if (event.key === "Escape") {
|
|
34957
|
+
event.preventDefault();
|
|
34958
|
+
onClose();
|
|
34959
|
+
return;
|
|
34960
|
+
}
|
|
34961
|
+
const handled = listRef.current?.onKeyDown({ event }) ?? false;
|
|
34962
|
+
if (handled) {
|
|
34963
|
+
event.preventDefault();
|
|
34964
|
+
}
|
|
34965
|
+
};
|
|
34966
|
+
document.addEventListener("keydown", handleKeyDown2);
|
|
34967
|
+
return () => document.removeEventListener("keydown", handleKeyDown2);
|
|
34968
|
+
}, [onClose]);
|
|
34969
|
+
return /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(
|
|
34970
|
+
SlashCommandList,
|
|
34971
|
+
{
|
|
34972
|
+
ref: listRef,
|
|
34973
|
+
items,
|
|
34974
|
+
messages,
|
|
34975
|
+
command: (item) => {
|
|
34976
|
+
item.command({ editor });
|
|
34977
|
+
onClose();
|
|
34978
|
+
}
|
|
34979
|
+
}
|
|
34980
|
+
);
|
|
34981
|
+
};
|
|
34982
|
+
var FloatingMenuContent = ({ editor }) => {
|
|
34983
|
+
const t = useSmartTranslations("UEditor");
|
|
34984
|
+
const [showCommands, setShowCommands] = (0, import_react72.useState)(false);
|
|
34985
|
+
if (showCommands) {
|
|
34986
|
+
return /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(FloatingSlashCommandMenu, { editor, onClose: () => setShowCommands(false) });
|
|
34987
|
+
}
|
|
34988
|
+
return /* @__PURE__ */ (0, import_jsx_runtime92.jsxs)(
|
|
34989
|
+
"button",
|
|
34990
|
+
{
|
|
34991
|
+
type: "button",
|
|
34992
|
+
onClick: () => setShowCommands(true),
|
|
34993
|
+
className: "flex items-center gap-1 px-2 py-1.5 rounded-lg hover:bg-accent transition-all group",
|
|
34994
|
+
children: [
|
|
34995
|
+
/* @__PURE__ */ (0, import_jsx_runtime92.jsx)(import_lucide_react54.Plus, { className: "w-4 h-4 text-muted-foreground group-hover:text-foreground" }),
|
|
34996
|
+
/* @__PURE__ */ (0, import_jsx_runtime92.jsx)("span", { className: "text-sm text-muted-foreground group-hover:text-foreground", children: t("floatingMenu.addBlock") })
|
|
34997
|
+
]
|
|
34998
|
+
}
|
|
34999
|
+
);
|
|
35000
|
+
};
|
|
34690
35001
|
function applyTableCellBackground(editor, color) {
|
|
34691
35002
|
const value = color || null;
|
|
34692
35003
|
const { state, view } = editor;
|
|
@@ -35425,7 +35736,8 @@ var LinkPreviewContent = ({ editor, onEdit }) => {
|
|
|
35425
35736
|
const url = editor.getAttributes("link").href || "";
|
|
35426
35737
|
const t = useSmartTranslations("UEditor");
|
|
35427
35738
|
const handleOpen = () => {
|
|
35428
|
-
|
|
35739
|
+
const safeUrl = sanitizeUEditorUrl(url, "link");
|
|
35740
|
+
if (safeUrl) window.open(safeUrl, "_blank", "noopener,noreferrer");
|
|
35429
35741
|
};
|
|
35430
35742
|
const handleUnlink = () => {
|
|
35431
35743
|
editor.chain().focus().unsetLink().run();
|
|
@@ -35448,23 +35760,26 @@ var CustomBubbleMenu = ({
|
|
|
35448
35760
|
const BUBBLE_MENU_ESTIMATED_HEIGHT = 44;
|
|
35449
35761
|
const [isVisible, setIsVisible] = (0, import_react72.useState)(false);
|
|
35450
35762
|
const [linkInputOpen, setLinkInputOpen] = (0, import_react72.useState)(false);
|
|
35451
|
-
const [position,
|
|
35763
|
+
const [position, setPosition2] = (0, import_react72.useState)({
|
|
35452
35764
|
top: 0,
|
|
35453
35765
|
left: 0,
|
|
35454
35766
|
placement: "top"
|
|
35455
35767
|
});
|
|
35456
35768
|
const menuRef = (0, import_react72.useRef)(null);
|
|
35457
35769
|
const keepOpenRef = (0, import_react72.useRef)(false);
|
|
35770
|
+
const [keepOpen, setKeepOpenState] = (0, import_react72.useState)(false);
|
|
35458
35771
|
const showTimeoutRef = (0, import_react72.useRef)(null);
|
|
35459
35772
|
const suppressShowUntilRef = (0, import_react72.useRef)(0);
|
|
35460
35773
|
const setKeepOpen = (0, import_react72.useCallback)((next) => {
|
|
35461
35774
|
keepOpenRef.current = next;
|
|
35775
|
+
setKeepOpenState(next);
|
|
35462
35776
|
if (!next) setLinkInputOpen(false);
|
|
35463
35777
|
if (next) setIsVisible(true);
|
|
35464
35778
|
}, []);
|
|
35465
35779
|
const closeBubbleMenu = (0, import_react72.useCallback)(() => {
|
|
35466
35780
|
suppressShowUntilRef.current = Date.now() + 1e3;
|
|
35467
35781
|
keepOpenRef.current = false;
|
|
35782
|
+
setKeepOpenState(false);
|
|
35468
35783
|
setLinkInputOpen(false);
|
|
35469
35784
|
setIsVisible(false);
|
|
35470
35785
|
if (showTimeoutRef.current) {
|
|
@@ -35520,7 +35835,7 @@ var CustomBubbleMenu = ({
|
|
|
35520
35835
|
Math.max(viewportPadding, (start.left + end.left) / 2)
|
|
35521
35836
|
);
|
|
35522
35837
|
const top = placement === "top" ? Math.max(viewportPadding, selectionTop - BUBBLE_MENU_OFFSET) : Math.min(window.innerHeight - viewportPadding, selectionBottom + BUBBLE_MENU_OFFSET);
|
|
35523
|
-
|
|
35838
|
+
setPosition2({ top, left, placement });
|
|
35524
35839
|
if (keepOpenRef.current) {
|
|
35525
35840
|
clearShowTimeout();
|
|
35526
35841
|
setIsVisible(true);
|
|
@@ -35570,13 +35885,13 @@ var CustomBubbleMenu = ({
|
|
|
35570
35885
|
onMouseDown: (e) => {
|
|
35571
35886
|
const target = e.target;
|
|
35572
35887
|
if (target?.closest?.("[data-ueditor-close-on-select]")) {
|
|
35573
|
-
|
|
35888
|
+
setKeepOpen(false);
|
|
35574
35889
|
} else if (target?.closest?.("[data-ueditor-keep-open]")) {
|
|
35575
|
-
|
|
35890
|
+
setKeepOpen(true);
|
|
35576
35891
|
}
|
|
35577
35892
|
e.preventDefault();
|
|
35578
35893
|
},
|
|
35579
|
-
children: editor.isActive("link") && !
|
|
35894
|
+
children: editor.isActive("link") && !keepOpen && !linkInputOpen ? /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(
|
|
35580
35895
|
LinkPreviewContent,
|
|
35581
35896
|
{
|
|
35582
35897
|
editor,
|
|
@@ -35602,6 +35917,54 @@ var CustomBubbleMenu = ({
|
|
|
35602
35917
|
document.body
|
|
35603
35918
|
);
|
|
35604
35919
|
};
|
|
35920
|
+
var CustomFloatingMenu = ({ editor }) => {
|
|
35921
|
+
const FLOATING_MENU_OFFSET = 16;
|
|
35922
|
+
const [isVisible, setIsVisible] = (0, import_react72.useState)(false);
|
|
35923
|
+
const [position, setPosition2] = (0, import_react72.useState)({ top: 0, left: 0 });
|
|
35924
|
+
(0, import_react72.useEffect)(() => {
|
|
35925
|
+
const updatePosition = () => {
|
|
35926
|
+
const { state, view } = editor;
|
|
35927
|
+
const { $from, empty } = state.selection;
|
|
35928
|
+
const isEmptyTextBlock = $from.parent.isTextblock && $from.parent.type.name === "paragraph" && $from.parent.textContent === "" && empty;
|
|
35929
|
+
if (!isEmptyTextBlock || !view.hasFocus()) {
|
|
35930
|
+
setIsVisible(false);
|
|
35931
|
+
return;
|
|
35932
|
+
}
|
|
35933
|
+
const coords = view.coordsAtPos($from.pos);
|
|
35934
|
+
setPosition2({ top: coords.top - FLOATING_MENU_OFFSET, left: coords.left });
|
|
35935
|
+
setIsVisible(true);
|
|
35936
|
+
};
|
|
35937
|
+
const handleBlur = () => setIsVisible(false);
|
|
35938
|
+
editor.on("selectionUpdate", updatePosition);
|
|
35939
|
+
editor.on("focus", updatePosition);
|
|
35940
|
+
editor.on("blur", handleBlur);
|
|
35941
|
+
editor.on("update", updatePosition);
|
|
35942
|
+
return () => {
|
|
35943
|
+
editor.off("selectionUpdate", updatePosition);
|
|
35944
|
+
editor.off("focus", updatePosition);
|
|
35945
|
+
editor.off("blur", handleBlur);
|
|
35946
|
+
editor.off("update", updatePosition);
|
|
35947
|
+
};
|
|
35948
|
+
}, [editor]);
|
|
35949
|
+
if (!isVisible) return null;
|
|
35950
|
+
return (0, import_react_dom8.createPortal)(
|
|
35951
|
+
/* @__PURE__ */ (0, import_jsx_runtime92.jsx)(
|
|
35952
|
+
"div",
|
|
35953
|
+
{
|
|
35954
|
+
"data-popover": true,
|
|
35955
|
+
className: "fixed z-99999 rounded-2xl border border-border/50 bg-card text-card-foreground shadow-lg backdrop-blur-sm overflow-hidden animate-in fade-in-0 slide-in-from-bottom-2",
|
|
35956
|
+
style: {
|
|
35957
|
+
top: `${position.top}px`,
|
|
35958
|
+
left: `${position.left}px`,
|
|
35959
|
+
transform: "translate(-50%, -100%)"
|
|
35960
|
+
},
|
|
35961
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
35962
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(FloatingMenuContent, { editor })
|
|
35963
|
+
}
|
|
35964
|
+
),
|
|
35965
|
+
document.body
|
|
35966
|
+
);
|
|
35967
|
+
};
|
|
35605
35968
|
|
|
35606
35969
|
// src/components/UEditor/CharacterCount.tsx
|
|
35607
35970
|
var import_jsx_runtime93 = require("react/jsx-runtime");
|
|
@@ -38677,7 +39040,7 @@ var Selection = class {
|
|
|
38677
39040
|
found.
|
|
38678
39041
|
*/
|
|
38679
39042
|
static findFrom($pos, dir, textOnly = false) {
|
|
38680
|
-
let inner = $pos.parent.inlineContent ? new
|
|
39043
|
+
let inner = $pos.parent.inlineContent ? new TextSelection4($pos) : findSelectionIn($pos.node(0), $pos.parent, $pos.pos, $pos.index(), dir, textOnly);
|
|
38681
39044
|
if (inner)
|
|
38682
39045
|
return inner;
|
|
38683
39046
|
for (let depth = $pos.depth - 1; depth >= 0; depth--) {
|
|
@@ -38746,7 +39109,7 @@ var Selection = class {
|
|
|
38746
39109
|
returns the bookmark for that.
|
|
38747
39110
|
*/
|
|
38748
39111
|
getBookmark() {
|
|
38749
|
-
return
|
|
39112
|
+
return TextSelection4.between(this.$anchor, this.$head).getBookmark();
|
|
38750
39113
|
}
|
|
38751
39114
|
};
|
|
38752
39115
|
Selection.prototype.visible = true;
|
|
@@ -38766,7 +39129,7 @@ function checkTextSelection($pos) {
|
|
|
38766
39129
|
console["warn"]("TextSelection endpoint not pointing into a node with inline content (" + $pos.parent.type.name + ")");
|
|
38767
39130
|
}
|
|
38768
39131
|
}
|
|
38769
|
-
var
|
|
39132
|
+
var TextSelection4 = class _TextSelection extends Selection {
|
|
38770
39133
|
/**
|
|
38771
39134
|
Construct a text selection between the given points.
|
|
38772
39135
|
*/
|
|
@@ -38852,7 +39215,7 @@ var TextSelection3 = class _TextSelection extends Selection {
|
|
|
38852
39215
|
return new _TextSelection($anchor, $head);
|
|
38853
39216
|
}
|
|
38854
39217
|
};
|
|
38855
|
-
Selection.jsonID("text",
|
|
39218
|
+
Selection.jsonID("text", TextSelection4);
|
|
38856
39219
|
var TextBookmark = class _TextBookmark {
|
|
38857
39220
|
constructor(anchor, head) {
|
|
38858
39221
|
this.anchor = anchor;
|
|
@@ -38862,7 +39225,7 @@ var TextBookmark = class _TextBookmark {
|
|
|
38862
39225
|
return new _TextBookmark(mapping.map(this.anchor), mapping.map(this.head));
|
|
38863
39226
|
}
|
|
38864
39227
|
resolve(doc) {
|
|
38865
|
-
return
|
|
39228
|
+
return TextSelection4.between(doc.resolve(this.anchor), doc.resolve(this.head));
|
|
38866
39229
|
}
|
|
38867
39230
|
};
|
|
38868
39231
|
var NodeSelection2 = class _NodeSelection extends Selection {
|
|
@@ -38981,7 +39344,7 @@ var AllBookmark = {
|
|
|
38981
39344
|
};
|
|
38982
39345
|
function findSelectionIn(doc, node, pos, index, dir, text = false) {
|
|
38983
39346
|
if (node.inlineContent)
|
|
38984
|
-
return
|
|
39347
|
+
return TextSelection4.create(doc, pos);
|
|
38985
39348
|
for (let i = index - (dir > 0 ? 0 : 1); dir > 0 ? i < node.childCount : i >= 0; i += dir) {
|
|
38986
39349
|
let child = node.child(i);
|
|
38987
39350
|
if (!child.isAtom) {
|
|
@@ -39573,7 +39936,7 @@ var CellSelection = class CellSelection2 extends Selection {
|
|
|
39573
39936
|
else if (tableChanged && this.isColSelection()) return CellSelection2.colSelection($anchorCell, $headCell);
|
|
39574
39937
|
else return new CellSelection2($anchorCell, $headCell);
|
|
39575
39938
|
}
|
|
39576
|
-
return
|
|
39939
|
+
return TextSelection4.between($anchorCell, $headCell);
|
|
39577
39940
|
}
|
|
39578
39941
|
content() {
|
|
39579
39942
|
const table = this.$anchorCell.node(-1);
|
|
@@ -40188,7 +40551,7 @@ function shiftArrow(axis, dir) {
|
|
|
40188
40551
|
};
|
|
40189
40552
|
}
|
|
40190
40553
|
function atEndOfCell(view, axis, dir) {
|
|
40191
|
-
if (!(view.state.selection instanceof
|
|
40554
|
+
if (!(view.state.selection instanceof TextSelection4)) return null;
|
|
40192
40555
|
const { $head } = view.state.selection;
|
|
40193
40556
|
for (let d = $head.depth - 1; d >= 0; d--) {
|
|
40194
40557
|
const parent = $head.node(d);
|
|
@@ -41308,7 +41671,7 @@ function TableControls({ editor, containerRef }) {
|
|
|
41308
41671
|
destructive: true
|
|
41309
41672
|
}
|
|
41310
41673
|
];
|
|
41311
|
-
}, [layout, runAtActiveCell, t]);
|
|
41674
|
+
}, [editor, layout, runAtActiveCell, t]);
|
|
41312
41675
|
const getRowHandleMenuItems = import_react74.default.useCallback((rowHandle) => [
|
|
41313
41676
|
{
|
|
41314
41677
|
label: t("tableMenu.addRowBefore"),
|
|
@@ -41524,6 +41887,42 @@ var UEDITOR_PROSEMIRROR_CLASS_NAME = cn(
|
|
|
41524
41887
|
"[&_th]:px-2",
|
|
41525
41888
|
"[&_th]:py-0",
|
|
41526
41889
|
"[&_th_p]:my-0",
|
|
41890
|
+
"[&_td[data-formula]]:pr-7",
|
|
41891
|
+
"[&_th[data-formula]]:pr-7",
|
|
41892
|
+
"[&_td[data-formula]]:before:pointer-events-none",
|
|
41893
|
+
"[&_th[data-formula]]:before:pointer-events-none",
|
|
41894
|
+
"[&_td[data-formula]]:before:absolute",
|
|
41895
|
+
"[&_th[data-formula]]:before:absolute",
|
|
41896
|
+
"[&_td[data-formula]]:before:right-1",
|
|
41897
|
+
"[&_th[data-formula]]:before:right-1",
|
|
41898
|
+
"[&_td[data-formula]]:before:top-1",
|
|
41899
|
+
"[&_th[data-formula]]:before:top-1",
|
|
41900
|
+
"[&_td[data-formula]]:before:z-[1]",
|
|
41901
|
+
"[&_th[data-formula]]:before:z-[1]",
|
|
41902
|
+
"[&_td[data-formula]]:before:rounded-sm",
|
|
41903
|
+
"[&_th[data-formula]]:before:rounded-sm",
|
|
41904
|
+
"[&_td[data-formula]]:before:bg-primary/10",
|
|
41905
|
+
"[&_th[data-formula]]:before:bg-primary/10",
|
|
41906
|
+
"[&_td[data-formula]]:before:px-1",
|
|
41907
|
+
"[&_th[data-formula]]:before:px-1",
|
|
41908
|
+
"[&_td[data-formula]]:before:font-mono",
|
|
41909
|
+
"[&_th[data-formula]]:before:font-mono",
|
|
41910
|
+
"[&_td[data-formula]]:before:text-[9px]",
|
|
41911
|
+
"[&_th[data-formula]]:before:text-[9px]",
|
|
41912
|
+
"[&_td[data-formula]]:before:font-semibold",
|
|
41913
|
+
"[&_th[data-formula]]:before:font-semibold",
|
|
41914
|
+
"[&_td[data-formula]]:before:leading-4",
|
|
41915
|
+
"[&_th[data-formula]]:before:leading-4",
|
|
41916
|
+
"[&_td[data-formula]]:before:text-primary",
|
|
41917
|
+
"[&_th[data-formula]]:before:text-primary",
|
|
41918
|
+
"[&_td[data-formula]]:before:content-['fx']",
|
|
41919
|
+
"[&_th[data-formula]]:before:content-['fx']",
|
|
41920
|
+
"[&_td[data-formula-state='error']]:bg-destructive/5",
|
|
41921
|
+
"[&_th[data-formula-state='error']]:bg-destructive/5",
|
|
41922
|
+
"[&_td[data-formula-state='error']]:before:bg-destructive/10",
|
|
41923
|
+
"[&_th[data-formula-state='error']]:before:bg-destructive/10",
|
|
41924
|
+
"[&_td[data-formula-state='error']]:before:text-destructive",
|
|
41925
|
+
"[&_th[data-formula-state='error']]:before:text-destructive",
|
|
41527
41926
|
"[&_td[colwidth]]:min-w-0",
|
|
41528
41927
|
"[&_th[colwidth]]:min-w-0",
|
|
41529
41928
|
"[&_td[data-colwidth]]:min-w-0",
|
|
@@ -42029,7 +42428,7 @@ function useUEditorTableInteractions(editor, editable = true) {
|
|
|
42029
42428
|
clearHoveredTableCell();
|
|
42030
42429
|
clearAllTableResizeHover();
|
|
42031
42430
|
};
|
|
42032
|
-
}, [beginResize, cancelResize, cleanupRowResize, clearActiveTableCell, clearAllTableResizeHover, clearHoveredTableCell, editable, editor, handleRowResizePointerMove, handleRowResizePointerUp, hideColumnGuide, hideRowGuide, isRowResizing, scheduleTableLayoutSync, showColumnGuide, showRowGuide, syncActiveRowResizeGuide, syncActiveTableCellFromSelection, updateActiveCellHighlight]);
|
|
42431
|
+
}, [beginResize, cancelResize, cleanupRowResize, clearActiveTableCell, clearAllTableResizeHover, clearHoveredTableCell, editable, editor, handleRowResizePointerMove, handleRowResizePointerUp, hideColumnGuide, hideRowGuide, isRowResizing, scheduleTableLayoutSync, setActiveTableCell, setHoveredTableCell, showColumnGuide, showRowGuide, syncActiveRowResizeGuide, syncActiveTableCellFromSelection, updateActiveCellHighlight]);
|
|
42033
42432
|
return {
|
|
42034
42433
|
editorContentRef,
|
|
42035
42434
|
tableColumnGuideRef,
|
|
@@ -42038,9 +42437,402 @@ function useUEditorTableInteractions(editor, editable = true) {
|
|
|
42038
42437
|
};
|
|
42039
42438
|
}
|
|
42040
42439
|
|
|
42440
|
+
// src/components/UEditor/use-formula-coordinate-overlay.ts
|
|
42441
|
+
var import_react77 = require("react");
|
|
42442
|
+
var import_tables8 = require("@tiptap/pm/tables");
|
|
42443
|
+
|
|
42444
|
+
// src/components/UEditor/table-formula-range-picker.ts
|
|
42445
|
+
var import_state11 = require("@tiptap/pm/state");
|
|
42446
|
+
var import_tables7 = require("@tiptap/pm/tables");
|
|
42447
|
+
function getCellText2(cellNode) {
|
|
42448
|
+
return cellNode.textBetween(0, cellNode.content.size, "\n").trim();
|
|
42449
|
+
}
|
|
42450
|
+
function getFormulaEditingTableContext(view) {
|
|
42451
|
+
const { $from } = view.state.selection;
|
|
42452
|
+
for (let depth = $from.depth; depth > 0; depth -= 1) {
|
|
42453
|
+
const node = $from.node(depth);
|
|
42454
|
+
if (node.type.name !== "tableCell" && node.type.name !== "tableHeader") continue;
|
|
42455
|
+
if (!getCellText2(node).startsWith("=")) return null;
|
|
42456
|
+
const cellPos = $from.before(depth);
|
|
42457
|
+
const cellDom = view.nodeDOM(cellPos);
|
|
42458
|
+
if (!(cellDom instanceof HTMLTableCellElement)) return null;
|
|
42459
|
+
const tableDepth = depth - 2;
|
|
42460
|
+
const tableNode = tableDepth > 0 ? $from.node(tableDepth) : null;
|
|
42461
|
+
if (!tableNode || tableNode.type.name !== "table") return null;
|
|
42462
|
+
const tableDom = cellDom.closest("table");
|
|
42463
|
+
if (!(tableDom instanceof HTMLTableElement)) return null;
|
|
42464
|
+
return {
|
|
42465
|
+
cellContentEnd: $from.end(depth),
|
|
42466
|
+
cellContentStart: $from.start(depth),
|
|
42467
|
+
cellDom,
|
|
42468
|
+
formula: getCellText2(node),
|
|
42469
|
+
tableDom,
|
|
42470
|
+
tableNode,
|
|
42471
|
+
tablePos: $from.before(tableDepth)
|
|
42472
|
+
};
|
|
42473
|
+
}
|
|
42474
|
+
return null;
|
|
42475
|
+
}
|
|
42476
|
+
function cancelFormulaEditing(view) {
|
|
42477
|
+
const context = getFormulaEditingTableContext(view);
|
|
42478
|
+
if (!context) return false;
|
|
42479
|
+
let tr = view.state.tr.delete(context.cellContentStart, context.cellContentEnd);
|
|
42480
|
+
const selectionPos = Math.min(context.cellContentStart, tr.doc.content.size);
|
|
42481
|
+
tr = tr.setSelection(import_state11.TextSelection.near(tr.doc.resolve(selectionPos)));
|
|
42482
|
+
view.dispatch(tr);
|
|
42483
|
+
view.focus();
|
|
42484
|
+
return true;
|
|
42485
|
+
}
|
|
42486
|
+
function getReferenceInsertionPrefix(view, cellContentStart, insertionPos) {
|
|
42487
|
+
const textBeforeCursor = view.state.doc.textBetween(cellContentStart, insertionPos, "", "");
|
|
42488
|
+
const isInsideFunctionArguments = /[A-Z]+\([^)]*$/i.test(textBeforeCursor);
|
|
42489
|
+
const followsReference = /[A-Z]+[1-9]\d*(?::[A-Z]+[1-9]\d*)?\s*$/i.test(textBeforeCursor);
|
|
42490
|
+
return isInsideFunctionArguments && followsReference ? "," : "";
|
|
42491
|
+
}
|
|
42492
|
+
function findTableForPos(view, pos) {
|
|
42493
|
+
const $pos = view.state.doc.resolve(pos);
|
|
42494
|
+
for (let depth = $pos.depth; depth > 0; depth -= 1) {
|
|
42495
|
+
const node = $pos.node(depth);
|
|
42496
|
+
if (node.type.name === "table") {
|
|
42497
|
+
return {
|
|
42498
|
+
node,
|
|
42499
|
+
pos: $pos.before(depth),
|
|
42500
|
+
start: $pos.start(depth)
|
|
42501
|
+
};
|
|
42502
|
+
}
|
|
42503
|
+
}
|
|
42504
|
+
return null;
|
|
42505
|
+
}
|
|
42506
|
+
function getCellRelativePosFromDomPos2(map, tableStart, domPos) {
|
|
42507
|
+
const relativeDomPos = domPos - tableStart;
|
|
42508
|
+
const seen = /* @__PURE__ */ new Set();
|
|
42509
|
+
for (const relativeCellPos of map.map) {
|
|
42510
|
+
if (seen.has(relativeCellPos)) continue;
|
|
42511
|
+
seen.add(relativeCellPos);
|
|
42512
|
+
if (relativeDomPos === relativeCellPos || relativeDomPos === relativeCellPos + 1) {
|
|
42513
|
+
return relativeCellPos;
|
|
42514
|
+
}
|
|
42515
|
+
}
|
|
42516
|
+
return null;
|
|
42517
|
+
}
|
|
42518
|
+
function getFormulaTableCellInfo(view, target, tablePos) {
|
|
42519
|
+
const element = resolveEventElement(target);
|
|
42520
|
+
const cell = element?.closest?.("th,td");
|
|
42521
|
+
if (!(cell instanceof HTMLTableCellElement)) return null;
|
|
42522
|
+
const domPos = view.posAtDOM(cell, 0);
|
|
42523
|
+
const tableInfo = findTableForPos(view, domPos);
|
|
42524
|
+
if (!tableInfo || tableInfo.pos !== tablePos) return null;
|
|
42525
|
+
const map = import_tables7.TableMap.get(tableInfo.node);
|
|
42526
|
+
const relativeCellPos = getCellRelativePosFromDomPos2(map, tableInfo.start, domPos);
|
|
42527
|
+
if (relativeCellPos == null) return null;
|
|
42528
|
+
const rect = map.findCell(relativeCellPos);
|
|
42529
|
+
return {
|
|
42530
|
+
cell,
|
|
42531
|
+
label: `${indexToColumnName(rect.left)}${rect.top + 1}`,
|
|
42532
|
+
rect
|
|
42533
|
+
};
|
|
42534
|
+
}
|
|
42535
|
+
function normalizeFormulaRangeLabel(fromLabel, toLabel) {
|
|
42536
|
+
return fromLabel === toLabel ? fromLabel : `${fromLabel}:${toLabel}`;
|
|
42537
|
+
}
|
|
42538
|
+
function replacePickedLabel(view, pickState, nextLabel, currentCell) {
|
|
42539
|
+
if (nextLabel === pickState.currentLabel && currentCell === pickState.currentCell) return pickState;
|
|
42540
|
+
if (nextLabel === pickState.currentLabel) {
|
|
42541
|
+
return {
|
|
42542
|
+
...pickState,
|
|
42543
|
+
currentCell
|
|
42544
|
+
};
|
|
42545
|
+
}
|
|
42546
|
+
let tr = view.state.tr.insertText(nextLabel, pickState.insertedFrom, pickState.insertedTo);
|
|
42547
|
+
const nextTo = pickState.insertedFrom + nextLabel.length;
|
|
42548
|
+
tr = tr.setSelection(import_state11.TextSelection.create(tr.doc, nextTo));
|
|
42549
|
+
view.dispatch(tr);
|
|
42550
|
+
return {
|
|
42551
|
+
...pickState,
|
|
42552
|
+
insertedTo: nextTo,
|
|
42553
|
+
currentLabel: nextLabel,
|
|
42554
|
+
currentCell
|
|
42555
|
+
};
|
|
42556
|
+
}
|
|
42557
|
+
function beginFormulaRangePick(view, event) {
|
|
42558
|
+
if (event.button !== 0) return null;
|
|
42559
|
+
const formulaCell = getFormulaEditingTableContext(view);
|
|
42560
|
+
if (!formulaCell) return null;
|
|
42561
|
+
const picked = getFormulaTableCellInfo(view, event.target, formulaCell.tablePos);
|
|
42562
|
+
if (!picked || picked.cell === formulaCell.cellDom) return null;
|
|
42563
|
+
const { from, to } = view.state.selection;
|
|
42564
|
+
const prefix = from === to ? getReferenceInsertionPrefix(view, formulaCell.cellContentStart, from) : "";
|
|
42565
|
+
const insertedText = `${prefix}${picked.label}`;
|
|
42566
|
+
const insertedFrom = from + prefix.length;
|
|
42567
|
+
let tr = view.state.tr.insertText(insertedText, from, to);
|
|
42568
|
+
tr = tr.setSelection(import_state11.TextSelection.create(tr.doc, from + insertedText.length));
|
|
42569
|
+
view.dispatch(tr);
|
|
42570
|
+
view.focus();
|
|
42571
|
+
event.preventDefault();
|
|
42572
|
+
event.stopPropagation();
|
|
42573
|
+
return {
|
|
42574
|
+
anchorLabel: picked.label,
|
|
42575
|
+
anchorCell: picked.cell,
|
|
42576
|
+
tablePos: formulaCell.tablePos,
|
|
42577
|
+
insertedFrom,
|
|
42578
|
+
insertedTo: insertedFrom + picked.label.length,
|
|
42579
|
+
currentLabel: picked.label,
|
|
42580
|
+
currentCell: picked.cell
|
|
42581
|
+
};
|
|
42582
|
+
}
|
|
42583
|
+
function updateFormulaRangePick(view, pickState, event) {
|
|
42584
|
+
const picked = getFormulaTableCellInfo(view, event.target, pickState.tablePos);
|
|
42585
|
+
if (!picked) return pickState;
|
|
42586
|
+
event.preventDefault();
|
|
42587
|
+
event.stopPropagation();
|
|
42588
|
+
return replacePickedLabel(
|
|
42589
|
+
view,
|
|
42590
|
+
pickState,
|
|
42591
|
+
normalizeFormulaRangeLabel(pickState.anchorLabel, picked.label),
|
|
42592
|
+
picked.cell
|
|
42593
|
+
);
|
|
42594
|
+
}
|
|
42595
|
+
function getFormulaRangePickHighlight(container, pickState) {
|
|
42596
|
+
if (!container.contains(pickState.anchorCell) || !container.contains(pickState.currentCell)) return null;
|
|
42597
|
+
const containerRect = container.getBoundingClientRect();
|
|
42598
|
+
const anchorRect = pickState.anchorCell.getBoundingClientRect();
|
|
42599
|
+
const currentRect = pickState.currentCell.getBoundingClientRect();
|
|
42600
|
+
const left = Math.min(anchorRect.left, currentRect.left) - containerRect.left + container.scrollLeft;
|
|
42601
|
+
const top = Math.min(anchorRect.top, currentRect.top) - containerRect.top + container.scrollTop;
|
|
42602
|
+
const right = Math.max(anchorRect.right, currentRect.right) - containerRect.left + container.scrollLeft;
|
|
42603
|
+
const bottom = Math.max(anchorRect.bottom, currentRect.bottom) - containerRect.top + container.scrollTop;
|
|
42604
|
+
return {
|
|
42605
|
+
left,
|
|
42606
|
+
top,
|
|
42607
|
+
width: Math.max(0, right - left),
|
|
42608
|
+
height: Math.max(0, bottom - top)
|
|
42609
|
+
};
|
|
42610
|
+
}
|
|
42611
|
+
|
|
42612
|
+
// src/components/UEditor/use-formula-coordinate-overlay.ts
|
|
42613
|
+
function setPosition(element, left, top, width, height) {
|
|
42614
|
+
element.style.left = `${left}px`;
|
|
42615
|
+
element.style.top = `${top}px`;
|
|
42616
|
+
if (width != null) element.style.width = `${Math.max(0, width)}px`;
|
|
42617
|
+
if (height != null) element.style.height = `${Math.max(0, height)}px`;
|
|
42618
|
+
}
|
|
42619
|
+
function createCoordinateLabel(kind, label) {
|
|
42620
|
+
const element = document.createElement("span");
|
|
42621
|
+
element.dataset.ueditorFormulaCoordinate = kind;
|
|
42622
|
+
element.textContent = label;
|
|
42623
|
+
element.className = "pointer-events-none absolute z-30 flex items-center justify-center rounded-[3px] border border-primary/35 bg-primary/90 px-1 font-mono text-[10px] font-semibold leading-none text-primary-foreground shadow-sm";
|
|
42624
|
+
return element;
|
|
42625
|
+
}
|
|
42626
|
+
function createReferenceHighlight(label) {
|
|
42627
|
+
const element = document.createElement("span");
|
|
42628
|
+
element.dataset.ueditorFormulaReference = label;
|
|
42629
|
+
element.className = "pointer-events-none absolute z-[21] rounded-[2px] border-2 border-emerald-500 bg-emerald-500/15";
|
|
42630
|
+
return element;
|
|
42631
|
+
}
|
|
42632
|
+
function useFormulaCoordinateOverlay(editor, containerRef, labels) {
|
|
42633
|
+
const overlayRef = (0, import_react77.useRef)(null);
|
|
42634
|
+
(0, import_react77.useEffect)(() => {
|
|
42635
|
+
if (!editor) return void 0;
|
|
42636
|
+
const overlay = overlayRef.current;
|
|
42637
|
+
const container = containerRef.current;
|
|
42638
|
+
if (!overlay || !container) return void 0;
|
|
42639
|
+
let animationFrame = null;
|
|
42640
|
+
let activeTable = null;
|
|
42641
|
+
let activeTablePos = null;
|
|
42642
|
+
let hoverLabel = null;
|
|
42643
|
+
const clearOverlay = () => {
|
|
42644
|
+
activeTable = null;
|
|
42645
|
+
activeTablePos = null;
|
|
42646
|
+
hoverLabel = null;
|
|
42647
|
+
overlay.replaceChildren();
|
|
42648
|
+
overlay.style.display = "none";
|
|
42649
|
+
};
|
|
42650
|
+
const syncOverlay = () => {
|
|
42651
|
+
animationFrame = null;
|
|
42652
|
+
if (editor.isDestroyed) return;
|
|
42653
|
+
const context = getFormulaEditingTableContext(editor.view);
|
|
42654
|
+
if (!context) {
|
|
42655
|
+
clearOverlay();
|
|
42656
|
+
return;
|
|
42657
|
+
}
|
|
42658
|
+
const containerRect = container.getBoundingClientRect();
|
|
42659
|
+
const tableRect = context.tableDom.getBoundingClientRect();
|
|
42660
|
+
const tableLeft = tableRect.left - containerRect.left + container.scrollLeft;
|
|
42661
|
+
const tableTop = tableRect.top - containerRect.top + container.scrollTop;
|
|
42662
|
+
const map = import_tables8.TableMap.get(context.tableNode);
|
|
42663
|
+
const columnSegments = Array(map.width);
|
|
42664
|
+
const rowSegments = Array(map.height);
|
|
42665
|
+
const cellInfos = [];
|
|
42666
|
+
for (const cell of context.tableDom.querySelectorAll("th,td")) {
|
|
42667
|
+
if (!(cell instanceof HTMLTableCellElement)) continue;
|
|
42668
|
+
const info = getFormulaTableCellInfo(editor.view, cell, context.tablePos);
|
|
42669
|
+
if (!info) continue;
|
|
42670
|
+
cellInfos.push(info);
|
|
42671
|
+
const cellRect = cell.getBoundingClientRect();
|
|
42672
|
+
const columnSpan = Math.max(1, info.rect.right - info.rect.left);
|
|
42673
|
+
const rowSpan = Math.max(1, info.rect.bottom - info.rect.top);
|
|
42674
|
+
const columnWidth = cellRect.width / columnSpan;
|
|
42675
|
+
const rowHeight = cellRect.height / rowSpan;
|
|
42676
|
+
for (let column = info.rect.left; column < info.rect.right; column += 1) {
|
|
42677
|
+
if (!columnSegments[column] || columnSpan < columnSegments[column].span) {
|
|
42678
|
+
columnSegments[column] = {
|
|
42679
|
+
start: cellRect.left - containerRect.left + container.scrollLeft + (column - info.rect.left) * columnWidth,
|
|
42680
|
+
size: columnWidth,
|
|
42681
|
+
span: columnSpan
|
|
42682
|
+
};
|
|
42683
|
+
}
|
|
42684
|
+
}
|
|
42685
|
+
for (let row = info.rect.top; row < info.rect.bottom; row += 1) {
|
|
42686
|
+
if (!rowSegments[row] || rowSpan < rowSegments[row].span) {
|
|
42687
|
+
rowSegments[row] = {
|
|
42688
|
+
start: cellRect.top - containerRect.top + container.scrollTop + (row - info.rect.top) * rowHeight,
|
|
42689
|
+
size: rowHeight,
|
|
42690
|
+
span: rowSpan
|
|
42691
|
+
};
|
|
42692
|
+
}
|
|
42693
|
+
}
|
|
42694
|
+
}
|
|
42695
|
+
const fallbackColumnWidth = map.width > 0 ? tableRect.width / map.width : 0;
|
|
42696
|
+
const fallbackRowHeight = map.height > 0 ? tableRect.height / map.height : 0;
|
|
42697
|
+
const children = [];
|
|
42698
|
+
const actions = document.createElement("div");
|
|
42699
|
+
actions.dataset.ueditorFormulaActions = "";
|
|
42700
|
+
actions.className = "pointer-events-auto absolute z-50 flex items-center gap-1 rounded-md border border-border bg-background p-1 shadow-md";
|
|
42701
|
+
const formulaCellRect = context.cellDom.getBoundingClientRect();
|
|
42702
|
+
setPosition(
|
|
42703
|
+
actions,
|
|
42704
|
+
formulaCellRect.left - containerRect.left + container.scrollLeft,
|
|
42705
|
+
formulaCellRect.bottom - containerRect.top + container.scrollTop + 4
|
|
42706
|
+
);
|
|
42707
|
+
const applyButton = document.createElement("button");
|
|
42708
|
+
applyButton.type = "button";
|
|
42709
|
+
applyButton.dataset.ueditorFormulaApply = "";
|
|
42710
|
+
applyButton.disabled = isDraftTableFormula(context.formula);
|
|
42711
|
+
applyButton.textContent = `${applyButton.disabled ? "" : "\u2713 "}${labels.apply} (Enter)`;
|
|
42712
|
+
applyButton.className = "rounded bg-primary px-2.5 py-1 text-xs font-medium text-primary-foreground hover:bg-primary/90 disabled:cursor-not-allowed disabled:opacity-45";
|
|
42713
|
+
const cancelButton = document.createElement("button");
|
|
42714
|
+
cancelButton.type = "button";
|
|
42715
|
+
cancelButton.dataset.ueditorFormulaCancel = "";
|
|
42716
|
+
cancelButton.textContent = `${labels.cancel} (Esc)`;
|
|
42717
|
+
cancelButton.className = "rounded px-2.5 py-1 text-xs font-medium text-foreground hover:bg-muted";
|
|
42718
|
+
const preserveSelection = (event) => {
|
|
42719
|
+
event.preventDefault();
|
|
42720
|
+
event.stopPropagation();
|
|
42721
|
+
};
|
|
42722
|
+
applyButton.addEventListener("mousedown", preserveSelection);
|
|
42723
|
+
cancelButton.addEventListener("mousedown", preserveSelection);
|
|
42724
|
+
applyButton.addEventListener("click", (event) => {
|
|
42725
|
+
event.preventDefault();
|
|
42726
|
+
event.stopPropagation();
|
|
42727
|
+
if (!applyButton.disabled) recalculateActiveTableFormulas(editor);
|
|
42728
|
+
});
|
|
42729
|
+
cancelButton.addEventListener("click", (event) => {
|
|
42730
|
+
event.preventDefault();
|
|
42731
|
+
event.stopPropagation();
|
|
42732
|
+
cancelFormulaEditing(editor.view);
|
|
42733
|
+
});
|
|
42734
|
+
actions.append(applyButton, cancelButton);
|
|
42735
|
+
children.push(actions);
|
|
42736
|
+
for (let column = 0; column < map.width; column += 1) {
|
|
42737
|
+
const segment = columnSegments[column] ?? {
|
|
42738
|
+
start: tableLeft + column * fallbackColumnWidth,
|
|
42739
|
+
size: fallbackColumnWidth,
|
|
42740
|
+
span: map.width
|
|
42741
|
+
};
|
|
42742
|
+
const label = createCoordinateLabel("column", indexToColumnName(column));
|
|
42743
|
+
setPosition(label, segment.start, Math.max(0, tableTop - 22), segment.size, 20);
|
|
42744
|
+
children.push(label);
|
|
42745
|
+
}
|
|
42746
|
+
for (let row = 0; row < map.height; row += 1) {
|
|
42747
|
+
const segment = rowSegments[row] ?? {
|
|
42748
|
+
start: tableTop + row * fallbackRowHeight,
|
|
42749
|
+
size: fallbackRowHeight,
|
|
42750
|
+
span: map.height
|
|
42751
|
+
};
|
|
42752
|
+
const label = createCoordinateLabel("row", String(row + 1));
|
|
42753
|
+
setPosition(label, Math.max(0, tableLeft - 28), segment.start, 26, segment.size);
|
|
42754
|
+
children.push(label);
|
|
42755
|
+
}
|
|
42756
|
+
const references = new Set(getTableFormulaReferences(context.formula));
|
|
42757
|
+
for (const info of cellInfos) {
|
|
42758
|
+
if (!info || !references.has(info.label)) continue;
|
|
42759
|
+
const cellRect = info.cell.getBoundingClientRect();
|
|
42760
|
+
const highlight = createReferenceHighlight(info.label);
|
|
42761
|
+
setPosition(
|
|
42762
|
+
highlight,
|
|
42763
|
+
cellRect.left - containerRect.left + container.scrollLeft + 2,
|
|
42764
|
+
cellRect.top - containerRect.top + container.scrollTop + 2,
|
|
42765
|
+
cellRect.width - 4,
|
|
42766
|
+
cellRect.height - 4
|
|
42767
|
+
);
|
|
42768
|
+
children.push(highlight);
|
|
42769
|
+
}
|
|
42770
|
+
hoverLabel = document.createElement("span");
|
|
42771
|
+
hoverLabel.dataset.ueditorFormulaHoverLabel = "";
|
|
42772
|
+
hoverLabel.className = "pointer-events-none absolute z-40 hidden rounded bg-foreground px-1.5 py-1 font-mono text-[10px] font-semibold leading-none text-background shadow-md";
|
|
42773
|
+
children.push(hoverLabel);
|
|
42774
|
+
overlay.replaceChildren(...children);
|
|
42775
|
+
overlay.style.display = "block";
|
|
42776
|
+
activeTable = context.tableDom;
|
|
42777
|
+
activeTablePos = context.tablePos;
|
|
42778
|
+
};
|
|
42779
|
+
const scheduleSync = () => {
|
|
42780
|
+
if (animationFrame != null) return;
|
|
42781
|
+
animationFrame = window.requestAnimationFrame(syncOverlay);
|
|
42782
|
+
};
|
|
42783
|
+
const handleMouseMove2 = (event) => {
|
|
42784
|
+
if (!activeTable || activeTablePos == null || !hoverLabel) return;
|
|
42785
|
+
const target = event.target instanceof Element ? event.target.closest("th,td") : null;
|
|
42786
|
+
if (!(target instanceof HTMLTableCellElement) || target.closest("table") !== activeTable) {
|
|
42787
|
+
hoverLabel.style.display = "none";
|
|
42788
|
+
return;
|
|
42789
|
+
}
|
|
42790
|
+
const info = getFormulaTableCellInfo(editor.view, target, activeTablePos);
|
|
42791
|
+
if (!info) {
|
|
42792
|
+
hoverLabel.style.display = "none";
|
|
42793
|
+
return;
|
|
42794
|
+
}
|
|
42795
|
+
const containerRect = container.getBoundingClientRect();
|
|
42796
|
+
const cellRect = target.getBoundingClientRect();
|
|
42797
|
+
hoverLabel.textContent = info.label;
|
|
42798
|
+
hoverLabel.style.display = "block";
|
|
42799
|
+
setPosition(
|
|
42800
|
+
hoverLabel,
|
|
42801
|
+
cellRect.left - containerRect.left + container.scrollLeft + 4,
|
|
42802
|
+
cellRect.top - containerRect.top + container.scrollTop + 4
|
|
42803
|
+
);
|
|
42804
|
+
};
|
|
42805
|
+
const handleMouseLeave2 = () => {
|
|
42806
|
+
if (hoverLabel) hoverLabel.style.display = "none";
|
|
42807
|
+
};
|
|
42808
|
+
editor.on("selectionUpdate", scheduleSync);
|
|
42809
|
+
editor.on("update", scheduleSync);
|
|
42810
|
+
editor.on("focus", scheduleSync);
|
|
42811
|
+
editor.on("blur", scheduleSync);
|
|
42812
|
+
editor.view.dom.addEventListener("mousemove", handleMouseMove2);
|
|
42813
|
+
editor.view.dom.addEventListener("mouseleave", handleMouseLeave2);
|
|
42814
|
+
container.addEventListener(UEDITOR_TABLE_LAYOUT_CHANGE_EVENT, scheduleSync);
|
|
42815
|
+
window.addEventListener("resize", scheduleSync);
|
|
42816
|
+
scheduleSync();
|
|
42817
|
+
return () => {
|
|
42818
|
+
editor.off("selectionUpdate", scheduleSync);
|
|
42819
|
+
editor.off("update", scheduleSync);
|
|
42820
|
+
editor.off("focus", scheduleSync);
|
|
42821
|
+
editor.off("blur", scheduleSync);
|
|
42822
|
+
editor.view.dom.removeEventListener("mousemove", handleMouseMove2);
|
|
42823
|
+
editor.view.dom.removeEventListener("mouseleave", handleMouseLeave2);
|
|
42824
|
+
container.removeEventListener(UEDITOR_TABLE_LAYOUT_CHANGE_EVENT, scheduleSync);
|
|
42825
|
+
window.removeEventListener("resize", scheduleSync);
|
|
42826
|
+
if (animationFrame != null) window.cancelAnimationFrame(animationFrame);
|
|
42827
|
+
clearOverlay();
|
|
42828
|
+
};
|
|
42829
|
+
}, [containerRef, editor, labels.apply, labels.cancel]);
|
|
42830
|
+
return overlayRef;
|
|
42831
|
+
}
|
|
42832
|
+
|
|
42041
42833
|
// src/components/UEditor/menu-bar.tsx
|
|
42042
|
-
var
|
|
42043
|
-
var
|
|
42834
|
+
var import_react78 = __toESM(require("react"), 1);
|
|
42835
|
+
var import_react79 = require("@tiptap/react");
|
|
42044
42836
|
var import_lucide_react58 = require("lucide-react");
|
|
42045
42837
|
|
|
42046
42838
|
// src/components/UEditor/preview-html.ts
|
|
@@ -42221,7 +43013,7 @@ function renderMenuItems(items) {
|
|
|
42221
43013
|
case "sub":
|
|
42222
43014
|
return /* @__PURE__ */ (0, import_jsx_runtime99.jsx)(DropdownMenuSub, { label: item.label, icon: item.icon, disabled: item.disabled, children: renderMenuItems(item.items) }, i);
|
|
42223
43015
|
case "custom":
|
|
42224
|
-
return /* @__PURE__ */ (0, import_jsx_runtime99.jsx)(
|
|
43016
|
+
return /* @__PURE__ */ (0, import_jsx_runtime99.jsx)(import_react78.default.Fragment, { children: item.render() }, item.key);
|
|
42225
43017
|
}
|
|
42226
43018
|
});
|
|
42227
43019
|
}
|
|
@@ -42597,7 +43389,7 @@ function buildTableMenuItems(t, editor, onInsertTable) {
|
|
|
42597
43389
|
}
|
|
42598
43390
|
];
|
|
42599
43391
|
}
|
|
42600
|
-
var MenuBarTrigger =
|
|
43392
|
+
var MenuBarTrigger = import_react78.default.forwardRef(
|
|
42601
43393
|
({ children, className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime99.jsx)(
|
|
42602
43394
|
"button",
|
|
42603
43395
|
{
|
|
@@ -42629,18 +43421,18 @@ var MenuBar = ({
|
|
|
42629
43421
|
onPreview
|
|
42630
43422
|
}) => {
|
|
42631
43423
|
const t = useSmartTranslations("UEditor");
|
|
42632
|
-
(0,
|
|
43424
|
+
(0, import_react79.useEditorState)({
|
|
42633
43425
|
editor,
|
|
42634
43426
|
selector: ({ transactionNumber }) => transactionNumber
|
|
42635
43427
|
});
|
|
42636
|
-
const fileInputRef = (0,
|
|
42637
|
-
const [showImageInput, setShowImageInput] = (0,
|
|
42638
|
-
const [showLinkInput, setShowLinkInput] = (0,
|
|
42639
|
-
const [isInsertMenuOpen, setIsInsertMenuOpen] = (0,
|
|
42640
|
-
const [showSourceDialog, setShowSourceDialog] = (0,
|
|
42641
|
-
const [sourceHtml, setSourceHtml] = (0,
|
|
42642
|
-
const [showPreviewDialog, setShowPreviewDialog] = (0,
|
|
42643
|
-
const previewHtml = (0,
|
|
43428
|
+
const fileInputRef = (0, import_react78.useRef)(null);
|
|
43429
|
+
const [showImageInput, setShowImageInput] = (0, import_react78.useState)(false);
|
|
43430
|
+
const [showLinkInput, setShowLinkInput] = (0, import_react78.useState)(false);
|
|
43431
|
+
const [isInsertMenuOpen, setIsInsertMenuOpen] = (0, import_react78.useState)(false);
|
|
43432
|
+
const [showSourceDialog, setShowSourceDialog] = (0, import_react78.useState)(false);
|
|
43433
|
+
const [sourceHtml, setSourceHtml] = (0, import_react78.useState)("");
|
|
43434
|
+
const [showPreviewDialog, setShowPreviewDialog] = (0, import_react78.useState)(false);
|
|
43435
|
+
const previewHtml = (0, import_react78.useMemo)(
|
|
42644
43436
|
() => showPreviewDialog ? prepareUEditorPreviewHtml(editor.getHTML()) : "",
|
|
42645
43437
|
[editor, showPreviewDialog]
|
|
42646
43438
|
);
|
|
@@ -42922,149 +43714,156 @@ var MenuBar = ({
|
|
|
42922
43714
|
] });
|
|
42923
43715
|
};
|
|
42924
43716
|
|
|
42925
|
-
// src/components/UEditor/table-formula-
|
|
42926
|
-
var
|
|
42927
|
-
var
|
|
42928
|
-
|
|
42929
|
-
|
|
42930
|
-
}
|
|
42931
|
-
|
|
42932
|
-
const
|
|
42933
|
-
|
|
42934
|
-
|
|
42935
|
-
|
|
42936
|
-
|
|
42937
|
-
|
|
42938
|
-
|
|
42939
|
-
|
|
42940
|
-
const
|
|
42941
|
-
|
|
42942
|
-
return {
|
|
42943
|
-
cellDom: cellDom instanceof HTMLTableCellElement ? cellDom : null,
|
|
42944
|
-
tablePos: $from.before(tableDepth)
|
|
43717
|
+
// src/components/UEditor/table-formula-bar.tsx
|
|
43718
|
+
var import_react80 = require("react");
|
|
43719
|
+
var import_react81 = require("@tiptap/react");
|
|
43720
|
+
var import_lucide_react59 = require("lucide-react");
|
|
43721
|
+
var import_jsx_runtime100 = require("react/jsx-runtime");
|
|
43722
|
+
function TableFormulaBar({ editor }) {
|
|
43723
|
+
const t = useSmartTranslations("UEditor");
|
|
43724
|
+
const inputRef = (0, import_react80.useRef)(null);
|
|
43725
|
+
const [draftState, setDraftState] = (0, import_react80.useState)(null);
|
|
43726
|
+
(0, import_react81.useEditorState)({
|
|
43727
|
+
editor,
|
|
43728
|
+
selector: ({ transactionNumber }) => transactionNumber
|
|
43729
|
+
});
|
|
43730
|
+
const selectedCell = getSelectedTableFormulaCell(editor);
|
|
43731
|
+
(0, import_react80.useEffect)(() => {
|
|
43732
|
+
const focusFormulaInput = () => {
|
|
43733
|
+
inputRef.current?.focus();
|
|
42945
43734
|
};
|
|
42946
|
-
|
|
42947
|
-
|
|
42948
|
-
|
|
42949
|
-
|
|
42950
|
-
|
|
42951
|
-
for (let depth = $pos.depth; depth > 0; depth -= 1) {
|
|
42952
|
-
const node = $pos.node(depth);
|
|
42953
|
-
if (node.type.name === "table") {
|
|
42954
|
-
return {
|
|
42955
|
-
node,
|
|
42956
|
-
pos: $pos.before(depth),
|
|
42957
|
-
start: $pos.start(depth)
|
|
42958
|
-
};
|
|
42959
|
-
}
|
|
42960
|
-
}
|
|
42961
|
-
return null;
|
|
42962
|
-
}
|
|
42963
|
-
function getCellRelativePosFromDomPos2(map, tableStart, domPos) {
|
|
42964
|
-
const relativeDomPos = domPos - tableStart;
|
|
42965
|
-
const seen = /* @__PURE__ */ new Set();
|
|
42966
|
-
for (const relativeCellPos of map.map) {
|
|
42967
|
-
if (seen.has(relativeCellPos)) continue;
|
|
42968
|
-
seen.add(relativeCellPos);
|
|
42969
|
-
if (relativeDomPos === relativeCellPos || relativeDomPos === relativeCellPos + 1) {
|
|
42970
|
-
return relativeCellPos;
|
|
42971
|
-
}
|
|
42972
|
-
}
|
|
42973
|
-
return null;
|
|
42974
|
-
}
|
|
42975
|
-
function getPickedCellLabel(view, target, tablePos) {
|
|
42976
|
-
const element = resolveEventElement(target);
|
|
42977
|
-
const cell = element?.closest?.("th,td");
|
|
42978
|
-
if (!(cell instanceof HTMLTableCellElement)) return null;
|
|
42979
|
-
const domPos = view.posAtDOM(cell, 0);
|
|
42980
|
-
const tableInfo = findTableForPos(view, domPos);
|
|
42981
|
-
if (!tableInfo || tableInfo.pos !== tablePos) return null;
|
|
42982
|
-
const map = import_tables7.TableMap.get(tableInfo.node);
|
|
42983
|
-
const relativeCellPos = getCellRelativePosFromDomPos2(map, tableInfo.start, domPos);
|
|
42984
|
-
if (relativeCellPos == null) return null;
|
|
42985
|
-
const rect = map.findCell(relativeCellPos);
|
|
42986
|
-
return {
|
|
42987
|
-
cell,
|
|
42988
|
-
label: `${indexToColumnName(rect.left)}${rect.top + 1}`
|
|
42989
|
-
};
|
|
42990
|
-
}
|
|
42991
|
-
function normalizeFormulaRangeLabel(fromLabel, toLabel) {
|
|
42992
|
-
return fromLabel === toLabel ? fromLabel : `${fromLabel}:${toLabel}`;
|
|
42993
|
-
}
|
|
42994
|
-
function replacePickedLabel(view, pickState, nextLabel, currentCell) {
|
|
42995
|
-
if (nextLabel === pickState.currentLabel && currentCell === pickState.currentCell) return pickState;
|
|
42996
|
-
if (nextLabel === pickState.currentLabel) {
|
|
42997
|
-
return {
|
|
42998
|
-
...pickState,
|
|
42999
|
-
currentCell
|
|
43735
|
+
const handleDoubleClick = (event) => {
|
|
43736
|
+
const target = resolveEventElement(event.target);
|
|
43737
|
+
if (!target?.closest?.("td[data-formula],th[data-formula]")) return;
|
|
43738
|
+
event.preventDefault();
|
|
43739
|
+
focusFormulaInput();
|
|
43000
43740
|
};
|
|
43001
|
-
|
|
43002
|
-
|
|
43003
|
-
|
|
43004
|
-
|
|
43005
|
-
|
|
43006
|
-
return
|
|
43007
|
-
|
|
43008
|
-
|
|
43009
|
-
|
|
43010
|
-
|
|
43741
|
+
editor.view.dom.addEventListener("dblclick", handleDoubleClick);
|
|
43742
|
+
return () => {
|
|
43743
|
+
editor.view.dom.removeEventListener("dblclick", handleDoubleClick);
|
|
43744
|
+
};
|
|
43745
|
+
}, [editor]);
|
|
43746
|
+
if (!selectedCell) return null;
|
|
43747
|
+
const selectedCellKey = `${selectedCell.tablePos}:${selectedCell.row}:${selectedCell.column}`;
|
|
43748
|
+
const draft = draftState?.cellKey === selectedCellKey && draftState.sourceFormula === selectedCell.formula ? draftState.value : selectedCell.formula;
|
|
43749
|
+
const setDraft = (value) => {
|
|
43750
|
+
setDraftState({
|
|
43751
|
+
cellKey: selectedCellKey,
|
|
43752
|
+
sourceFormula: selectedCell.formula,
|
|
43753
|
+
value
|
|
43754
|
+
});
|
|
43011
43755
|
};
|
|
43012
|
-
|
|
43013
|
-
|
|
43014
|
-
|
|
43015
|
-
|
|
43016
|
-
|
|
43017
|
-
|
|
43018
|
-
|
|
43019
|
-
|
|
43020
|
-
let tr = view.state.tr.insertText(picked.label, from, to);
|
|
43021
|
-
tr = tr.setSelection(import_state10.TextSelection.create(tr.doc, from + picked.label.length));
|
|
43022
|
-
view.dispatch(tr);
|
|
43023
|
-
view.focus();
|
|
43024
|
-
event.preventDefault();
|
|
43025
|
-
event.stopPropagation();
|
|
43026
|
-
return {
|
|
43027
|
-
anchorLabel: picked.label,
|
|
43028
|
-
anchorCell: picked.cell,
|
|
43029
|
-
tablePos: formulaCell.tablePos,
|
|
43030
|
-
insertedFrom: from,
|
|
43031
|
-
insertedTo: from + picked.label.length,
|
|
43032
|
-
currentLabel: picked.label,
|
|
43033
|
-
currentCell: picked.cell
|
|
43756
|
+
const applyDraft = () => {
|
|
43757
|
+
const normalized = normalizeFormulaInput(draft);
|
|
43758
|
+
if (normalized) {
|
|
43759
|
+
setSelectedTableCellFormula(editor, normalized);
|
|
43760
|
+
} else {
|
|
43761
|
+
clearSelectedTableCellFormula(editor);
|
|
43762
|
+
}
|
|
43763
|
+
setDraftState(null);
|
|
43034
43764
|
};
|
|
43035
|
-
|
|
43036
|
-
|
|
43037
|
-
|
|
43038
|
-
|
|
43039
|
-
|
|
43040
|
-
event.stopPropagation();
|
|
43041
|
-
return replacePickedLabel(
|
|
43042
|
-
view,
|
|
43043
|
-
pickState,
|
|
43044
|
-
normalizeFormulaRangeLabel(pickState.anchorLabel, picked.label),
|
|
43045
|
-
picked.cell
|
|
43046
|
-
);
|
|
43047
|
-
}
|
|
43048
|
-
function getFormulaRangePickHighlight(container, pickState) {
|
|
43049
|
-
if (!container.contains(pickState.anchorCell) || !container.contains(pickState.currentCell)) return null;
|
|
43050
|
-
const containerRect = container.getBoundingClientRect();
|
|
43051
|
-
const anchorRect = pickState.anchorCell.getBoundingClientRect();
|
|
43052
|
-
const currentRect = pickState.currentCell.getBoundingClientRect();
|
|
43053
|
-
const left = Math.min(anchorRect.left, currentRect.left) - containerRect.left + container.scrollLeft;
|
|
43054
|
-
const top = Math.min(anchorRect.top, currentRect.top) - containerRect.top + container.scrollTop;
|
|
43055
|
-
const right = Math.max(anchorRect.right, currentRect.right) - containerRect.left + container.scrollLeft;
|
|
43056
|
-
const bottom = Math.max(anchorRect.bottom, currentRect.bottom) - containerRect.top + container.scrollTop;
|
|
43057
|
-
return {
|
|
43058
|
-
left,
|
|
43059
|
-
top,
|
|
43060
|
-
width: Math.max(0, right - left),
|
|
43061
|
-
height: Math.max(0, bottom - top)
|
|
43765
|
+
const cancelDraft = () => {
|
|
43766
|
+
setDraftState(null);
|
|
43767
|
+
window.setTimeout(() => {
|
|
43768
|
+
if (!editor.isDestroyed) editor.commands.focus();
|
|
43769
|
+
}, 0);
|
|
43062
43770
|
};
|
|
43771
|
+
return /* @__PURE__ */ (0, import_jsx_runtime100.jsxs)(
|
|
43772
|
+
"div",
|
|
43773
|
+
{
|
|
43774
|
+
"data-ueditor-formula-bar": "",
|
|
43775
|
+
role: "toolbar",
|
|
43776
|
+
"aria-label": t("tableMenu.formulaBar"),
|
|
43777
|
+
className: "flex min-h-10 items-center gap-2 border-b border-border/60 bg-muted/25 px-2 py-1.5",
|
|
43778
|
+
children: [
|
|
43779
|
+
/* @__PURE__ */ (0, import_jsx_runtime100.jsx)(
|
|
43780
|
+
"span",
|
|
43781
|
+
{
|
|
43782
|
+
"data-ueditor-formula-cell-label": "",
|
|
43783
|
+
className: "flex h-7 min-w-12 items-center justify-center rounded-md border border-border/60 bg-background px-2 font-mono text-xs font-semibold text-foreground",
|
|
43784
|
+
children: selectedCell.label
|
|
43785
|
+
}
|
|
43786
|
+
),
|
|
43787
|
+
/* @__PURE__ */ (0, import_jsx_runtime100.jsx)(import_lucide_react59.Sigma, { "aria-hidden": "true", className: "h-4 w-4 shrink-0 text-primary" }),
|
|
43788
|
+
/* @__PURE__ */ (0, import_jsx_runtime100.jsx)(
|
|
43789
|
+
"input",
|
|
43790
|
+
{
|
|
43791
|
+
ref: inputRef,
|
|
43792
|
+
"data-ueditor-formula-input": "",
|
|
43793
|
+
value: draft,
|
|
43794
|
+
onChange: (event) => setDraft(event.target.value),
|
|
43795
|
+
onKeyDown: (event) => {
|
|
43796
|
+
event.stopPropagation();
|
|
43797
|
+
if (event.key === "Enter") {
|
|
43798
|
+
event.preventDefault();
|
|
43799
|
+
applyDraft();
|
|
43800
|
+
} else if (event.key === "Escape") {
|
|
43801
|
+
event.preventDefault();
|
|
43802
|
+
cancelDraft();
|
|
43803
|
+
}
|
|
43804
|
+
},
|
|
43805
|
+
"aria-label": t("tableMenu.editFormula"),
|
|
43806
|
+
className: "h-7 min-w-0 flex-1 rounded-md border border-border/60 bg-background px-2 font-mono text-sm text-foreground outline-none focus:border-primary/40 focus:ring-2 focus:ring-primary/15"
|
|
43807
|
+
}
|
|
43808
|
+
),
|
|
43809
|
+
selectedCell.formulaState === "error" && /* @__PURE__ */ (0, import_jsx_runtime100.jsxs)(
|
|
43810
|
+
"span",
|
|
43811
|
+
{
|
|
43812
|
+
role: "status",
|
|
43813
|
+
className: "hidden shrink-0 items-center gap-1 text-xs font-medium text-destructive sm:inline-flex",
|
|
43814
|
+
children: [
|
|
43815
|
+
/* @__PURE__ */ (0, import_jsx_runtime100.jsx)(import_lucide_react59.AlertCircle, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
|
|
43816
|
+
t("tableMenu.formulaError")
|
|
43817
|
+
]
|
|
43818
|
+
}
|
|
43819
|
+
),
|
|
43820
|
+
/* @__PURE__ */ (0, import_jsx_runtime100.jsx)(
|
|
43821
|
+
"button",
|
|
43822
|
+
{
|
|
43823
|
+
type: "button",
|
|
43824
|
+
onClick: applyDraft,
|
|
43825
|
+
"aria-label": t("tableMenu.apply"),
|
|
43826
|
+
title: t("tableMenu.apply"),
|
|
43827
|
+
className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-primary text-primary-foreground transition-colors hover:bg-primary/90",
|
|
43828
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(import_lucide_react59.Check, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
43829
|
+
}
|
|
43830
|
+
),
|
|
43831
|
+
/* @__PURE__ */ (0, import_jsx_runtime100.jsx)(
|
|
43832
|
+
"button",
|
|
43833
|
+
{
|
|
43834
|
+
type: "button",
|
|
43835
|
+
onClick: () => {
|
|
43836
|
+
convertSelectedTableCellFormulaToValue(editor);
|
|
43837
|
+
setDraftState(null);
|
|
43838
|
+
},
|
|
43839
|
+
"aria-label": t("tableMenu.convertToValue"),
|
|
43840
|
+
title: t("tableMenu.convertToValue"),
|
|
43841
|
+
className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md border border-border/60 bg-background text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
|
|
43842
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(import_lucide_react59.Hash, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
43843
|
+
}
|
|
43844
|
+
),
|
|
43845
|
+
/* @__PURE__ */ (0, import_jsx_runtime100.jsx)(
|
|
43846
|
+
"button",
|
|
43847
|
+
{
|
|
43848
|
+
type: "button",
|
|
43849
|
+
onClick: () => {
|
|
43850
|
+
clearSelectedTableCellFormula(editor);
|
|
43851
|
+
setDraftState(null);
|
|
43852
|
+
},
|
|
43853
|
+
"aria-label": t("tableMenu.clearCell"),
|
|
43854
|
+
title: t("tableMenu.clearCell"),
|
|
43855
|
+
className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md border border-destructive/30 bg-background text-destructive transition-colors hover:bg-destructive/10",
|
|
43856
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(import_lucide_react59.Trash2, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
43857
|
+
}
|
|
43858
|
+
)
|
|
43859
|
+
]
|
|
43860
|
+
}
|
|
43861
|
+
);
|
|
43063
43862
|
}
|
|
43064
43863
|
|
|
43065
43864
|
// src/components/UEditor/UEditor.tsx
|
|
43066
|
-
var
|
|
43067
|
-
var UEditor =
|
|
43865
|
+
var import_jsx_runtime101 = require("react/jsx-runtime");
|
|
43866
|
+
var UEditor = import_react82.default.forwardRef(({
|
|
43068
43867
|
content = "",
|
|
43069
43868
|
onChange,
|
|
43070
43869
|
onHtmlChange,
|
|
@@ -43082,6 +43881,7 @@ var UEditor = import_react79.default.forwardRef(({
|
|
|
43082
43881
|
autofocus = false,
|
|
43083
43882
|
showToolbar = true,
|
|
43084
43883
|
showBubbleMenu = true,
|
|
43884
|
+
showFloatingMenu = false,
|
|
43085
43885
|
showCharacterCount = true,
|
|
43086
43886
|
showFooter = true,
|
|
43087
43887
|
maxCharacters,
|
|
@@ -43105,13 +43905,14 @@ var UEditor = import_react79.default.forwardRef(({
|
|
|
43105
43905
|
}, ref) => {
|
|
43106
43906
|
const t = useSmartTranslations("UEditor");
|
|
43107
43907
|
const effectivePlaceholder = placeholder ?? t("placeholder");
|
|
43108
|
-
const inFlightPrepareRef = (0,
|
|
43109
|
-
const lastAppliedContentRef = (0,
|
|
43110
|
-
const scheduledFormulaRecalculateRef = (0,
|
|
43111
|
-
const
|
|
43112
|
-
const
|
|
43113
|
-
const
|
|
43114
|
-
const
|
|
43908
|
+
const inFlightPrepareRef = (0, import_react82.useRef)(null);
|
|
43909
|
+
const lastAppliedContentRef = (0, import_react82.useRef)(content ?? "");
|
|
43910
|
+
const scheduledFormulaRecalculateRef = (0, import_react82.useRef)(false);
|
|
43911
|
+
const editorInstanceRef = (0, import_react82.useRef)(null);
|
|
43912
|
+
const formulaRangePickRef = (0, import_react82.useRef)(null);
|
|
43913
|
+
const formulaRangeSurfaceRef = (0, import_react82.useRef)(null);
|
|
43914
|
+
const [formulaRangeHighlight, setFormulaRangeHighlight] = import_react82.default.useState(null);
|
|
43915
|
+
const scheduleFormulaRecalculate = import_react82.default.useCallback((editor2, options) => {
|
|
43115
43916
|
if (editor2.isDestroyed || scheduledFormulaRecalculateRef.current) return;
|
|
43116
43917
|
if (!options?.force && isEditingTableFormulaText(editor2)) return;
|
|
43117
43918
|
scheduledFormulaRecalculateRef.current = true;
|
|
@@ -43122,7 +43923,7 @@ var UEditor = import_react79.default.forwardRef(({
|
|
|
43122
43923
|
}
|
|
43123
43924
|
});
|
|
43124
43925
|
}, []);
|
|
43125
|
-
const resolvedUploadFile = (0,
|
|
43926
|
+
const resolvedUploadFile = (0, import_react82.useMemo)(() => {
|
|
43126
43927
|
if (uploadFile) return uploadFile;
|
|
43127
43928
|
if (uploadFileForSave) {
|
|
43128
43929
|
return async (file) => {
|
|
@@ -43132,7 +43933,7 @@ var UEditor = import_react79.default.forwardRef(({
|
|
|
43132
43933
|
}
|
|
43133
43934
|
return uploadImage;
|
|
43134
43935
|
}, [uploadFile, uploadFileForSave, uploadImage]);
|
|
43135
|
-
const extensions = (0,
|
|
43936
|
+
const extensions = (0, import_react82.useMemo)(
|
|
43136
43937
|
() => [
|
|
43137
43938
|
...buildUEditorExtensions({
|
|
43138
43939
|
placeholder: effectivePlaceholder,
|
|
@@ -43151,11 +43952,11 @@ var UEditor = import_react79.default.forwardRef(({
|
|
|
43151
43952
|
],
|
|
43152
43953
|
[effectivePlaceholder, t, maxCharacters, uploadImage, resolvedUploadFile, imageInsertMode, maxImageFileSize, allowedImageMimeTypes, fallbackToDataUrl, editable, fetchMetadata, extraExtensions]
|
|
43153
43954
|
);
|
|
43154
|
-
const syncFormulaRangeHighlight =
|
|
43955
|
+
const syncFormulaRangeHighlight = import_react82.default.useCallback((pickState) => {
|
|
43155
43956
|
const container = formulaRangeSurfaceRef.current;
|
|
43156
43957
|
setFormulaRangeHighlight(container && pickState ? getFormulaRangePickHighlight(container, pickState) : null);
|
|
43157
43958
|
}, []);
|
|
43158
|
-
const editor = (0,
|
|
43959
|
+
const editor = (0, import_react83.useEditor)({
|
|
43159
43960
|
immediatelyRender: false,
|
|
43160
43961
|
extensions,
|
|
43161
43962
|
content,
|
|
@@ -43196,6 +43997,29 @@ var UEditor = import_react79.default.forwardRef(({
|
|
|
43196
43997
|
},
|
|
43197
43998
|
keydown: (_view, event) => {
|
|
43198
43999
|
if (!(event instanceof KeyboardEvent)) return false;
|
|
44000
|
+
const formulaContext = getFormulaEditingTableContext(_view);
|
|
44001
|
+
if (formulaContext && event.key === "Enter") {
|
|
44002
|
+
event.preventDefault();
|
|
44003
|
+
event.stopPropagation();
|
|
44004
|
+
const activeEditor2 = editorInstanceRef.current;
|
|
44005
|
+
if (activeEditor2 && !isDraftTableFormula(formulaContext.formula)) {
|
|
44006
|
+
recalculateActiveTableFormulas(activeEditor2);
|
|
44007
|
+
}
|
|
44008
|
+
return true;
|
|
44009
|
+
}
|
|
44010
|
+
if (formulaContext && event.key === "Escape") {
|
|
44011
|
+
event.preventDefault();
|
|
44012
|
+
event.stopPropagation();
|
|
44013
|
+
cancelFormulaEditing(_view);
|
|
44014
|
+
return true;
|
|
44015
|
+
}
|
|
44016
|
+
const activeEditor = editorInstanceRef.current;
|
|
44017
|
+
if (activeEditor && (event.key === "Backspace" || event.key === "Delete") && getSelectedTableFormulaCell(activeEditor)) {
|
|
44018
|
+
event.preventDefault();
|
|
44019
|
+
event.stopPropagation();
|
|
44020
|
+
clearSelectedTableCellFormula(activeEditor);
|
|
44021
|
+
return true;
|
|
44022
|
+
}
|
|
43199
44023
|
if (event.key === "ArrowLeft" || event.key === "ArrowRight" || event.key === "ArrowUp" || event.key === "ArrowDown") {
|
|
43200
44024
|
event.stopPropagation();
|
|
43201
44025
|
}
|
|
@@ -43206,7 +44030,7 @@ var UEditor = import_react79.default.forwardRef(({
|
|
|
43206
44030
|
if (event.button !== 0) return false;
|
|
43207
44031
|
const target = resolveEventElement(event.target);
|
|
43208
44032
|
const anchor = target?.closest?.("a[href]");
|
|
43209
|
-
const href = anchor?.getAttribute("href") ?? "";
|
|
44033
|
+
const href = sanitizeUEditorUrl(anchor?.getAttribute("href") ?? "", "link");
|
|
43210
44034
|
if (!href) return false;
|
|
43211
44035
|
if (editable) return false;
|
|
43212
44036
|
if (!view.state.selection.empty) return false;
|
|
@@ -43232,17 +44056,29 @@ var UEditor = import_react79.default.forwardRef(({
|
|
|
43232
44056
|
onSelectionUpdate: ({ editor: editor2 }) => {
|
|
43233
44057
|
scheduleFormulaRecalculate(editor2);
|
|
43234
44058
|
},
|
|
43235
|
-
onBlur: ({ editor: editor2 }) => {
|
|
44059
|
+
onBlur: ({ editor: editor2, event }) => {
|
|
44060
|
+
const nextTarget = event.relatedTarget;
|
|
44061
|
+
if (nextTarget instanceof Element && nextTarget.closest("[data-ueditor-formula-bar]")) return;
|
|
43236
44062
|
scheduleFormulaRecalculate(editor2, { force: true });
|
|
43237
44063
|
}
|
|
43238
44064
|
});
|
|
44065
|
+
(0, import_react82.useEffect)(() => {
|
|
44066
|
+
editorInstanceRef.current = editor;
|
|
44067
|
+
return () => {
|
|
44068
|
+
if (editorInstanceRef.current === editor) editorInstanceRef.current = null;
|
|
44069
|
+
};
|
|
44070
|
+
}, [editor]);
|
|
43239
44071
|
const {
|
|
43240
44072
|
editorContentRef,
|
|
43241
44073
|
tableColumnGuideRef,
|
|
43242
44074
|
tableRowGuideRef,
|
|
43243
44075
|
activeTableCellHighlightRef
|
|
43244
44076
|
} = useUEditorTableInteractions(editor, editable);
|
|
43245
|
-
(
|
|
44077
|
+
const formulaCoordinateOverlayRef = useFormulaCoordinateOverlay(editor, editorContentRef, {
|
|
44078
|
+
apply: t("tableMenu.apply"),
|
|
44079
|
+
cancel: t("imageInput.cancelBtn")
|
|
44080
|
+
});
|
|
44081
|
+
(0, import_react82.useImperativeHandle)(
|
|
43246
44082
|
ref,
|
|
43247
44083
|
() => ({
|
|
43248
44084
|
editor,
|
|
@@ -43267,7 +44103,7 @@ var UEditor = import_react79.default.forwardRef(({
|
|
|
43267
44103
|
}),
|
|
43268
44104
|
[content, editor, uploadImageForSave, uploadFileForSave, uploadImageConcurrency]
|
|
43269
44105
|
);
|
|
43270
|
-
(0,
|
|
44106
|
+
(0, import_react82.useEffect)(() => {
|
|
43271
44107
|
if (!editor) return;
|
|
43272
44108
|
queueMicrotask(() => {
|
|
43273
44109
|
if (!editor.isDestroyed) {
|
|
@@ -43275,7 +44111,7 @@ var UEditor = import_react79.default.forwardRef(({
|
|
|
43275
44111
|
}
|
|
43276
44112
|
});
|
|
43277
44113
|
}, [editor]);
|
|
43278
|
-
(0,
|
|
44114
|
+
(0, import_react82.useEffect)(() => {
|
|
43279
44115
|
if (!editor) return;
|
|
43280
44116
|
const nextContent = content ?? "";
|
|
43281
44117
|
if (lastAppliedContentRef.current === nextContent) return;
|
|
@@ -43294,7 +44130,7 @@ var UEditor = import_react79.default.forwardRef(({
|
|
|
43294
44130
|
}
|
|
43295
44131
|
}, [content, editor]);
|
|
43296
44132
|
if (!editor) {
|
|
43297
|
-
return /* @__PURE__ */ (0,
|
|
44133
|
+
return /* @__PURE__ */ (0, import_jsx_runtime101.jsx)(
|
|
43298
44134
|
"div",
|
|
43299
44135
|
{
|
|
43300
44136
|
className: cn("w-full border bg-background flex items-center justify-center text-muted-foreground", className),
|
|
@@ -43303,7 +44139,7 @@ var UEditor = import_react79.default.forwardRef(({
|
|
|
43303
44139
|
}
|
|
43304
44140
|
);
|
|
43305
44141
|
}
|
|
43306
|
-
return /* @__PURE__ */ (0,
|
|
44142
|
+
return /* @__PURE__ */ (0, import_jsx_runtime101.jsxs)(
|
|
43307
44143
|
"div",
|
|
43308
44144
|
{
|
|
43309
44145
|
className: cn(
|
|
@@ -43319,7 +44155,7 @@ var UEditor = import_react79.default.forwardRef(({
|
|
|
43319
44155
|
className
|
|
43320
44156
|
),
|
|
43321
44157
|
children: [
|
|
43322
|
-
editable && showMenuBar && /* @__PURE__ */ (0,
|
|
44158
|
+
editable && showMenuBar && /* @__PURE__ */ (0, import_jsx_runtime101.jsx)(
|
|
43323
44159
|
MenuBar,
|
|
43324
44160
|
{
|
|
43325
44161
|
editor,
|
|
@@ -43334,7 +44170,7 @@ var UEditor = import_react79.default.forwardRef(({
|
|
|
43334
44170
|
onPreview
|
|
43335
44171
|
}
|
|
43336
44172
|
),
|
|
43337
|
-
editable && showToolbar && /* @__PURE__ */ (0,
|
|
44173
|
+
editable && showToolbar && /* @__PURE__ */ (0, import_jsx_runtime101.jsx)(
|
|
43338
44174
|
EditorToolbar,
|
|
43339
44175
|
{
|
|
43340
44176
|
editor,
|
|
@@ -43349,7 +44185,8 @@ var UEditor = import_react79.default.forwardRef(({
|
|
|
43349
44185
|
letterSpacings
|
|
43350
44186
|
}
|
|
43351
44187
|
),
|
|
43352
|
-
editable &&
|
|
44188
|
+
editable && /* @__PURE__ */ (0, import_jsx_runtime101.jsx)(TableFormulaBar, { editor }),
|
|
44189
|
+
editable && showBubbleMenu && /* @__PURE__ */ (0, import_jsx_runtime101.jsx)(
|
|
43353
44190
|
CustomBubbleMenu,
|
|
43354
44191
|
{
|
|
43355
44192
|
editor,
|
|
@@ -43357,7 +44194,8 @@ var UEditor = import_react79.default.forwardRef(({
|
|
|
43357
44194
|
lineHeights
|
|
43358
44195
|
}
|
|
43359
44196
|
),
|
|
43360
|
-
/* @__PURE__ */ (0,
|
|
44197
|
+
editable && showFloatingMenu && /* @__PURE__ */ (0, import_jsx_runtime101.jsx)(CustomFloatingMenu, { editor }),
|
|
44198
|
+
/* @__PURE__ */ (0, import_jsx_runtime101.jsxs)(
|
|
43361
44199
|
"div",
|
|
43362
44200
|
{
|
|
43363
44201
|
ref: (node) => {
|
|
@@ -43370,7 +44208,7 @@ var UEditor = import_react79.default.forwardRef(({
|
|
|
43370
44208
|
maxHeight
|
|
43371
44209
|
},
|
|
43372
44210
|
children: [
|
|
43373
|
-
/* @__PURE__ */ (0,
|
|
44211
|
+
/* @__PURE__ */ (0, import_jsx_runtime101.jsx)(
|
|
43374
44212
|
"span",
|
|
43375
44213
|
{
|
|
43376
44214
|
ref: tableColumnGuideRef,
|
|
@@ -43378,7 +44216,7 @@ var UEditor = import_react79.default.forwardRef(({
|
|
|
43378
44216
|
className: "pointer-events-none absolute z-20 bg-primary opacity-0 transition-opacity duration-100"
|
|
43379
44217
|
}
|
|
43380
44218
|
),
|
|
43381
|
-
/* @__PURE__ */ (0,
|
|
44219
|
+
/* @__PURE__ */ (0, import_jsx_runtime101.jsx)(
|
|
43382
44220
|
"span",
|
|
43383
44221
|
{
|
|
43384
44222
|
ref: tableRowGuideRef,
|
|
@@ -43386,7 +44224,7 @@ var UEditor = import_react79.default.forwardRef(({
|
|
|
43386
44224
|
className: "pointer-events-none absolute z-20 bg-primary opacity-0 transition-opacity duration-100"
|
|
43387
44225
|
}
|
|
43388
44226
|
),
|
|
43389
|
-
/* @__PURE__ */ (0,
|
|
44227
|
+
/* @__PURE__ */ (0, import_jsx_runtime101.jsx)(
|
|
43390
44228
|
"span",
|
|
43391
44229
|
{
|
|
43392
44230
|
ref: activeTableCellHighlightRef,
|
|
@@ -43395,7 +44233,15 @@ var UEditor = import_react79.default.forwardRef(({
|
|
|
43395
44233
|
className: "pointer-events-none hidden absolute z-20 rounded-[2px] border-2 border-primary bg-primary/10"
|
|
43396
44234
|
}
|
|
43397
44235
|
),
|
|
43398
|
-
|
|
44236
|
+
/* @__PURE__ */ (0, import_jsx_runtime101.jsx)(
|
|
44237
|
+
"div",
|
|
44238
|
+
{
|
|
44239
|
+
ref: formulaCoordinateOverlayRef,
|
|
44240
|
+
"data-ueditor-formula-coordinate-overlay": "",
|
|
44241
|
+
className: "pointer-events-none absolute inset-0 z-[21] hidden overflow-visible"
|
|
44242
|
+
}
|
|
44243
|
+
),
|
|
44244
|
+
formulaRangeHighlight && /* @__PURE__ */ (0, import_jsx_runtime101.jsx)(
|
|
43399
44245
|
"span",
|
|
43400
44246
|
{
|
|
43401
44247
|
"aria-hidden": "true",
|
|
@@ -43409,9 +44255,9 @@ var UEditor = import_react79.default.forwardRef(({
|
|
|
43409
44255
|
}
|
|
43410
44256
|
}
|
|
43411
44257
|
),
|
|
43412
|
-
editable && /* @__PURE__ */ (0,
|
|
43413
|
-
/* @__PURE__ */ (0,
|
|
43414
|
-
|
|
44258
|
+
editable && /* @__PURE__ */ (0, import_jsx_runtime101.jsx)(TableControls, { editor, containerRef: editorContentRef }),
|
|
44259
|
+
/* @__PURE__ */ (0, import_jsx_runtime101.jsx)(
|
|
44260
|
+
import_react83.EditorContent,
|
|
43415
44261
|
{
|
|
43416
44262
|
editor,
|
|
43417
44263
|
className: "min-h-full"
|
|
@@ -43420,7 +44266,7 @@ var UEditor = import_react79.default.forwardRef(({
|
|
|
43420
44266
|
]
|
|
43421
44267
|
}
|
|
43422
44268
|
),
|
|
43423
|
-
showFooter && showCharacterCount && /* @__PURE__ */ (0,
|
|
44269
|
+
showFooter && showCharacterCount && /* @__PURE__ */ (0, import_jsx_runtime101.jsx)(CharacterCountDisplay, { editor, maxCharacters })
|
|
43424
44270
|
]
|
|
43425
44271
|
}
|
|
43426
44272
|
);
|