@underverse-ui/underverse 1.0.185 → 1.0.187
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 +204 -50
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +205 -51
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -815,7 +815,8 @@ var en_default = {
|
|
|
815
815
|
addBlock: "Add block"
|
|
816
816
|
},
|
|
817
817
|
linkInput: {
|
|
818
|
-
placeholder: "Paste or type a link..."
|
|
818
|
+
placeholder: "Paste or type a link...",
|
|
819
|
+
invalid: "Enter a valid link, such as https://example.com."
|
|
819
820
|
},
|
|
820
821
|
imageInput: {
|
|
821
822
|
urlTab: "URL",
|
|
@@ -1242,7 +1243,8 @@ var vi_default = {
|
|
|
1242
1243
|
addBlock: "Th\xEAm kh\u1ED1i"
|
|
1243
1244
|
},
|
|
1244
1245
|
linkInput: {
|
|
1245
|
-
placeholder: "D\xE1n ho\u1EB7c nh\u1EADp li\xEAn k\u1EBFt..."
|
|
1246
|
+
placeholder: "D\xE1n ho\u1EB7c nh\u1EADp li\xEAn k\u1EBFt...",
|
|
1247
|
+
invalid: "Vui l\xF2ng nh\u1EADp li\xEAn k\u1EBFt h\u1EE3p l\u1EC7, v\xED d\u1EE5: https://example.com."
|
|
1246
1248
|
},
|
|
1247
1249
|
imageInput: {
|
|
1248
1250
|
urlTab: "URL",
|
|
@@ -1669,7 +1671,8 @@ var ko_default = {
|
|
|
1669
1671
|
addBlock: "\uBE14\uB85D \uCD94\uAC00"
|
|
1670
1672
|
},
|
|
1671
1673
|
linkInput: {
|
|
1672
|
-
placeholder: "\uB9C1\uD06C \uBD99\uC5EC\uB123\uAE30 \uB610\uB294 \uC785\uB825..."
|
|
1674
|
+
placeholder: "\uB9C1\uD06C \uBD99\uC5EC\uB123\uAE30 \uB610\uB294 \uC785\uB825...",
|
|
1675
|
+
invalid: "https://example.com\uACFC \uAC19\uC740 \uC62C\uBC14\uB978 \uB9C1\uD06C\uB97C \uC785\uB825\uD574 \uC8FC\uC138\uC694."
|
|
1673
1676
|
},
|
|
1674
1677
|
imageInput: {
|
|
1675
1678
|
urlTab: "URL",
|
|
@@ -2096,7 +2099,8 @@ var ja_default = {
|
|
|
2096
2099
|
addBlock: "\u30D6\u30ED\u30C3\u30AF\u3092\u8FFD\u52A0"
|
|
2097
2100
|
},
|
|
2098
2101
|
linkInput: {
|
|
2099
|
-
placeholder: "\u30EA\u30F3\u30AF\u3092\u8CBC\u308A\u4ED8\u3051\u307E\u305F\u306F\u5165\u529B..."
|
|
2102
|
+
placeholder: "\u30EA\u30F3\u30AF\u3092\u8CBC\u308A\u4ED8\u3051\u307E\u305F\u306F\u5165\u529B...",
|
|
2103
|
+
invalid: "https://example.com \u306E\u3088\u3046\u306A\u6709\u52B9\u306A\u30EA\u30F3\u30AF\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044\u3002"
|
|
2100
2104
|
},
|
|
2101
2105
|
imageInput: {
|
|
2102
2106
|
urlTab: "URL",
|
|
@@ -27566,6 +27570,32 @@ function isProtocolRelativeUrl(value) {
|
|
|
27566
27570
|
function isRelativeUrl(value) {
|
|
27567
27571
|
return value.startsWith("/") || value.startsWith("./") || value.startsWith("../") || value.startsWith("#");
|
|
27568
27572
|
}
|
|
27573
|
+
function isValidIpv4Hostname(hostname) {
|
|
27574
|
+
const parts = hostname.split(".");
|
|
27575
|
+
return parts.length === 4 && parts.every((part) => /^\d{1,3}$/.test(part) && Number(part) <= 255);
|
|
27576
|
+
}
|
|
27577
|
+
function isValidWebHostname(hostname) {
|
|
27578
|
+
const normalized = hostname.toLowerCase();
|
|
27579
|
+
if (normalized === "localhost" || isValidIpv4Hostname(normalized)) return true;
|
|
27580
|
+
if (normalized.startsWith("[") && normalized.endsWith("]") && normalized.includes(":")) return true;
|
|
27581
|
+
const labels = normalized.split(".");
|
|
27582
|
+
if (labels.length < 2) return false;
|
|
27583
|
+
const validLabel = /^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)$/;
|
|
27584
|
+
return labels.every((label) => validLabel.test(label)) && /[a-z]/.test(labels.at(-1) ?? "");
|
|
27585
|
+
}
|
|
27586
|
+
function isValidLinkUrl(parsed) {
|
|
27587
|
+
if (parsed.protocol === "http:" || parsed.protocol === "https:") {
|
|
27588
|
+
return isValidWebHostname(parsed.hostname);
|
|
27589
|
+
}
|
|
27590
|
+
if (parsed.protocol === "mailto:") {
|
|
27591
|
+
return /^[^@]+@[^@]+\.[^@]+$/.test(decodeURIComponent(parsed.pathname));
|
|
27592
|
+
}
|
|
27593
|
+
if (parsed.protocol === "tel:") {
|
|
27594
|
+
const number = decodeURIComponent(parsed.pathname);
|
|
27595
|
+
return /^\+?[\d().-]+$/.test(number) && (number.match(/\d/g)?.length ?? 0) >= 3;
|
|
27596
|
+
}
|
|
27597
|
+
return false;
|
|
27598
|
+
}
|
|
27569
27599
|
function isDataImageUrl(value) {
|
|
27570
27600
|
return /^data:image\/(?:png|jpe?g|gif|webp|svg\+xml|bmp|x-icon|avif);base64,/i.test(value);
|
|
27571
27601
|
}
|
|
@@ -27583,7 +27613,7 @@ function isSafeUEditorUrl(raw, kind) {
|
|
|
27583
27613
|
const parsed = new URL(value);
|
|
27584
27614
|
if (kind === "image") return IMAGE_PROTOCOLS.has(parsed.protocol);
|
|
27585
27615
|
if (kind === "file") return FILE_PROTOCOLS.has(parsed.protocol);
|
|
27586
|
-
return LINK_PROTOCOLS.has(parsed.protocol);
|
|
27616
|
+
return LINK_PROTOCOLS.has(parsed.protocol) && isValidLinkUrl(parsed);
|
|
27587
27617
|
} catch {
|
|
27588
27618
|
return false;
|
|
27589
27619
|
}
|
|
@@ -32379,7 +32409,7 @@ function deleteSelectedImage(editor) {
|
|
|
32379
32409
|
}
|
|
32380
32410
|
|
|
32381
32411
|
// src/components/UEditor/inputs.tsx
|
|
32382
|
-
import { useEffect as useEffect36, useRef as useRef32, useState as useState48 } from "react";
|
|
32412
|
+
import { useEffect as useEffect36, useId as useId15, useRef as useRef32, useState as useState48 } from "react";
|
|
32383
32413
|
import { Check as Check13, X as X19 } from "lucide-react";
|
|
32384
32414
|
import { jsx as jsx90, jsxs as jsxs74 } from "react/jsx-runtime";
|
|
32385
32415
|
function normalizeUrl(raw) {
|
|
@@ -32392,7 +32422,9 @@ var LinkInput = ({
|
|
|
32392
32422
|
}) => {
|
|
32393
32423
|
const t = useSmartTranslations("UEditor");
|
|
32394
32424
|
const [url, setUrl] = useState48(initialUrl);
|
|
32425
|
+
const [error, setError] = useState48("");
|
|
32395
32426
|
const inputRef = useRef32(null);
|
|
32427
|
+
const errorId = useId15();
|
|
32396
32428
|
useEffect36(() => {
|
|
32397
32429
|
inputRef.current?.focus();
|
|
32398
32430
|
inputRef.current?.select();
|
|
@@ -32400,22 +32432,35 @@ var LinkInput = ({
|
|
|
32400
32432
|
const handleSubmit = (e) => {
|
|
32401
32433
|
e.preventDefault();
|
|
32402
32434
|
const normalized = normalizeUrl(url);
|
|
32403
|
-
if (normalized)
|
|
32435
|
+
if (!normalized) {
|
|
32436
|
+
setError(t("linkInput.invalid"));
|
|
32437
|
+
return;
|
|
32438
|
+
}
|
|
32439
|
+
setError("");
|
|
32440
|
+
onSubmit(normalized);
|
|
32404
32441
|
};
|
|
32405
|
-
return /* @__PURE__ */ jsxs74("form", { onSubmit: handleSubmit, className: "
|
|
32406
|
-
/* @__PURE__ */
|
|
32407
|
-
|
|
32408
|
-
|
|
32409
|
-
|
|
32410
|
-
|
|
32411
|
-
|
|
32412
|
-
|
|
32413
|
-
|
|
32414
|
-
|
|
32415
|
-
|
|
32416
|
-
|
|
32417
|
-
|
|
32418
|
-
|
|
32442
|
+
return /* @__PURE__ */ jsxs74("form", { onSubmit: handleSubmit, className: "p-2", children: [
|
|
32443
|
+
/* @__PURE__ */ jsxs74("div", { className: "flex items-center gap-2", children: [
|
|
32444
|
+
/* @__PURE__ */ jsx90(
|
|
32445
|
+
"input",
|
|
32446
|
+
{
|
|
32447
|
+
ref: inputRef,
|
|
32448
|
+
type: "text",
|
|
32449
|
+
value: url,
|
|
32450
|
+
onChange: (e) => {
|
|
32451
|
+
setUrl(e.target.value);
|
|
32452
|
+
if (error) setError("");
|
|
32453
|
+
},
|
|
32454
|
+
placeholder: t("linkInput.placeholder"),
|
|
32455
|
+
"aria-invalid": Boolean(error),
|
|
32456
|
+
"aria-describedby": error ? errorId : void 0,
|
|
32457
|
+
className: "flex-1 px-3 py-2 text-sm bg-muted/50 border-0 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary/20 aria-invalid:ring-2 aria-invalid:ring-destructive/40"
|
|
32458
|
+
}
|
|
32459
|
+
),
|
|
32460
|
+
/* @__PURE__ */ jsx90("button", { type: "submit", className: "p-2 rounded-lg bg-primary text-primary-foreground hover:bg-primary/90 transition-colors", children: /* @__PURE__ */ jsx90(Check13, { className: "w-4 h-4" }) }),
|
|
32461
|
+
/* @__PURE__ */ jsx90("button", { type: "button", onClick: onCancel, className: "p-2 rounded-lg hover:bg-muted transition-colors text-muted-foreground", children: /* @__PURE__ */ jsx90(X19, { className: "w-4 h-4" }) })
|
|
32462
|
+
] }),
|
|
32463
|
+
error ? /* @__PURE__ */ jsx90("p", { id: errorId, role: "alert", className: "mt-1.5 px-1 text-xs text-destructive", children: error }) : null
|
|
32419
32464
|
] });
|
|
32420
32465
|
};
|
|
32421
32466
|
var ImageInput = ({ onSubmit, onCancel }) => {
|
|
@@ -32476,8 +32521,28 @@ var ImageInput = ({ onSubmit, onCancel }) => {
|
|
|
32476
32521
|
] });
|
|
32477
32522
|
};
|
|
32478
32523
|
|
|
32479
|
-
// src/components/UEditor/
|
|
32524
|
+
// src/components/UEditor/link-commands.ts
|
|
32480
32525
|
import { TextSelection as TextSelection2 } from "@tiptap/pm/state";
|
|
32526
|
+
function applyEditorLink(editor, href) {
|
|
32527
|
+
const isEditingLink = editor.isActive("link");
|
|
32528
|
+
const hasSelectedText = !editor.state.selection.empty;
|
|
32529
|
+
const chain = editor.chain().focus();
|
|
32530
|
+
if (isEditingLink) {
|
|
32531
|
+
chain.extendMarkRange("link");
|
|
32532
|
+
}
|
|
32533
|
+
chain.setLink({ href });
|
|
32534
|
+
if (!hasSelectedText && !isEditingLink) {
|
|
32535
|
+
chain.insertContent(href);
|
|
32536
|
+
}
|
|
32537
|
+
return chain.command(({ tr }) => {
|
|
32538
|
+
tr.setSelection(TextSelection2.create(tr.doc, tr.selection.to));
|
|
32539
|
+
tr.removeStoredMark(editor.schema.marks.link);
|
|
32540
|
+
return true;
|
|
32541
|
+
}).run();
|
|
32542
|
+
}
|
|
32543
|
+
|
|
32544
|
+
// src/components/UEditor/table-cell-commands.ts
|
|
32545
|
+
import { TextSelection as TextSelection3 } from "@tiptap/pm/state";
|
|
32481
32546
|
import { selectedRect, TableMap as TableMap2 } from "@tiptap/pm/tables";
|
|
32482
32547
|
function getCellSelectionPositions(selection) {
|
|
32483
32548
|
const value = selection;
|
|
@@ -32511,7 +32576,7 @@ function getFocusableCellPos(editor, cellPos) {
|
|
|
32511
32576
|
return node?.isTextblock ? offset + 1 : cellPos + 1;
|
|
32512
32577
|
}
|
|
32513
32578
|
function focusCell(editor, cellPos) {
|
|
32514
|
-
const selection =
|
|
32579
|
+
const selection = TextSelection3.near(editor.state.doc.resolve(getFocusableCellPos(editor, cellPos)));
|
|
32515
32580
|
editor.view.dispatch(editor.state.tr.setSelection(selection));
|
|
32516
32581
|
editor.view.focus();
|
|
32517
32582
|
}
|
|
@@ -33057,7 +33122,7 @@ var EditorToolbar = ({
|
|
|
33057
33122
|
{
|
|
33058
33123
|
initialUrl: String(editor.getAttributes("link").href ?? ""),
|
|
33059
33124
|
onSubmit: (url) => {
|
|
33060
|
-
editor
|
|
33125
|
+
applyEditorLink(editor, url);
|
|
33061
33126
|
setShowLinkInput(false);
|
|
33062
33127
|
},
|
|
33063
33128
|
onCancel: () => setShowLinkInput(false)
|
|
@@ -33294,7 +33359,7 @@ var EditorToolbar = ({
|
|
|
33294
33359
|
{
|
|
33295
33360
|
initialUrl: String(editor.getAttributes("link").href ?? ""),
|
|
33296
33361
|
onSubmit: (url) => {
|
|
33297
|
-
editor
|
|
33362
|
+
applyEditorLink(editor, url);
|
|
33298
33363
|
setShowLinkInput(false);
|
|
33299
33364
|
},
|
|
33300
33365
|
onCancel: () => setShowLinkInput(false)
|
|
@@ -33916,7 +33981,7 @@ import {
|
|
|
33916
33981
|
} from "lucide-react";
|
|
33917
33982
|
|
|
33918
33983
|
// src/components/UEditor/table-formula-commands.ts
|
|
33919
|
-
import { TextSelection as
|
|
33984
|
+
import { TextSelection as TextSelection4 } from "@tiptap/pm/state";
|
|
33920
33985
|
import { selectedRect as selectedRect2, setCellAttr as setCellAttr2, TableMap as TableMap3 } from "@tiptap/pm/tables";
|
|
33921
33986
|
|
|
33922
33987
|
// src/components/UEditor/table-formula.ts
|
|
@@ -34532,7 +34597,7 @@ function restoreSelectionAtTableCell(editor, anchor) {
|
|
|
34532
34597
|
if (!cellNode || cellNode.type.name !== "tableCell" && cellNode.type.name !== "tableHeader") return false;
|
|
34533
34598
|
const selectionPos = Math.min(cellPos + 2, editor.state.doc.content.size);
|
|
34534
34599
|
editor.view.dispatch(
|
|
34535
|
-
editor.state.tr.setSelection(
|
|
34600
|
+
editor.state.tr.setSelection(TextSelection4.near(editor.state.doc.resolve(selectionPos))).setMeta("addToHistory", false)
|
|
34536
34601
|
);
|
|
34537
34602
|
return true;
|
|
34538
34603
|
}
|
|
@@ -34609,7 +34674,7 @@ function removeSelectedTableCellFormula(editor, clearContent) {
|
|
|
34609
34674
|
);
|
|
34610
34675
|
let transaction = editor.state.tr.replaceWith(selectedCell.cellPos, selectedCell.cellPos + selectedCell.node.nodeSize, nextCell2).setMeta(UEDITOR_TABLE_FORMULA_RECALCULATE_META, true);
|
|
34611
34676
|
const selectionPos = Math.min(selectedCell.cellPos + 2, transaction.doc.content.size);
|
|
34612
|
-
transaction = transaction.setSelection(
|
|
34677
|
+
transaction = transaction.setSelection(TextSelection4.near(transaction.doc.resolve(selectionPos)));
|
|
34613
34678
|
editor.view.dispatch(transaction);
|
|
34614
34679
|
recalculateActiveTableFormulas(editor);
|
|
34615
34680
|
restoreSelectionAtTableCell(editor, selectedCell);
|
|
@@ -35095,7 +35160,7 @@ var BubbleMenuContent = ({
|
|
|
35095
35160
|
{
|
|
35096
35161
|
initialUrl: editor.getAttributes("link").href || "",
|
|
35097
35162
|
onSubmit: (url) => {
|
|
35098
|
-
editor
|
|
35163
|
+
applyEditorLink(editor, url);
|
|
35099
35164
|
setShowLinkInput(false);
|
|
35100
35165
|
requestAnimationFrame(() => editor.commands.focus());
|
|
35101
35166
|
},
|
|
@@ -38952,7 +39017,7 @@ var Selection = class {
|
|
|
38952
39017
|
found.
|
|
38953
39018
|
*/
|
|
38954
39019
|
static findFrom($pos, dir, textOnly = false) {
|
|
38955
|
-
let inner = $pos.parent.inlineContent ? new
|
|
39020
|
+
let inner = $pos.parent.inlineContent ? new TextSelection5($pos) : findSelectionIn($pos.node(0), $pos.parent, $pos.pos, $pos.index(), dir, textOnly);
|
|
38956
39021
|
if (inner)
|
|
38957
39022
|
return inner;
|
|
38958
39023
|
for (let depth = $pos.depth - 1; depth >= 0; depth--) {
|
|
@@ -39021,7 +39086,7 @@ var Selection = class {
|
|
|
39021
39086
|
returns the bookmark for that.
|
|
39022
39087
|
*/
|
|
39023
39088
|
getBookmark() {
|
|
39024
|
-
return
|
|
39089
|
+
return TextSelection5.between(this.$anchor, this.$head).getBookmark();
|
|
39025
39090
|
}
|
|
39026
39091
|
};
|
|
39027
39092
|
Selection.prototype.visible = true;
|
|
@@ -39041,7 +39106,7 @@ function checkTextSelection($pos) {
|
|
|
39041
39106
|
console["warn"]("TextSelection endpoint not pointing into a node with inline content (" + $pos.parent.type.name + ")");
|
|
39042
39107
|
}
|
|
39043
39108
|
}
|
|
39044
|
-
var
|
|
39109
|
+
var TextSelection5 = class _TextSelection extends Selection {
|
|
39045
39110
|
/**
|
|
39046
39111
|
Construct a text selection between the given points.
|
|
39047
39112
|
*/
|
|
@@ -39127,7 +39192,7 @@ var TextSelection4 = class _TextSelection extends Selection {
|
|
|
39127
39192
|
return new _TextSelection($anchor, $head);
|
|
39128
39193
|
}
|
|
39129
39194
|
};
|
|
39130
|
-
Selection.jsonID("text",
|
|
39195
|
+
Selection.jsonID("text", TextSelection5);
|
|
39131
39196
|
var TextBookmark = class _TextBookmark {
|
|
39132
39197
|
constructor(anchor, head) {
|
|
39133
39198
|
this.anchor = anchor;
|
|
@@ -39137,7 +39202,7 @@ var TextBookmark = class _TextBookmark {
|
|
|
39137
39202
|
return new _TextBookmark(mapping.map(this.anchor), mapping.map(this.head));
|
|
39138
39203
|
}
|
|
39139
39204
|
resolve(doc) {
|
|
39140
|
-
return
|
|
39205
|
+
return TextSelection5.between(doc.resolve(this.anchor), doc.resolve(this.head));
|
|
39141
39206
|
}
|
|
39142
39207
|
};
|
|
39143
39208
|
var NodeSelection2 = class _NodeSelection extends Selection {
|
|
@@ -39256,7 +39321,7 @@ var AllBookmark = {
|
|
|
39256
39321
|
};
|
|
39257
39322
|
function findSelectionIn(doc, node, pos, index, dir, text = false) {
|
|
39258
39323
|
if (node.inlineContent)
|
|
39259
|
-
return
|
|
39324
|
+
return TextSelection5.create(doc, pos);
|
|
39260
39325
|
for (let i = index - (dir > 0 ? 0 : 1); dir > 0 ? i < node.childCount : i >= 0; i += dir) {
|
|
39261
39326
|
let child = node.child(i);
|
|
39262
39327
|
if (!child.isAtom) {
|
|
@@ -39848,7 +39913,7 @@ var CellSelection = class CellSelection2 extends Selection {
|
|
|
39848
39913
|
else if (tableChanged && this.isColSelection()) return CellSelection2.colSelection($anchorCell, $headCell);
|
|
39849
39914
|
else return new CellSelection2($anchorCell, $headCell);
|
|
39850
39915
|
}
|
|
39851
|
-
return
|
|
39916
|
+
return TextSelection5.between($anchorCell, $headCell);
|
|
39852
39917
|
}
|
|
39853
39918
|
content() {
|
|
39854
39919
|
const table = this.$anchorCell.node(-1);
|
|
@@ -40463,7 +40528,7 @@ function shiftArrow(axis, dir) {
|
|
|
40463
40528
|
};
|
|
40464
40529
|
}
|
|
40465
40530
|
function atEndOfCell(view, axis, dir) {
|
|
40466
|
-
if (!(view.state.selection instanceof
|
|
40531
|
+
if (!(view.state.selection instanceof TextSelection5)) return null;
|
|
40467
40532
|
const { $head } = view.state.selection;
|
|
40468
40533
|
for (let d = $head.depth - 1; d >= 0; d--) {
|
|
40469
40534
|
const parent = $head.node(d);
|
|
@@ -42365,7 +42430,7 @@ import { useEffect as useEffect39, useRef as useRef37 } from "react";
|
|
|
42365
42430
|
import { TableMap as TableMap6 } from "@tiptap/pm/tables";
|
|
42366
42431
|
|
|
42367
42432
|
// src/components/UEditor/table-formula-range-picker.ts
|
|
42368
|
-
import { TextSelection as
|
|
42433
|
+
import { TextSelection as TextSelection6 } from "@tiptap/pm/state";
|
|
42369
42434
|
import { TableMap as TableMap5 } from "@tiptap/pm/tables";
|
|
42370
42435
|
function getCellText2(cellNode) {
|
|
42371
42436
|
return cellNode.textBetween(0, cellNode.content.size, "\n").trim();
|
|
@@ -42384,6 +42449,9 @@ function getFormulaEditingTableContext(view) {
|
|
|
42384
42449
|
if (!tableNode || tableNode.type.name !== "table") return null;
|
|
42385
42450
|
const tableDom = cellDom.closest("table");
|
|
42386
42451
|
if (!(tableDom instanceof HTMLTableElement)) return null;
|
|
42452
|
+
const tableMap = TableMap5.get(tableNode);
|
|
42453
|
+
const tableStart = $from.start(tableDepth);
|
|
42454
|
+
const formulaCellRect = tableMap.findCell(cellPos - tableStart);
|
|
42387
42455
|
return {
|
|
42388
42456
|
cellContentEnd: $from.end(depth),
|
|
42389
42457
|
cellContentStart: $from.start(depth),
|
|
@@ -42391,17 +42459,56 @@ function getFormulaEditingTableContext(view) {
|
|
|
42391
42459
|
formula: getCellText2(node),
|
|
42392
42460
|
tableDom,
|
|
42393
42461
|
tableNode,
|
|
42394
|
-
tablePos: $from.before(tableDepth)
|
|
42462
|
+
tablePos: $from.before(tableDepth),
|
|
42463
|
+
formulaCellLabel: `${indexToColumnName(formulaCellRect.left)}${formulaCellRect.top + 1}`
|
|
42395
42464
|
};
|
|
42396
42465
|
}
|
|
42397
42466
|
return null;
|
|
42398
42467
|
}
|
|
42468
|
+
function collectTableFormulaCells(tableNode) {
|
|
42469
|
+
const tableMap = TableMap5.get(tableNode);
|
|
42470
|
+
const formulas = [];
|
|
42471
|
+
tableNode.forEach((rowNode, rowOffset) => {
|
|
42472
|
+
rowNode.forEach((cellNode, cellOffset) => {
|
|
42473
|
+
const formula = typeof cellNode.attrs.formula === "string" ? cellNode.attrs.formula.trim() : "";
|
|
42474
|
+
if (!formula) return;
|
|
42475
|
+
const rect = tableMap.findCell(rowOffset + 1 + cellOffset);
|
|
42476
|
+
formulas.push({
|
|
42477
|
+
label: `${indexToColumnName(rect.left)}${rect.top + 1}`,
|
|
42478
|
+
formula
|
|
42479
|
+
});
|
|
42480
|
+
});
|
|
42481
|
+
});
|
|
42482
|
+
return formulas;
|
|
42483
|
+
}
|
|
42484
|
+
function getFormulaCycleBlockedLabels(tableNode, formulaCellLabel) {
|
|
42485
|
+
const graph = buildTableFormulaDependencyGraph(collectTableFormulaCells(tableNode));
|
|
42486
|
+
const blocked = /* @__PURE__ */ new Set();
|
|
42487
|
+
const queue = [formulaCellLabel.toUpperCase()];
|
|
42488
|
+
for (let index = 0; index < queue.length; index += 1) {
|
|
42489
|
+
const label = queue[index];
|
|
42490
|
+
if (!label || blocked.has(label)) continue;
|
|
42491
|
+
blocked.add(label);
|
|
42492
|
+
for (const dependent of graph.dependents.get(label) ?? []) {
|
|
42493
|
+
if (!blocked.has(dependent)) queue.push(dependent);
|
|
42494
|
+
}
|
|
42495
|
+
}
|
|
42496
|
+
return blocked;
|
|
42497
|
+
}
|
|
42498
|
+
function getReferenceLabels(reference) {
|
|
42499
|
+
const range = parseTableCellRange(reference);
|
|
42500
|
+
return range ? getTableCellRangeLabels(range) : [reference.toUpperCase()];
|
|
42501
|
+
}
|
|
42502
|
+
function isBlockedFormulaReference(tableNode, formulaCellLabel, reference) {
|
|
42503
|
+
const blockedLabels = getFormulaCycleBlockedLabels(tableNode, formulaCellLabel);
|
|
42504
|
+
return getReferenceLabels(reference).some((label) => blockedLabels.has(label));
|
|
42505
|
+
}
|
|
42399
42506
|
function cancelFormulaEditing(view) {
|
|
42400
42507
|
const context = getFormulaEditingTableContext(view);
|
|
42401
42508
|
if (!context) return false;
|
|
42402
42509
|
let tr = view.state.tr.delete(context.cellContentStart, context.cellContentEnd);
|
|
42403
42510
|
const selectionPos = Math.min(context.cellContentStart, tr.doc.content.size);
|
|
42404
|
-
tr = tr.setSelection(
|
|
42511
|
+
tr = tr.setSelection(TextSelection6.near(tr.doc.resolve(selectionPos)));
|
|
42405
42512
|
view.dispatch(tr);
|
|
42406
42513
|
view.focus();
|
|
42407
42514
|
return true;
|
|
@@ -42459,19 +42566,30 @@ function normalizeFormulaRangeLabel(fromLabel, toLabel) {
|
|
|
42459
42566
|
return fromLabel === toLabel ? fromLabel : `${fromLabel}:${toLabel}`;
|
|
42460
42567
|
}
|
|
42461
42568
|
function replacePickedLabel(view, pickState, nextLabel, currentCell) {
|
|
42462
|
-
|
|
42569
|
+
const formulaContext = getFormulaEditingTableContext(view);
|
|
42570
|
+
const blocked = formulaContext ? isBlockedFormulaReference(formulaContext.tableNode, pickState.formulaCellLabel, nextLabel) : false;
|
|
42571
|
+
if (blocked) {
|
|
42572
|
+
return {
|
|
42573
|
+
...pickState,
|
|
42574
|
+
blocked: true,
|
|
42575
|
+
currentCell
|
|
42576
|
+
};
|
|
42577
|
+
}
|
|
42578
|
+
if (nextLabel === pickState.currentLabel && currentCell === pickState.currentCell && !pickState.blocked) return pickState;
|
|
42463
42579
|
if (nextLabel === pickState.currentLabel) {
|
|
42464
42580
|
return {
|
|
42465
42581
|
...pickState,
|
|
42582
|
+
blocked: false,
|
|
42466
42583
|
currentCell
|
|
42467
42584
|
};
|
|
42468
42585
|
}
|
|
42469
42586
|
let tr = view.state.tr.insertText(nextLabel, pickState.insertedFrom, pickState.insertedTo);
|
|
42470
42587
|
const nextTo = pickState.insertedFrom + nextLabel.length;
|
|
42471
|
-
tr = tr.setSelection(
|
|
42588
|
+
tr = tr.setSelection(TextSelection6.create(tr.doc, nextTo));
|
|
42472
42589
|
view.dispatch(tr);
|
|
42473
42590
|
return {
|
|
42474
42591
|
...pickState,
|
|
42592
|
+
blocked: false,
|
|
42475
42593
|
insertedTo: nextTo,
|
|
42476
42594
|
currentLabel: nextLabel,
|
|
42477
42595
|
currentCell
|
|
@@ -42483,12 +42601,28 @@ function beginFormulaRangePick(view, event) {
|
|
|
42483
42601
|
if (!formulaCell) return null;
|
|
42484
42602
|
const picked = getFormulaTableCellInfo(view, event.target, formulaCell.tablePos);
|
|
42485
42603
|
if (!picked || picked.cell === formulaCell.cellDom) return null;
|
|
42604
|
+
const blocked = isBlockedFormulaReference(formulaCell.tableNode, formulaCell.formulaCellLabel, picked.label);
|
|
42605
|
+
if (blocked) {
|
|
42606
|
+
event.preventDefault();
|
|
42607
|
+
event.stopPropagation();
|
|
42608
|
+
return {
|
|
42609
|
+
anchorLabel: picked.label,
|
|
42610
|
+
anchorCell: picked.cell,
|
|
42611
|
+
tablePos: formulaCell.tablePos,
|
|
42612
|
+
insertedFrom: view.state.selection.from,
|
|
42613
|
+
insertedTo: view.state.selection.from,
|
|
42614
|
+
currentLabel: "",
|
|
42615
|
+
currentCell: picked.cell,
|
|
42616
|
+
blocked: true,
|
|
42617
|
+
formulaCellLabel: formulaCell.formulaCellLabel
|
|
42618
|
+
};
|
|
42619
|
+
}
|
|
42486
42620
|
const { from, to } = view.state.selection;
|
|
42487
42621
|
const prefix = from === to ? getReferenceInsertionPrefix(view, formulaCell.cellContentStart, from) : "";
|
|
42488
42622
|
const insertedText = `${prefix}${picked.label}`;
|
|
42489
42623
|
const insertedFrom = from + prefix.length;
|
|
42490
42624
|
let tr = view.state.tr.insertText(insertedText, from, to);
|
|
42491
|
-
tr = tr.setSelection(
|
|
42625
|
+
tr = tr.setSelection(TextSelection6.create(tr.doc, from + insertedText.length));
|
|
42492
42626
|
view.dispatch(tr);
|
|
42493
42627
|
view.focus();
|
|
42494
42628
|
event.preventDefault();
|
|
@@ -42500,7 +42634,9 @@ function beginFormulaRangePick(view, event) {
|
|
|
42500
42634
|
insertedFrom,
|
|
42501
42635
|
insertedTo: insertedFrom + picked.label.length,
|
|
42502
42636
|
currentLabel: picked.label,
|
|
42503
|
-
currentCell: picked.cell
|
|
42637
|
+
currentCell: picked.cell,
|
|
42638
|
+
blocked: false,
|
|
42639
|
+
formulaCellLabel: formulaCell.formulaCellLabel
|
|
42504
42640
|
};
|
|
42505
42641
|
}
|
|
42506
42642
|
function updateFormulaRangePick(view, pickState, event) {
|
|
@@ -42528,7 +42664,8 @@ function getFormulaRangePickHighlight(container, pickState) {
|
|
|
42528
42664
|
left,
|
|
42529
42665
|
top,
|
|
42530
42666
|
width: Math.max(0, right - left),
|
|
42531
|
-
height: Math.max(0, bottom - top)
|
|
42667
|
+
height: Math.max(0, bottom - top),
|
|
42668
|
+
blocked: pickState.blocked
|
|
42532
42669
|
};
|
|
42533
42670
|
}
|
|
42534
42671
|
|
|
@@ -42552,6 +42689,12 @@ function createReferenceHighlight(label) {
|
|
|
42552
42689
|
element.className = "pointer-events-none absolute z-[21] rounded-[2px] border-2 border-emerald-500 bg-emerald-500/15";
|
|
42553
42690
|
return element;
|
|
42554
42691
|
}
|
|
42692
|
+
function createBlockedReferenceHighlight(label) {
|
|
42693
|
+
const element = document.createElement("span");
|
|
42694
|
+
element.dataset.ueditorFormulaBlockedReference = label;
|
|
42695
|
+
element.className = "pointer-events-none absolute z-[22] rounded-[2px] border-2 border-destructive/80 bg-destructive/10";
|
|
42696
|
+
return element;
|
|
42697
|
+
}
|
|
42555
42698
|
function useFormulaCoordinateOverlay(editor, containerRef, labels) {
|
|
42556
42699
|
const overlayRef = useRef37(null);
|
|
42557
42700
|
useEffect39(() => {
|
|
@@ -42563,6 +42706,7 @@ function useFormulaCoordinateOverlay(editor, containerRef, labels) {
|
|
|
42563
42706
|
let activeTable = null;
|
|
42564
42707
|
let activeTablePos = null;
|
|
42565
42708
|
let hoverLabel = null;
|
|
42709
|
+
const scrollListenerOptions = { passive: true, capture: true };
|
|
42566
42710
|
const clearOverlay = () => {
|
|
42567
42711
|
activeTable = null;
|
|
42568
42712
|
activeTablePos = null;
|
|
@@ -42622,10 +42766,12 @@ function useFormulaCoordinateOverlay(editor, containerRef, labels) {
|
|
|
42622
42766
|
actions.dataset.ueditorFormulaActions = "";
|
|
42623
42767
|
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
42768
|
const formulaCellRect = context.cellDom.getBoundingClientRect();
|
|
42769
|
+
const formulaActionsHeight = 34;
|
|
42770
|
+
const formulaActionsTop = formulaCellRect.bottom + 4 + formulaActionsHeight > tableRect.bottom ? Math.max(containerRect.top + 4, formulaCellRect.top - formulaActionsHeight - 4) : formulaCellRect.bottom + 4;
|
|
42625
42771
|
setPosition(
|
|
42626
42772
|
actions,
|
|
42627
42773
|
formulaCellRect.left - containerRect.left + container.scrollLeft,
|
|
42628
|
-
|
|
42774
|
+
formulaActionsTop - containerRect.top + container.scrollTop
|
|
42629
42775
|
);
|
|
42630
42776
|
const applyButton = document.createElement("button");
|
|
42631
42777
|
applyButton.type = "button";
|
|
@@ -42677,10 +42823,12 @@ function useFormulaCoordinateOverlay(editor, containerRef, labels) {
|
|
|
42677
42823
|
children.push(label);
|
|
42678
42824
|
}
|
|
42679
42825
|
const references = new Set(getTableFormulaReferences(context.formula));
|
|
42826
|
+
const blockedReferences = getFormulaCycleBlockedLabels(context.tableNode, context.formulaCellLabel);
|
|
42680
42827
|
for (const info of cellInfos) {
|
|
42681
|
-
if (!info
|
|
42828
|
+
if (!info) continue;
|
|
42682
42829
|
const cellRect = info.cell.getBoundingClientRect();
|
|
42683
|
-
const highlight = createReferenceHighlight(info.label);
|
|
42830
|
+
const highlight = info.label !== context.formulaCellLabel && blockedReferences.has(info.label) ? createBlockedReferenceHighlight(info.label) : references.has(info.label) ? createReferenceHighlight(info.label) : null;
|
|
42831
|
+
if (!highlight) continue;
|
|
42684
42832
|
setPosition(
|
|
42685
42833
|
highlight,
|
|
42686
42834
|
cellRect.left - containerRect.left + container.scrollLeft + 2,
|
|
@@ -42735,6 +42883,7 @@ function useFormulaCoordinateOverlay(editor, containerRef, labels) {
|
|
|
42735
42883
|
editor.view.dom.addEventListener("mousemove", handleMouseMove2);
|
|
42736
42884
|
editor.view.dom.addEventListener("mouseleave", handleMouseLeave2);
|
|
42737
42885
|
container.addEventListener(UEDITOR_TABLE_LAYOUT_CHANGE_EVENT, scheduleSync);
|
|
42886
|
+
container.addEventListener("scroll", scheduleSync, scrollListenerOptions);
|
|
42738
42887
|
window.addEventListener("resize", scheduleSync);
|
|
42739
42888
|
scheduleSync();
|
|
42740
42889
|
return () => {
|
|
@@ -42745,6 +42894,7 @@ function useFormulaCoordinateOverlay(editor, containerRef, labels) {
|
|
|
42745
42894
|
editor.view.dom.removeEventListener("mousemove", handleMouseMove2);
|
|
42746
42895
|
editor.view.dom.removeEventListener("mouseleave", handleMouseLeave2);
|
|
42747
42896
|
container.removeEventListener(UEDITOR_TABLE_LAYOUT_CHANGE_EVENT, scheduleSync);
|
|
42897
|
+
container.removeEventListener("scroll", scheduleSync, scrollListenerOptions);
|
|
42748
42898
|
window.removeEventListener("resize", scheduleSync);
|
|
42749
42899
|
if (animationFrame != null) window.cancelAnimationFrame(animationFrame);
|
|
42750
42900
|
clearOverlay();
|
|
@@ -43439,7 +43589,7 @@ var MenuBar = ({
|
|
|
43439
43589
|
closeInsertMenu();
|
|
43440
43590
|
};
|
|
43441
43591
|
const handleLinkSubmit = (url) => {
|
|
43442
|
-
editor
|
|
43592
|
+
applyEditorLink(editor, url);
|
|
43443
43593
|
closeInsertMenu();
|
|
43444
43594
|
};
|
|
43445
43595
|
const insertMenuItems = [
|
|
@@ -44191,7 +44341,7 @@ var UEditor = React88.forwardRef(({
|
|
|
44191
44341
|
{
|
|
44192
44342
|
ref: formulaCoordinateOverlayRef,
|
|
44193
44343
|
"data-ueditor-formula-coordinate-overlay": "",
|
|
44194
|
-
className: "pointer-events-none absolute inset-0 z-[
|
|
44344
|
+
className: "pointer-events-none absolute inset-0 z-[35] hidden overflow-visible"
|
|
44195
44345
|
}
|
|
44196
44346
|
),
|
|
44197
44347
|
formulaRangeHighlight && /* @__PURE__ */ jsx100(
|
|
@@ -44199,7 +44349,11 @@ var UEditor = React88.forwardRef(({
|
|
|
44199
44349
|
{
|
|
44200
44350
|
"aria-hidden": "true",
|
|
44201
44351
|
"data-ueditor-formula-range-highlight": "",
|
|
44202
|
-
|
|
44352
|
+
"data-ueditor-formula-range-blocked": formulaRangeHighlight.blocked ? "true" : void 0,
|
|
44353
|
+
className: cn(
|
|
44354
|
+
"pointer-events-none absolute z-20 rounded-[2px] border-2",
|
|
44355
|
+
formulaRangeHighlight.blocked ? "border-destructive bg-destructive/15" : "border-primary bg-primary/10"
|
|
44356
|
+
),
|
|
44203
44357
|
style: {
|
|
44204
44358
|
left: formulaRangeHighlight.left,
|
|
44205
44359
|
top: formulaRangeHighlight.top,
|