@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.js
CHANGED
|
@@ -891,6 +891,11 @@ var en_default = {
|
|
|
891
891
|
borderColor: "Border Color",
|
|
892
892
|
clearBorder: "Clear Border",
|
|
893
893
|
formula: "Formula",
|
|
894
|
+
formulaBar: "Formula bar",
|
|
895
|
+
formulaError: "Formula error",
|
|
896
|
+
editFormula: "Edit formula",
|
|
897
|
+
clearCell: "Clear cell",
|
|
898
|
+
convertToValue: "Convert to value",
|
|
894
899
|
numberFormat: "Number Format",
|
|
895
900
|
formatText: "Text",
|
|
896
901
|
formatDate: "Date",
|
|
@@ -1313,6 +1318,11 @@ var vi_default = {
|
|
|
1313
1318
|
borderColor: "M\xE0u vi\u1EC1n",
|
|
1314
1319
|
clearBorder: "X\xF3a vi\u1EC1n",
|
|
1315
1320
|
formula: "C\xF4ng th\u1EE9c",
|
|
1321
|
+
formulaBar: "Thanh c\xF4ng th\u1EE9c",
|
|
1322
|
+
formulaError: "L\u1ED7i c\xF4ng th\u1EE9c",
|
|
1323
|
+
editFormula: "S\u1EEDa c\xF4ng th\u1EE9c",
|
|
1324
|
+
clearCell: "X\xF3a n\u1ED9i dung \xF4",
|
|
1325
|
+
convertToValue: "Chuy\u1EC3n th\xE0nh gi\xE1 tr\u1ECB",
|
|
1316
1326
|
numberFormat: "\u0110\u1ECBnh d\u1EA1ng s\u1ED1",
|
|
1317
1327
|
formatText: "Ch\u1EEF",
|
|
1318
1328
|
formatDate: "Ng\xE0y",
|
|
@@ -1735,6 +1745,11 @@ var ko_default = {
|
|
|
1735
1745
|
borderColor: "\uD14C\uB450\uB9AC \uC0C9\uC0C1",
|
|
1736
1746
|
clearBorder: "\uD14C\uB450\uB9AC \uC9C0\uC6B0\uAE30",
|
|
1737
1747
|
formula: "\uC218\uC2DD",
|
|
1748
|
+
formulaBar: "\uC218\uC2DD \uC785\uB825\uC904",
|
|
1749
|
+
formulaError: "\uC218\uC2DD \uC624\uB958",
|
|
1750
|
+
editFormula: "\uC218\uC2DD \uD3B8\uC9D1",
|
|
1751
|
+
clearCell: "\uC140 \uB0B4\uC6A9 \uC0AD\uC81C",
|
|
1752
|
+
convertToValue: "\uAC12\uC73C\uB85C \uBCC0\uD658",
|
|
1738
1753
|
numberFormat: "\uD45C\uC2DC \uD615\uC2DD",
|
|
1739
1754
|
formatText: "\uD14D\uC2A4\uD2B8",
|
|
1740
1755
|
formatDate: "\uB0A0\uC9DC",
|
|
@@ -2157,6 +2172,11 @@ var ja_default = {
|
|
|
2157
2172
|
borderColor: "\u67A0\u7DDA\u306E\u8272",
|
|
2158
2173
|
clearBorder: "\u67A0\u7DDA\u3092\u30AF\u30EA\u30A2",
|
|
2159
2174
|
formula: "\u6570\u5F0F",
|
|
2175
|
+
formulaBar: "\u6570\u5F0F\u30D0\u30FC",
|
|
2176
|
+
formulaError: "\u6570\u5F0F\u30A8\u30E9\u30FC",
|
|
2177
|
+
editFormula: "\u6570\u5F0F\u3092\u7DE8\u96C6",
|
|
2178
|
+
clearCell: "\u30BB\u30EB\u3092\u30AF\u30EA\u30A2",
|
|
2179
|
+
convertToValue: "\u5024\u306B\u5909\u63DB",
|
|
2160
2180
|
numberFormat: "\u8868\u793A\u5F62\u5F0F",
|
|
2161
2181
|
formatText: "\u30C6\u30AD\u30B9\u30C8",
|
|
2162
2182
|
formatDate: "\u65E5\u4ED8",
|
|
@@ -27369,7 +27389,7 @@ function useLocale2() {
|
|
|
27369
27389
|
}
|
|
27370
27390
|
|
|
27371
27391
|
// src/components/UEditor/UEditor.tsx
|
|
27372
|
-
import
|
|
27392
|
+
import React88, { useEffect as useEffect41, useImperativeHandle as useImperativeHandle4, useMemo as useMemo27, useRef as useRef40 } from "react";
|
|
27373
27393
|
import { useEditor, EditorContent } from "@tiptap/react";
|
|
27374
27394
|
|
|
27375
27395
|
// src/components/UEditor/extensions.ts
|
|
@@ -27532,6 +27552,54 @@ import { ReactNodeViewRenderer as ReactNodeViewRenderer2 } from "@tiptap/react";
|
|
|
27532
27552
|
import React72, { useEffect as useEffect32, useState as useState44 } from "react";
|
|
27533
27553
|
import { NodeViewWrapper as NodeViewWrapper2 } from "@tiptap/react";
|
|
27534
27554
|
import { AlertCircle as AlertCircle4, ExternalLink as ExternalLink2, Globe as Globe2, RefreshCw } from "lucide-react";
|
|
27555
|
+
|
|
27556
|
+
// src/components/UEditor/url-safety.ts
|
|
27557
|
+
var LINK_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:", "mailto:", "tel:"]);
|
|
27558
|
+
var IMAGE_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:"]);
|
|
27559
|
+
var FILE_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:", "blob:"]);
|
|
27560
|
+
function normalizeUrlInput(raw) {
|
|
27561
|
+
return raw.trim().replace(/[\u0000-\u001F\u007F\s]+/g, "");
|
|
27562
|
+
}
|
|
27563
|
+
function isProtocolRelativeUrl(value) {
|
|
27564
|
+
return value.startsWith("//");
|
|
27565
|
+
}
|
|
27566
|
+
function isRelativeUrl(value) {
|
|
27567
|
+
return value.startsWith("/") || value.startsWith("./") || value.startsWith("../") || value.startsWith("#");
|
|
27568
|
+
}
|
|
27569
|
+
function isDataImageUrl(value) {
|
|
27570
|
+
return /^data:image\/(?:png|jpe?g|gif|webp|svg\+xml|bmp|x-icon|avif);base64,/i.test(value);
|
|
27571
|
+
}
|
|
27572
|
+
function isDataFileUrl(value) {
|
|
27573
|
+
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);
|
|
27574
|
+
}
|
|
27575
|
+
function isSafeUEditorUrl(raw, kind) {
|
|
27576
|
+
const value = normalizeUrlInput(raw);
|
|
27577
|
+
if (!value) return false;
|
|
27578
|
+
if (kind === "image" && isDataImageUrl(value)) return true;
|
|
27579
|
+
if (kind === "file" && isDataFileUrl(value)) return true;
|
|
27580
|
+
if (isProtocolRelativeUrl(value)) return false;
|
|
27581
|
+
if (isRelativeUrl(value)) return true;
|
|
27582
|
+
try {
|
|
27583
|
+
const parsed = new URL(value);
|
|
27584
|
+
if (kind === "image") return IMAGE_PROTOCOLS.has(parsed.protocol);
|
|
27585
|
+
if (kind === "file") return FILE_PROTOCOLS.has(parsed.protocol);
|
|
27586
|
+
return LINK_PROTOCOLS.has(parsed.protocol);
|
|
27587
|
+
} catch {
|
|
27588
|
+
return false;
|
|
27589
|
+
}
|
|
27590
|
+
}
|
|
27591
|
+
function sanitizeUEditorUrl(raw, kind) {
|
|
27592
|
+
const value = raw.trim();
|
|
27593
|
+
if (!value) return "";
|
|
27594
|
+
if (isSafeUEditorUrl(value, kind)) return normalizeUrlInput(value);
|
|
27595
|
+
if (kind === "link" && !isProtocolRelativeUrl(value) && !/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(value)) {
|
|
27596
|
+
const withProtocol = `https://${value}`;
|
|
27597
|
+
return isSafeUEditorUrl(withProtocol, kind) ? withProtocol : "";
|
|
27598
|
+
}
|
|
27599
|
+
return "";
|
|
27600
|
+
}
|
|
27601
|
+
|
|
27602
|
+
// src/components/UEditor/BookmarkView.tsx
|
|
27535
27603
|
import { jsx as jsx80, jsxs as jsxs65 } from "react/jsx-runtime";
|
|
27536
27604
|
var BookmarkView = ({ node, updateAttributes, selected, editor }) => {
|
|
27537
27605
|
const t = useSmartTranslations("UEditor");
|
|
@@ -27539,17 +27607,25 @@ var BookmarkView = ({ node, updateAttributes, selected, editor }) => {
|
|
|
27539
27607
|
const [loading2, setLoading] = useState44(false);
|
|
27540
27608
|
const [error, setError] = useState44(false);
|
|
27541
27609
|
const [retryToken, setRetryToken] = useState44(0);
|
|
27542
|
-
const
|
|
27610
|
+
const lastFetchKeyRef = React72.useRef("");
|
|
27543
27611
|
const fetchRequestIdRef = React72.useRef(0);
|
|
27612
|
+
const safeUrl = sanitizeUEditorUrl(String(url ?? ""), "link");
|
|
27613
|
+
const safeImage = sanitizeUEditorUrl(String(image ?? ""), "image");
|
|
27614
|
+
const clickToOpenNewTabMessage = t("bookmark.clickToOpenNewTab");
|
|
27615
|
+
const clickToOpenLinkMessage = t("bookmark.clickToOpenLink");
|
|
27616
|
+
const noDescriptionMessage = t("bookmark.noDescription");
|
|
27544
27617
|
useEffect32(() => {
|
|
27545
27618
|
if (title && retryToken === 0) return;
|
|
27546
|
-
if (
|
|
27547
|
-
|
|
27619
|
+
if (!safeUrl) return;
|
|
27620
|
+
const fetchKey = `${safeUrl}\0${retryToken}`;
|
|
27621
|
+
if (lastFetchKeyRef.current === fetchKey) return;
|
|
27622
|
+
lastFetchKeyRef.current = fetchKey;
|
|
27548
27623
|
const requestId = fetchRequestIdRef.current + 1;
|
|
27549
27624
|
fetchRequestIdRef.current = requestId;
|
|
27625
|
+
let active = true;
|
|
27550
27626
|
const safeUpdateAttributes = (attrs) => {
|
|
27551
27627
|
Promise.resolve().then(() => {
|
|
27552
|
-
if (fetchRequestIdRef.current === requestId && editor && !editor.isDestroyed) {
|
|
27628
|
+
if (active && fetchRequestIdRef.current === requestId && editor && !editor.isDestroyed) {
|
|
27553
27629
|
updateAttributes(attrs);
|
|
27554
27630
|
}
|
|
27555
27631
|
});
|
|
@@ -27558,43 +27634,75 @@ var BookmarkView = ({ node, updateAttributes, selected, editor }) => {
|
|
|
27558
27634
|
const fetchMetadata = extension?.options?.fetchMetadata;
|
|
27559
27635
|
if (!fetchMetadata) {
|
|
27560
27636
|
try {
|
|
27561
|
-
const hostname = new URL(
|
|
27637
|
+
const hostname = new URL(safeUrl).hostname;
|
|
27562
27638
|
safeUpdateAttributes({
|
|
27563
|
-
title: hostname ||
|
|
27564
|
-
description:
|
|
27639
|
+
title: hostname || safeUrl,
|
|
27640
|
+
description: clickToOpenNewTabMessage,
|
|
27565
27641
|
publisher: hostname
|
|
27566
27642
|
});
|
|
27567
27643
|
} catch {
|
|
27568
27644
|
safeUpdateAttributes({
|
|
27569
|
-
title:
|
|
27570
|
-
description:
|
|
27645
|
+
title: safeUrl,
|
|
27646
|
+
description: clickToOpenLinkMessage,
|
|
27571
27647
|
publisher: ""
|
|
27572
27648
|
});
|
|
27573
27649
|
}
|
|
27574
|
-
return
|
|
27650
|
+
return () => {
|
|
27651
|
+
active = false;
|
|
27652
|
+
};
|
|
27575
27653
|
}
|
|
27576
|
-
|
|
27577
|
-
|
|
27578
|
-
|
|
27579
|
-
|
|
27654
|
+
queueMicrotask(() => {
|
|
27655
|
+
if (!active) return;
|
|
27656
|
+
setLoading(true);
|
|
27657
|
+
setError(false);
|
|
27658
|
+
});
|
|
27659
|
+
fetchMetadata(safeUrl).then((meta) => {
|
|
27660
|
+
if (!active || fetchRequestIdRef.current !== requestId) return;
|
|
27661
|
+
const hostname = (() => {
|
|
27662
|
+
try {
|
|
27663
|
+
return new URL(safeUrl).hostname;
|
|
27664
|
+
} catch {
|
|
27665
|
+
return "";
|
|
27666
|
+
}
|
|
27667
|
+
})();
|
|
27580
27668
|
safeUpdateAttributes({
|
|
27581
|
-
title: meta.title ||
|
|
27582
|
-
description: meta.description ||
|
|
27583
|
-
image: meta.image
|
|
27584
|
-
publisher: meta.publisher ||
|
|
27669
|
+
title: meta.title || hostname || safeUrl,
|
|
27670
|
+
description: meta.description || noDescriptionMessage,
|
|
27671
|
+
image: meta.image ? sanitizeUEditorUrl(meta.image, "image") : "",
|
|
27672
|
+
publisher: meta.publisher || hostname
|
|
27585
27673
|
});
|
|
27586
27674
|
setLoading(false);
|
|
27587
27675
|
}).catch(() => {
|
|
27588
|
-
if (fetchRequestIdRef.current !== requestId) return;
|
|
27676
|
+
if (!active || fetchRequestIdRef.current !== requestId) return;
|
|
27589
27677
|
setError(true);
|
|
27590
27678
|
setLoading(false);
|
|
27591
27679
|
});
|
|
27592
|
-
return
|
|
27593
|
-
|
|
27594
|
-
|
|
27595
|
-
|
|
27596
|
-
|
|
27597
|
-
|
|
27680
|
+
return () => {
|
|
27681
|
+
active = false;
|
|
27682
|
+
};
|
|
27683
|
+
}, [
|
|
27684
|
+
safeUrl,
|
|
27685
|
+
title,
|
|
27686
|
+
editor,
|
|
27687
|
+
updateAttributes,
|
|
27688
|
+
retryToken,
|
|
27689
|
+
clickToOpenNewTabMessage,
|
|
27690
|
+
clickToOpenLinkMessage,
|
|
27691
|
+
noDescriptionMessage
|
|
27692
|
+
]);
|
|
27693
|
+
const openLink = () => {
|
|
27694
|
+
if (safeUrl) window.open(safeUrl, "_blank", "noopener,noreferrer");
|
|
27695
|
+
};
|
|
27696
|
+
const handleOpenLink = (event) => {
|
|
27697
|
+
event.preventDefault();
|
|
27698
|
+
event.stopPropagation();
|
|
27699
|
+
openLink();
|
|
27700
|
+
};
|
|
27701
|
+
const handleKeyDown2 = (event) => {
|
|
27702
|
+
if (event.key !== "Enter") return;
|
|
27703
|
+
event.preventDefault();
|
|
27704
|
+
event.stopPropagation();
|
|
27705
|
+
openLink();
|
|
27598
27706
|
};
|
|
27599
27707
|
const handleRetryPreview = (event) => {
|
|
27600
27708
|
event.preventDefault();
|
|
@@ -27603,7 +27711,7 @@ var BookmarkView = ({ node, updateAttributes, selected, editor }) => {
|
|
|
27603
27711
|
};
|
|
27604
27712
|
const displayPublisher = publisher || (() => {
|
|
27605
27713
|
try {
|
|
27606
|
-
return new URL(
|
|
27714
|
+
return safeUrl ? new URL(safeUrl).hostname : "";
|
|
27607
27715
|
} catch {
|
|
27608
27716
|
return "";
|
|
27609
27717
|
}
|
|
@@ -27616,6 +27724,10 @@ var BookmarkView = ({ node, updateAttributes, selected, editor }) => {
|
|
|
27616
27724
|
selected && "ring-2 ring-primary/45 border-primary/20 shadow-sm"
|
|
27617
27725
|
),
|
|
27618
27726
|
onClick: handleOpenLink,
|
|
27727
|
+
onKeyDown: handleKeyDown2,
|
|
27728
|
+
role: "link",
|
|
27729
|
+
tabIndex: safeUrl ? 0 : -1,
|
|
27730
|
+
"aria-disabled": !safeUrl,
|
|
27619
27731
|
children: loading2 ? /* @__PURE__ */ jsxs65("div", { className: "flex items-center justify-between p-4 gap-4 animate-pulse", children: [
|
|
27620
27732
|
/* @__PURE__ */ jsxs65("div", { className: "flex-1 flex flex-col gap-2", children: [
|
|
27621
27733
|
/* @__PURE__ */ jsx80("div", { className: "h-4 w-1/3 bg-muted-foreground/20 rounded" }),
|
|
@@ -27630,11 +27742,11 @@ var BookmarkView = ({ node, updateAttributes, selected, editor }) => {
|
|
|
27630
27742
|
/* @__PURE__ */ jsx80(Globe2, { className: "h-3 w-3 flex-shrink-0" }),
|
|
27631
27743
|
displayPublisher
|
|
27632
27744
|
] }),
|
|
27633
|
-
/* @__PURE__ */ jsx80("h4", { className: "text-sm font-semibold text-foreground line-clamp-1 break-all", children: title || url }),
|
|
27745
|
+
/* @__PURE__ */ jsx80("h4", { className: "text-sm font-semibold text-foreground line-clamp-1 break-all", children: title || safeUrl || String(url ?? "") }),
|
|
27634
27746
|
/* @__PURE__ */ jsx80("p", { className: "text-xs text-muted-foreground line-clamp-2 leading-relaxed", children: description })
|
|
27635
27747
|
] }),
|
|
27636
27748
|
/* @__PURE__ */ jsxs65("span", { className: "text-[11px] text-primary/80 font-mono mt-2 truncate flex items-center gap-1", children: [
|
|
27637
|
-
url,
|
|
27749
|
+
safeUrl || String(url ?? ""),
|
|
27638
27750
|
/* @__PURE__ */ jsx80(ExternalLink2, { className: "h-3 w-3 flex-shrink-0" })
|
|
27639
27751
|
] }),
|
|
27640
27752
|
error && /* @__PURE__ */ jsxs65("div", { className: "mt-2 flex flex-wrap items-center gap-2 text-xs text-destructive", children: [
|
|
@@ -27656,10 +27768,10 @@ var BookmarkView = ({ node, updateAttributes, selected, editor }) => {
|
|
|
27656
27768
|
)
|
|
27657
27769
|
] })
|
|
27658
27770
|
] }),
|
|
27659
|
-
|
|
27771
|
+
safeImage && /* @__PURE__ */ jsx80("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__ */ jsx80(
|
|
27660
27772
|
"img",
|
|
27661
27773
|
{
|
|
27662
|
-
src:
|
|
27774
|
+
src: safeImage,
|
|
27663
27775
|
alt: title || "Link preview image",
|
|
27664
27776
|
className: "absolute inset-0 w-full h-full object-cover transition-opacity duration-300",
|
|
27665
27777
|
onError: (e) => {
|
|
@@ -27689,11 +27801,11 @@ var Bookmark = Node3.create({
|
|
|
27689
27801
|
return {
|
|
27690
27802
|
url: {
|
|
27691
27803
|
default: "",
|
|
27692
|
-
parseHTML: (element) => element.getAttribute("href") || element.getAttribute("data-url") || "",
|
|
27693
|
-
renderHTML: (attributes) =>
|
|
27694
|
-
|
|
27695
|
-
"data-url":
|
|
27696
|
-
}
|
|
27804
|
+
parseHTML: (element) => sanitizeUEditorUrl(element.getAttribute("href") || element.getAttribute("data-url") || "", "link"),
|
|
27805
|
+
renderHTML: (attributes) => {
|
|
27806
|
+
const url = sanitizeUEditorUrl(String(attributes.url ?? ""), "link");
|
|
27807
|
+
return url ? { href: url, "data-url": url } : {};
|
|
27808
|
+
}
|
|
27697
27809
|
},
|
|
27698
27810
|
title: {
|
|
27699
27811
|
default: "",
|
|
@@ -27711,10 +27823,11 @@ var Bookmark = Node3.create({
|
|
|
27711
27823
|
},
|
|
27712
27824
|
image: {
|
|
27713
27825
|
default: "",
|
|
27714
|
-
parseHTML: (element) => element.getAttribute("data-image") || "",
|
|
27715
|
-
renderHTML: (attributes) =>
|
|
27716
|
-
|
|
27717
|
-
|
|
27826
|
+
parseHTML: (element) => sanitizeUEditorUrl(element.getAttribute("data-image") || "", "image"),
|
|
27827
|
+
renderHTML: (attributes) => {
|
|
27828
|
+
const image = sanitizeUEditorUrl(String(attributes.image ?? ""), "image");
|
|
27829
|
+
return image ? { "data-image": image } : {};
|
|
27830
|
+
}
|
|
27718
27831
|
},
|
|
27719
27832
|
publisher: {
|
|
27720
27833
|
default: "",
|
|
@@ -27747,9 +27860,12 @@ var Bookmark = Node3.create({
|
|
|
27747
27860
|
addCommands() {
|
|
27748
27861
|
return {
|
|
27749
27862
|
setBookmark: (attributes) => ({ commands }) => {
|
|
27863
|
+
const url = sanitizeUEditorUrl(attributes.url, "link");
|
|
27864
|
+
if (!url) return false;
|
|
27865
|
+
const image = attributes.image ? sanitizeUEditorUrl(attributes.image, "image") : "";
|
|
27750
27866
|
return commands.insertContent({
|
|
27751
27867
|
type: this.name,
|
|
27752
|
-
attrs: attributes
|
|
27868
|
+
attrs: { ...attributes, url, image }
|
|
27753
27869
|
});
|
|
27754
27870
|
}
|
|
27755
27871
|
};
|
|
@@ -27766,7 +27882,6 @@ var Bookmark = Node3.create({
|
|
|
27766
27882
|
if (!text || !URL_REGEX.test(text)) return false;
|
|
27767
27883
|
const { selection } = view.state;
|
|
27768
27884
|
const parent = selection.$from.parent;
|
|
27769
|
-
console.log("DEBUG handlePaste name:", parent.type.name, "size:", parent.content.size);
|
|
27770
27885
|
const isEmptyParagraph = parent.type.name === "paragraph" && parent.content.size === 0;
|
|
27771
27886
|
if (isEmptyParagraph) {
|
|
27772
27887
|
event.preventDefault();
|
|
@@ -27794,15 +27909,15 @@ import { NodeViewWrapper as NodeViewWrapper3 } from "@tiptap/react";
|
|
|
27794
27909
|
import { Download as Download2, File as LucideFile, FileText as FileText2, Image as Image3, Music, RefreshCw as RefreshCw2, Video } from "lucide-react";
|
|
27795
27910
|
import { jsx as jsx81, jsxs as jsxs66 } from "react/jsx-runtime";
|
|
27796
27911
|
function formatBytes(bytes, decimals = 1) {
|
|
27797
|
-
if (!bytes || bytes <= 0) return "0 Bytes";
|
|
27912
|
+
if (!Number.isFinite(bytes) || bytes <= 0) return "0 Bytes";
|
|
27798
27913
|
const k = 1024;
|
|
27799
27914
|
const dm = decimals < 0 ? 0 : decimals;
|
|
27800
|
-
const sizes = ["Bytes", "KB", "MB", "GB"];
|
|
27801
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
27915
|
+
const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB"];
|
|
27916
|
+
const i = Math.min(Math.floor(Math.log(bytes) / Math.log(k)), sizes.length - 1);
|
|
27802
27917
|
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
|
|
27803
27918
|
}
|
|
27804
27919
|
function getFileIcon2(type) {
|
|
27805
|
-
const mime = (type || "").toLowerCase();
|
|
27920
|
+
const mime = String(type || "").toLowerCase();
|
|
27806
27921
|
if (mime.includes("pdf")) return FileText2;
|
|
27807
27922
|
if (mime.startsWith("image/")) return Image3;
|
|
27808
27923
|
if (mime.startsWith("audio/")) return Music;
|
|
@@ -27828,11 +27943,13 @@ function dataURLtoFile(dataurl, filename, mimeType) {
|
|
|
27828
27943
|
}
|
|
27829
27944
|
}
|
|
27830
27945
|
return new File([bytes], filename, { type: mime });
|
|
27831
|
-
} catch
|
|
27832
|
-
console.error("Error converting data URI to file:", e);
|
|
27946
|
+
} catch {
|
|
27833
27947
|
return null;
|
|
27834
27948
|
}
|
|
27835
27949
|
}
|
|
27950
|
+
function getErrorMessage(error) {
|
|
27951
|
+
return error instanceof Error && error.message ? error.message : "Upload failed";
|
|
27952
|
+
}
|
|
27836
27953
|
var FileCardView = ({ node, selected, editor, updateAttributes }) => {
|
|
27837
27954
|
const t = useSmartTranslations("UEditor");
|
|
27838
27955
|
const { src, fileName, fileSize, fileType } = node.attrs;
|
|
@@ -27840,49 +27957,64 @@ var FileCardView = ({ node, selected, editor, updateAttributes }) => {
|
|
|
27840
27957
|
const [uploadError, setUploadError] = useState45(null);
|
|
27841
27958
|
const [retryToken, setRetryToken] = useState45(0);
|
|
27842
27959
|
const uploadAttemptedRef = useRef28(false);
|
|
27960
|
+
const lastUploadSrcRef = useRef28("");
|
|
27961
|
+
const uploadRequestIdRef = useRef28(0);
|
|
27962
|
+
const safeSrc = sanitizeUEditorUrl(String(src ?? ""), "file");
|
|
27963
|
+
const extension = editor?.extensionManager?.extensions?.find((item) => item.name === "fileCard");
|
|
27964
|
+
const uploadFn = extension?.options?.upload;
|
|
27843
27965
|
useEffect33(() => {
|
|
27844
|
-
if (
|
|
27966
|
+
if (lastUploadSrcRef.current !== safeSrc) {
|
|
27967
|
+
lastUploadSrcRef.current = safeSrc;
|
|
27968
|
+
uploadAttemptedRef.current = false;
|
|
27969
|
+
}
|
|
27970
|
+
if (!safeSrc || !safeSrc.startsWith("data:") || !uploadFn) {
|
|
27845
27971
|
return;
|
|
27846
27972
|
}
|
|
27847
|
-
|
|
27848
|
-
const uploadFn = extension?.options?.upload;
|
|
27849
|
-
if (!uploadFn || uploadAttemptedRef.current || !updateAttributes) {
|
|
27973
|
+
if (uploadAttemptedRef.current || !updateAttributes) {
|
|
27850
27974
|
return;
|
|
27851
27975
|
}
|
|
27852
27976
|
uploadAttemptedRef.current = true;
|
|
27853
27977
|
setUploadError(null);
|
|
27978
|
+
const requestId = uploadRequestIdRef.current + 1;
|
|
27979
|
+
uploadRequestIdRef.current = requestId;
|
|
27980
|
+
let active = true;
|
|
27854
27981
|
const runUpload = async () => {
|
|
27855
27982
|
try {
|
|
27856
|
-
const file = dataURLtoFile(
|
|
27983
|
+
const file = dataURLtoFile(safeSrc, fileName || "file", fileType);
|
|
27857
27984
|
if (!file) {
|
|
27858
27985
|
throw new Error("Failed to parse file data");
|
|
27859
27986
|
}
|
|
27860
27987
|
const resolvedUrl = await uploadFn(file);
|
|
27988
|
+
const nextSrc = sanitizeUEditorUrl(String(resolvedUrl ?? ""), "file");
|
|
27989
|
+
if (!nextSrc) throw new Error("Upload handler returned an unsafe URL");
|
|
27861
27990
|
Promise.resolve().then(() => {
|
|
27862
|
-
if (editor && !editor.isDestroyed) {
|
|
27863
|
-
updateAttributes({ src:
|
|
27991
|
+
if (active && uploadRequestIdRef.current === requestId && editor && !editor.isDestroyed) {
|
|
27992
|
+
updateAttributes({ src: nextSrc });
|
|
27864
27993
|
}
|
|
27865
27994
|
});
|
|
27866
|
-
} catch (
|
|
27867
|
-
|
|
27868
|
-
setUploadError(
|
|
27995
|
+
} catch (error) {
|
|
27996
|
+
if (!active || uploadRequestIdRef.current !== requestId) return;
|
|
27997
|
+
setUploadError(getErrorMessage(error));
|
|
27869
27998
|
uploadAttemptedRef.current = false;
|
|
27870
27999
|
}
|
|
27871
28000
|
};
|
|
27872
28001
|
void runUpload();
|
|
27873
|
-
|
|
28002
|
+
return () => {
|
|
28003
|
+
active = false;
|
|
28004
|
+
};
|
|
28005
|
+
}, [safeSrc, fileName, fileType, editor, updateAttributes, uploadFn, retryToken]);
|
|
27874
28006
|
const handleDownload = (e) => {
|
|
27875
28007
|
e.preventDefault();
|
|
27876
28008
|
e.stopPropagation();
|
|
27877
|
-
if (!
|
|
28009
|
+
if (!safeSrc) return;
|
|
27878
28010
|
const link = document.createElement("a");
|
|
27879
|
-
link.href =
|
|
28011
|
+
link.href = safeSrc;
|
|
27880
28012
|
link.download = fileName || "download";
|
|
27881
28013
|
document.body.appendChild(link);
|
|
27882
28014
|
link.click();
|
|
27883
28015
|
document.body.removeChild(link);
|
|
27884
28016
|
};
|
|
27885
|
-
const
|
|
28017
|
+
const hasInlineUpload = safeSrc.startsWith("data:") && Boolean(uploadFn);
|
|
27886
28018
|
const handleRetryUpload = (event) => {
|
|
27887
28019
|
event.preventDefault();
|
|
27888
28020
|
event.stopPropagation();
|
|
@@ -27905,11 +28037,11 @@ var FileCardView = ({ node, selected, editor, updateAttributes }) => {
|
|
|
27905
28037
|
/* @__PURE__ */ jsx81("div", { className: "text-sm font-semibold text-foreground truncate break-all", children: fileName || t("fileCard.unnamed") }),
|
|
27906
28038
|
/* @__PURE__ */ jsxs66("div", { className: "text-xs text-muted-foreground flex items-center gap-2 mt-0.5", children: [
|
|
27907
28039
|
fileSize ? /* @__PURE__ */ jsx81("span", { children: formatBytes(fileSize) }) : null,
|
|
27908
|
-
|
|
28040
|
+
hasInlineUpload && /* @__PURE__ */ jsx81("span", { className: cn(
|
|
27909
28041
|
"font-medium truncate max-w-50",
|
|
27910
28042
|
uploadError ? "text-destructive" : "text-primary animate-pulse"
|
|
27911
28043
|
), children: uploadError ? `${t("fileCard.uploadFailed") || "T\u1EA3i l\xEAn th\u1EA5t b\u1EA1i"}: ${uploadError}` : t("fileCard.uploading") }),
|
|
27912
|
-
|
|
28044
|
+
hasInlineUpload && uploadError && /* @__PURE__ */ jsxs66(
|
|
27913
28045
|
"button",
|
|
27914
28046
|
{
|
|
27915
28047
|
type: "button",
|
|
@@ -27924,7 +28056,7 @@ var FileCardView = ({ node, selected, editor, updateAttributes }) => {
|
|
|
27924
28056
|
] })
|
|
27925
28057
|
] })
|
|
27926
28058
|
] }),
|
|
27927
|
-
|
|
28059
|
+
safeSrc && /* @__PURE__ */ jsx81(
|
|
27928
28060
|
"button",
|
|
27929
28061
|
{
|
|
27930
28062
|
type: "button",
|
|
@@ -27953,11 +28085,11 @@ var FileCard = Node4.create({
|
|
|
27953
28085
|
return {
|
|
27954
28086
|
src: {
|
|
27955
28087
|
default: "",
|
|
27956
|
-
parseHTML: (element) => element.getAttribute("href") || element.getAttribute("data-src") || "",
|
|
27957
|
-
renderHTML: (attributes) =>
|
|
27958
|
-
|
|
27959
|
-
"data-src":
|
|
27960
|
-
}
|
|
28088
|
+
parseHTML: (element) => sanitizeUEditorUrl(element.getAttribute("href") || element.getAttribute("data-src") || "", "file"),
|
|
28089
|
+
renderHTML: (attributes) => {
|
|
28090
|
+
const src = sanitizeUEditorUrl(String(attributes.src ?? ""), "file");
|
|
28091
|
+
return src ? { href: src, "data-src": src } : {};
|
|
28092
|
+
}
|
|
27961
28093
|
},
|
|
27962
28094
|
fileName: {
|
|
27963
28095
|
default: "",
|
|
@@ -27969,13 +28101,15 @@ var FileCard = Node4.create({
|
|
|
27969
28101
|
fileSize: {
|
|
27970
28102
|
default: null,
|
|
27971
28103
|
parseHTML: (element) => {
|
|
27972
|
-
const
|
|
27973
|
-
|
|
28104
|
+
const rawValue = element.getAttribute("data-file-size");
|
|
28105
|
+
const value = rawValue ? Number(rawValue) : Number.NaN;
|
|
28106
|
+
return Number.isFinite(value) && value >= 0 ? value : null;
|
|
27974
28107
|
},
|
|
27975
28108
|
renderHTML: (attributes) => {
|
|
27976
|
-
|
|
28109
|
+
const fileSize = Number(attributes.fileSize);
|
|
28110
|
+
if (!Number.isFinite(fileSize) || fileSize < 0) return {};
|
|
27977
28111
|
return {
|
|
27978
|
-
"data-file-size":
|
|
28112
|
+
"data-file-size": fileSize
|
|
27979
28113
|
};
|
|
27980
28114
|
}
|
|
27981
28115
|
},
|
|
@@ -28010,9 +28144,16 @@ var FileCard = Node4.create({
|
|
|
28010
28144
|
addCommands() {
|
|
28011
28145
|
return {
|
|
28012
28146
|
setFileCard: (attributes) => ({ commands }) => {
|
|
28147
|
+
const src = sanitizeUEditorUrl(attributes.src, "file");
|
|
28148
|
+
if (!src) return false;
|
|
28149
|
+
const fileSize = Number(attributes.fileSize);
|
|
28013
28150
|
return commands.insertContent({
|
|
28014
28151
|
type: this.name,
|
|
28015
|
-
attrs:
|
|
28152
|
+
attrs: {
|
|
28153
|
+
...attributes,
|
|
28154
|
+
src,
|
|
28155
|
+
fileSize: Number.isFinite(fileSize) && fileSize >= 0 ? fileSize : null
|
|
28156
|
+
}
|
|
28016
28157
|
});
|
|
28017
28158
|
}
|
|
28018
28159
|
};
|
|
@@ -28443,6 +28584,10 @@ function destroyTippyInstance(instance) {
|
|
|
28443
28584
|
if (!instance || instance.state.isDestroyed) return;
|
|
28444
28585
|
instance.destroy();
|
|
28445
28586
|
}
|
|
28587
|
+
function setTippyReferenceClientRect(instance, clientRect) {
|
|
28588
|
+
if (!instance || instance.state.isDestroyed) return;
|
|
28589
|
+
instance.setProps({ getReferenceClientRect: clientRect });
|
|
28590
|
+
}
|
|
28446
28591
|
|
|
28447
28592
|
// src/components/UEditor/slash-command.tsx
|
|
28448
28593
|
import { jsx as jsx84, jsxs as jsxs68 } from "react/jsx-runtime";
|
|
@@ -28815,9 +28960,7 @@ var SlashCommand = Extension.create({
|
|
|
28815
28960
|
if (!props.clientRect) {
|
|
28816
28961
|
return;
|
|
28817
28962
|
}
|
|
28818
|
-
popup
|
|
28819
|
-
getReferenceClientRect: props.clientRect
|
|
28820
|
-
});
|
|
28963
|
+
setTippyReferenceClientRect(getFirstTippyInstance(popup), props.clientRect);
|
|
28821
28964
|
},
|
|
28822
28965
|
onKeyDown(props) {
|
|
28823
28966
|
if (!props || !props.event) return false;
|
|
@@ -29554,45 +29697,6 @@ function getClipboardTsvTableContent(dataTransfer) {
|
|
|
29554
29697
|
);
|
|
29555
29698
|
}
|
|
29556
29699
|
|
|
29557
|
-
// src/components/UEditor/url-safety.ts
|
|
29558
|
-
var LINK_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:", "mailto:", "tel:"]);
|
|
29559
|
-
var IMAGE_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:"]);
|
|
29560
|
-
function normalizeUrlInput(raw) {
|
|
29561
|
-
return raw.trim().replace(/[\u0000-\u001F\u007F\s]+/g, "");
|
|
29562
|
-
}
|
|
29563
|
-
function isProtocolRelativeUrl(value) {
|
|
29564
|
-
return value.startsWith("//");
|
|
29565
|
-
}
|
|
29566
|
-
function isRelativeUrl(value) {
|
|
29567
|
-
return value.startsWith("/") || value.startsWith("./") || value.startsWith("../") || value.startsWith("#");
|
|
29568
|
-
}
|
|
29569
|
-
function isDataImageUrl(value) {
|
|
29570
|
-
return /^data:image\/(?:png|jpe?g|gif|webp|svg\+xml|bmp|x-icon|avif);base64,/i.test(value);
|
|
29571
|
-
}
|
|
29572
|
-
function isSafeUEditorUrl(raw, kind) {
|
|
29573
|
-
const value = normalizeUrlInput(raw);
|
|
29574
|
-
if (!value) return false;
|
|
29575
|
-
if (kind === "image" && isDataImageUrl(value)) return true;
|
|
29576
|
-
if (isRelativeUrl(value)) return true;
|
|
29577
|
-
if (isProtocolRelativeUrl(value)) return false;
|
|
29578
|
-
try {
|
|
29579
|
-
const parsed = new URL(value);
|
|
29580
|
-
return kind === "image" ? IMAGE_PROTOCOLS.has(parsed.protocol) : LINK_PROTOCOLS.has(parsed.protocol);
|
|
29581
|
-
} catch {
|
|
29582
|
-
return false;
|
|
29583
|
-
}
|
|
29584
|
-
}
|
|
29585
|
-
function sanitizeUEditorUrl(raw, kind) {
|
|
29586
|
-
const value = raw.trim();
|
|
29587
|
-
if (!value) return "";
|
|
29588
|
-
if (isSafeUEditorUrl(value, kind)) return normalizeUrlInput(value);
|
|
29589
|
-
if (kind === "link" && !isProtocolRelativeUrl(value) && !/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(value)) {
|
|
29590
|
-
const withProtocol = `https://${value}`;
|
|
29591
|
-
return isSafeUEditorUrl(withProtocol, kind) ? withProtocol : "";
|
|
29592
|
-
}
|
|
29593
|
-
return "";
|
|
29594
|
-
}
|
|
29595
|
-
|
|
29596
29700
|
// src/components/UEditor/clipboard-images.ts
|
|
29597
29701
|
var DEFAULT_UEDITOR_IMAGE_MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
29598
29702
|
var DEFAULT_UEDITOR_IMAGE_MIME_TYPES = ["image/png", "image/jpeg", "image/webp", "image/gif", "image/svg+xml"];
|
|
@@ -29834,9 +29938,7 @@ var EmojiSuggestion = Extension3.create({
|
|
|
29834
29938
|
if (!props.clientRect) {
|
|
29835
29939
|
return;
|
|
29836
29940
|
}
|
|
29837
|
-
popup
|
|
29838
|
-
getReferenceClientRect: props.clientRect
|
|
29839
|
-
});
|
|
29941
|
+
setTippyReferenceClientRect(getFirstTippyInstance(popup), props.clientRect);
|
|
29840
29942
|
},
|
|
29841
29943
|
onKeyDown(props) {
|
|
29842
29944
|
if (!props || !props.event) return false;
|
|
@@ -29905,11 +30007,17 @@ function isInTableCell(editor) {
|
|
|
29905
30007
|
}
|
|
29906
30008
|
function buildFormulaSuggestionItems({ query }) {
|
|
29907
30009
|
const normalizedQuery = query.trim().toUpperCase();
|
|
30010
|
+
if (!isFormulaFunctionSuggestionQuery(normalizedQuery)) {
|
|
30011
|
+
return [];
|
|
30012
|
+
}
|
|
29908
30013
|
if (!normalizedQuery) {
|
|
29909
30014
|
return FORMULA_FUNCTIONS;
|
|
29910
30015
|
}
|
|
29911
30016
|
return FORMULA_FUNCTIONS.filter((item) => item.name.startsWith(normalizedQuery));
|
|
29912
30017
|
}
|
|
30018
|
+
function isFormulaFunctionSuggestionQuery(query) {
|
|
30019
|
+
return /^[A-Z]*$/i.test(query.trim());
|
|
30020
|
+
}
|
|
29913
30021
|
var FormulaSuggestionList = forwardRef16((props, ref) => {
|
|
29914
30022
|
const t = useSmartTranslations("UEditor");
|
|
29915
30023
|
const [selectedIndex, setSelectedIndex] = React76.useState(0);
|
|
@@ -29927,7 +30035,7 @@ var FormulaSuggestionList = forwardRef16((props, ref) => {
|
|
|
29927
30035
|
setSelectedIndex((prev) => (prev + 1) % props.items.length);
|
|
29928
30036
|
return true;
|
|
29929
30037
|
}
|
|
29930
|
-
if (event.key === "
|
|
30038
|
+
if (event.key === "Tab") {
|
|
29931
30039
|
const item = props.items[selectedIndex];
|
|
29932
30040
|
if (item) {
|
|
29933
30041
|
props.command(item);
|
|
@@ -29980,7 +30088,11 @@ var FormulaSuggestion = Extension4.create({
|
|
|
29980
30088
|
editor: this.editor,
|
|
29981
30089
|
char: "=",
|
|
29982
30090
|
pluginKey: new PluginKey3("formulaSuggestion"),
|
|
29983
|
-
allow: ({ editor }) =>
|
|
30091
|
+
allow: ({ editor, range }) => {
|
|
30092
|
+
if (!isInTableCell(editor)) return false;
|
|
30093
|
+
const suggestionText = editor.state.doc.textBetween(range.from, range.to, "", "");
|
|
30094
|
+
return suggestionText.startsWith("=") && isFormulaFunctionSuggestionQuery(suggestionText.slice(1));
|
|
30095
|
+
},
|
|
29984
30096
|
command: ({ editor, range, props }) => {
|
|
29985
30097
|
insertFormulaFunction(editor, range, props);
|
|
29986
30098
|
},
|
|
@@ -30013,9 +30125,7 @@ var FormulaSuggestion = Extension4.create({
|
|
|
30013
30125
|
if (!props.clientRect) {
|
|
30014
30126
|
return;
|
|
30015
30127
|
}
|
|
30016
|
-
popup
|
|
30017
|
-
getReferenceClientRect: props.clientRect
|
|
30018
|
-
});
|
|
30128
|
+
setTippyReferenceClientRect(getFirstTippyInstance(popup), props.clientRect);
|
|
30019
30129
|
},
|
|
30020
30130
|
onKeyDown(props) {
|
|
30021
30131
|
const popupInstance = getFirstTippyInstance(popup);
|
|
@@ -31766,7 +31876,6 @@ function buildUEditorExtensions({
|
|
|
31766
31876
|
Gapcursor,
|
|
31767
31877
|
Link.configure({
|
|
31768
31878
|
openOnClick: false,
|
|
31769
|
-
protocols: ["http", "https", "mailto", "tel"],
|
|
31770
31879
|
isAllowedUri: (url) => isSafeUEditorUrl(url ?? "", "link"),
|
|
31771
31880
|
HTMLAttributes: {
|
|
31772
31881
|
class: "text-primary underline underline-offset-2 hover:text-primary/80 cursor-pointer"
|
|
@@ -32858,7 +32967,7 @@ var EditorToolbar = ({
|
|
|
32858
32967
|
const [showImageInput, setShowImageInput] = useState49(false);
|
|
32859
32968
|
const [showLinkInput, setShowLinkInput] = useState49(false);
|
|
32860
32969
|
const [isTableMenuOpen, setIsTableMenuOpen] = useState49(false);
|
|
32861
|
-
const
|
|
32970
|
+
const [preservedTableAnchorPos, setPreservedTableAnchorPos] = useState49(null);
|
|
32862
32971
|
const fileInputRef = useRef33(null);
|
|
32863
32972
|
const [isUploadingImage, setIsUploadingImage] = useState49(false);
|
|
32864
32973
|
const [imageUploadError, setImageUploadError] = useState49(null);
|
|
@@ -32873,7 +32982,7 @@ var EditorToolbar = ({
|
|
|
32873
32982
|
const tableAlignAttr = tableAttrs.tableAlign ?? tableAttrs.textAlign;
|
|
32874
32983
|
const currentTableAlign = tableAlignAttr === "center" || tableAlignAttr === "right" ? tableAlignAttr : "left";
|
|
32875
32984
|
const isTableSelected = tableInfo !== null;
|
|
32876
|
-
const hasTableContext = isTableSelected ||
|
|
32985
|
+
const hasTableContext = isTableSelected || preservedTableAnchorPos !== null;
|
|
32877
32986
|
const canMergeCells = hasTableContext && editor.can().mergeCells();
|
|
32878
32987
|
const canSplitCell = hasTableContext && editor.can().splitCell();
|
|
32879
32988
|
const currentCellVerticalAlign = normalizeStyleValue(editor.getAttributes("tableCell").verticalAlign || editor.getAttributes("tableHeader").verticalAlign) || "";
|
|
@@ -32898,7 +33007,7 @@ var EditorToolbar = ({
|
|
|
32898
33007
|
const displayedFontFamilyValue = currentFontFamily || defaultFontFamilyValue;
|
|
32899
33008
|
const displayedFontSizeLabel = currentFontSize ? currentFontSizeLabel : "13";
|
|
32900
33009
|
const activeFontSize = currentFontSize || "13px";
|
|
32901
|
-
const tableCommandAnchorPos =
|
|
33010
|
+
const tableCommandAnchorPos = preservedTableAnchorPos ?? tableAnchorPos ?? void 0;
|
|
32902
33011
|
const isMedium = variant === "medium";
|
|
32903
33012
|
const isMediumFull = variant === "medium-full";
|
|
32904
33013
|
const isFull = variant === "default" || variant === "full" || variant === "notion" || !variant;
|
|
@@ -33574,7 +33683,7 @@ var EditorToolbar = ({
|
|
|
33574
33683
|
isOpen: isTableMenuOpen,
|
|
33575
33684
|
onOpenChange: (open) => {
|
|
33576
33685
|
setIsTableMenuOpen(open);
|
|
33577
|
-
|
|
33686
|
+
setPreservedTableAnchorPos(open ? getTableAnchorPos(editor) : null);
|
|
33578
33687
|
},
|
|
33579
33688
|
trigger: /* @__PURE__ */ jsxs75(ToolbarButton, { onClick: () => {
|
|
33580
33689
|
}, title: t("toolbar.table"), children: [
|
|
@@ -33591,6 +33700,7 @@ var EditorToolbar = ({
|
|
|
33591
33700
|
onInsert: (rows, cols) => {
|
|
33592
33701
|
editor.chain().focus().insertTable({ rows, cols, withHeaderRow: true }).run();
|
|
33593
33702
|
setIsTableMenuOpen(false);
|
|
33703
|
+
setPreservedTableAnchorPos(null);
|
|
33594
33704
|
}
|
|
33595
33705
|
}
|
|
33596
33706
|
),
|
|
@@ -33806,6 +33916,7 @@ import {
|
|
|
33806
33916
|
} from "lucide-react";
|
|
33807
33917
|
|
|
33808
33918
|
// src/components/UEditor/table-formula-commands.ts
|
|
33919
|
+
import { TextSelection as TextSelection3 } from "@tiptap/pm/state";
|
|
33809
33920
|
import { selectedRect as selectedRect2, setCellAttr as setCellAttr2, TableMap as TableMap3 } from "@tiptap/pm/tables";
|
|
33810
33921
|
|
|
33811
33922
|
// src/components/UEditor/table-formula.ts
|
|
@@ -33983,51 +34094,98 @@ function buildTableFormulaDependencyGraph(cells) {
|
|
|
33983
34094
|
return { dependencies, dependents, formulas };
|
|
33984
34095
|
}
|
|
33985
34096
|
function getTableFormulaCircularReferences(graph) {
|
|
33986
|
-
const visiting = /* @__PURE__ */ new Set();
|
|
33987
|
-
const visited = /* @__PURE__ */ new Set();
|
|
33988
34097
|
const circular = /* @__PURE__ */ new Set();
|
|
33989
|
-
const
|
|
33990
|
-
const
|
|
33991
|
-
|
|
33992
|
-
|
|
33993
|
-
|
|
33994
|
-
|
|
34098
|
+
const visited = /* @__PURE__ */ new Set();
|
|
34099
|
+
const finishOrder = [];
|
|
34100
|
+
for (const startLabel of graph.formulas.keys()) {
|
|
34101
|
+
if (visited.has(startLabel)) continue;
|
|
34102
|
+
visited.add(startLabel);
|
|
34103
|
+
const stack = [
|
|
34104
|
+
{
|
|
34105
|
+
label: startLabel,
|
|
34106
|
+
references: Array.from(graph.dependencies.get(startLabel) ?? []).filter((ref) => graph.formulas.has(ref)),
|
|
34107
|
+
nextIndex: 0
|
|
33995
34108
|
}
|
|
33996
|
-
|
|
33997
|
-
|
|
33998
|
-
|
|
33999
|
-
|
|
34000
|
-
|
|
34001
|
-
|
|
34002
|
-
|
|
34003
|
-
|
|
34109
|
+
];
|
|
34110
|
+
while (stack.length > 0) {
|
|
34111
|
+
const frame = stack[stack.length - 1];
|
|
34112
|
+
if (!frame) break;
|
|
34113
|
+
const reference = frame.references[frame.nextIndex];
|
|
34114
|
+
if (reference) {
|
|
34115
|
+
frame.nextIndex += 1;
|
|
34116
|
+
if (!visited.has(reference)) {
|
|
34117
|
+
visited.add(reference);
|
|
34118
|
+
stack.push({
|
|
34119
|
+
label: reference,
|
|
34120
|
+
references: Array.from(graph.dependencies.get(reference) ?? []).filter((ref) => graph.formulas.has(ref)),
|
|
34121
|
+
nextIndex: 0
|
|
34122
|
+
});
|
|
34123
|
+
}
|
|
34124
|
+
continue;
|
|
34004
34125
|
}
|
|
34126
|
+
stack.pop();
|
|
34127
|
+
finishOrder.push(frame.label);
|
|
34128
|
+
}
|
|
34129
|
+
}
|
|
34130
|
+
const assigned = /* @__PURE__ */ new Set();
|
|
34131
|
+
for (let index = finishOrder.length - 1; index >= 0; index -= 1) {
|
|
34132
|
+
const startLabel = finishOrder[index];
|
|
34133
|
+
if (!startLabel || assigned.has(startLabel)) continue;
|
|
34134
|
+
const component = [];
|
|
34135
|
+
const stack = [startLabel];
|
|
34136
|
+
assigned.add(startLabel);
|
|
34137
|
+
while (stack.length > 0) {
|
|
34138
|
+
const label = stack.pop();
|
|
34139
|
+
if (!label) continue;
|
|
34140
|
+
component.push(label);
|
|
34141
|
+
for (const dependent of graph.dependents.get(label) ?? []) {
|
|
34142
|
+
if (!graph.formulas.has(dependent) || assigned.has(dependent)) continue;
|
|
34143
|
+
assigned.add(dependent);
|
|
34144
|
+
stack.push(dependent);
|
|
34145
|
+
}
|
|
34146
|
+
}
|
|
34147
|
+
if (component.length > 1) {
|
|
34148
|
+
for (const label of component) circular.add(label);
|
|
34149
|
+
} else {
|
|
34150
|
+
const label = component[0];
|
|
34151
|
+
if (label && graph.dependencies.get(label)?.has(label)) circular.add(label);
|
|
34005
34152
|
}
|
|
34006
|
-
stack.pop();
|
|
34007
|
-
visiting.delete(label);
|
|
34008
|
-
visited.add(label);
|
|
34009
|
-
};
|
|
34010
|
-
for (const label of graph.formulas.keys()) {
|
|
34011
|
-
visit(label);
|
|
34012
34153
|
}
|
|
34013
34154
|
return circular;
|
|
34014
34155
|
}
|
|
34015
34156
|
function getTableFormulaRecalculationOrder(graph) {
|
|
34016
34157
|
const circular = getTableFormulaCircularReferences(graph);
|
|
34158
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
34017
34159
|
const visited = /* @__PURE__ */ new Set();
|
|
34018
34160
|
const order = [];
|
|
34019
|
-
const
|
|
34020
|
-
if (visited.has(
|
|
34021
|
-
|
|
34022
|
-
|
|
34023
|
-
|
|
34024
|
-
|
|
34161
|
+
for (const startLabel of graph.formulas.keys()) {
|
|
34162
|
+
if (visited.has(startLabel) || circular.has(startLabel)) continue;
|
|
34163
|
+
const stack = [];
|
|
34164
|
+
const push = (label) => {
|
|
34165
|
+
visiting.add(label);
|
|
34166
|
+
stack.push({
|
|
34167
|
+
label,
|
|
34168
|
+
references: Array.from(graph.dependencies.get(label) ?? []).filter((ref) => graph.formulas.has(ref) && !circular.has(ref)),
|
|
34169
|
+
nextIndex: 0
|
|
34170
|
+
});
|
|
34171
|
+
};
|
|
34172
|
+
push(startLabel);
|
|
34173
|
+
while (stack.length > 0) {
|
|
34174
|
+
const frame = stack[stack.length - 1];
|
|
34175
|
+
if (!frame) break;
|
|
34176
|
+
const reference = frame.references[frame.nextIndex];
|
|
34177
|
+
if (reference) {
|
|
34178
|
+
frame.nextIndex += 1;
|
|
34179
|
+
if (!visited.has(reference) && !visiting.has(reference)) {
|
|
34180
|
+
push(reference);
|
|
34181
|
+
}
|
|
34182
|
+
continue;
|
|
34025
34183
|
}
|
|
34184
|
+
stack.pop();
|
|
34185
|
+
visiting.delete(frame.label);
|
|
34186
|
+
visited.add(frame.label);
|
|
34187
|
+
order.push(frame.label);
|
|
34026
34188
|
}
|
|
34027
|
-
order.push(label);
|
|
34028
|
-
};
|
|
34029
|
-
for (const label of graph.formulas.keys()) {
|
|
34030
|
-
visit(label);
|
|
34031
34189
|
}
|
|
34032
34190
|
return { order, circular };
|
|
34033
34191
|
}
|
|
@@ -34086,9 +34244,11 @@ function tokenizeFormula(formula) {
|
|
|
34086
34244
|
index += 1;
|
|
34087
34245
|
continue;
|
|
34088
34246
|
}
|
|
34089
|
-
const numberMatch = formula.slice(index).match(
|
|
34247
|
+
const numberMatch = formula.slice(index).match(/^(?:\d+(?:\.\d*)?|\.\d+)/);
|
|
34090
34248
|
if (numberMatch?.[0]) {
|
|
34091
|
-
|
|
34249
|
+
const value = Number.parseFloat(numberMatch[0]);
|
|
34250
|
+
if (!Number.isFinite(value)) return null;
|
|
34251
|
+
tokens.push({ type: "number", value });
|
|
34092
34252
|
index += numberMatch[0].length;
|
|
34093
34253
|
continue;
|
|
34094
34254
|
}
|
|
@@ -34106,6 +34266,28 @@ function tokenizeFormula(formula) {
|
|
|
34106
34266
|
}
|
|
34107
34267
|
return tokens;
|
|
34108
34268
|
}
|
|
34269
|
+
function toFiniteFormulaResult(value) {
|
|
34270
|
+
return Number.isFinite(value) ? { value, error: null } : { value: null, error: "invalid-formula" };
|
|
34271
|
+
}
|
|
34272
|
+
function parseTableCellNumericValue(value) {
|
|
34273
|
+
if (typeof value === "number") {
|
|
34274
|
+
return Number.isFinite(value) ? value : null;
|
|
34275
|
+
}
|
|
34276
|
+
let normalized = String(value ?? "").trim();
|
|
34277
|
+
if (!normalized || normalized.startsWith("#")) return null;
|
|
34278
|
+
const isPercent = normalized.endsWith("%");
|
|
34279
|
+
if (isPercent) normalized = normalized.slice(0, -1).trim();
|
|
34280
|
+
const currencyMatch = normalized.match(/^([+-]?)\$(.+)$/);
|
|
34281
|
+
if (currencyMatch) {
|
|
34282
|
+
normalized = `${currencyMatch[1] ?? ""}${currencyMatch[2] ?? ""}`.trim();
|
|
34283
|
+
}
|
|
34284
|
+
const groupedNumber = /^[+-]?\d{1,3}(?:,\d{3})+(?:\.\d*)?(?:[eE][+-]?\d+)?$/;
|
|
34285
|
+
const plainNumber = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/;
|
|
34286
|
+
if (!groupedNumber.test(normalized) && !plainNumber.test(normalized)) return null;
|
|
34287
|
+
const parsed = Number(normalized.replace(/,/g, ""));
|
|
34288
|
+
if (!Number.isFinite(parsed)) return null;
|
|
34289
|
+
return isPercent ? parsed / 100 : parsed;
|
|
34290
|
+
}
|
|
34109
34291
|
var FormulaParser = class {
|
|
34110
34292
|
constructor(tokens, getCellValue) {
|
|
34111
34293
|
this.tokens = tokens;
|
|
@@ -34123,10 +34305,7 @@ var FormulaParser = class {
|
|
|
34123
34305
|
this.index += 1;
|
|
34124
34306
|
const right = this.parseTerm();
|
|
34125
34307
|
if (right.error) return right;
|
|
34126
|
-
left =
|
|
34127
|
-
value: operator.value === "+" ? left.value + right.value : left.value - right.value,
|
|
34128
|
-
error: null
|
|
34129
|
-
};
|
|
34308
|
+
left = toFiniteFormulaResult(operator.value === "+" ? left.value + right.value : left.value - right.value);
|
|
34130
34309
|
}
|
|
34131
34310
|
return left;
|
|
34132
34311
|
}
|
|
@@ -34141,10 +34320,7 @@ var FormulaParser = class {
|
|
|
34141
34320
|
if (operator.value === "/" && right.value === 0) {
|
|
34142
34321
|
return { value: null, error: "division-by-zero" };
|
|
34143
34322
|
}
|
|
34144
|
-
left =
|
|
34145
|
-
value: operator.value === "*" ? left.value * right.value : left.value / right.value,
|
|
34146
|
-
error: null
|
|
34147
|
-
};
|
|
34323
|
+
left = toFiniteFormulaResult(operator.value === "*" ? left.value * right.value : left.value / right.value);
|
|
34148
34324
|
}
|
|
34149
34325
|
return left;
|
|
34150
34326
|
}
|
|
@@ -34153,11 +34329,11 @@ var FormulaParser = class {
|
|
|
34153
34329
|
if (!token) {
|
|
34154
34330
|
return { value: null, error: "invalid-formula" };
|
|
34155
34331
|
}
|
|
34156
|
-
if (token.type === "operator" && token.value === "-") {
|
|
34332
|
+
if (token.type === "operator" && (token.value === "-" || token.value === "+")) {
|
|
34157
34333
|
this.index += 1;
|
|
34158
34334
|
const value = this.parseFactor();
|
|
34159
34335
|
if (value.error) return value;
|
|
34160
|
-
return
|
|
34336
|
+
return toFiniteFormulaResult(token.value === "-" ? -value.value : value.value);
|
|
34161
34337
|
}
|
|
34162
34338
|
if (token.type === "number") {
|
|
34163
34339
|
this.index += 1;
|
|
@@ -34202,11 +34378,10 @@ var FormulaParser = class {
|
|
|
34202
34378
|
if (cellValue2 != null) values.push(cellValue2);
|
|
34203
34379
|
continue;
|
|
34204
34380
|
}
|
|
34205
|
-
const cellValue = this.
|
|
34206
|
-
if (cellValue
|
|
34207
|
-
values.push(cellValue.value);
|
|
34381
|
+
const cellValue = this.readOptionalCellNumber(label);
|
|
34382
|
+
if (cellValue != null) values.push(cellValue);
|
|
34208
34383
|
}
|
|
34209
|
-
} else if (
|
|
34384
|
+
} else if (token.type === "cell") {
|
|
34210
34385
|
this.index += 1;
|
|
34211
34386
|
const cellValue = this.readOptionalCellNumber(token.value);
|
|
34212
34387
|
if (cellValue != null) values.push(cellValue);
|
|
@@ -34223,28 +34398,24 @@ var FormulaParser = class {
|
|
|
34223
34398
|
}
|
|
34224
34399
|
return { value: null, error: "invalid-formula" };
|
|
34225
34400
|
}
|
|
34226
|
-
if (values.
|
|
34227
|
-
|
|
34401
|
+
if (name === "SUM") return toFiniteFormulaResult(values.reduce((sum, value) => sum + value, 0));
|
|
34402
|
+
if (name === "AVG") {
|
|
34403
|
+
return values.length > 0 ? toFiniteFormulaResult(values.reduce((sum, value) => sum + value, 0) / values.length) : { value: null, error: "division-by-zero" };
|
|
34228
34404
|
}
|
|
34229
|
-
if (name === "
|
|
34230
|
-
if (name === "
|
|
34231
|
-
if (name === "MIN") return { value: Math.min(...values), error: null };
|
|
34232
|
-
if (name === "MAX") return { value: Math.max(...values), error: null };
|
|
34405
|
+
if (name === "MIN") return toFiniteFormulaResult(values.length > 0 ? Math.min(...values) : 0);
|
|
34406
|
+
if (name === "MAX") return toFiniteFormulaResult(values.length > 0 ? Math.max(...values) : 0);
|
|
34233
34407
|
if (name === "COUNT") return { value: values.length, error: null };
|
|
34234
34408
|
return { value: null, error: "invalid-formula" };
|
|
34235
34409
|
}
|
|
34236
34410
|
readCellNumber(label) {
|
|
34237
|
-
const
|
|
34238
|
-
|
|
34239
|
-
if (!Number.isFinite(parsed)) {
|
|
34411
|
+
const parsed = parseTableCellNumericValue(this.getCellValue(label));
|
|
34412
|
+
if (parsed == null) {
|
|
34240
34413
|
return { value: null, error: "invalid-reference" };
|
|
34241
34414
|
}
|
|
34242
34415
|
return { value: parsed, error: null };
|
|
34243
34416
|
}
|
|
34244
34417
|
readOptionalCellNumber(label) {
|
|
34245
|
-
|
|
34246
|
-
const parsed = typeof value === "number" ? value : Number.parseFloat(String(value ?? "").trim());
|
|
34247
|
-
return Number.isFinite(parsed) ? parsed : null;
|
|
34418
|
+
return parseTableCellNumericValue(this.getCellValue(label));
|
|
34248
34419
|
}
|
|
34249
34420
|
peekOperator(operators) {
|
|
34250
34421
|
const token = this.tokens[this.index];
|
|
@@ -34309,6 +34480,75 @@ function getSelectionTableCellNode(editor) {
|
|
|
34309
34480
|
}
|
|
34310
34481
|
return null;
|
|
34311
34482
|
}
|
|
34483
|
+
function getSelectionTableCellPos(editor) {
|
|
34484
|
+
const cellSelection = editor.state.selection;
|
|
34485
|
+
const anchorCellPos = cellSelection.$anchorCell?.pos;
|
|
34486
|
+
if (typeof anchorCellPos === "number") {
|
|
34487
|
+
const node = editor.state.doc.nodeAt(anchorCellPos);
|
|
34488
|
+
if (node?.type.name === "tableCell" || node?.type.name === "tableHeader") {
|
|
34489
|
+
return anchorCellPos;
|
|
34490
|
+
}
|
|
34491
|
+
}
|
|
34492
|
+
const { $from } = editor.state.selection;
|
|
34493
|
+
for (let depth = $from.depth; depth > 0; depth -= 1) {
|
|
34494
|
+
const node = $from.node(depth);
|
|
34495
|
+
if (node.type.name === "tableCell" || node.type.name === "tableHeader") {
|
|
34496
|
+
return $from.before(depth);
|
|
34497
|
+
}
|
|
34498
|
+
}
|
|
34499
|
+
return null;
|
|
34500
|
+
}
|
|
34501
|
+
function getSelectionTableCellAnchor(editor) {
|
|
34502
|
+
const cellPos = getSelectionTableCellPos(editor);
|
|
34503
|
+
if (cellPos == null) return null;
|
|
34504
|
+
const node = editor.state.doc.nodeAt(cellPos);
|
|
34505
|
+
if (!node || node.type.name !== "tableCell" && node.type.name !== "tableHeader") return null;
|
|
34506
|
+
const $cell = editor.state.doc.resolve(Math.min(cellPos + 1, editor.state.doc.content.size));
|
|
34507
|
+
for (let depth = $cell.depth; depth > 0; depth -= 1) {
|
|
34508
|
+
const tableNode = $cell.node(depth);
|
|
34509
|
+
if (tableNode.type.name !== "table") continue;
|
|
34510
|
+
const tableStart = $cell.start(depth);
|
|
34511
|
+
const rect = safeFindCell2(TableMap3.get(tableNode), cellPos - tableStart);
|
|
34512
|
+
if (!rect) return null;
|
|
34513
|
+
return {
|
|
34514
|
+
cellPos,
|
|
34515
|
+
column: rect.left,
|
|
34516
|
+
label: `${indexToColumnName(rect.left)}${rect.top + 1}`,
|
|
34517
|
+
node,
|
|
34518
|
+
row: rect.top,
|
|
34519
|
+
tablePos: tableStart - 1
|
|
34520
|
+
};
|
|
34521
|
+
}
|
|
34522
|
+
return null;
|
|
34523
|
+
}
|
|
34524
|
+
function restoreSelectionAtTableCell(editor, anchor) {
|
|
34525
|
+
const tableNode = editor.state.doc.nodeAt(anchor.tablePos);
|
|
34526
|
+
if (!tableNode || tableNode.type.name !== "table") return false;
|
|
34527
|
+
const map = TableMap3.get(tableNode);
|
|
34528
|
+
if (anchor.row >= map.height || anchor.column >= map.width) return false;
|
|
34529
|
+
const relativeCellPos = map.positionAt(anchor.row, anchor.column, tableNode);
|
|
34530
|
+
const cellPos = anchor.tablePos + 1 + relativeCellPos;
|
|
34531
|
+
const cellNode = editor.state.doc.nodeAt(cellPos);
|
|
34532
|
+
if (!cellNode || cellNode.type.name !== "tableCell" && cellNode.type.name !== "tableHeader") return false;
|
|
34533
|
+
const selectionPos = Math.min(cellPos + 2, editor.state.doc.content.size);
|
|
34534
|
+
editor.view.dispatch(
|
|
34535
|
+
editor.state.tr.setSelection(TextSelection3.near(editor.state.doc.resolve(selectionPos))).setMeta("addToHistory", false)
|
|
34536
|
+
);
|
|
34537
|
+
return true;
|
|
34538
|
+
}
|
|
34539
|
+
function getSelectedTableFormulaCell(editor) {
|
|
34540
|
+
const selectedCell = getSelectionTableCellAnchor(editor);
|
|
34541
|
+
if (!selectedCell) return null;
|
|
34542
|
+
const formula = typeof selectedCell.node.attrs.formula === "string" ? selectedCell.node.attrs.formula.trim() : "";
|
|
34543
|
+
if (!formula) return null;
|
|
34544
|
+
const computedValue = typeof selectedCell.node.attrs.computedValue === "string" ? selectedCell.node.attrs.computedValue : "";
|
|
34545
|
+
return {
|
|
34546
|
+
...selectedCell,
|
|
34547
|
+
computedValue,
|
|
34548
|
+
formula,
|
|
34549
|
+
formulaState: computedValue.startsWith("#") ? "error" : "computed"
|
|
34550
|
+
};
|
|
34551
|
+
}
|
|
34312
34552
|
function getSelectionTableInfo(editor) {
|
|
34313
34553
|
const { $from } = editor.state.selection;
|
|
34314
34554
|
for (let depth = $from.depth; depth > 0; depth -= 1) {
|
|
@@ -34355,6 +34595,28 @@ function createCellDisplayContent(cellNode, displayValue) {
|
|
|
34355
34595
|
}
|
|
34356
34596
|
return [paragraphType.create(null, cellNode.type.schema.text(displayValue))];
|
|
34357
34597
|
}
|
|
34598
|
+
function removeSelectedTableCellFormula(editor, clearContent) {
|
|
34599
|
+
const selectedCell = getSelectedTableFormulaCell(editor);
|
|
34600
|
+
if (!selectedCell) return false;
|
|
34601
|
+
const nextCell2 = selectedCell.node.type.create(
|
|
34602
|
+
{
|
|
34603
|
+
...selectedCell.node.attrs,
|
|
34604
|
+
formula: null,
|
|
34605
|
+
computedValue: null
|
|
34606
|
+
},
|
|
34607
|
+
clearContent ? createCellDisplayContent(selectedCell.node, "") : selectedCell.node.content,
|
|
34608
|
+
selectedCell.node.marks
|
|
34609
|
+
);
|
|
34610
|
+
let transaction = editor.state.tr.replaceWith(selectedCell.cellPos, selectedCell.cellPos + selectedCell.node.nodeSize, nextCell2).setMeta(UEDITOR_TABLE_FORMULA_RECALCULATE_META, true);
|
|
34611
|
+
const selectionPos = Math.min(selectedCell.cellPos + 2, transaction.doc.content.size);
|
|
34612
|
+
transaction = transaction.setSelection(TextSelection3.near(transaction.doc.resolve(selectionPos)));
|
|
34613
|
+
editor.view.dispatch(transaction);
|
|
34614
|
+
recalculateActiveTableFormulas(editor);
|
|
34615
|
+
restoreSelectionAtTableCell(editor, selectedCell);
|
|
34616
|
+
editor.view.focus();
|
|
34617
|
+
dispatchTableLayoutChange(editor);
|
|
34618
|
+
return true;
|
|
34619
|
+
}
|
|
34358
34620
|
function buildTableValueMap(tableNode) {
|
|
34359
34621
|
const map = TableMap3.get(tableNode);
|
|
34360
34622
|
const values = /* @__PURE__ */ new Map();
|
|
@@ -34377,21 +34639,15 @@ function normalizeFormulaInput(formula) {
|
|
|
34377
34639
|
function setSelectedTableCellFormula(editor, formula) {
|
|
34378
34640
|
const normalized = normalizeFormulaInput(formula);
|
|
34379
34641
|
const { state, view } = editor;
|
|
34642
|
+
const selectionAnchor = getSelectionTableCellAnchor(editor);
|
|
34380
34643
|
if (!normalized) {
|
|
34381
|
-
|
|
34382
|
-
const clearedValue2 = setCellAttr2("computedValue", null)(editor.state, view.dispatch.bind(view));
|
|
34383
|
-
if (clearedFormula || clearedValue2) {
|
|
34384
|
-
recalculateActiveTableFormulas(editor);
|
|
34385
|
-
view.focus();
|
|
34386
|
-
dispatchTableLayoutChange(editor);
|
|
34387
|
-
return true;
|
|
34388
|
-
}
|
|
34389
|
-
return false;
|
|
34644
|
+
return clearSelectedTableCellFormula(editor);
|
|
34390
34645
|
}
|
|
34391
34646
|
const appliedFormula = setCellAttr2("formula", normalized)(state, view.dispatch.bind(view));
|
|
34392
34647
|
const clearedValue = setCellAttr2("computedValue", null)(editor.state, view.dispatch.bind(view));
|
|
34393
34648
|
if (appliedFormula || clearedValue) {
|
|
34394
34649
|
recalculateActiveTableFormulas(editor);
|
|
34650
|
+
if (selectionAnchor) restoreSelectionAtTableCell(editor, selectionAnchor);
|
|
34395
34651
|
view.focus();
|
|
34396
34652
|
dispatchTableLayoutChange(editor);
|
|
34397
34653
|
return true;
|
|
@@ -34399,7 +34655,10 @@ function setSelectedTableCellFormula(editor, formula) {
|
|
|
34399
34655
|
return false;
|
|
34400
34656
|
}
|
|
34401
34657
|
function clearSelectedTableCellFormula(editor) {
|
|
34402
|
-
return
|
|
34658
|
+
return removeSelectedTableCellFormula(editor, true);
|
|
34659
|
+
}
|
|
34660
|
+
function convertSelectedTableCellFormulaToValue(editor) {
|
|
34661
|
+
return removeSelectedTableCellFormula(editor, false);
|
|
34403
34662
|
}
|
|
34404
34663
|
function setSelectedTableCellNumberFormat(editor, numberFormat) {
|
|
34405
34664
|
const value = numberFormat && numberFormat !== "text" ? numberFormat : null;
|
|
@@ -34599,6 +34858,58 @@ function recalculateAllTableFormulas(editor) {
|
|
|
34599
34858
|
|
|
34600
34859
|
// src/components/UEditor/menus.tsx
|
|
34601
34860
|
import { Fragment as Fragment30, jsx as jsx92, jsxs as jsxs76 } from "react/jsx-runtime";
|
|
34861
|
+
var FloatingSlashCommandMenu = ({ editor, onClose }) => {
|
|
34862
|
+
const t = useSmartTranslations("UEditor");
|
|
34863
|
+
const messages = useMemo25(() => buildSlashCommandMessages(t), [t]);
|
|
34864
|
+
const items = useMemo25(() => buildSlashCommandItems({ query: "", messages }), [messages]);
|
|
34865
|
+
const listRef = useRef34(null);
|
|
34866
|
+
useEffect37(() => {
|
|
34867
|
+
const handleKeyDown2 = (event) => {
|
|
34868
|
+
if (event.key === "Escape") {
|
|
34869
|
+
event.preventDefault();
|
|
34870
|
+
onClose();
|
|
34871
|
+
return;
|
|
34872
|
+
}
|
|
34873
|
+
const handled = listRef.current?.onKeyDown({ event }) ?? false;
|
|
34874
|
+
if (handled) {
|
|
34875
|
+
event.preventDefault();
|
|
34876
|
+
}
|
|
34877
|
+
};
|
|
34878
|
+
document.addEventListener("keydown", handleKeyDown2);
|
|
34879
|
+
return () => document.removeEventListener("keydown", handleKeyDown2);
|
|
34880
|
+
}, [onClose]);
|
|
34881
|
+
return /* @__PURE__ */ jsx92(
|
|
34882
|
+
SlashCommandList,
|
|
34883
|
+
{
|
|
34884
|
+
ref: listRef,
|
|
34885
|
+
items,
|
|
34886
|
+
messages,
|
|
34887
|
+
command: (item) => {
|
|
34888
|
+
item.command({ editor });
|
|
34889
|
+
onClose();
|
|
34890
|
+
}
|
|
34891
|
+
}
|
|
34892
|
+
);
|
|
34893
|
+
};
|
|
34894
|
+
var FloatingMenuContent = ({ editor }) => {
|
|
34895
|
+
const t = useSmartTranslations("UEditor");
|
|
34896
|
+
const [showCommands, setShowCommands] = useState50(false);
|
|
34897
|
+
if (showCommands) {
|
|
34898
|
+
return /* @__PURE__ */ jsx92(FloatingSlashCommandMenu, { editor, onClose: () => setShowCommands(false) });
|
|
34899
|
+
}
|
|
34900
|
+
return /* @__PURE__ */ jsxs76(
|
|
34901
|
+
"button",
|
|
34902
|
+
{
|
|
34903
|
+
type: "button",
|
|
34904
|
+
onClick: () => setShowCommands(true),
|
|
34905
|
+
className: "flex items-center gap-1 px-2 py-1.5 rounded-lg hover:bg-accent transition-all group",
|
|
34906
|
+
children: [
|
|
34907
|
+
/* @__PURE__ */ jsx92(Plus3, { className: "w-4 h-4 text-muted-foreground group-hover:text-foreground" }),
|
|
34908
|
+
/* @__PURE__ */ jsx92("span", { className: "text-sm text-muted-foreground group-hover:text-foreground", children: t("floatingMenu.addBlock") })
|
|
34909
|
+
]
|
|
34910
|
+
}
|
|
34911
|
+
);
|
|
34912
|
+
};
|
|
34602
34913
|
function applyTableCellBackground(editor, color) {
|
|
34603
34914
|
const value = color || null;
|
|
34604
34915
|
const { state, view } = editor;
|
|
@@ -35337,7 +35648,8 @@ var LinkPreviewContent = ({ editor, onEdit }) => {
|
|
|
35337
35648
|
const url = editor.getAttributes("link").href || "";
|
|
35338
35649
|
const t = useSmartTranslations("UEditor");
|
|
35339
35650
|
const handleOpen = () => {
|
|
35340
|
-
|
|
35651
|
+
const safeUrl = sanitizeUEditorUrl(url, "link");
|
|
35652
|
+
if (safeUrl) window.open(safeUrl, "_blank", "noopener,noreferrer");
|
|
35341
35653
|
};
|
|
35342
35654
|
const handleUnlink = () => {
|
|
35343
35655
|
editor.chain().focus().unsetLink().run();
|
|
@@ -35360,23 +35672,26 @@ var CustomBubbleMenu = ({
|
|
|
35360
35672
|
const BUBBLE_MENU_ESTIMATED_HEIGHT = 44;
|
|
35361
35673
|
const [isVisible, setIsVisible] = useState50(false);
|
|
35362
35674
|
const [linkInputOpen, setLinkInputOpen] = useState50(false);
|
|
35363
|
-
const [position,
|
|
35675
|
+
const [position, setPosition2] = useState50({
|
|
35364
35676
|
top: 0,
|
|
35365
35677
|
left: 0,
|
|
35366
35678
|
placement: "top"
|
|
35367
35679
|
});
|
|
35368
35680
|
const menuRef = useRef34(null);
|
|
35369
35681
|
const keepOpenRef = useRef34(false);
|
|
35682
|
+
const [keepOpen, setKeepOpenState] = useState50(false);
|
|
35370
35683
|
const showTimeoutRef = useRef34(null);
|
|
35371
35684
|
const suppressShowUntilRef = useRef34(0);
|
|
35372
35685
|
const setKeepOpen = useCallback23((next) => {
|
|
35373
35686
|
keepOpenRef.current = next;
|
|
35687
|
+
setKeepOpenState(next);
|
|
35374
35688
|
if (!next) setLinkInputOpen(false);
|
|
35375
35689
|
if (next) setIsVisible(true);
|
|
35376
35690
|
}, []);
|
|
35377
35691
|
const closeBubbleMenu = useCallback23(() => {
|
|
35378
35692
|
suppressShowUntilRef.current = Date.now() + 1e3;
|
|
35379
35693
|
keepOpenRef.current = false;
|
|
35694
|
+
setKeepOpenState(false);
|
|
35380
35695
|
setLinkInputOpen(false);
|
|
35381
35696
|
setIsVisible(false);
|
|
35382
35697
|
if (showTimeoutRef.current) {
|
|
@@ -35432,7 +35747,7 @@ var CustomBubbleMenu = ({
|
|
|
35432
35747
|
Math.max(viewportPadding, (start.left + end.left) / 2)
|
|
35433
35748
|
);
|
|
35434
35749
|
const top = placement === "top" ? Math.max(viewportPadding, selectionTop - BUBBLE_MENU_OFFSET) : Math.min(window.innerHeight - viewportPadding, selectionBottom + BUBBLE_MENU_OFFSET);
|
|
35435
|
-
|
|
35750
|
+
setPosition2({ top, left, placement });
|
|
35436
35751
|
if (keepOpenRef.current) {
|
|
35437
35752
|
clearShowTimeout();
|
|
35438
35753
|
setIsVisible(true);
|
|
@@ -35482,13 +35797,13 @@ var CustomBubbleMenu = ({
|
|
|
35482
35797
|
onMouseDown: (e) => {
|
|
35483
35798
|
const target = e.target;
|
|
35484
35799
|
if (target?.closest?.("[data-ueditor-close-on-select]")) {
|
|
35485
|
-
|
|
35800
|
+
setKeepOpen(false);
|
|
35486
35801
|
} else if (target?.closest?.("[data-ueditor-keep-open]")) {
|
|
35487
|
-
|
|
35802
|
+
setKeepOpen(true);
|
|
35488
35803
|
}
|
|
35489
35804
|
e.preventDefault();
|
|
35490
35805
|
},
|
|
35491
|
-
children: editor.isActive("link") && !
|
|
35806
|
+
children: editor.isActive("link") && !keepOpen && !linkInputOpen ? /* @__PURE__ */ jsx92(
|
|
35492
35807
|
LinkPreviewContent,
|
|
35493
35808
|
{
|
|
35494
35809
|
editor,
|
|
@@ -35514,6 +35829,54 @@ var CustomBubbleMenu = ({
|
|
|
35514
35829
|
document.body
|
|
35515
35830
|
);
|
|
35516
35831
|
};
|
|
35832
|
+
var CustomFloatingMenu = ({ editor }) => {
|
|
35833
|
+
const FLOATING_MENU_OFFSET = 16;
|
|
35834
|
+
const [isVisible, setIsVisible] = useState50(false);
|
|
35835
|
+
const [position, setPosition2] = useState50({ top: 0, left: 0 });
|
|
35836
|
+
useEffect37(() => {
|
|
35837
|
+
const updatePosition = () => {
|
|
35838
|
+
const { state, view } = editor;
|
|
35839
|
+
const { $from, empty } = state.selection;
|
|
35840
|
+
const isEmptyTextBlock = $from.parent.isTextblock && $from.parent.type.name === "paragraph" && $from.parent.textContent === "" && empty;
|
|
35841
|
+
if (!isEmptyTextBlock || !view.hasFocus()) {
|
|
35842
|
+
setIsVisible(false);
|
|
35843
|
+
return;
|
|
35844
|
+
}
|
|
35845
|
+
const coords = view.coordsAtPos($from.pos);
|
|
35846
|
+
setPosition2({ top: coords.top - FLOATING_MENU_OFFSET, left: coords.left });
|
|
35847
|
+
setIsVisible(true);
|
|
35848
|
+
};
|
|
35849
|
+
const handleBlur = () => setIsVisible(false);
|
|
35850
|
+
editor.on("selectionUpdate", updatePosition);
|
|
35851
|
+
editor.on("focus", updatePosition);
|
|
35852
|
+
editor.on("blur", handleBlur);
|
|
35853
|
+
editor.on("update", updatePosition);
|
|
35854
|
+
return () => {
|
|
35855
|
+
editor.off("selectionUpdate", updatePosition);
|
|
35856
|
+
editor.off("focus", updatePosition);
|
|
35857
|
+
editor.off("blur", handleBlur);
|
|
35858
|
+
editor.off("update", updatePosition);
|
|
35859
|
+
};
|
|
35860
|
+
}, [editor]);
|
|
35861
|
+
if (!isVisible) return null;
|
|
35862
|
+
return createPortal8(
|
|
35863
|
+
/* @__PURE__ */ jsx92(
|
|
35864
|
+
"div",
|
|
35865
|
+
{
|
|
35866
|
+
"data-popover": true,
|
|
35867
|
+
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",
|
|
35868
|
+
style: {
|
|
35869
|
+
top: `${position.top}px`,
|
|
35870
|
+
left: `${position.left}px`,
|
|
35871
|
+
transform: "translate(-50%, -100%)"
|
|
35872
|
+
},
|
|
35873
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
35874
|
+
children: /* @__PURE__ */ jsx92(FloatingMenuContent, { editor })
|
|
35875
|
+
}
|
|
35876
|
+
),
|
|
35877
|
+
document.body
|
|
35878
|
+
);
|
|
35879
|
+
};
|
|
35517
35880
|
|
|
35518
35881
|
// src/components/UEditor/CharacterCount.tsx
|
|
35519
35882
|
import { jsxs as jsxs77 } from "react/jsx-runtime";
|
|
@@ -38589,7 +38952,7 @@ var Selection = class {
|
|
|
38589
38952
|
found.
|
|
38590
38953
|
*/
|
|
38591
38954
|
static findFrom($pos, dir, textOnly = false) {
|
|
38592
|
-
let inner = $pos.parent.inlineContent ? new
|
|
38955
|
+
let inner = $pos.parent.inlineContent ? new TextSelection4($pos) : findSelectionIn($pos.node(0), $pos.parent, $pos.pos, $pos.index(), dir, textOnly);
|
|
38593
38956
|
if (inner)
|
|
38594
38957
|
return inner;
|
|
38595
38958
|
for (let depth = $pos.depth - 1; depth >= 0; depth--) {
|
|
@@ -38658,7 +39021,7 @@ var Selection = class {
|
|
|
38658
39021
|
returns the bookmark for that.
|
|
38659
39022
|
*/
|
|
38660
39023
|
getBookmark() {
|
|
38661
|
-
return
|
|
39024
|
+
return TextSelection4.between(this.$anchor, this.$head).getBookmark();
|
|
38662
39025
|
}
|
|
38663
39026
|
};
|
|
38664
39027
|
Selection.prototype.visible = true;
|
|
@@ -38678,7 +39041,7 @@ function checkTextSelection($pos) {
|
|
|
38678
39041
|
console["warn"]("TextSelection endpoint not pointing into a node with inline content (" + $pos.parent.type.name + ")");
|
|
38679
39042
|
}
|
|
38680
39043
|
}
|
|
38681
|
-
var
|
|
39044
|
+
var TextSelection4 = class _TextSelection extends Selection {
|
|
38682
39045
|
/**
|
|
38683
39046
|
Construct a text selection between the given points.
|
|
38684
39047
|
*/
|
|
@@ -38764,7 +39127,7 @@ var TextSelection3 = class _TextSelection extends Selection {
|
|
|
38764
39127
|
return new _TextSelection($anchor, $head);
|
|
38765
39128
|
}
|
|
38766
39129
|
};
|
|
38767
|
-
Selection.jsonID("text",
|
|
39130
|
+
Selection.jsonID("text", TextSelection4);
|
|
38768
39131
|
var TextBookmark = class _TextBookmark {
|
|
38769
39132
|
constructor(anchor, head) {
|
|
38770
39133
|
this.anchor = anchor;
|
|
@@ -38774,7 +39137,7 @@ var TextBookmark = class _TextBookmark {
|
|
|
38774
39137
|
return new _TextBookmark(mapping.map(this.anchor), mapping.map(this.head));
|
|
38775
39138
|
}
|
|
38776
39139
|
resolve(doc) {
|
|
38777
|
-
return
|
|
39140
|
+
return TextSelection4.between(doc.resolve(this.anchor), doc.resolve(this.head));
|
|
38778
39141
|
}
|
|
38779
39142
|
};
|
|
38780
39143
|
var NodeSelection2 = class _NodeSelection extends Selection {
|
|
@@ -38893,7 +39256,7 @@ var AllBookmark = {
|
|
|
38893
39256
|
};
|
|
38894
39257
|
function findSelectionIn(doc, node, pos, index, dir, text = false) {
|
|
38895
39258
|
if (node.inlineContent)
|
|
38896
|
-
return
|
|
39259
|
+
return TextSelection4.create(doc, pos);
|
|
38897
39260
|
for (let i = index - (dir > 0 ? 0 : 1); dir > 0 ? i < node.childCount : i >= 0; i += dir) {
|
|
38898
39261
|
let child = node.child(i);
|
|
38899
39262
|
if (!child.isAtom) {
|
|
@@ -39485,7 +39848,7 @@ var CellSelection = class CellSelection2 extends Selection {
|
|
|
39485
39848
|
else if (tableChanged && this.isColSelection()) return CellSelection2.colSelection($anchorCell, $headCell);
|
|
39486
39849
|
else return new CellSelection2($anchorCell, $headCell);
|
|
39487
39850
|
}
|
|
39488
|
-
return
|
|
39851
|
+
return TextSelection4.between($anchorCell, $headCell);
|
|
39489
39852
|
}
|
|
39490
39853
|
content() {
|
|
39491
39854
|
const table = this.$anchorCell.node(-1);
|
|
@@ -40100,7 +40463,7 @@ function shiftArrow(axis, dir) {
|
|
|
40100
40463
|
};
|
|
40101
40464
|
}
|
|
40102
40465
|
function atEndOfCell(view, axis, dir) {
|
|
40103
|
-
if (!(view.state.selection instanceof
|
|
40466
|
+
if (!(view.state.selection instanceof TextSelection4)) return null;
|
|
40104
40467
|
const { $head } = view.state.selection;
|
|
40105
40468
|
for (let d = $head.depth - 1; d >= 0; d--) {
|
|
40106
40469
|
const parent = $head.node(d);
|
|
@@ -41231,7 +41594,7 @@ function TableControls({ editor, containerRef }) {
|
|
|
41231
41594
|
destructive: true
|
|
41232
41595
|
}
|
|
41233
41596
|
];
|
|
41234
|
-
}, [layout, runAtActiveCell, t]);
|
|
41597
|
+
}, [editor, layout, runAtActiveCell, t]);
|
|
41235
41598
|
const getRowHandleMenuItems = React83.useCallback((rowHandle) => [
|
|
41236
41599
|
{
|
|
41237
41600
|
label: t("tableMenu.addRowBefore"),
|
|
@@ -41447,6 +41810,42 @@ var UEDITOR_PROSEMIRROR_CLASS_NAME = cn(
|
|
|
41447
41810
|
"[&_th]:px-2",
|
|
41448
41811
|
"[&_th]:py-0",
|
|
41449
41812
|
"[&_th_p]:my-0",
|
|
41813
|
+
"[&_td[data-formula]]:pr-7",
|
|
41814
|
+
"[&_th[data-formula]]:pr-7",
|
|
41815
|
+
"[&_td[data-formula]]:before:pointer-events-none",
|
|
41816
|
+
"[&_th[data-formula]]:before:pointer-events-none",
|
|
41817
|
+
"[&_td[data-formula]]:before:absolute",
|
|
41818
|
+
"[&_th[data-formula]]:before:absolute",
|
|
41819
|
+
"[&_td[data-formula]]:before:right-1",
|
|
41820
|
+
"[&_th[data-formula]]:before:right-1",
|
|
41821
|
+
"[&_td[data-formula]]:before:top-1",
|
|
41822
|
+
"[&_th[data-formula]]:before:top-1",
|
|
41823
|
+
"[&_td[data-formula]]:before:z-[1]",
|
|
41824
|
+
"[&_th[data-formula]]:before:z-[1]",
|
|
41825
|
+
"[&_td[data-formula]]:before:rounded-sm",
|
|
41826
|
+
"[&_th[data-formula]]:before:rounded-sm",
|
|
41827
|
+
"[&_td[data-formula]]:before:bg-primary/10",
|
|
41828
|
+
"[&_th[data-formula]]:before:bg-primary/10",
|
|
41829
|
+
"[&_td[data-formula]]:before:px-1",
|
|
41830
|
+
"[&_th[data-formula]]:before:px-1",
|
|
41831
|
+
"[&_td[data-formula]]:before:font-mono",
|
|
41832
|
+
"[&_th[data-formula]]:before:font-mono",
|
|
41833
|
+
"[&_td[data-formula]]:before:text-[9px]",
|
|
41834
|
+
"[&_th[data-formula]]:before:text-[9px]",
|
|
41835
|
+
"[&_td[data-formula]]:before:font-semibold",
|
|
41836
|
+
"[&_th[data-formula]]:before:font-semibold",
|
|
41837
|
+
"[&_td[data-formula]]:before:leading-4",
|
|
41838
|
+
"[&_th[data-formula]]:before:leading-4",
|
|
41839
|
+
"[&_td[data-formula]]:before:text-primary",
|
|
41840
|
+
"[&_th[data-formula]]:before:text-primary",
|
|
41841
|
+
"[&_td[data-formula]]:before:content-['fx']",
|
|
41842
|
+
"[&_th[data-formula]]:before:content-['fx']",
|
|
41843
|
+
"[&_td[data-formula-state='error']]:bg-destructive/5",
|
|
41844
|
+
"[&_th[data-formula-state='error']]:bg-destructive/5",
|
|
41845
|
+
"[&_td[data-formula-state='error']]:before:bg-destructive/10",
|
|
41846
|
+
"[&_th[data-formula-state='error']]:before:bg-destructive/10",
|
|
41847
|
+
"[&_td[data-formula-state='error']]:before:text-destructive",
|
|
41848
|
+
"[&_th[data-formula-state='error']]:before:text-destructive",
|
|
41450
41849
|
"[&_td[colwidth]]:min-w-0",
|
|
41451
41850
|
"[&_th[colwidth]]:min-w-0",
|
|
41452
41851
|
"[&_td[data-colwidth]]:min-w-0",
|
|
@@ -41952,7 +42351,7 @@ function useUEditorTableInteractions(editor, editable = true) {
|
|
|
41952
42351
|
clearHoveredTableCell();
|
|
41953
42352
|
clearAllTableResizeHover();
|
|
41954
42353
|
};
|
|
41955
|
-
}, [beginResize, cancelResize, cleanupRowResize, clearActiveTableCell, clearAllTableResizeHover, clearHoveredTableCell, editable, editor, handleRowResizePointerMove, handleRowResizePointerUp, hideColumnGuide, hideRowGuide, isRowResizing, scheduleTableLayoutSync, showColumnGuide, showRowGuide, syncActiveRowResizeGuide, syncActiveTableCellFromSelection, updateActiveCellHighlight]);
|
|
42354
|
+
}, [beginResize, cancelResize, cleanupRowResize, clearActiveTableCell, clearAllTableResizeHover, clearHoveredTableCell, editable, editor, handleRowResizePointerMove, handleRowResizePointerUp, hideColumnGuide, hideRowGuide, isRowResizing, scheduleTableLayoutSync, setActiveTableCell, setHoveredTableCell, showColumnGuide, showRowGuide, syncActiveRowResizeGuide, syncActiveTableCellFromSelection, updateActiveCellHighlight]);
|
|
41956
42355
|
return {
|
|
41957
42356
|
editorContentRef,
|
|
41958
42357
|
tableColumnGuideRef,
|
|
@@ -41961,8 +42360,401 @@ function useUEditorTableInteractions(editor, editable = true) {
|
|
|
41961
42360
|
};
|
|
41962
42361
|
}
|
|
41963
42362
|
|
|
42363
|
+
// src/components/UEditor/use-formula-coordinate-overlay.ts
|
|
42364
|
+
import { useEffect as useEffect39, useRef as useRef37 } from "react";
|
|
42365
|
+
import { TableMap as TableMap6 } from "@tiptap/pm/tables";
|
|
42366
|
+
|
|
42367
|
+
// src/components/UEditor/table-formula-range-picker.ts
|
|
42368
|
+
import { TextSelection as TextSelection5 } from "@tiptap/pm/state";
|
|
42369
|
+
import { TableMap as TableMap5 } from "@tiptap/pm/tables";
|
|
42370
|
+
function getCellText2(cellNode) {
|
|
42371
|
+
return cellNode.textBetween(0, cellNode.content.size, "\n").trim();
|
|
42372
|
+
}
|
|
42373
|
+
function getFormulaEditingTableContext(view) {
|
|
42374
|
+
const { $from } = view.state.selection;
|
|
42375
|
+
for (let depth = $from.depth; depth > 0; depth -= 1) {
|
|
42376
|
+
const node = $from.node(depth);
|
|
42377
|
+
if (node.type.name !== "tableCell" && node.type.name !== "tableHeader") continue;
|
|
42378
|
+
if (!getCellText2(node).startsWith("=")) return null;
|
|
42379
|
+
const cellPos = $from.before(depth);
|
|
42380
|
+
const cellDom = view.nodeDOM(cellPos);
|
|
42381
|
+
if (!(cellDom instanceof HTMLTableCellElement)) return null;
|
|
42382
|
+
const tableDepth = depth - 2;
|
|
42383
|
+
const tableNode = tableDepth > 0 ? $from.node(tableDepth) : null;
|
|
42384
|
+
if (!tableNode || tableNode.type.name !== "table") return null;
|
|
42385
|
+
const tableDom = cellDom.closest("table");
|
|
42386
|
+
if (!(tableDom instanceof HTMLTableElement)) return null;
|
|
42387
|
+
return {
|
|
42388
|
+
cellContentEnd: $from.end(depth),
|
|
42389
|
+
cellContentStart: $from.start(depth),
|
|
42390
|
+
cellDom,
|
|
42391
|
+
formula: getCellText2(node),
|
|
42392
|
+
tableDom,
|
|
42393
|
+
tableNode,
|
|
42394
|
+
tablePos: $from.before(tableDepth)
|
|
42395
|
+
};
|
|
42396
|
+
}
|
|
42397
|
+
return null;
|
|
42398
|
+
}
|
|
42399
|
+
function cancelFormulaEditing(view) {
|
|
42400
|
+
const context = getFormulaEditingTableContext(view);
|
|
42401
|
+
if (!context) return false;
|
|
42402
|
+
let tr = view.state.tr.delete(context.cellContentStart, context.cellContentEnd);
|
|
42403
|
+
const selectionPos = Math.min(context.cellContentStart, tr.doc.content.size);
|
|
42404
|
+
tr = tr.setSelection(TextSelection5.near(tr.doc.resolve(selectionPos)));
|
|
42405
|
+
view.dispatch(tr);
|
|
42406
|
+
view.focus();
|
|
42407
|
+
return true;
|
|
42408
|
+
}
|
|
42409
|
+
function getReferenceInsertionPrefix(view, cellContentStart, insertionPos) {
|
|
42410
|
+
const textBeforeCursor = view.state.doc.textBetween(cellContentStart, insertionPos, "", "");
|
|
42411
|
+
const isInsideFunctionArguments = /[A-Z]+\([^)]*$/i.test(textBeforeCursor);
|
|
42412
|
+
const followsReference = /[A-Z]+[1-9]\d*(?::[A-Z]+[1-9]\d*)?\s*$/i.test(textBeforeCursor);
|
|
42413
|
+
return isInsideFunctionArguments && followsReference ? "," : "";
|
|
42414
|
+
}
|
|
42415
|
+
function findTableForPos(view, pos) {
|
|
42416
|
+
const $pos = view.state.doc.resolve(pos);
|
|
42417
|
+
for (let depth = $pos.depth; depth > 0; depth -= 1) {
|
|
42418
|
+
const node = $pos.node(depth);
|
|
42419
|
+
if (node.type.name === "table") {
|
|
42420
|
+
return {
|
|
42421
|
+
node,
|
|
42422
|
+
pos: $pos.before(depth),
|
|
42423
|
+
start: $pos.start(depth)
|
|
42424
|
+
};
|
|
42425
|
+
}
|
|
42426
|
+
}
|
|
42427
|
+
return null;
|
|
42428
|
+
}
|
|
42429
|
+
function getCellRelativePosFromDomPos2(map, tableStart, domPos) {
|
|
42430
|
+
const relativeDomPos = domPos - tableStart;
|
|
42431
|
+
const seen = /* @__PURE__ */ new Set();
|
|
42432
|
+
for (const relativeCellPos of map.map) {
|
|
42433
|
+
if (seen.has(relativeCellPos)) continue;
|
|
42434
|
+
seen.add(relativeCellPos);
|
|
42435
|
+
if (relativeDomPos === relativeCellPos || relativeDomPos === relativeCellPos + 1) {
|
|
42436
|
+
return relativeCellPos;
|
|
42437
|
+
}
|
|
42438
|
+
}
|
|
42439
|
+
return null;
|
|
42440
|
+
}
|
|
42441
|
+
function getFormulaTableCellInfo(view, target, tablePos) {
|
|
42442
|
+
const element = resolveEventElement(target);
|
|
42443
|
+
const cell = element?.closest?.("th,td");
|
|
42444
|
+
if (!(cell instanceof HTMLTableCellElement)) return null;
|
|
42445
|
+
const domPos = view.posAtDOM(cell, 0);
|
|
42446
|
+
const tableInfo = findTableForPos(view, domPos);
|
|
42447
|
+
if (!tableInfo || tableInfo.pos !== tablePos) return null;
|
|
42448
|
+
const map = TableMap5.get(tableInfo.node);
|
|
42449
|
+
const relativeCellPos = getCellRelativePosFromDomPos2(map, tableInfo.start, domPos);
|
|
42450
|
+
if (relativeCellPos == null) return null;
|
|
42451
|
+
const rect = map.findCell(relativeCellPos);
|
|
42452
|
+
return {
|
|
42453
|
+
cell,
|
|
42454
|
+
label: `${indexToColumnName(rect.left)}${rect.top + 1}`,
|
|
42455
|
+
rect
|
|
42456
|
+
};
|
|
42457
|
+
}
|
|
42458
|
+
function normalizeFormulaRangeLabel(fromLabel, toLabel) {
|
|
42459
|
+
return fromLabel === toLabel ? fromLabel : `${fromLabel}:${toLabel}`;
|
|
42460
|
+
}
|
|
42461
|
+
function replacePickedLabel(view, pickState, nextLabel, currentCell) {
|
|
42462
|
+
if (nextLabel === pickState.currentLabel && currentCell === pickState.currentCell) return pickState;
|
|
42463
|
+
if (nextLabel === pickState.currentLabel) {
|
|
42464
|
+
return {
|
|
42465
|
+
...pickState,
|
|
42466
|
+
currentCell
|
|
42467
|
+
};
|
|
42468
|
+
}
|
|
42469
|
+
let tr = view.state.tr.insertText(nextLabel, pickState.insertedFrom, pickState.insertedTo);
|
|
42470
|
+
const nextTo = pickState.insertedFrom + nextLabel.length;
|
|
42471
|
+
tr = tr.setSelection(TextSelection5.create(tr.doc, nextTo));
|
|
42472
|
+
view.dispatch(tr);
|
|
42473
|
+
return {
|
|
42474
|
+
...pickState,
|
|
42475
|
+
insertedTo: nextTo,
|
|
42476
|
+
currentLabel: nextLabel,
|
|
42477
|
+
currentCell
|
|
42478
|
+
};
|
|
42479
|
+
}
|
|
42480
|
+
function beginFormulaRangePick(view, event) {
|
|
42481
|
+
if (event.button !== 0) return null;
|
|
42482
|
+
const formulaCell = getFormulaEditingTableContext(view);
|
|
42483
|
+
if (!formulaCell) return null;
|
|
42484
|
+
const picked = getFormulaTableCellInfo(view, event.target, formulaCell.tablePos);
|
|
42485
|
+
if (!picked || picked.cell === formulaCell.cellDom) return null;
|
|
42486
|
+
const { from, to } = view.state.selection;
|
|
42487
|
+
const prefix = from === to ? getReferenceInsertionPrefix(view, formulaCell.cellContentStart, from) : "";
|
|
42488
|
+
const insertedText = `${prefix}${picked.label}`;
|
|
42489
|
+
const insertedFrom = from + prefix.length;
|
|
42490
|
+
let tr = view.state.tr.insertText(insertedText, from, to);
|
|
42491
|
+
tr = tr.setSelection(TextSelection5.create(tr.doc, from + insertedText.length));
|
|
42492
|
+
view.dispatch(tr);
|
|
42493
|
+
view.focus();
|
|
42494
|
+
event.preventDefault();
|
|
42495
|
+
event.stopPropagation();
|
|
42496
|
+
return {
|
|
42497
|
+
anchorLabel: picked.label,
|
|
42498
|
+
anchorCell: picked.cell,
|
|
42499
|
+
tablePos: formulaCell.tablePos,
|
|
42500
|
+
insertedFrom,
|
|
42501
|
+
insertedTo: insertedFrom + picked.label.length,
|
|
42502
|
+
currentLabel: picked.label,
|
|
42503
|
+
currentCell: picked.cell
|
|
42504
|
+
};
|
|
42505
|
+
}
|
|
42506
|
+
function updateFormulaRangePick(view, pickState, event) {
|
|
42507
|
+
const picked = getFormulaTableCellInfo(view, event.target, pickState.tablePos);
|
|
42508
|
+
if (!picked) return pickState;
|
|
42509
|
+
event.preventDefault();
|
|
42510
|
+
event.stopPropagation();
|
|
42511
|
+
return replacePickedLabel(
|
|
42512
|
+
view,
|
|
42513
|
+
pickState,
|
|
42514
|
+
normalizeFormulaRangeLabel(pickState.anchorLabel, picked.label),
|
|
42515
|
+
picked.cell
|
|
42516
|
+
);
|
|
42517
|
+
}
|
|
42518
|
+
function getFormulaRangePickHighlight(container, pickState) {
|
|
42519
|
+
if (!container.contains(pickState.anchorCell) || !container.contains(pickState.currentCell)) return null;
|
|
42520
|
+
const containerRect = container.getBoundingClientRect();
|
|
42521
|
+
const anchorRect = pickState.anchorCell.getBoundingClientRect();
|
|
42522
|
+
const currentRect = pickState.currentCell.getBoundingClientRect();
|
|
42523
|
+
const left = Math.min(anchorRect.left, currentRect.left) - containerRect.left + container.scrollLeft;
|
|
42524
|
+
const top = Math.min(anchorRect.top, currentRect.top) - containerRect.top + container.scrollTop;
|
|
42525
|
+
const right = Math.max(anchorRect.right, currentRect.right) - containerRect.left + container.scrollLeft;
|
|
42526
|
+
const bottom = Math.max(anchorRect.bottom, currentRect.bottom) - containerRect.top + container.scrollTop;
|
|
42527
|
+
return {
|
|
42528
|
+
left,
|
|
42529
|
+
top,
|
|
42530
|
+
width: Math.max(0, right - left),
|
|
42531
|
+
height: Math.max(0, bottom - top)
|
|
42532
|
+
};
|
|
42533
|
+
}
|
|
42534
|
+
|
|
42535
|
+
// src/components/UEditor/use-formula-coordinate-overlay.ts
|
|
42536
|
+
function setPosition(element, left, top, width, height) {
|
|
42537
|
+
element.style.left = `${left}px`;
|
|
42538
|
+
element.style.top = `${top}px`;
|
|
42539
|
+
if (width != null) element.style.width = `${Math.max(0, width)}px`;
|
|
42540
|
+
if (height != null) element.style.height = `${Math.max(0, height)}px`;
|
|
42541
|
+
}
|
|
42542
|
+
function createCoordinateLabel(kind, label) {
|
|
42543
|
+
const element = document.createElement("span");
|
|
42544
|
+
element.dataset.ueditorFormulaCoordinate = kind;
|
|
42545
|
+
element.textContent = label;
|
|
42546
|
+
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";
|
|
42547
|
+
return element;
|
|
42548
|
+
}
|
|
42549
|
+
function createReferenceHighlight(label) {
|
|
42550
|
+
const element = document.createElement("span");
|
|
42551
|
+
element.dataset.ueditorFormulaReference = label;
|
|
42552
|
+
element.className = "pointer-events-none absolute z-[21] rounded-[2px] border-2 border-emerald-500 bg-emerald-500/15";
|
|
42553
|
+
return element;
|
|
42554
|
+
}
|
|
42555
|
+
function useFormulaCoordinateOverlay(editor, containerRef, labels) {
|
|
42556
|
+
const overlayRef = useRef37(null);
|
|
42557
|
+
useEffect39(() => {
|
|
42558
|
+
if (!editor) return void 0;
|
|
42559
|
+
const overlay = overlayRef.current;
|
|
42560
|
+
const container = containerRef.current;
|
|
42561
|
+
if (!overlay || !container) return void 0;
|
|
42562
|
+
let animationFrame = null;
|
|
42563
|
+
let activeTable = null;
|
|
42564
|
+
let activeTablePos = null;
|
|
42565
|
+
let hoverLabel = null;
|
|
42566
|
+
const clearOverlay = () => {
|
|
42567
|
+
activeTable = null;
|
|
42568
|
+
activeTablePos = null;
|
|
42569
|
+
hoverLabel = null;
|
|
42570
|
+
overlay.replaceChildren();
|
|
42571
|
+
overlay.style.display = "none";
|
|
42572
|
+
};
|
|
42573
|
+
const syncOverlay = () => {
|
|
42574
|
+
animationFrame = null;
|
|
42575
|
+
if (editor.isDestroyed) return;
|
|
42576
|
+
const context = getFormulaEditingTableContext(editor.view);
|
|
42577
|
+
if (!context) {
|
|
42578
|
+
clearOverlay();
|
|
42579
|
+
return;
|
|
42580
|
+
}
|
|
42581
|
+
const containerRect = container.getBoundingClientRect();
|
|
42582
|
+
const tableRect = context.tableDom.getBoundingClientRect();
|
|
42583
|
+
const tableLeft = tableRect.left - containerRect.left + container.scrollLeft;
|
|
42584
|
+
const tableTop = tableRect.top - containerRect.top + container.scrollTop;
|
|
42585
|
+
const map = TableMap6.get(context.tableNode);
|
|
42586
|
+
const columnSegments = Array(map.width);
|
|
42587
|
+
const rowSegments = Array(map.height);
|
|
42588
|
+
const cellInfos = [];
|
|
42589
|
+
for (const cell of context.tableDom.querySelectorAll("th,td")) {
|
|
42590
|
+
if (!(cell instanceof HTMLTableCellElement)) continue;
|
|
42591
|
+
const info = getFormulaTableCellInfo(editor.view, cell, context.tablePos);
|
|
42592
|
+
if (!info) continue;
|
|
42593
|
+
cellInfos.push(info);
|
|
42594
|
+
const cellRect = cell.getBoundingClientRect();
|
|
42595
|
+
const columnSpan = Math.max(1, info.rect.right - info.rect.left);
|
|
42596
|
+
const rowSpan = Math.max(1, info.rect.bottom - info.rect.top);
|
|
42597
|
+
const columnWidth = cellRect.width / columnSpan;
|
|
42598
|
+
const rowHeight = cellRect.height / rowSpan;
|
|
42599
|
+
for (let column = info.rect.left; column < info.rect.right; column += 1) {
|
|
42600
|
+
if (!columnSegments[column] || columnSpan < columnSegments[column].span) {
|
|
42601
|
+
columnSegments[column] = {
|
|
42602
|
+
start: cellRect.left - containerRect.left + container.scrollLeft + (column - info.rect.left) * columnWidth,
|
|
42603
|
+
size: columnWidth,
|
|
42604
|
+
span: columnSpan
|
|
42605
|
+
};
|
|
42606
|
+
}
|
|
42607
|
+
}
|
|
42608
|
+
for (let row = info.rect.top; row < info.rect.bottom; row += 1) {
|
|
42609
|
+
if (!rowSegments[row] || rowSpan < rowSegments[row].span) {
|
|
42610
|
+
rowSegments[row] = {
|
|
42611
|
+
start: cellRect.top - containerRect.top + container.scrollTop + (row - info.rect.top) * rowHeight,
|
|
42612
|
+
size: rowHeight,
|
|
42613
|
+
span: rowSpan
|
|
42614
|
+
};
|
|
42615
|
+
}
|
|
42616
|
+
}
|
|
42617
|
+
}
|
|
42618
|
+
const fallbackColumnWidth = map.width > 0 ? tableRect.width / map.width : 0;
|
|
42619
|
+
const fallbackRowHeight = map.height > 0 ? tableRect.height / map.height : 0;
|
|
42620
|
+
const children = [];
|
|
42621
|
+
const actions = document.createElement("div");
|
|
42622
|
+
actions.dataset.ueditorFormulaActions = "";
|
|
42623
|
+
actions.className = "pointer-events-auto absolute z-50 flex items-center gap-1 rounded-md border border-border bg-background p-1 shadow-md";
|
|
42624
|
+
const formulaCellRect = context.cellDom.getBoundingClientRect();
|
|
42625
|
+
setPosition(
|
|
42626
|
+
actions,
|
|
42627
|
+
formulaCellRect.left - containerRect.left + container.scrollLeft,
|
|
42628
|
+
formulaCellRect.bottom - containerRect.top + container.scrollTop + 4
|
|
42629
|
+
);
|
|
42630
|
+
const applyButton = document.createElement("button");
|
|
42631
|
+
applyButton.type = "button";
|
|
42632
|
+
applyButton.dataset.ueditorFormulaApply = "";
|
|
42633
|
+
applyButton.disabled = isDraftTableFormula(context.formula);
|
|
42634
|
+
applyButton.textContent = `${applyButton.disabled ? "" : "\u2713 "}${labels.apply} (Enter)`;
|
|
42635
|
+
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";
|
|
42636
|
+
const cancelButton = document.createElement("button");
|
|
42637
|
+
cancelButton.type = "button";
|
|
42638
|
+
cancelButton.dataset.ueditorFormulaCancel = "";
|
|
42639
|
+
cancelButton.textContent = `${labels.cancel} (Esc)`;
|
|
42640
|
+
cancelButton.className = "rounded px-2.5 py-1 text-xs font-medium text-foreground hover:bg-muted";
|
|
42641
|
+
const preserveSelection = (event) => {
|
|
42642
|
+
event.preventDefault();
|
|
42643
|
+
event.stopPropagation();
|
|
42644
|
+
};
|
|
42645
|
+
applyButton.addEventListener("mousedown", preserveSelection);
|
|
42646
|
+
cancelButton.addEventListener("mousedown", preserveSelection);
|
|
42647
|
+
applyButton.addEventListener("click", (event) => {
|
|
42648
|
+
event.preventDefault();
|
|
42649
|
+
event.stopPropagation();
|
|
42650
|
+
if (!applyButton.disabled) recalculateActiveTableFormulas(editor);
|
|
42651
|
+
});
|
|
42652
|
+
cancelButton.addEventListener("click", (event) => {
|
|
42653
|
+
event.preventDefault();
|
|
42654
|
+
event.stopPropagation();
|
|
42655
|
+
cancelFormulaEditing(editor.view);
|
|
42656
|
+
});
|
|
42657
|
+
actions.append(applyButton, cancelButton);
|
|
42658
|
+
children.push(actions);
|
|
42659
|
+
for (let column = 0; column < map.width; column += 1) {
|
|
42660
|
+
const segment = columnSegments[column] ?? {
|
|
42661
|
+
start: tableLeft + column * fallbackColumnWidth,
|
|
42662
|
+
size: fallbackColumnWidth,
|
|
42663
|
+
span: map.width
|
|
42664
|
+
};
|
|
42665
|
+
const label = createCoordinateLabel("column", indexToColumnName(column));
|
|
42666
|
+
setPosition(label, segment.start, Math.max(0, tableTop - 22), segment.size, 20);
|
|
42667
|
+
children.push(label);
|
|
42668
|
+
}
|
|
42669
|
+
for (let row = 0; row < map.height; row += 1) {
|
|
42670
|
+
const segment = rowSegments[row] ?? {
|
|
42671
|
+
start: tableTop + row * fallbackRowHeight,
|
|
42672
|
+
size: fallbackRowHeight,
|
|
42673
|
+
span: map.height
|
|
42674
|
+
};
|
|
42675
|
+
const label = createCoordinateLabel("row", String(row + 1));
|
|
42676
|
+
setPosition(label, Math.max(0, tableLeft - 28), segment.start, 26, segment.size);
|
|
42677
|
+
children.push(label);
|
|
42678
|
+
}
|
|
42679
|
+
const references = new Set(getTableFormulaReferences(context.formula));
|
|
42680
|
+
for (const info of cellInfos) {
|
|
42681
|
+
if (!info || !references.has(info.label)) continue;
|
|
42682
|
+
const cellRect = info.cell.getBoundingClientRect();
|
|
42683
|
+
const highlight = createReferenceHighlight(info.label);
|
|
42684
|
+
setPosition(
|
|
42685
|
+
highlight,
|
|
42686
|
+
cellRect.left - containerRect.left + container.scrollLeft + 2,
|
|
42687
|
+
cellRect.top - containerRect.top + container.scrollTop + 2,
|
|
42688
|
+
cellRect.width - 4,
|
|
42689
|
+
cellRect.height - 4
|
|
42690
|
+
);
|
|
42691
|
+
children.push(highlight);
|
|
42692
|
+
}
|
|
42693
|
+
hoverLabel = document.createElement("span");
|
|
42694
|
+
hoverLabel.dataset.ueditorFormulaHoverLabel = "";
|
|
42695
|
+
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";
|
|
42696
|
+
children.push(hoverLabel);
|
|
42697
|
+
overlay.replaceChildren(...children);
|
|
42698
|
+
overlay.style.display = "block";
|
|
42699
|
+
activeTable = context.tableDom;
|
|
42700
|
+
activeTablePos = context.tablePos;
|
|
42701
|
+
};
|
|
42702
|
+
const scheduleSync = () => {
|
|
42703
|
+
if (animationFrame != null) return;
|
|
42704
|
+
animationFrame = window.requestAnimationFrame(syncOverlay);
|
|
42705
|
+
};
|
|
42706
|
+
const handleMouseMove2 = (event) => {
|
|
42707
|
+
if (!activeTable || activeTablePos == null || !hoverLabel) return;
|
|
42708
|
+
const target = event.target instanceof Element ? event.target.closest("th,td") : null;
|
|
42709
|
+
if (!(target instanceof HTMLTableCellElement) || target.closest("table") !== activeTable) {
|
|
42710
|
+
hoverLabel.style.display = "none";
|
|
42711
|
+
return;
|
|
42712
|
+
}
|
|
42713
|
+
const info = getFormulaTableCellInfo(editor.view, target, activeTablePos);
|
|
42714
|
+
if (!info) {
|
|
42715
|
+
hoverLabel.style.display = "none";
|
|
42716
|
+
return;
|
|
42717
|
+
}
|
|
42718
|
+
const containerRect = container.getBoundingClientRect();
|
|
42719
|
+
const cellRect = target.getBoundingClientRect();
|
|
42720
|
+
hoverLabel.textContent = info.label;
|
|
42721
|
+
hoverLabel.style.display = "block";
|
|
42722
|
+
setPosition(
|
|
42723
|
+
hoverLabel,
|
|
42724
|
+
cellRect.left - containerRect.left + container.scrollLeft + 4,
|
|
42725
|
+
cellRect.top - containerRect.top + container.scrollTop + 4
|
|
42726
|
+
);
|
|
42727
|
+
};
|
|
42728
|
+
const handleMouseLeave2 = () => {
|
|
42729
|
+
if (hoverLabel) hoverLabel.style.display = "none";
|
|
42730
|
+
};
|
|
42731
|
+
editor.on("selectionUpdate", scheduleSync);
|
|
42732
|
+
editor.on("update", scheduleSync);
|
|
42733
|
+
editor.on("focus", scheduleSync);
|
|
42734
|
+
editor.on("blur", scheduleSync);
|
|
42735
|
+
editor.view.dom.addEventListener("mousemove", handleMouseMove2);
|
|
42736
|
+
editor.view.dom.addEventListener("mouseleave", handleMouseLeave2);
|
|
42737
|
+
container.addEventListener(UEDITOR_TABLE_LAYOUT_CHANGE_EVENT, scheduleSync);
|
|
42738
|
+
window.addEventListener("resize", scheduleSync);
|
|
42739
|
+
scheduleSync();
|
|
42740
|
+
return () => {
|
|
42741
|
+
editor.off("selectionUpdate", scheduleSync);
|
|
42742
|
+
editor.off("update", scheduleSync);
|
|
42743
|
+
editor.off("focus", scheduleSync);
|
|
42744
|
+
editor.off("blur", scheduleSync);
|
|
42745
|
+
editor.view.dom.removeEventListener("mousemove", handleMouseMove2);
|
|
42746
|
+
editor.view.dom.removeEventListener("mouseleave", handleMouseLeave2);
|
|
42747
|
+
container.removeEventListener(UEDITOR_TABLE_LAYOUT_CHANGE_EVENT, scheduleSync);
|
|
42748
|
+
window.removeEventListener("resize", scheduleSync);
|
|
42749
|
+
if (animationFrame != null) window.cancelAnimationFrame(animationFrame);
|
|
42750
|
+
clearOverlay();
|
|
42751
|
+
};
|
|
42752
|
+
}, [containerRef, editor, labels.apply, labels.cancel]);
|
|
42753
|
+
return overlayRef;
|
|
42754
|
+
}
|
|
42755
|
+
|
|
41964
42756
|
// src/components/UEditor/menu-bar.tsx
|
|
41965
|
-
import React86, { useMemo as useMemo26, useRef as
|
|
42757
|
+
import React86, { useMemo as useMemo26, useRef as useRef38, useState as useState51 } from "react";
|
|
41966
42758
|
import { useEditorState as useEditorState3 } from "@tiptap/react";
|
|
41967
42759
|
import {
|
|
41968
42760
|
AlignCenter as AlignCenter4,
|
|
@@ -42586,7 +43378,7 @@ var MenuBar = ({
|
|
|
42586
43378
|
editor,
|
|
42587
43379
|
selector: ({ transactionNumber }) => transactionNumber
|
|
42588
43380
|
});
|
|
42589
|
-
const fileInputRef =
|
|
43381
|
+
const fileInputRef = useRef38(null);
|
|
42590
43382
|
const [showImageInput, setShowImageInput] = useState51(false);
|
|
42591
43383
|
const [showLinkInput, setShowLinkInput] = useState51(false);
|
|
42592
43384
|
const [isInsertMenuOpen, setIsInsertMenuOpen] = useState51(false);
|
|
@@ -42875,149 +43667,156 @@ var MenuBar = ({
|
|
|
42875
43667
|
] });
|
|
42876
43668
|
};
|
|
42877
43669
|
|
|
42878
|
-
// src/components/UEditor/table-formula-
|
|
42879
|
-
import {
|
|
42880
|
-
import {
|
|
42881
|
-
|
|
42882
|
-
|
|
42883
|
-
}
|
|
42884
|
-
|
|
42885
|
-
const
|
|
42886
|
-
|
|
42887
|
-
|
|
42888
|
-
|
|
42889
|
-
|
|
42890
|
-
|
|
42891
|
-
|
|
42892
|
-
|
|
42893
|
-
const
|
|
42894
|
-
|
|
42895
|
-
return {
|
|
42896
|
-
cellDom: cellDom instanceof HTMLTableCellElement ? cellDom : null,
|
|
42897
|
-
tablePos: $from.before(tableDepth)
|
|
43670
|
+
// src/components/UEditor/table-formula-bar.tsx
|
|
43671
|
+
import { useEffect as useEffect40, useRef as useRef39, useState as useState52 } from "react";
|
|
43672
|
+
import { useEditorState as useEditorState4 } from "@tiptap/react";
|
|
43673
|
+
import { AlertCircle as AlertCircle5, Check as Check14, Hash as Hash2, Sigma as Sigma3, Trash2 as Trash26 } from "lucide-react";
|
|
43674
|
+
import { jsx as jsx99, jsxs as jsxs82 } from "react/jsx-runtime";
|
|
43675
|
+
function TableFormulaBar({ editor }) {
|
|
43676
|
+
const t = useSmartTranslations("UEditor");
|
|
43677
|
+
const inputRef = useRef39(null);
|
|
43678
|
+
const [draftState, setDraftState] = useState52(null);
|
|
43679
|
+
useEditorState4({
|
|
43680
|
+
editor,
|
|
43681
|
+
selector: ({ transactionNumber }) => transactionNumber
|
|
43682
|
+
});
|
|
43683
|
+
const selectedCell = getSelectedTableFormulaCell(editor);
|
|
43684
|
+
useEffect40(() => {
|
|
43685
|
+
const focusFormulaInput = () => {
|
|
43686
|
+
inputRef.current?.focus();
|
|
42898
43687
|
};
|
|
42899
|
-
|
|
42900
|
-
|
|
42901
|
-
|
|
42902
|
-
|
|
42903
|
-
|
|
42904
|
-
for (let depth = $pos.depth; depth > 0; depth -= 1) {
|
|
42905
|
-
const node = $pos.node(depth);
|
|
42906
|
-
if (node.type.name === "table") {
|
|
42907
|
-
return {
|
|
42908
|
-
node,
|
|
42909
|
-
pos: $pos.before(depth),
|
|
42910
|
-
start: $pos.start(depth)
|
|
42911
|
-
};
|
|
42912
|
-
}
|
|
42913
|
-
}
|
|
42914
|
-
return null;
|
|
42915
|
-
}
|
|
42916
|
-
function getCellRelativePosFromDomPos2(map, tableStart, domPos) {
|
|
42917
|
-
const relativeDomPos = domPos - tableStart;
|
|
42918
|
-
const seen = /* @__PURE__ */ new Set();
|
|
42919
|
-
for (const relativeCellPos of map.map) {
|
|
42920
|
-
if (seen.has(relativeCellPos)) continue;
|
|
42921
|
-
seen.add(relativeCellPos);
|
|
42922
|
-
if (relativeDomPos === relativeCellPos || relativeDomPos === relativeCellPos + 1) {
|
|
42923
|
-
return relativeCellPos;
|
|
42924
|
-
}
|
|
42925
|
-
}
|
|
42926
|
-
return null;
|
|
42927
|
-
}
|
|
42928
|
-
function getPickedCellLabel(view, target, tablePos) {
|
|
42929
|
-
const element = resolveEventElement(target);
|
|
42930
|
-
const cell = element?.closest?.("th,td");
|
|
42931
|
-
if (!(cell instanceof HTMLTableCellElement)) return null;
|
|
42932
|
-
const domPos = view.posAtDOM(cell, 0);
|
|
42933
|
-
const tableInfo = findTableForPos(view, domPos);
|
|
42934
|
-
if (!tableInfo || tableInfo.pos !== tablePos) return null;
|
|
42935
|
-
const map = TableMap5.get(tableInfo.node);
|
|
42936
|
-
const relativeCellPos = getCellRelativePosFromDomPos2(map, tableInfo.start, domPos);
|
|
42937
|
-
if (relativeCellPos == null) return null;
|
|
42938
|
-
const rect = map.findCell(relativeCellPos);
|
|
42939
|
-
return {
|
|
42940
|
-
cell,
|
|
42941
|
-
label: `${indexToColumnName(rect.left)}${rect.top + 1}`
|
|
42942
|
-
};
|
|
42943
|
-
}
|
|
42944
|
-
function normalizeFormulaRangeLabel(fromLabel, toLabel) {
|
|
42945
|
-
return fromLabel === toLabel ? fromLabel : `${fromLabel}:${toLabel}`;
|
|
42946
|
-
}
|
|
42947
|
-
function replacePickedLabel(view, pickState, nextLabel, currentCell) {
|
|
42948
|
-
if (nextLabel === pickState.currentLabel && currentCell === pickState.currentCell) return pickState;
|
|
42949
|
-
if (nextLabel === pickState.currentLabel) {
|
|
42950
|
-
return {
|
|
42951
|
-
...pickState,
|
|
42952
|
-
currentCell
|
|
43688
|
+
const handleDoubleClick = (event) => {
|
|
43689
|
+
const target = resolveEventElement(event.target);
|
|
43690
|
+
if (!target?.closest?.("td[data-formula],th[data-formula]")) return;
|
|
43691
|
+
event.preventDefault();
|
|
43692
|
+
focusFormulaInput();
|
|
42953
43693
|
};
|
|
42954
|
-
|
|
42955
|
-
|
|
42956
|
-
|
|
42957
|
-
|
|
42958
|
-
|
|
42959
|
-
return
|
|
42960
|
-
|
|
42961
|
-
|
|
42962
|
-
|
|
42963
|
-
|
|
43694
|
+
editor.view.dom.addEventListener("dblclick", handleDoubleClick);
|
|
43695
|
+
return () => {
|
|
43696
|
+
editor.view.dom.removeEventListener("dblclick", handleDoubleClick);
|
|
43697
|
+
};
|
|
43698
|
+
}, [editor]);
|
|
43699
|
+
if (!selectedCell) return null;
|
|
43700
|
+
const selectedCellKey = `${selectedCell.tablePos}:${selectedCell.row}:${selectedCell.column}`;
|
|
43701
|
+
const draft = draftState?.cellKey === selectedCellKey && draftState.sourceFormula === selectedCell.formula ? draftState.value : selectedCell.formula;
|
|
43702
|
+
const setDraft = (value) => {
|
|
43703
|
+
setDraftState({
|
|
43704
|
+
cellKey: selectedCellKey,
|
|
43705
|
+
sourceFormula: selectedCell.formula,
|
|
43706
|
+
value
|
|
43707
|
+
});
|
|
42964
43708
|
};
|
|
42965
|
-
|
|
42966
|
-
|
|
42967
|
-
|
|
42968
|
-
|
|
42969
|
-
|
|
42970
|
-
|
|
42971
|
-
|
|
42972
|
-
|
|
42973
|
-
let tr = view.state.tr.insertText(picked.label, from, to);
|
|
42974
|
-
tr = tr.setSelection(TextSelection4.create(tr.doc, from + picked.label.length));
|
|
42975
|
-
view.dispatch(tr);
|
|
42976
|
-
view.focus();
|
|
42977
|
-
event.preventDefault();
|
|
42978
|
-
event.stopPropagation();
|
|
42979
|
-
return {
|
|
42980
|
-
anchorLabel: picked.label,
|
|
42981
|
-
anchorCell: picked.cell,
|
|
42982
|
-
tablePos: formulaCell.tablePos,
|
|
42983
|
-
insertedFrom: from,
|
|
42984
|
-
insertedTo: from + picked.label.length,
|
|
42985
|
-
currentLabel: picked.label,
|
|
42986
|
-
currentCell: picked.cell
|
|
43709
|
+
const applyDraft = () => {
|
|
43710
|
+
const normalized = normalizeFormulaInput(draft);
|
|
43711
|
+
if (normalized) {
|
|
43712
|
+
setSelectedTableCellFormula(editor, normalized);
|
|
43713
|
+
} else {
|
|
43714
|
+
clearSelectedTableCellFormula(editor);
|
|
43715
|
+
}
|
|
43716
|
+
setDraftState(null);
|
|
42987
43717
|
};
|
|
42988
|
-
|
|
42989
|
-
|
|
42990
|
-
|
|
42991
|
-
|
|
42992
|
-
|
|
42993
|
-
event.stopPropagation();
|
|
42994
|
-
return replacePickedLabel(
|
|
42995
|
-
view,
|
|
42996
|
-
pickState,
|
|
42997
|
-
normalizeFormulaRangeLabel(pickState.anchorLabel, picked.label),
|
|
42998
|
-
picked.cell
|
|
42999
|
-
);
|
|
43000
|
-
}
|
|
43001
|
-
function getFormulaRangePickHighlight(container, pickState) {
|
|
43002
|
-
if (!container.contains(pickState.anchorCell) || !container.contains(pickState.currentCell)) return null;
|
|
43003
|
-
const containerRect = container.getBoundingClientRect();
|
|
43004
|
-
const anchorRect = pickState.anchorCell.getBoundingClientRect();
|
|
43005
|
-
const currentRect = pickState.currentCell.getBoundingClientRect();
|
|
43006
|
-
const left = Math.min(anchorRect.left, currentRect.left) - containerRect.left + container.scrollLeft;
|
|
43007
|
-
const top = Math.min(anchorRect.top, currentRect.top) - containerRect.top + container.scrollTop;
|
|
43008
|
-
const right = Math.max(anchorRect.right, currentRect.right) - containerRect.left + container.scrollLeft;
|
|
43009
|
-
const bottom = Math.max(anchorRect.bottom, currentRect.bottom) - containerRect.top + container.scrollTop;
|
|
43010
|
-
return {
|
|
43011
|
-
left,
|
|
43012
|
-
top,
|
|
43013
|
-
width: Math.max(0, right - left),
|
|
43014
|
-
height: Math.max(0, bottom - top)
|
|
43718
|
+
const cancelDraft = () => {
|
|
43719
|
+
setDraftState(null);
|
|
43720
|
+
window.setTimeout(() => {
|
|
43721
|
+
if (!editor.isDestroyed) editor.commands.focus();
|
|
43722
|
+
}, 0);
|
|
43015
43723
|
};
|
|
43724
|
+
return /* @__PURE__ */ jsxs82(
|
|
43725
|
+
"div",
|
|
43726
|
+
{
|
|
43727
|
+
"data-ueditor-formula-bar": "",
|
|
43728
|
+
role: "toolbar",
|
|
43729
|
+
"aria-label": t("tableMenu.formulaBar"),
|
|
43730
|
+
className: "flex min-h-10 items-center gap-2 border-b border-border/60 bg-muted/25 px-2 py-1.5",
|
|
43731
|
+
children: [
|
|
43732
|
+
/* @__PURE__ */ jsx99(
|
|
43733
|
+
"span",
|
|
43734
|
+
{
|
|
43735
|
+
"data-ueditor-formula-cell-label": "",
|
|
43736
|
+
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",
|
|
43737
|
+
children: selectedCell.label
|
|
43738
|
+
}
|
|
43739
|
+
),
|
|
43740
|
+
/* @__PURE__ */ jsx99(Sigma3, { "aria-hidden": "true", className: "h-4 w-4 shrink-0 text-primary" }),
|
|
43741
|
+
/* @__PURE__ */ jsx99(
|
|
43742
|
+
"input",
|
|
43743
|
+
{
|
|
43744
|
+
ref: inputRef,
|
|
43745
|
+
"data-ueditor-formula-input": "",
|
|
43746
|
+
value: draft,
|
|
43747
|
+
onChange: (event) => setDraft(event.target.value),
|
|
43748
|
+
onKeyDown: (event) => {
|
|
43749
|
+
event.stopPropagation();
|
|
43750
|
+
if (event.key === "Enter") {
|
|
43751
|
+
event.preventDefault();
|
|
43752
|
+
applyDraft();
|
|
43753
|
+
} else if (event.key === "Escape") {
|
|
43754
|
+
event.preventDefault();
|
|
43755
|
+
cancelDraft();
|
|
43756
|
+
}
|
|
43757
|
+
},
|
|
43758
|
+
"aria-label": t("tableMenu.editFormula"),
|
|
43759
|
+
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"
|
|
43760
|
+
}
|
|
43761
|
+
),
|
|
43762
|
+
selectedCell.formulaState === "error" && /* @__PURE__ */ jsxs82(
|
|
43763
|
+
"span",
|
|
43764
|
+
{
|
|
43765
|
+
role: "status",
|
|
43766
|
+
className: "hidden shrink-0 items-center gap-1 text-xs font-medium text-destructive sm:inline-flex",
|
|
43767
|
+
children: [
|
|
43768
|
+
/* @__PURE__ */ jsx99(AlertCircle5, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
|
|
43769
|
+
t("tableMenu.formulaError")
|
|
43770
|
+
]
|
|
43771
|
+
}
|
|
43772
|
+
),
|
|
43773
|
+
/* @__PURE__ */ jsx99(
|
|
43774
|
+
"button",
|
|
43775
|
+
{
|
|
43776
|
+
type: "button",
|
|
43777
|
+
onClick: applyDraft,
|
|
43778
|
+
"aria-label": t("tableMenu.apply"),
|
|
43779
|
+
title: t("tableMenu.apply"),
|
|
43780
|
+
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",
|
|
43781
|
+
children: /* @__PURE__ */ jsx99(Check14, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
43782
|
+
}
|
|
43783
|
+
),
|
|
43784
|
+
/* @__PURE__ */ jsx99(
|
|
43785
|
+
"button",
|
|
43786
|
+
{
|
|
43787
|
+
type: "button",
|
|
43788
|
+
onClick: () => {
|
|
43789
|
+
convertSelectedTableCellFormulaToValue(editor);
|
|
43790
|
+
setDraftState(null);
|
|
43791
|
+
},
|
|
43792
|
+
"aria-label": t("tableMenu.convertToValue"),
|
|
43793
|
+
title: t("tableMenu.convertToValue"),
|
|
43794
|
+
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",
|
|
43795
|
+
children: /* @__PURE__ */ jsx99(Hash2, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
43796
|
+
}
|
|
43797
|
+
),
|
|
43798
|
+
/* @__PURE__ */ jsx99(
|
|
43799
|
+
"button",
|
|
43800
|
+
{
|
|
43801
|
+
type: "button",
|
|
43802
|
+
onClick: () => {
|
|
43803
|
+
clearSelectedTableCellFormula(editor);
|
|
43804
|
+
setDraftState(null);
|
|
43805
|
+
},
|
|
43806
|
+
"aria-label": t("tableMenu.clearCell"),
|
|
43807
|
+
title: t("tableMenu.clearCell"),
|
|
43808
|
+
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",
|
|
43809
|
+
children: /* @__PURE__ */ jsx99(Trash26, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
43810
|
+
}
|
|
43811
|
+
)
|
|
43812
|
+
]
|
|
43813
|
+
}
|
|
43814
|
+
);
|
|
43016
43815
|
}
|
|
43017
43816
|
|
|
43018
43817
|
// src/components/UEditor/UEditor.tsx
|
|
43019
|
-
import { jsx as
|
|
43020
|
-
var UEditor =
|
|
43818
|
+
import { jsx as jsx100, jsxs as jsxs83 } from "react/jsx-runtime";
|
|
43819
|
+
var UEditor = React88.forwardRef(({
|
|
43021
43820
|
content = "",
|
|
43022
43821
|
onChange,
|
|
43023
43822
|
onHtmlChange,
|
|
@@ -43035,6 +43834,7 @@ var UEditor = React87.forwardRef(({
|
|
|
43035
43834
|
autofocus = false,
|
|
43036
43835
|
showToolbar = true,
|
|
43037
43836
|
showBubbleMenu = true,
|
|
43837
|
+
showFloatingMenu = false,
|
|
43038
43838
|
showCharacterCount = true,
|
|
43039
43839
|
showFooter = true,
|
|
43040
43840
|
maxCharacters,
|
|
@@ -43058,13 +43858,14 @@ var UEditor = React87.forwardRef(({
|
|
|
43058
43858
|
}, ref) => {
|
|
43059
43859
|
const t = useSmartTranslations("UEditor");
|
|
43060
43860
|
const effectivePlaceholder = placeholder ?? t("placeholder");
|
|
43061
|
-
const inFlightPrepareRef =
|
|
43062
|
-
const lastAppliedContentRef =
|
|
43063
|
-
const scheduledFormulaRecalculateRef =
|
|
43064
|
-
const
|
|
43065
|
-
const
|
|
43066
|
-
const
|
|
43067
|
-
const
|
|
43861
|
+
const inFlightPrepareRef = useRef40(null);
|
|
43862
|
+
const lastAppliedContentRef = useRef40(content ?? "");
|
|
43863
|
+
const scheduledFormulaRecalculateRef = useRef40(false);
|
|
43864
|
+
const editorInstanceRef = useRef40(null);
|
|
43865
|
+
const formulaRangePickRef = useRef40(null);
|
|
43866
|
+
const formulaRangeSurfaceRef = useRef40(null);
|
|
43867
|
+
const [formulaRangeHighlight, setFormulaRangeHighlight] = React88.useState(null);
|
|
43868
|
+
const scheduleFormulaRecalculate = React88.useCallback((editor2, options) => {
|
|
43068
43869
|
if (editor2.isDestroyed || scheduledFormulaRecalculateRef.current) return;
|
|
43069
43870
|
if (!options?.force && isEditingTableFormulaText(editor2)) return;
|
|
43070
43871
|
scheduledFormulaRecalculateRef.current = true;
|
|
@@ -43104,7 +43905,7 @@ var UEditor = React87.forwardRef(({
|
|
|
43104
43905
|
],
|
|
43105
43906
|
[effectivePlaceholder, t, maxCharacters, uploadImage, resolvedUploadFile, imageInsertMode, maxImageFileSize, allowedImageMimeTypes, fallbackToDataUrl, editable, fetchMetadata, extraExtensions]
|
|
43106
43907
|
);
|
|
43107
|
-
const syncFormulaRangeHighlight =
|
|
43908
|
+
const syncFormulaRangeHighlight = React88.useCallback((pickState) => {
|
|
43108
43909
|
const container = formulaRangeSurfaceRef.current;
|
|
43109
43910
|
setFormulaRangeHighlight(container && pickState ? getFormulaRangePickHighlight(container, pickState) : null);
|
|
43110
43911
|
}, []);
|
|
@@ -43149,6 +43950,29 @@ var UEditor = React87.forwardRef(({
|
|
|
43149
43950
|
},
|
|
43150
43951
|
keydown: (_view, event) => {
|
|
43151
43952
|
if (!(event instanceof KeyboardEvent)) return false;
|
|
43953
|
+
const formulaContext = getFormulaEditingTableContext(_view);
|
|
43954
|
+
if (formulaContext && event.key === "Enter") {
|
|
43955
|
+
event.preventDefault();
|
|
43956
|
+
event.stopPropagation();
|
|
43957
|
+
const activeEditor2 = editorInstanceRef.current;
|
|
43958
|
+
if (activeEditor2 && !isDraftTableFormula(formulaContext.formula)) {
|
|
43959
|
+
recalculateActiveTableFormulas(activeEditor2);
|
|
43960
|
+
}
|
|
43961
|
+
return true;
|
|
43962
|
+
}
|
|
43963
|
+
if (formulaContext && event.key === "Escape") {
|
|
43964
|
+
event.preventDefault();
|
|
43965
|
+
event.stopPropagation();
|
|
43966
|
+
cancelFormulaEditing(_view);
|
|
43967
|
+
return true;
|
|
43968
|
+
}
|
|
43969
|
+
const activeEditor = editorInstanceRef.current;
|
|
43970
|
+
if (activeEditor && (event.key === "Backspace" || event.key === "Delete") && getSelectedTableFormulaCell(activeEditor)) {
|
|
43971
|
+
event.preventDefault();
|
|
43972
|
+
event.stopPropagation();
|
|
43973
|
+
clearSelectedTableCellFormula(activeEditor);
|
|
43974
|
+
return true;
|
|
43975
|
+
}
|
|
43152
43976
|
if (event.key === "ArrowLeft" || event.key === "ArrowRight" || event.key === "ArrowUp" || event.key === "ArrowDown") {
|
|
43153
43977
|
event.stopPropagation();
|
|
43154
43978
|
}
|
|
@@ -43159,7 +43983,7 @@ var UEditor = React87.forwardRef(({
|
|
|
43159
43983
|
if (event.button !== 0) return false;
|
|
43160
43984
|
const target = resolveEventElement(event.target);
|
|
43161
43985
|
const anchor = target?.closest?.("a[href]");
|
|
43162
|
-
const href = anchor?.getAttribute("href") ?? "";
|
|
43986
|
+
const href = sanitizeUEditorUrl(anchor?.getAttribute("href") ?? "", "link");
|
|
43163
43987
|
if (!href) return false;
|
|
43164
43988
|
if (editable) return false;
|
|
43165
43989
|
if (!view.state.selection.empty) return false;
|
|
@@ -43185,16 +44009,28 @@ var UEditor = React87.forwardRef(({
|
|
|
43185
44009
|
onSelectionUpdate: ({ editor: editor2 }) => {
|
|
43186
44010
|
scheduleFormulaRecalculate(editor2);
|
|
43187
44011
|
},
|
|
43188
|
-
onBlur: ({ editor: editor2 }) => {
|
|
44012
|
+
onBlur: ({ editor: editor2, event }) => {
|
|
44013
|
+
const nextTarget = event.relatedTarget;
|
|
44014
|
+
if (nextTarget instanceof Element && nextTarget.closest("[data-ueditor-formula-bar]")) return;
|
|
43189
44015
|
scheduleFormulaRecalculate(editor2, { force: true });
|
|
43190
44016
|
}
|
|
43191
44017
|
});
|
|
44018
|
+
useEffect41(() => {
|
|
44019
|
+
editorInstanceRef.current = editor;
|
|
44020
|
+
return () => {
|
|
44021
|
+
if (editorInstanceRef.current === editor) editorInstanceRef.current = null;
|
|
44022
|
+
};
|
|
44023
|
+
}, [editor]);
|
|
43192
44024
|
const {
|
|
43193
44025
|
editorContentRef,
|
|
43194
44026
|
tableColumnGuideRef,
|
|
43195
44027
|
tableRowGuideRef,
|
|
43196
44028
|
activeTableCellHighlightRef
|
|
43197
44029
|
} = useUEditorTableInteractions(editor, editable);
|
|
44030
|
+
const formulaCoordinateOverlayRef = useFormulaCoordinateOverlay(editor, editorContentRef, {
|
|
44031
|
+
apply: t("tableMenu.apply"),
|
|
44032
|
+
cancel: t("imageInput.cancelBtn")
|
|
44033
|
+
});
|
|
43198
44034
|
useImperativeHandle4(
|
|
43199
44035
|
ref,
|
|
43200
44036
|
() => ({
|
|
@@ -43220,7 +44056,7 @@ var UEditor = React87.forwardRef(({
|
|
|
43220
44056
|
}),
|
|
43221
44057
|
[content, editor, uploadImageForSave, uploadFileForSave, uploadImageConcurrency]
|
|
43222
44058
|
);
|
|
43223
|
-
|
|
44059
|
+
useEffect41(() => {
|
|
43224
44060
|
if (!editor) return;
|
|
43225
44061
|
queueMicrotask(() => {
|
|
43226
44062
|
if (!editor.isDestroyed) {
|
|
@@ -43228,7 +44064,7 @@ var UEditor = React87.forwardRef(({
|
|
|
43228
44064
|
}
|
|
43229
44065
|
});
|
|
43230
44066
|
}, [editor]);
|
|
43231
|
-
|
|
44067
|
+
useEffect41(() => {
|
|
43232
44068
|
if (!editor) return;
|
|
43233
44069
|
const nextContent = content ?? "";
|
|
43234
44070
|
if (lastAppliedContentRef.current === nextContent) return;
|
|
@@ -43247,7 +44083,7 @@ var UEditor = React87.forwardRef(({
|
|
|
43247
44083
|
}
|
|
43248
44084
|
}, [content, editor]);
|
|
43249
44085
|
if (!editor) {
|
|
43250
|
-
return /* @__PURE__ */
|
|
44086
|
+
return /* @__PURE__ */ jsx100(
|
|
43251
44087
|
"div",
|
|
43252
44088
|
{
|
|
43253
44089
|
className: cn("w-full border bg-background flex items-center justify-center text-muted-foreground", className),
|
|
@@ -43256,7 +44092,7 @@ var UEditor = React87.forwardRef(({
|
|
|
43256
44092
|
}
|
|
43257
44093
|
);
|
|
43258
44094
|
}
|
|
43259
|
-
return /* @__PURE__ */
|
|
44095
|
+
return /* @__PURE__ */ jsxs83(
|
|
43260
44096
|
"div",
|
|
43261
44097
|
{
|
|
43262
44098
|
className: cn(
|
|
@@ -43272,7 +44108,7 @@ var UEditor = React87.forwardRef(({
|
|
|
43272
44108
|
className
|
|
43273
44109
|
),
|
|
43274
44110
|
children: [
|
|
43275
|
-
editable && showMenuBar && /* @__PURE__ */
|
|
44111
|
+
editable && showMenuBar && /* @__PURE__ */ jsx100(
|
|
43276
44112
|
MenuBar,
|
|
43277
44113
|
{
|
|
43278
44114
|
editor,
|
|
@@ -43287,7 +44123,7 @@ var UEditor = React87.forwardRef(({
|
|
|
43287
44123
|
onPreview
|
|
43288
44124
|
}
|
|
43289
44125
|
),
|
|
43290
|
-
editable && showToolbar && /* @__PURE__ */
|
|
44126
|
+
editable && showToolbar && /* @__PURE__ */ jsx100(
|
|
43291
44127
|
EditorToolbar,
|
|
43292
44128
|
{
|
|
43293
44129
|
editor,
|
|
@@ -43302,7 +44138,8 @@ var UEditor = React87.forwardRef(({
|
|
|
43302
44138
|
letterSpacings
|
|
43303
44139
|
}
|
|
43304
44140
|
),
|
|
43305
|
-
editable &&
|
|
44141
|
+
editable && /* @__PURE__ */ jsx100(TableFormulaBar, { editor }),
|
|
44142
|
+
editable && showBubbleMenu && /* @__PURE__ */ jsx100(
|
|
43306
44143
|
CustomBubbleMenu,
|
|
43307
44144
|
{
|
|
43308
44145
|
editor,
|
|
@@ -43310,7 +44147,8 @@ var UEditor = React87.forwardRef(({
|
|
|
43310
44147
|
lineHeights
|
|
43311
44148
|
}
|
|
43312
44149
|
),
|
|
43313
|
-
/* @__PURE__ */
|
|
44150
|
+
editable && showFloatingMenu && /* @__PURE__ */ jsx100(CustomFloatingMenu, { editor }),
|
|
44151
|
+
/* @__PURE__ */ jsxs83(
|
|
43314
44152
|
"div",
|
|
43315
44153
|
{
|
|
43316
44154
|
ref: (node) => {
|
|
@@ -43323,7 +44161,7 @@ var UEditor = React87.forwardRef(({
|
|
|
43323
44161
|
maxHeight
|
|
43324
44162
|
},
|
|
43325
44163
|
children: [
|
|
43326
|
-
/* @__PURE__ */
|
|
44164
|
+
/* @__PURE__ */ jsx100(
|
|
43327
44165
|
"span",
|
|
43328
44166
|
{
|
|
43329
44167
|
ref: tableColumnGuideRef,
|
|
@@ -43331,7 +44169,7 @@ var UEditor = React87.forwardRef(({
|
|
|
43331
44169
|
className: "pointer-events-none absolute z-20 bg-primary opacity-0 transition-opacity duration-100"
|
|
43332
44170
|
}
|
|
43333
44171
|
),
|
|
43334
|
-
/* @__PURE__ */
|
|
44172
|
+
/* @__PURE__ */ jsx100(
|
|
43335
44173
|
"span",
|
|
43336
44174
|
{
|
|
43337
44175
|
ref: tableRowGuideRef,
|
|
@@ -43339,7 +44177,7 @@ var UEditor = React87.forwardRef(({
|
|
|
43339
44177
|
className: "pointer-events-none absolute z-20 bg-primary opacity-0 transition-opacity duration-100"
|
|
43340
44178
|
}
|
|
43341
44179
|
),
|
|
43342
|
-
/* @__PURE__ */
|
|
44180
|
+
/* @__PURE__ */ jsx100(
|
|
43343
44181
|
"span",
|
|
43344
44182
|
{
|
|
43345
44183
|
ref: activeTableCellHighlightRef,
|
|
@@ -43348,7 +44186,15 @@ var UEditor = React87.forwardRef(({
|
|
|
43348
44186
|
className: "pointer-events-none hidden absolute z-20 rounded-[2px] border-2 border-primary bg-primary/10"
|
|
43349
44187
|
}
|
|
43350
44188
|
),
|
|
43351
|
-
|
|
44189
|
+
/* @__PURE__ */ jsx100(
|
|
44190
|
+
"div",
|
|
44191
|
+
{
|
|
44192
|
+
ref: formulaCoordinateOverlayRef,
|
|
44193
|
+
"data-ueditor-formula-coordinate-overlay": "",
|
|
44194
|
+
className: "pointer-events-none absolute inset-0 z-[21] hidden overflow-visible"
|
|
44195
|
+
}
|
|
44196
|
+
),
|
|
44197
|
+
formulaRangeHighlight && /* @__PURE__ */ jsx100(
|
|
43352
44198
|
"span",
|
|
43353
44199
|
{
|
|
43354
44200
|
"aria-hidden": "true",
|
|
@@ -43362,8 +44208,8 @@ var UEditor = React87.forwardRef(({
|
|
|
43362
44208
|
}
|
|
43363
44209
|
}
|
|
43364
44210
|
),
|
|
43365
|
-
editable && /* @__PURE__ */
|
|
43366
|
-
/* @__PURE__ */
|
|
44211
|
+
editable && /* @__PURE__ */ jsx100(TableControls, { editor, containerRef: editorContentRef }),
|
|
44212
|
+
/* @__PURE__ */ jsx100(
|
|
43367
44213
|
EditorContent,
|
|
43368
44214
|
{
|
|
43369
44215
|
editor,
|
|
@@ -43373,7 +44219,7 @@ var UEditor = React87.forwardRef(({
|
|
|
43373
44219
|
]
|
|
43374
44220
|
}
|
|
43375
44221
|
),
|
|
43376
|
-
showFooter && showCharacterCount && /* @__PURE__ */
|
|
44222
|
+
showFooter && showCharacterCount && /* @__PURE__ */ jsx100(CharacterCountDisplay, { editor, maxCharacters })
|
|
43377
44223
|
]
|
|
43378
44224
|
}
|
|
43379
44225
|
);
|