@underverse-ui/underverse 1.0.184 → 1.0.186
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 +782 -205
- 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 +776 -199
- 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;
|
|
@@ -29933,7 +30035,7 @@ var FormulaSuggestionList = forwardRef16((props, ref) => {
|
|
|
29933
30035
|
setSelectedIndex((prev) => (prev + 1) % props.items.length);
|
|
29934
30036
|
return true;
|
|
29935
30037
|
}
|
|
29936
|
-
if (event.key === "
|
|
30038
|
+
if (event.key === "Tab") {
|
|
29937
30039
|
const item = props.items[selectedIndex];
|
|
29938
30040
|
if (item) {
|
|
29939
30041
|
props.command(item);
|
|
@@ -30023,9 +30125,7 @@ var FormulaSuggestion = Extension4.create({
|
|
|
30023
30125
|
if (!props.clientRect) {
|
|
30024
30126
|
return;
|
|
30025
30127
|
}
|
|
30026
|
-
popup
|
|
30027
|
-
getReferenceClientRect: props.clientRect
|
|
30028
|
-
});
|
|
30128
|
+
setTippyReferenceClientRect(getFirstTippyInstance(popup), props.clientRect);
|
|
30029
30129
|
},
|
|
30030
30130
|
onKeyDown(props) {
|
|
30031
30131
|
const popupInstance = getFirstTippyInstance(popup);
|
|
@@ -31776,7 +31876,6 @@ function buildUEditorExtensions({
|
|
|
31776
31876
|
Gapcursor,
|
|
31777
31877
|
Link.configure({
|
|
31778
31878
|
openOnClick: false,
|
|
31779
|
-
protocols: ["http", "https", "mailto", "tel"],
|
|
31780
31879
|
isAllowedUri: (url) => isSafeUEditorUrl(url ?? "", "link"),
|
|
31781
31880
|
HTMLAttributes: {
|
|
31782
31881
|
class: "text-primary underline underline-offset-2 hover:text-primary/80 cursor-pointer"
|
|
@@ -32868,7 +32967,7 @@ var EditorToolbar = ({
|
|
|
32868
32967
|
const [showImageInput, setShowImageInput] = useState49(false);
|
|
32869
32968
|
const [showLinkInput, setShowLinkInput] = useState49(false);
|
|
32870
32969
|
const [isTableMenuOpen, setIsTableMenuOpen] = useState49(false);
|
|
32871
|
-
const
|
|
32970
|
+
const [preservedTableAnchorPos, setPreservedTableAnchorPos] = useState49(null);
|
|
32872
32971
|
const fileInputRef = useRef33(null);
|
|
32873
32972
|
const [isUploadingImage, setIsUploadingImage] = useState49(false);
|
|
32874
32973
|
const [imageUploadError, setImageUploadError] = useState49(null);
|
|
@@ -32883,7 +32982,7 @@ var EditorToolbar = ({
|
|
|
32883
32982
|
const tableAlignAttr = tableAttrs.tableAlign ?? tableAttrs.textAlign;
|
|
32884
32983
|
const currentTableAlign = tableAlignAttr === "center" || tableAlignAttr === "right" ? tableAlignAttr : "left";
|
|
32885
32984
|
const isTableSelected = tableInfo !== null;
|
|
32886
|
-
const hasTableContext = isTableSelected ||
|
|
32985
|
+
const hasTableContext = isTableSelected || preservedTableAnchorPos !== null;
|
|
32887
32986
|
const canMergeCells = hasTableContext && editor.can().mergeCells();
|
|
32888
32987
|
const canSplitCell = hasTableContext && editor.can().splitCell();
|
|
32889
32988
|
const currentCellVerticalAlign = normalizeStyleValue(editor.getAttributes("tableCell").verticalAlign || editor.getAttributes("tableHeader").verticalAlign) || "";
|
|
@@ -32908,7 +33007,7 @@ var EditorToolbar = ({
|
|
|
32908
33007
|
const displayedFontFamilyValue = currentFontFamily || defaultFontFamilyValue;
|
|
32909
33008
|
const displayedFontSizeLabel = currentFontSize ? currentFontSizeLabel : "13";
|
|
32910
33009
|
const activeFontSize = currentFontSize || "13px";
|
|
32911
|
-
const tableCommandAnchorPos =
|
|
33010
|
+
const tableCommandAnchorPos = preservedTableAnchorPos ?? tableAnchorPos ?? void 0;
|
|
32912
33011
|
const isMedium = variant === "medium";
|
|
32913
33012
|
const isMediumFull = variant === "medium-full";
|
|
32914
33013
|
const isFull = variant === "default" || variant === "full" || variant === "notion" || !variant;
|
|
@@ -33584,7 +33683,7 @@ var EditorToolbar = ({
|
|
|
33584
33683
|
isOpen: isTableMenuOpen,
|
|
33585
33684
|
onOpenChange: (open) => {
|
|
33586
33685
|
setIsTableMenuOpen(open);
|
|
33587
|
-
|
|
33686
|
+
setPreservedTableAnchorPos(open ? getTableAnchorPos(editor) : null);
|
|
33588
33687
|
},
|
|
33589
33688
|
trigger: /* @__PURE__ */ jsxs75(ToolbarButton, { onClick: () => {
|
|
33590
33689
|
}, title: t("toolbar.table"), children: [
|
|
@@ -33601,6 +33700,7 @@ var EditorToolbar = ({
|
|
|
33601
33700
|
onInsert: (rows, cols) => {
|
|
33602
33701
|
editor.chain().focus().insertTable({ rows, cols, withHeaderRow: true }).run();
|
|
33603
33702
|
setIsTableMenuOpen(false);
|
|
33703
|
+
setPreservedTableAnchorPos(null);
|
|
33604
33704
|
}
|
|
33605
33705
|
}
|
|
33606
33706
|
),
|
|
@@ -33816,6 +33916,7 @@ import {
|
|
|
33816
33916
|
} from "lucide-react";
|
|
33817
33917
|
|
|
33818
33918
|
// src/components/UEditor/table-formula-commands.ts
|
|
33919
|
+
import { TextSelection as TextSelection3 } from "@tiptap/pm/state";
|
|
33819
33920
|
import { selectedRect as selectedRect2, setCellAttr as setCellAttr2, TableMap as TableMap3 } from "@tiptap/pm/tables";
|
|
33820
33921
|
|
|
33821
33922
|
// src/components/UEditor/table-formula.ts
|
|
@@ -34379,6 +34480,75 @@ function getSelectionTableCellNode(editor) {
|
|
|
34379
34480
|
}
|
|
34380
34481
|
return null;
|
|
34381
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
|
+
}
|
|
34382
34552
|
function getSelectionTableInfo(editor) {
|
|
34383
34553
|
const { $from } = editor.state.selection;
|
|
34384
34554
|
for (let depth = $from.depth; depth > 0; depth -= 1) {
|
|
@@ -34425,6 +34595,28 @@ function createCellDisplayContent(cellNode, displayValue) {
|
|
|
34425
34595
|
}
|
|
34426
34596
|
return [paragraphType.create(null, cellNode.type.schema.text(displayValue))];
|
|
34427
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
|
+
}
|
|
34428
34620
|
function buildTableValueMap(tableNode) {
|
|
34429
34621
|
const map = TableMap3.get(tableNode);
|
|
34430
34622
|
const values = /* @__PURE__ */ new Map();
|
|
@@ -34447,21 +34639,15 @@ function normalizeFormulaInput(formula) {
|
|
|
34447
34639
|
function setSelectedTableCellFormula(editor, formula) {
|
|
34448
34640
|
const normalized = normalizeFormulaInput(formula);
|
|
34449
34641
|
const { state, view } = editor;
|
|
34642
|
+
const selectionAnchor = getSelectionTableCellAnchor(editor);
|
|
34450
34643
|
if (!normalized) {
|
|
34451
|
-
|
|
34452
|
-
const clearedValue2 = setCellAttr2("computedValue", null)(editor.state, view.dispatch.bind(view));
|
|
34453
|
-
if (clearedFormula || clearedValue2) {
|
|
34454
|
-
recalculateActiveTableFormulas(editor);
|
|
34455
|
-
view.focus();
|
|
34456
|
-
dispatchTableLayoutChange(editor);
|
|
34457
|
-
return true;
|
|
34458
|
-
}
|
|
34459
|
-
return false;
|
|
34644
|
+
return clearSelectedTableCellFormula(editor);
|
|
34460
34645
|
}
|
|
34461
34646
|
const appliedFormula = setCellAttr2("formula", normalized)(state, view.dispatch.bind(view));
|
|
34462
34647
|
const clearedValue = setCellAttr2("computedValue", null)(editor.state, view.dispatch.bind(view));
|
|
34463
34648
|
if (appliedFormula || clearedValue) {
|
|
34464
34649
|
recalculateActiveTableFormulas(editor);
|
|
34650
|
+
if (selectionAnchor) restoreSelectionAtTableCell(editor, selectionAnchor);
|
|
34465
34651
|
view.focus();
|
|
34466
34652
|
dispatchTableLayoutChange(editor);
|
|
34467
34653
|
return true;
|
|
@@ -34469,7 +34655,10 @@ function setSelectedTableCellFormula(editor, formula) {
|
|
|
34469
34655
|
return false;
|
|
34470
34656
|
}
|
|
34471
34657
|
function clearSelectedTableCellFormula(editor) {
|
|
34472
|
-
return
|
|
34658
|
+
return removeSelectedTableCellFormula(editor, true);
|
|
34659
|
+
}
|
|
34660
|
+
function convertSelectedTableCellFormulaToValue(editor) {
|
|
34661
|
+
return removeSelectedTableCellFormula(editor, false);
|
|
34473
34662
|
}
|
|
34474
34663
|
function setSelectedTableCellNumberFormat(editor, numberFormat) {
|
|
34475
34664
|
const value = numberFormat && numberFormat !== "text" ? numberFormat : null;
|
|
@@ -34669,6 +34858,58 @@ function recalculateAllTableFormulas(editor) {
|
|
|
34669
34858
|
|
|
34670
34859
|
// src/components/UEditor/menus.tsx
|
|
34671
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
|
+
};
|
|
34672
34913
|
function applyTableCellBackground(editor, color) {
|
|
34673
34914
|
const value = color || null;
|
|
34674
34915
|
const { state, view } = editor;
|
|
@@ -35407,7 +35648,8 @@ var LinkPreviewContent = ({ editor, onEdit }) => {
|
|
|
35407
35648
|
const url = editor.getAttributes("link").href || "";
|
|
35408
35649
|
const t = useSmartTranslations("UEditor");
|
|
35409
35650
|
const handleOpen = () => {
|
|
35410
|
-
|
|
35651
|
+
const safeUrl = sanitizeUEditorUrl(url, "link");
|
|
35652
|
+
if (safeUrl) window.open(safeUrl, "_blank", "noopener,noreferrer");
|
|
35411
35653
|
};
|
|
35412
35654
|
const handleUnlink = () => {
|
|
35413
35655
|
editor.chain().focus().unsetLink().run();
|
|
@@ -35437,16 +35679,19 @@ var CustomBubbleMenu = ({
|
|
|
35437
35679
|
});
|
|
35438
35680
|
const menuRef = useRef34(null);
|
|
35439
35681
|
const keepOpenRef = useRef34(false);
|
|
35682
|
+
const [keepOpen, setKeepOpenState] = useState50(false);
|
|
35440
35683
|
const showTimeoutRef = useRef34(null);
|
|
35441
35684
|
const suppressShowUntilRef = useRef34(0);
|
|
35442
35685
|
const setKeepOpen = useCallback23((next) => {
|
|
35443
35686
|
keepOpenRef.current = next;
|
|
35687
|
+
setKeepOpenState(next);
|
|
35444
35688
|
if (!next) setLinkInputOpen(false);
|
|
35445
35689
|
if (next) setIsVisible(true);
|
|
35446
35690
|
}, []);
|
|
35447
35691
|
const closeBubbleMenu = useCallback23(() => {
|
|
35448
35692
|
suppressShowUntilRef.current = Date.now() + 1e3;
|
|
35449
35693
|
keepOpenRef.current = false;
|
|
35694
|
+
setKeepOpenState(false);
|
|
35450
35695
|
setLinkInputOpen(false);
|
|
35451
35696
|
setIsVisible(false);
|
|
35452
35697
|
if (showTimeoutRef.current) {
|
|
@@ -35552,13 +35797,13 @@ var CustomBubbleMenu = ({
|
|
|
35552
35797
|
onMouseDown: (e) => {
|
|
35553
35798
|
const target = e.target;
|
|
35554
35799
|
if (target?.closest?.("[data-ueditor-close-on-select]")) {
|
|
35555
|
-
|
|
35800
|
+
setKeepOpen(false);
|
|
35556
35801
|
} else if (target?.closest?.("[data-ueditor-keep-open]")) {
|
|
35557
|
-
|
|
35802
|
+
setKeepOpen(true);
|
|
35558
35803
|
}
|
|
35559
35804
|
e.preventDefault();
|
|
35560
35805
|
},
|
|
35561
|
-
children: editor.isActive("link") && !
|
|
35806
|
+
children: editor.isActive("link") && !keepOpen && !linkInputOpen ? /* @__PURE__ */ jsx92(
|
|
35562
35807
|
LinkPreviewContent,
|
|
35563
35808
|
{
|
|
35564
35809
|
editor,
|
|
@@ -35584,6 +35829,54 @@ var CustomBubbleMenu = ({
|
|
|
35584
35829
|
document.body
|
|
35585
35830
|
);
|
|
35586
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
|
+
};
|
|
35587
35880
|
|
|
35588
35881
|
// src/components/UEditor/CharacterCount.tsx
|
|
35589
35882
|
import { jsxs as jsxs77 } from "react/jsx-runtime";
|
|
@@ -38659,7 +38952,7 @@ var Selection = class {
|
|
|
38659
38952
|
found.
|
|
38660
38953
|
*/
|
|
38661
38954
|
static findFrom($pos, dir, textOnly = false) {
|
|
38662
|
-
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);
|
|
38663
38956
|
if (inner)
|
|
38664
38957
|
return inner;
|
|
38665
38958
|
for (let depth = $pos.depth - 1; depth >= 0; depth--) {
|
|
@@ -38728,7 +39021,7 @@ var Selection = class {
|
|
|
38728
39021
|
returns the bookmark for that.
|
|
38729
39022
|
*/
|
|
38730
39023
|
getBookmark() {
|
|
38731
|
-
return
|
|
39024
|
+
return TextSelection4.between(this.$anchor, this.$head).getBookmark();
|
|
38732
39025
|
}
|
|
38733
39026
|
};
|
|
38734
39027
|
Selection.prototype.visible = true;
|
|
@@ -38748,7 +39041,7 @@ function checkTextSelection($pos) {
|
|
|
38748
39041
|
console["warn"]("TextSelection endpoint not pointing into a node with inline content (" + $pos.parent.type.name + ")");
|
|
38749
39042
|
}
|
|
38750
39043
|
}
|
|
38751
|
-
var
|
|
39044
|
+
var TextSelection4 = class _TextSelection extends Selection {
|
|
38752
39045
|
/**
|
|
38753
39046
|
Construct a text selection between the given points.
|
|
38754
39047
|
*/
|
|
@@ -38834,7 +39127,7 @@ var TextSelection3 = class _TextSelection extends Selection {
|
|
|
38834
39127
|
return new _TextSelection($anchor, $head);
|
|
38835
39128
|
}
|
|
38836
39129
|
};
|
|
38837
|
-
Selection.jsonID("text",
|
|
39130
|
+
Selection.jsonID("text", TextSelection4);
|
|
38838
39131
|
var TextBookmark = class _TextBookmark {
|
|
38839
39132
|
constructor(anchor, head) {
|
|
38840
39133
|
this.anchor = anchor;
|
|
@@ -38844,7 +39137,7 @@ var TextBookmark = class _TextBookmark {
|
|
|
38844
39137
|
return new _TextBookmark(mapping.map(this.anchor), mapping.map(this.head));
|
|
38845
39138
|
}
|
|
38846
39139
|
resolve(doc) {
|
|
38847
|
-
return
|
|
39140
|
+
return TextSelection4.between(doc.resolve(this.anchor), doc.resolve(this.head));
|
|
38848
39141
|
}
|
|
38849
39142
|
};
|
|
38850
39143
|
var NodeSelection2 = class _NodeSelection extends Selection {
|
|
@@ -38963,7 +39256,7 @@ var AllBookmark = {
|
|
|
38963
39256
|
};
|
|
38964
39257
|
function findSelectionIn(doc, node, pos, index, dir, text = false) {
|
|
38965
39258
|
if (node.inlineContent)
|
|
38966
|
-
return
|
|
39259
|
+
return TextSelection4.create(doc, pos);
|
|
38967
39260
|
for (let i = index - (dir > 0 ? 0 : 1); dir > 0 ? i < node.childCount : i >= 0; i += dir) {
|
|
38968
39261
|
let child = node.child(i);
|
|
38969
39262
|
if (!child.isAtom) {
|
|
@@ -39555,7 +39848,7 @@ var CellSelection = class CellSelection2 extends Selection {
|
|
|
39555
39848
|
else if (tableChanged && this.isColSelection()) return CellSelection2.colSelection($anchorCell, $headCell);
|
|
39556
39849
|
else return new CellSelection2($anchorCell, $headCell);
|
|
39557
39850
|
}
|
|
39558
|
-
return
|
|
39851
|
+
return TextSelection4.between($anchorCell, $headCell);
|
|
39559
39852
|
}
|
|
39560
39853
|
content() {
|
|
39561
39854
|
const table = this.$anchorCell.node(-1);
|
|
@@ -40170,7 +40463,7 @@ function shiftArrow(axis, dir) {
|
|
|
40170
40463
|
};
|
|
40171
40464
|
}
|
|
40172
40465
|
function atEndOfCell(view, axis, dir) {
|
|
40173
|
-
if (!(view.state.selection instanceof
|
|
40466
|
+
if (!(view.state.selection instanceof TextSelection4)) return null;
|
|
40174
40467
|
const { $head } = view.state.selection;
|
|
40175
40468
|
for (let d = $head.depth - 1; d >= 0; d--) {
|
|
40176
40469
|
const parent = $head.node(d);
|
|
@@ -41301,7 +41594,7 @@ function TableControls({ editor, containerRef }) {
|
|
|
41301
41594
|
destructive: true
|
|
41302
41595
|
}
|
|
41303
41596
|
];
|
|
41304
|
-
}, [layout, runAtActiveCell, t]);
|
|
41597
|
+
}, [editor, layout, runAtActiveCell, t]);
|
|
41305
41598
|
const getRowHandleMenuItems = React83.useCallback((rowHandle) => [
|
|
41306
41599
|
{
|
|
41307
41600
|
label: t("tableMenu.addRowBefore"),
|
|
@@ -41517,6 +41810,42 @@ var UEDITOR_PROSEMIRROR_CLASS_NAME = cn(
|
|
|
41517
41810
|
"[&_th]:px-2",
|
|
41518
41811
|
"[&_th]:py-0",
|
|
41519
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",
|
|
41520
41849
|
"[&_td[colwidth]]:min-w-0",
|
|
41521
41850
|
"[&_th[colwidth]]:min-w-0",
|
|
41522
41851
|
"[&_td[data-colwidth]]:min-w-0",
|
|
@@ -42022,7 +42351,7 @@ function useUEditorTableInteractions(editor, editable = true) {
|
|
|
42022
42351
|
clearHoveredTableCell();
|
|
42023
42352
|
clearAllTableResizeHover();
|
|
42024
42353
|
};
|
|
42025
|
-
}, [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]);
|
|
42026
42355
|
return {
|
|
42027
42356
|
editorContentRef,
|
|
42028
42357
|
tableColumnGuideRef,
|
|
@@ -42036,7 +42365,7 @@ import { useEffect as useEffect39, useRef as useRef37 } from "react";
|
|
|
42036
42365
|
import { TableMap as TableMap6 } from "@tiptap/pm/tables";
|
|
42037
42366
|
|
|
42038
42367
|
// src/components/UEditor/table-formula-range-picker.ts
|
|
42039
|
-
import { TextSelection as
|
|
42368
|
+
import { TextSelection as TextSelection5 } from "@tiptap/pm/state";
|
|
42040
42369
|
import { TableMap as TableMap5 } from "@tiptap/pm/tables";
|
|
42041
42370
|
function getCellText2(cellNode) {
|
|
42042
42371
|
return cellNode.textBetween(0, cellNode.content.size, "\n").trim();
|
|
@@ -42055,6 +42384,9 @@ function getFormulaEditingTableContext(view) {
|
|
|
42055
42384
|
if (!tableNode || tableNode.type.name !== "table") return null;
|
|
42056
42385
|
const tableDom = cellDom.closest("table");
|
|
42057
42386
|
if (!(tableDom instanceof HTMLTableElement)) return null;
|
|
42387
|
+
const tableMap = TableMap5.get(tableNode);
|
|
42388
|
+
const tableStart = $from.start(tableDepth);
|
|
42389
|
+
const formulaCellRect = tableMap.findCell(cellPos - tableStart);
|
|
42058
42390
|
return {
|
|
42059
42391
|
cellContentEnd: $from.end(depth),
|
|
42060
42392
|
cellContentStart: $from.start(depth),
|
|
@@ -42062,17 +42394,56 @@ function getFormulaEditingTableContext(view) {
|
|
|
42062
42394
|
formula: getCellText2(node),
|
|
42063
42395
|
tableDom,
|
|
42064
42396
|
tableNode,
|
|
42065
|
-
tablePos: $from.before(tableDepth)
|
|
42397
|
+
tablePos: $from.before(tableDepth),
|
|
42398
|
+
formulaCellLabel: `${indexToColumnName(formulaCellRect.left)}${formulaCellRect.top + 1}`
|
|
42066
42399
|
};
|
|
42067
42400
|
}
|
|
42068
42401
|
return null;
|
|
42069
42402
|
}
|
|
42403
|
+
function collectTableFormulaCells(tableNode) {
|
|
42404
|
+
const tableMap = TableMap5.get(tableNode);
|
|
42405
|
+
const formulas = [];
|
|
42406
|
+
tableNode.forEach((rowNode, rowOffset) => {
|
|
42407
|
+
rowNode.forEach((cellNode, cellOffset) => {
|
|
42408
|
+
const formula = typeof cellNode.attrs.formula === "string" ? cellNode.attrs.formula.trim() : "";
|
|
42409
|
+
if (!formula) return;
|
|
42410
|
+
const rect = tableMap.findCell(rowOffset + 1 + cellOffset);
|
|
42411
|
+
formulas.push({
|
|
42412
|
+
label: `${indexToColumnName(rect.left)}${rect.top + 1}`,
|
|
42413
|
+
formula
|
|
42414
|
+
});
|
|
42415
|
+
});
|
|
42416
|
+
});
|
|
42417
|
+
return formulas;
|
|
42418
|
+
}
|
|
42419
|
+
function getFormulaCycleBlockedLabels(tableNode, formulaCellLabel) {
|
|
42420
|
+
const graph = buildTableFormulaDependencyGraph(collectTableFormulaCells(tableNode));
|
|
42421
|
+
const blocked = /* @__PURE__ */ new Set();
|
|
42422
|
+
const queue = [formulaCellLabel.toUpperCase()];
|
|
42423
|
+
for (let index = 0; index < queue.length; index += 1) {
|
|
42424
|
+
const label = queue[index];
|
|
42425
|
+
if (!label || blocked.has(label)) continue;
|
|
42426
|
+
blocked.add(label);
|
|
42427
|
+
for (const dependent of graph.dependents.get(label) ?? []) {
|
|
42428
|
+
if (!blocked.has(dependent)) queue.push(dependent);
|
|
42429
|
+
}
|
|
42430
|
+
}
|
|
42431
|
+
return blocked;
|
|
42432
|
+
}
|
|
42433
|
+
function getReferenceLabels(reference) {
|
|
42434
|
+
const range = parseTableCellRange(reference);
|
|
42435
|
+
return range ? getTableCellRangeLabels(range) : [reference.toUpperCase()];
|
|
42436
|
+
}
|
|
42437
|
+
function isBlockedFormulaReference(tableNode, formulaCellLabel, reference) {
|
|
42438
|
+
const blockedLabels = getFormulaCycleBlockedLabels(tableNode, formulaCellLabel);
|
|
42439
|
+
return getReferenceLabels(reference).some((label) => blockedLabels.has(label));
|
|
42440
|
+
}
|
|
42070
42441
|
function cancelFormulaEditing(view) {
|
|
42071
42442
|
const context = getFormulaEditingTableContext(view);
|
|
42072
42443
|
if (!context) return false;
|
|
42073
42444
|
let tr = view.state.tr.delete(context.cellContentStart, context.cellContentEnd);
|
|
42074
42445
|
const selectionPos = Math.min(context.cellContentStart, tr.doc.content.size);
|
|
42075
|
-
tr = tr.setSelection(
|
|
42446
|
+
tr = tr.setSelection(TextSelection5.near(tr.doc.resolve(selectionPos)));
|
|
42076
42447
|
view.dispatch(tr);
|
|
42077
42448
|
view.focus();
|
|
42078
42449
|
return true;
|
|
@@ -42130,19 +42501,30 @@ function normalizeFormulaRangeLabel(fromLabel, toLabel) {
|
|
|
42130
42501
|
return fromLabel === toLabel ? fromLabel : `${fromLabel}:${toLabel}`;
|
|
42131
42502
|
}
|
|
42132
42503
|
function replacePickedLabel(view, pickState, nextLabel, currentCell) {
|
|
42133
|
-
|
|
42504
|
+
const formulaContext = getFormulaEditingTableContext(view);
|
|
42505
|
+
const blocked = formulaContext ? isBlockedFormulaReference(formulaContext.tableNode, pickState.formulaCellLabel, nextLabel) : false;
|
|
42506
|
+
if (blocked) {
|
|
42507
|
+
return {
|
|
42508
|
+
...pickState,
|
|
42509
|
+
blocked: true,
|
|
42510
|
+
currentCell
|
|
42511
|
+
};
|
|
42512
|
+
}
|
|
42513
|
+
if (nextLabel === pickState.currentLabel && currentCell === pickState.currentCell && !pickState.blocked) return pickState;
|
|
42134
42514
|
if (nextLabel === pickState.currentLabel) {
|
|
42135
42515
|
return {
|
|
42136
42516
|
...pickState,
|
|
42517
|
+
blocked: false,
|
|
42137
42518
|
currentCell
|
|
42138
42519
|
};
|
|
42139
42520
|
}
|
|
42140
42521
|
let tr = view.state.tr.insertText(nextLabel, pickState.insertedFrom, pickState.insertedTo);
|
|
42141
42522
|
const nextTo = pickState.insertedFrom + nextLabel.length;
|
|
42142
|
-
tr = tr.setSelection(
|
|
42523
|
+
tr = tr.setSelection(TextSelection5.create(tr.doc, nextTo));
|
|
42143
42524
|
view.dispatch(tr);
|
|
42144
42525
|
return {
|
|
42145
42526
|
...pickState,
|
|
42527
|
+
blocked: false,
|
|
42146
42528
|
insertedTo: nextTo,
|
|
42147
42529
|
currentLabel: nextLabel,
|
|
42148
42530
|
currentCell
|
|
@@ -42154,12 +42536,28 @@ function beginFormulaRangePick(view, event) {
|
|
|
42154
42536
|
if (!formulaCell) return null;
|
|
42155
42537
|
const picked = getFormulaTableCellInfo(view, event.target, formulaCell.tablePos);
|
|
42156
42538
|
if (!picked || picked.cell === formulaCell.cellDom) return null;
|
|
42539
|
+
const blocked = isBlockedFormulaReference(formulaCell.tableNode, formulaCell.formulaCellLabel, picked.label);
|
|
42540
|
+
if (blocked) {
|
|
42541
|
+
event.preventDefault();
|
|
42542
|
+
event.stopPropagation();
|
|
42543
|
+
return {
|
|
42544
|
+
anchorLabel: picked.label,
|
|
42545
|
+
anchorCell: picked.cell,
|
|
42546
|
+
tablePos: formulaCell.tablePos,
|
|
42547
|
+
insertedFrom: view.state.selection.from,
|
|
42548
|
+
insertedTo: view.state.selection.from,
|
|
42549
|
+
currentLabel: "",
|
|
42550
|
+
currentCell: picked.cell,
|
|
42551
|
+
blocked: true,
|
|
42552
|
+
formulaCellLabel: formulaCell.formulaCellLabel
|
|
42553
|
+
};
|
|
42554
|
+
}
|
|
42157
42555
|
const { from, to } = view.state.selection;
|
|
42158
42556
|
const prefix = from === to ? getReferenceInsertionPrefix(view, formulaCell.cellContentStart, from) : "";
|
|
42159
42557
|
const insertedText = `${prefix}${picked.label}`;
|
|
42160
42558
|
const insertedFrom = from + prefix.length;
|
|
42161
42559
|
let tr = view.state.tr.insertText(insertedText, from, to);
|
|
42162
|
-
tr = tr.setSelection(
|
|
42560
|
+
tr = tr.setSelection(TextSelection5.create(tr.doc, from + insertedText.length));
|
|
42163
42561
|
view.dispatch(tr);
|
|
42164
42562
|
view.focus();
|
|
42165
42563
|
event.preventDefault();
|
|
@@ -42171,7 +42569,9 @@ function beginFormulaRangePick(view, event) {
|
|
|
42171
42569
|
insertedFrom,
|
|
42172
42570
|
insertedTo: insertedFrom + picked.label.length,
|
|
42173
42571
|
currentLabel: picked.label,
|
|
42174
|
-
currentCell: picked.cell
|
|
42572
|
+
currentCell: picked.cell,
|
|
42573
|
+
blocked: false,
|
|
42574
|
+
formulaCellLabel: formulaCell.formulaCellLabel
|
|
42175
42575
|
};
|
|
42176
42576
|
}
|
|
42177
42577
|
function updateFormulaRangePick(view, pickState, event) {
|
|
@@ -42199,7 +42599,8 @@ function getFormulaRangePickHighlight(container, pickState) {
|
|
|
42199
42599
|
left,
|
|
42200
42600
|
top,
|
|
42201
42601
|
width: Math.max(0, right - left),
|
|
42202
|
-
height: Math.max(0, bottom - top)
|
|
42602
|
+
height: Math.max(0, bottom - top),
|
|
42603
|
+
blocked: pickState.blocked
|
|
42203
42604
|
};
|
|
42204
42605
|
}
|
|
42205
42606
|
|
|
@@ -42223,6 +42624,12 @@ function createReferenceHighlight(label) {
|
|
|
42223
42624
|
element.className = "pointer-events-none absolute z-[21] rounded-[2px] border-2 border-emerald-500 bg-emerald-500/15";
|
|
42224
42625
|
return element;
|
|
42225
42626
|
}
|
|
42627
|
+
function createBlockedReferenceHighlight(label) {
|
|
42628
|
+
const element = document.createElement("span");
|
|
42629
|
+
element.dataset.ueditorFormulaBlockedReference = label;
|
|
42630
|
+
element.className = "pointer-events-none absolute z-[22] rounded-[2px] border-2 border-destructive/80 bg-destructive/10";
|
|
42631
|
+
return element;
|
|
42632
|
+
}
|
|
42226
42633
|
function useFormulaCoordinateOverlay(editor, containerRef, labels) {
|
|
42227
42634
|
const overlayRef = useRef37(null);
|
|
42228
42635
|
useEffect39(() => {
|
|
@@ -42234,6 +42641,7 @@ function useFormulaCoordinateOverlay(editor, containerRef, labels) {
|
|
|
42234
42641
|
let activeTable = null;
|
|
42235
42642
|
let activeTablePos = null;
|
|
42236
42643
|
let hoverLabel = null;
|
|
42644
|
+
const scrollListenerOptions = { passive: true, capture: true };
|
|
42237
42645
|
const clearOverlay = () => {
|
|
42238
42646
|
activeTable = null;
|
|
42239
42647
|
activeTablePos = null;
|
|
@@ -42293,10 +42701,12 @@ function useFormulaCoordinateOverlay(editor, containerRef, labels) {
|
|
|
42293
42701
|
actions.dataset.ueditorFormulaActions = "";
|
|
42294
42702
|
actions.className = "pointer-events-auto absolute z-50 flex items-center gap-1 rounded-md border border-border bg-background p-1 shadow-md";
|
|
42295
42703
|
const formulaCellRect = context.cellDom.getBoundingClientRect();
|
|
42704
|
+
const formulaActionsHeight = 34;
|
|
42705
|
+
const formulaActionsTop = formulaCellRect.bottom + 4 + formulaActionsHeight > tableRect.bottom ? Math.max(containerRect.top + 4, formulaCellRect.top - formulaActionsHeight - 4) : formulaCellRect.bottom + 4;
|
|
42296
42706
|
setPosition(
|
|
42297
42707
|
actions,
|
|
42298
42708
|
formulaCellRect.left - containerRect.left + container.scrollLeft,
|
|
42299
|
-
|
|
42709
|
+
formulaActionsTop - containerRect.top + container.scrollTop
|
|
42300
42710
|
);
|
|
42301
42711
|
const applyButton = document.createElement("button");
|
|
42302
42712
|
applyButton.type = "button";
|
|
@@ -42348,10 +42758,12 @@ function useFormulaCoordinateOverlay(editor, containerRef, labels) {
|
|
|
42348
42758
|
children.push(label);
|
|
42349
42759
|
}
|
|
42350
42760
|
const references = new Set(getTableFormulaReferences(context.formula));
|
|
42761
|
+
const blockedReferences = getFormulaCycleBlockedLabels(context.tableNode, context.formulaCellLabel);
|
|
42351
42762
|
for (const info of cellInfos) {
|
|
42352
|
-
if (!info
|
|
42763
|
+
if (!info) continue;
|
|
42353
42764
|
const cellRect = info.cell.getBoundingClientRect();
|
|
42354
|
-
const highlight = createReferenceHighlight(info.label);
|
|
42765
|
+
const highlight = info.label !== context.formulaCellLabel && blockedReferences.has(info.label) ? createBlockedReferenceHighlight(info.label) : references.has(info.label) ? createReferenceHighlight(info.label) : null;
|
|
42766
|
+
if (!highlight) continue;
|
|
42355
42767
|
setPosition(
|
|
42356
42768
|
highlight,
|
|
42357
42769
|
cellRect.left - containerRect.left + container.scrollLeft + 2,
|
|
@@ -42406,6 +42818,7 @@ function useFormulaCoordinateOverlay(editor, containerRef, labels) {
|
|
|
42406
42818
|
editor.view.dom.addEventListener("mousemove", handleMouseMove2);
|
|
42407
42819
|
editor.view.dom.addEventListener("mouseleave", handleMouseLeave2);
|
|
42408
42820
|
container.addEventListener(UEDITOR_TABLE_LAYOUT_CHANGE_EVENT, scheduleSync);
|
|
42821
|
+
container.addEventListener("scroll", scheduleSync, scrollListenerOptions);
|
|
42409
42822
|
window.addEventListener("resize", scheduleSync);
|
|
42410
42823
|
scheduleSync();
|
|
42411
42824
|
return () => {
|
|
@@ -42416,6 +42829,7 @@ function useFormulaCoordinateOverlay(editor, containerRef, labels) {
|
|
|
42416
42829
|
editor.view.dom.removeEventListener("mousemove", handleMouseMove2);
|
|
42417
42830
|
editor.view.dom.removeEventListener("mouseleave", handleMouseLeave2);
|
|
42418
42831
|
container.removeEventListener(UEDITOR_TABLE_LAYOUT_CHANGE_EVENT, scheduleSync);
|
|
42832
|
+
container.removeEventListener("scroll", scheduleSync, scrollListenerOptions);
|
|
42419
42833
|
window.removeEventListener("resize", scheduleSync);
|
|
42420
42834
|
if (animationFrame != null) window.cancelAnimationFrame(animationFrame);
|
|
42421
42835
|
clearOverlay();
|
|
@@ -43338,9 +43752,156 @@ var MenuBar = ({
|
|
|
43338
43752
|
] });
|
|
43339
43753
|
};
|
|
43340
43754
|
|
|
43341
|
-
// src/components/UEditor/
|
|
43755
|
+
// src/components/UEditor/table-formula-bar.tsx
|
|
43756
|
+
import { useEffect as useEffect40, useRef as useRef39, useState as useState52 } from "react";
|
|
43757
|
+
import { useEditorState as useEditorState4 } from "@tiptap/react";
|
|
43758
|
+
import { AlertCircle as AlertCircle5, Check as Check14, Hash as Hash2, Sigma as Sigma3, Trash2 as Trash26 } from "lucide-react";
|
|
43342
43759
|
import { jsx as jsx99, jsxs as jsxs82 } from "react/jsx-runtime";
|
|
43343
|
-
|
|
43760
|
+
function TableFormulaBar({ editor }) {
|
|
43761
|
+
const t = useSmartTranslations("UEditor");
|
|
43762
|
+
const inputRef = useRef39(null);
|
|
43763
|
+
const [draftState, setDraftState] = useState52(null);
|
|
43764
|
+
useEditorState4({
|
|
43765
|
+
editor,
|
|
43766
|
+
selector: ({ transactionNumber }) => transactionNumber
|
|
43767
|
+
});
|
|
43768
|
+
const selectedCell = getSelectedTableFormulaCell(editor);
|
|
43769
|
+
useEffect40(() => {
|
|
43770
|
+
const focusFormulaInput = () => {
|
|
43771
|
+
inputRef.current?.focus();
|
|
43772
|
+
};
|
|
43773
|
+
const handleDoubleClick = (event) => {
|
|
43774
|
+
const target = resolveEventElement(event.target);
|
|
43775
|
+
if (!target?.closest?.("td[data-formula],th[data-formula]")) return;
|
|
43776
|
+
event.preventDefault();
|
|
43777
|
+
focusFormulaInput();
|
|
43778
|
+
};
|
|
43779
|
+
editor.view.dom.addEventListener("dblclick", handleDoubleClick);
|
|
43780
|
+
return () => {
|
|
43781
|
+
editor.view.dom.removeEventListener("dblclick", handleDoubleClick);
|
|
43782
|
+
};
|
|
43783
|
+
}, [editor]);
|
|
43784
|
+
if (!selectedCell) return null;
|
|
43785
|
+
const selectedCellKey = `${selectedCell.tablePos}:${selectedCell.row}:${selectedCell.column}`;
|
|
43786
|
+
const draft = draftState?.cellKey === selectedCellKey && draftState.sourceFormula === selectedCell.formula ? draftState.value : selectedCell.formula;
|
|
43787
|
+
const setDraft = (value) => {
|
|
43788
|
+
setDraftState({
|
|
43789
|
+
cellKey: selectedCellKey,
|
|
43790
|
+
sourceFormula: selectedCell.formula,
|
|
43791
|
+
value
|
|
43792
|
+
});
|
|
43793
|
+
};
|
|
43794
|
+
const applyDraft = () => {
|
|
43795
|
+
const normalized = normalizeFormulaInput(draft);
|
|
43796
|
+
if (normalized) {
|
|
43797
|
+
setSelectedTableCellFormula(editor, normalized);
|
|
43798
|
+
} else {
|
|
43799
|
+
clearSelectedTableCellFormula(editor);
|
|
43800
|
+
}
|
|
43801
|
+
setDraftState(null);
|
|
43802
|
+
};
|
|
43803
|
+
const cancelDraft = () => {
|
|
43804
|
+
setDraftState(null);
|
|
43805
|
+
window.setTimeout(() => {
|
|
43806
|
+
if (!editor.isDestroyed) editor.commands.focus();
|
|
43807
|
+
}, 0);
|
|
43808
|
+
};
|
|
43809
|
+
return /* @__PURE__ */ jsxs82(
|
|
43810
|
+
"div",
|
|
43811
|
+
{
|
|
43812
|
+
"data-ueditor-formula-bar": "",
|
|
43813
|
+
role: "toolbar",
|
|
43814
|
+
"aria-label": t("tableMenu.formulaBar"),
|
|
43815
|
+
className: "flex min-h-10 items-center gap-2 border-b border-border/60 bg-muted/25 px-2 py-1.5",
|
|
43816
|
+
children: [
|
|
43817
|
+
/* @__PURE__ */ jsx99(
|
|
43818
|
+
"span",
|
|
43819
|
+
{
|
|
43820
|
+
"data-ueditor-formula-cell-label": "",
|
|
43821
|
+
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",
|
|
43822
|
+
children: selectedCell.label
|
|
43823
|
+
}
|
|
43824
|
+
),
|
|
43825
|
+
/* @__PURE__ */ jsx99(Sigma3, { "aria-hidden": "true", className: "h-4 w-4 shrink-0 text-primary" }),
|
|
43826
|
+
/* @__PURE__ */ jsx99(
|
|
43827
|
+
"input",
|
|
43828
|
+
{
|
|
43829
|
+
ref: inputRef,
|
|
43830
|
+
"data-ueditor-formula-input": "",
|
|
43831
|
+
value: draft,
|
|
43832
|
+
onChange: (event) => setDraft(event.target.value),
|
|
43833
|
+
onKeyDown: (event) => {
|
|
43834
|
+
event.stopPropagation();
|
|
43835
|
+
if (event.key === "Enter") {
|
|
43836
|
+
event.preventDefault();
|
|
43837
|
+
applyDraft();
|
|
43838
|
+
} else if (event.key === "Escape") {
|
|
43839
|
+
event.preventDefault();
|
|
43840
|
+
cancelDraft();
|
|
43841
|
+
}
|
|
43842
|
+
},
|
|
43843
|
+
"aria-label": t("tableMenu.editFormula"),
|
|
43844
|
+
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"
|
|
43845
|
+
}
|
|
43846
|
+
),
|
|
43847
|
+
selectedCell.formulaState === "error" && /* @__PURE__ */ jsxs82(
|
|
43848
|
+
"span",
|
|
43849
|
+
{
|
|
43850
|
+
role: "status",
|
|
43851
|
+
className: "hidden shrink-0 items-center gap-1 text-xs font-medium text-destructive sm:inline-flex",
|
|
43852
|
+
children: [
|
|
43853
|
+
/* @__PURE__ */ jsx99(AlertCircle5, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
|
|
43854
|
+
t("tableMenu.formulaError")
|
|
43855
|
+
]
|
|
43856
|
+
}
|
|
43857
|
+
),
|
|
43858
|
+
/* @__PURE__ */ jsx99(
|
|
43859
|
+
"button",
|
|
43860
|
+
{
|
|
43861
|
+
type: "button",
|
|
43862
|
+
onClick: applyDraft,
|
|
43863
|
+
"aria-label": t("tableMenu.apply"),
|
|
43864
|
+
title: t("tableMenu.apply"),
|
|
43865
|
+
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",
|
|
43866
|
+
children: /* @__PURE__ */ jsx99(Check14, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
43867
|
+
}
|
|
43868
|
+
),
|
|
43869
|
+
/* @__PURE__ */ jsx99(
|
|
43870
|
+
"button",
|
|
43871
|
+
{
|
|
43872
|
+
type: "button",
|
|
43873
|
+
onClick: () => {
|
|
43874
|
+
convertSelectedTableCellFormulaToValue(editor);
|
|
43875
|
+
setDraftState(null);
|
|
43876
|
+
},
|
|
43877
|
+
"aria-label": t("tableMenu.convertToValue"),
|
|
43878
|
+
title: t("tableMenu.convertToValue"),
|
|
43879
|
+
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",
|
|
43880
|
+
children: /* @__PURE__ */ jsx99(Hash2, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
43881
|
+
}
|
|
43882
|
+
),
|
|
43883
|
+
/* @__PURE__ */ jsx99(
|
|
43884
|
+
"button",
|
|
43885
|
+
{
|
|
43886
|
+
type: "button",
|
|
43887
|
+
onClick: () => {
|
|
43888
|
+
clearSelectedTableCellFormula(editor);
|
|
43889
|
+
setDraftState(null);
|
|
43890
|
+
},
|
|
43891
|
+
"aria-label": t("tableMenu.clearCell"),
|
|
43892
|
+
title: t("tableMenu.clearCell"),
|
|
43893
|
+
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",
|
|
43894
|
+
children: /* @__PURE__ */ jsx99(Trash26, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
43895
|
+
}
|
|
43896
|
+
)
|
|
43897
|
+
]
|
|
43898
|
+
}
|
|
43899
|
+
);
|
|
43900
|
+
}
|
|
43901
|
+
|
|
43902
|
+
// src/components/UEditor/UEditor.tsx
|
|
43903
|
+
import { jsx as jsx100, jsxs as jsxs83 } from "react/jsx-runtime";
|
|
43904
|
+
var UEditor = React88.forwardRef(({
|
|
43344
43905
|
content = "",
|
|
43345
43906
|
onChange,
|
|
43346
43907
|
onHtmlChange,
|
|
@@ -43358,6 +43919,7 @@ var UEditor = React87.forwardRef(({
|
|
|
43358
43919
|
autofocus = false,
|
|
43359
43920
|
showToolbar = true,
|
|
43360
43921
|
showBubbleMenu = true,
|
|
43922
|
+
showFloatingMenu = false,
|
|
43361
43923
|
showCharacterCount = true,
|
|
43362
43924
|
showFooter = true,
|
|
43363
43925
|
maxCharacters,
|
|
@@ -43381,14 +43943,14 @@ var UEditor = React87.forwardRef(({
|
|
|
43381
43943
|
}, ref) => {
|
|
43382
43944
|
const t = useSmartTranslations("UEditor");
|
|
43383
43945
|
const effectivePlaceholder = placeholder ?? t("placeholder");
|
|
43384
|
-
const inFlightPrepareRef =
|
|
43385
|
-
const lastAppliedContentRef =
|
|
43386
|
-
const scheduledFormulaRecalculateRef =
|
|
43387
|
-
const editorInstanceRef =
|
|
43388
|
-
const formulaRangePickRef =
|
|
43389
|
-
const formulaRangeSurfaceRef =
|
|
43390
|
-
const [formulaRangeHighlight, setFormulaRangeHighlight] =
|
|
43391
|
-
const scheduleFormulaRecalculate =
|
|
43946
|
+
const inFlightPrepareRef = useRef40(null);
|
|
43947
|
+
const lastAppliedContentRef = useRef40(content ?? "");
|
|
43948
|
+
const scheduledFormulaRecalculateRef = useRef40(false);
|
|
43949
|
+
const editorInstanceRef = useRef40(null);
|
|
43950
|
+
const formulaRangePickRef = useRef40(null);
|
|
43951
|
+
const formulaRangeSurfaceRef = useRef40(null);
|
|
43952
|
+
const [formulaRangeHighlight, setFormulaRangeHighlight] = React88.useState(null);
|
|
43953
|
+
const scheduleFormulaRecalculate = React88.useCallback((editor2, options) => {
|
|
43392
43954
|
if (editor2.isDestroyed || scheduledFormulaRecalculateRef.current) return;
|
|
43393
43955
|
if (!options?.force && isEditingTableFormulaText(editor2)) return;
|
|
43394
43956
|
scheduledFormulaRecalculateRef.current = true;
|
|
@@ -43428,7 +43990,7 @@ var UEditor = React87.forwardRef(({
|
|
|
43428
43990
|
],
|
|
43429
43991
|
[effectivePlaceholder, t, maxCharacters, uploadImage, resolvedUploadFile, imageInsertMode, maxImageFileSize, allowedImageMimeTypes, fallbackToDataUrl, editable, fetchMetadata, extraExtensions]
|
|
43430
43992
|
);
|
|
43431
|
-
const syncFormulaRangeHighlight =
|
|
43993
|
+
const syncFormulaRangeHighlight = React88.useCallback((pickState) => {
|
|
43432
43994
|
const container = formulaRangeSurfaceRef.current;
|
|
43433
43995
|
setFormulaRangeHighlight(container && pickState ? getFormulaRangePickHighlight(container, pickState) : null);
|
|
43434
43996
|
}, []);
|
|
@@ -43477,9 +44039,9 @@ var UEditor = React87.forwardRef(({
|
|
|
43477
44039
|
if (formulaContext && event.key === "Enter") {
|
|
43478
44040
|
event.preventDefault();
|
|
43479
44041
|
event.stopPropagation();
|
|
43480
|
-
const
|
|
43481
|
-
if (
|
|
43482
|
-
recalculateActiveTableFormulas(
|
|
44042
|
+
const activeEditor2 = editorInstanceRef.current;
|
|
44043
|
+
if (activeEditor2 && !isDraftTableFormula(formulaContext.formula)) {
|
|
44044
|
+
recalculateActiveTableFormulas(activeEditor2);
|
|
43483
44045
|
}
|
|
43484
44046
|
return true;
|
|
43485
44047
|
}
|
|
@@ -43489,6 +44051,13 @@ var UEditor = React87.forwardRef(({
|
|
|
43489
44051
|
cancelFormulaEditing(_view);
|
|
43490
44052
|
return true;
|
|
43491
44053
|
}
|
|
44054
|
+
const activeEditor = editorInstanceRef.current;
|
|
44055
|
+
if (activeEditor && (event.key === "Backspace" || event.key === "Delete") && getSelectedTableFormulaCell(activeEditor)) {
|
|
44056
|
+
event.preventDefault();
|
|
44057
|
+
event.stopPropagation();
|
|
44058
|
+
clearSelectedTableCellFormula(activeEditor);
|
|
44059
|
+
return true;
|
|
44060
|
+
}
|
|
43492
44061
|
if (event.key === "ArrowLeft" || event.key === "ArrowRight" || event.key === "ArrowUp" || event.key === "ArrowDown") {
|
|
43493
44062
|
event.stopPropagation();
|
|
43494
44063
|
}
|
|
@@ -43499,7 +44068,7 @@ var UEditor = React87.forwardRef(({
|
|
|
43499
44068
|
if (event.button !== 0) return false;
|
|
43500
44069
|
const target = resolveEventElement(event.target);
|
|
43501
44070
|
const anchor = target?.closest?.("a[href]");
|
|
43502
|
-
const href = anchor?.getAttribute("href") ?? "";
|
|
44071
|
+
const href = sanitizeUEditorUrl(anchor?.getAttribute("href") ?? "", "link");
|
|
43503
44072
|
if (!href) return false;
|
|
43504
44073
|
if (editable) return false;
|
|
43505
44074
|
if (!view.state.selection.empty) return false;
|
|
@@ -43525,11 +44094,13 @@ var UEditor = React87.forwardRef(({
|
|
|
43525
44094
|
onSelectionUpdate: ({ editor: editor2 }) => {
|
|
43526
44095
|
scheduleFormulaRecalculate(editor2);
|
|
43527
44096
|
},
|
|
43528
|
-
onBlur: ({ editor: editor2 }) => {
|
|
44097
|
+
onBlur: ({ editor: editor2, event }) => {
|
|
44098
|
+
const nextTarget = event.relatedTarget;
|
|
44099
|
+
if (nextTarget instanceof Element && nextTarget.closest("[data-ueditor-formula-bar]")) return;
|
|
43529
44100
|
scheduleFormulaRecalculate(editor2, { force: true });
|
|
43530
44101
|
}
|
|
43531
44102
|
});
|
|
43532
|
-
|
|
44103
|
+
useEffect41(() => {
|
|
43533
44104
|
editorInstanceRef.current = editor;
|
|
43534
44105
|
return () => {
|
|
43535
44106
|
if (editorInstanceRef.current === editor) editorInstanceRef.current = null;
|
|
@@ -43570,7 +44141,7 @@ var UEditor = React87.forwardRef(({
|
|
|
43570
44141
|
}),
|
|
43571
44142
|
[content, editor, uploadImageForSave, uploadFileForSave, uploadImageConcurrency]
|
|
43572
44143
|
);
|
|
43573
|
-
|
|
44144
|
+
useEffect41(() => {
|
|
43574
44145
|
if (!editor) return;
|
|
43575
44146
|
queueMicrotask(() => {
|
|
43576
44147
|
if (!editor.isDestroyed) {
|
|
@@ -43578,7 +44149,7 @@ var UEditor = React87.forwardRef(({
|
|
|
43578
44149
|
}
|
|
43579
44150
|
});
|
|
43580
44151
|
}, [editor]);
|
|
43581
|
-
|
|
44152
|
+
useEffect41(() => {
|
|
43582
44153
|
if (!editor) return;
|
|
43583
44154
|
const nextContent = content ?? "";
|
|
43584
44155
|
if (lastAppliedContentRef.current === nextContent) return;
|
|
@@ -43597,7 +44168,7 @@ var UEditor = React87.forwardRef(({
|
|
|
43597
44168
|
}
|
|
43598
44169
|
}, [content, editor]);
|
|
43599
44170
|
if (!editor) {
|
|
43600
|
-
return /* @__PURE__ */
|
|
44171
|
+
return /* @__PURE__ */ jsx100(
|
|
43601
44172
|
"div",
|
|
43602
44173
|
{
|
|
43603
44174
|
className: cn("w-full border bg-background flex items-center justify-center text-muted-foreground", className),
|
|
@@ -43606,7 +44177,7 @@ var UEditor = React87.forwardRef(({
|
|
|
43606
44177
|
}
|
|
43607
44178
|
);
|
|
43608
44179
|
}
|
|
43609
|
-
return /* @__PURE__ */
|
|
44180
|
+
return /* @__PURE__ */ jsxs83(
|
|
43610
44181
|
"div",
|
|
43611
44182
|
{
|
|
43612
44183
|
className: cn(
|
|
@@ -43622,7 +44193,7 @@ var UEditor = React87.forwardRef(({
|
|
|
43622
44193
|
className
|
|
43623
44194
|
),
|
|
43624
44195
|
children: [
|
|
43625
|
-
editable && showMenuBar && /* @__PURE__ */
|
|
44196
|
+
editable && showMenuBar && /* @__PURE__ */ jsx100(
|
|
43626
44197
|
MenuBar,
|
|
43627
44198
|
{
|
|
43628
44199
|
editor,
|
|
@@ -43637,7 +44208,7 @@ var UEditor = React87.forwardRef(({
|
|
|
43637
44208
|
onPreview
|
|
43638
44209
|
}
|
|
43639
44210
|
),
|
|
43640
|
-
editable && showToolbar && /* @__PURE__ */
|
|
44211
|
+
editable && showToolbar && /* @__PURE__ */ jsx100(
|
|
43641
44212
|
EditorToolbar,
|
|
43642
44213
|
{
|
|
43643
44214
|
editor,
|
|
@@ -43652,7 +44223,8 @@ var UEditor = React87.forwardRef(({
|
|
|
43652
44223
|
letterSpacings
|
|
43653
44224
|
}
|
|
43654
44225
|
),
|
|
43655
|
-
editable &&
|
|
44226
|
+
editable && /* @__PURE__ */ jsx100(TableFormulaBar, { editor }),
|
|
44227
|
+
editable && showBubbleMenu && /* @__PURE__ */ jsx100(
|
|
43656
44228
|
CustomBubbleMenu,
|
|
43657
44229
|
{
|
|
43658
44230
|
editor,
|
|
@@ -43660,7 +44232,8 @@ var UEditor = React87.forwardRef(({
|
|
|
43660
44232
|
lineHeights
|
|
43661
44233
|
}
|
|
43662
44234
|
),
|
|
43663
|
-
/* @__PURE__ */
|
|
44235
|
+
editable && showFloatingMenu && /* @__PURE__ */ jsx100(CustomFloatingMenu, { editor }),
|
|
44236
|
+
/* @__PURE__ */ jsxs83(
|
|
43664
44237
|
"div",
|
|
43665
44238
|
{
|
|
43666
44239
|
ref: (node) => {
|
|
@@ -43673,7 +44246,7 @@ var UEditor = React87.forwardRef(({
|
|
|
43673
44246
|
maxHeight
|
|
43674
44247
|
},
|
|
43675
44248
|
children: [
|
|
43676
|
-
/* @__PURE__ */
|
|
44249
|
+
/* @__PURE__ */ jsx100(
|
|
43677
44250
|
"span",
|
|
43678
44251
|
{
|
|
43679
44252
|
ref: tableColumnGuideRef,
|
|
@@ -43681,7 +44254,7 @@ var UEditor = React87.forwardRef(({
|
|
|
43681
44254
|
className: "pointer-events-none absolute z-20 bg-primary opacity-0 transition-opacity duration-100"
|
|
43682
44255
|
}
|
|
43683
44256
|
),
|
|
43684
|
-
/* @__PURE__ */
|
|
44257
|
+
/* @__PURE__ */ jsx100(
|
|
43685
44258
|
"span",
|
|
43686
44259
|
{
|
|
43687
44260
|
ref: tableRowGuideRef,
|
|
@@ -43689,7 +44262,7 @@ var UEditor = React87.forwardRef(({
|
|
|
43689
44262
|
className: "pointer-events-none absolute z-20 bg-primary opacity-0 transition-opacity duration-100"
|
|
43690
44263
|
}
|
|
43691
44264
|
),
|
|
43692
|
-
/* @__PURE__ */
|
|
44265
|
+
/* @__PURE__ */ jsx100(
|
|
43693
44266
|
"span",
|
|
43694
44267
|
{
|
|
43695
44268
|
ref: activeTableCellHighlightRef,
|
|
@@ -43698,20 +44271,24 @@ var UEditor = React87.forwardRef(({
|
|
|
43698
44271
|
className: "pointer-events-none hidden absolute z-20 rounded-[2px] border-2 border-primary bg-primary/10"
|
|
43699
44272
|
}
|
|
43700
44273
|
),
|
|
43701
|
-
/* @__PURE__ */
|
|
44274
|
+
/* @__PURE__ */ jsx100(
|
|
43702
44275
|
"div",
|
|
43703
44276
|
{
|
|
43704
44277
|
ref: formulaCoordinateOverlayRef,
|
|
43705
44278
|
"data-ueditor-formula-coordinate-overlay": "",
|
|
43706
|
-
className: "pointer-events-none absolute inset-0 z-[
|
|
44279
|
+
className: "pointer-events-none absolute inset-0 z-[35] hidden overflow-visible"
|
|
43707
44280
|
}
|
|
43708
44281
|
),
|
|
43709
|
-
formulaRangeHighlight && /* @__PURE__ */
|
|
44282
|
+
formulaRangeHighlight && /* @__PURE__ */ jsx100(
|
|
43710
44283
|
"span",
|
|
43711
44284
|
{
|
|
43712
44285
|
"aria-hidden": "true",
|
|
43713
44286
|
"data-ueditor-formula-range-highlight": "",
|
|
43714
|
-
|
|
44287
|
+
"data-ueditor-formula-range-blocked": formulaRangeHighlight.blocked ? "true" : void 0,
|
|
44288
|
+
className: cn(
|
|
44289
|
+
"pointer-events-none absolute z-20 rounded-[2px] border-2",
|
|
44290
|
+
formulaRangeHighlight.blocked ? "border-destructive bg-destructive/15" : "border-primary bg-primary/10"
|
|
44291
|
+
),
|
|
43715
44292
|
style: {
|
|
43716
44293
|
left: formulaRangeHighlight.left,
|
|
43717
44294
|
top: formulaRangeHighlight.top,
|
|
@@ -43720,8 +44297,8 @@ var UEditor = React87.forwardRef(({
|
|
|
43720
44297
|
}
|
|
43721
44298
|
}
|
|
43722
44299
|
),
|
|
43723
|
-
editable && /* @__PURE__ */
|
|
43724
|
-
/* @__PURE__ */
|
|
44300
|
+
editable && /* @__PURE__ */ jsx100(TableControls, { editor, containerRef: editorContentRef }),
|
|
44301
|
+
/* @__PURE__ */ jsx100(
|
|
43725
44302
|
EditorContent,
|
|
43726
44303
|
{
|
|
43727
44304
|
editor,
|
|
@@ -43731,7 +44308,7 @@ var UEditor = React87.forwardRef(({
|
|
|
43731
44308
|
]
|
|
43732
44309
|
}
|
|
43733
44310
|
),
|
|
43734
|
-
showFooter && showCharacterCount && /* @__PURE__ */
|
|
44311
|
+
showFooter && showCharacterCount && /* @__PURE__ */ jsx100(CharacterCountDisplay, { editor, maxCharacters })
|
|
43735
44312
|
]
|
|
43736
44313
|
}
|
|
43737
44314
|
);
|