@trafica/editor 1.0.37 → 1.0.39
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/dist/index.d.mts +10 -15
- package/dist/index.d.ts +10 -15
- package/dist/index.js +135 -859
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +137 -861
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { forwardRef, useRef, useEffect, useImperativeHandle, useMemo, useCallback, useState, useLayoutEffect, useSyncExternalStore } from 'react';
|
|
2
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
3
3
|
import { createPortal } from 'react-dom';
|
|
4
4
|
|
|
5
5
|
// src/editor/core/DocumentModel.ts
|
|
@@ -7831,848 +7831,6 @@ var jsonSerializer = {
|
|
|
7831
7831
|
}
|
|
7832
7832
|
}
|
|
7833
7833
|
};
|
|
7834
|
-
function Editor({
|
|
7835
|
-
engine,
|
|
7836
|
-
placeholder = "Start writing...",
|
|
7837
|
-
className = "",
|
|
7838
|
-
readOnly = false,
|
|
7839
|
-
onHTMLChange,
|
|
7840
|
-
onJSONChange,
|
|
7841
|
-
onOpenLinkPopup,
|
|
7842
|
-
onUploadImage: _onUploadImage
|
|
7843
|
-
}) {
|
|
7844
|
-
const containerRef = useRef(null);
|
|
7845
|
-
const isRenderingRef = useRef(false);
|
|
7846
|
-
const scrollCaretIntoView = useCallback(() => {
|
|
7847
|
-
var _a, _b;
|
|
7848
|
-
const sel = window.getSelection();
|
|
7849
|
-
if (!sel || sel.rangeCount === 0) return;
|
|
7850
|
-
const range = sel.getRangeAt(0).cloneRange();
|
|
7851
|
-
range.collapse(true);
|
|
7852
|
-
const caretRect = range.getBoundingClientRect();
|
|
7853
|
-
let rect = caretRect;
|
|
7854
|
-
if (!rect || rect.top === 0 && rect.bottom === 0 && rect.left === 0) {
|
|
7855
|
-
const node = range.startContainer;
|
|
7856
|
-
const el = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
|
|
7857
|
-
if (el) rect = el.getBoundingClientRect();
|
|
7858
|
-
}
|
|
7859
|
-
if (!rect || rect.bottom === 0) return;
|
|
7860
|
-
let scrollEl = (_b = (_a = containerRef.current) == null ? void 0 : _a.parentElement) != null ? _b : null;
|
|
7861
|
-
while (scrollEl) {
|
|
7862
|
-
const style = window.getComputedStyle(scrollEl);
|
|
7863
|
-
const overflow = style.overflow + style.overflowY;
|
|
7864
|
-
if (/auto|scroll/.test(overflow)) break;
|
|
7865
|
-
scrollEl = scrollEl.parentElement;
|
|
7866
|
-
}
|
|
7867
|
-
const PADDING = 24;
|
|
7868
|
-
if (scrollEl) {
|
|
7869
|
-
const containerRect = scrollEl.getBoundingClientRect();
|
|
7870
|
-
if (rect.bottom > containerRect.bottom - PADDING) {
|
|
7871
|
-
scrollEl.scrollBy({ top: rect.bottom - containerRect.bottom + PADDING, behavior: "smooth" });
|
|
7872
|
-
} else if (rect.top < containerRect.top + PADDING) {
|
|
7873
|
-
scrollEl.scrollBy({ top: rect.top - containerRect.top - PADDING, behavior: "smooth" });
|
|
7874
|
-
}
|
|
7875
|
-
} else {
|
|
7876
|
-
if (rect.bottom > window.innerHeight - PADDING) {
|
|
7877
|
-
window.scrollBy({ top: rect.bottom - window.innerHeight + PADDING, behavior: "smooth" });
|
|
7878
|
-
} else if (rect.top < PADDING) {
|
|
7879
|
-
window.scrollBy({ top: rect.top - PADDING, behavior: "smooth" });
|
|
7880
|
-
}
|
|
7881
|
-
}
|
|
7882
|
-
}, []);
|
|
7883
|
-
const isComposingRef = useRef(false);
|
|
7884
|
-
const stateRef = useRef(engine.getState());
|
|
7885
|
-
const [selectedImagePath, setSelectedImagePath] = useState(null);
|
|
7886
|
-
const [findReplaceOpen, setFindReplaceOpen] = useState(false);
|
|
7887
|
-
const [findReplaceMode, setFindReplaceMode] = useState("find");
|
|
7888
|
-
const [linkPopupOpen, setLinkPopupOpen] = useState(false);
|
|
7889
|
-
const [isSourceMode, setIsSourceMode] = useState(false);
|
|
7890
|
-
const [sourceHTML, setSourceHTML] = useState("");
|
|
7891
|
-
const [editorHeight, setEditorHeight] = useState(null);
|
|
7892
|
-
const editorRootRef = useRef(null);
|
|
7893
|
-
const resizeDragRef = useRef(null);
|
|
7894
|
-
const [linkTooltip, setLinkTooltip] = useState(null);
|
|
7895
|
-
const linkTooltipTimerRef = useRef(null);
|
|
7896
|
-
const handleToggleSource = useCallback(() => {
|
|
7897
|
-
if (!isSourceMode) {
|
|
7898
|
-
const html = htmlSerializer.serialize(engine.getState().doc);
|
|
7899
|
-
setSourceHTML(prettyPrintHTML(html));
|
|
7900
|
-
setIsSourceMode(true);
|
|
7901
|
-
} else {
|
|
7902
|
-
const newDoc = htmlSerializer.deserialize(sourceHTML);
|
|
7903
|
-
const tr = createTransaction();
|
|
7904
|
-
tr.steps.push(tr_replaceDoc(newDoc));
|
|
7905
|
-
engine.dispatch(tr);
|
|
7906
|
-
setIsSourceMode(false);
|
|
7907
|
-
}
|
|
7908
|
-
}, [isSourceMode, sourceHTML, engine]);
|
|
7909
|
-
useEffect(() => {
|
|
7910
|
-
const onMouseMove = (e) => {
|
|
7911
|
-
const drag = resizeDragRef.current;
|
|
7912
|
-
if (!drag) return;
|
|
7913
|
-
const newHeight = Math.max(120, drag.startHeight + (e.clientY - drag.startY));
|
|
7914
|
-
setEditorHeight(newHeight);
|
|
7915
|
-
};
|
|
7916
|
-
const onMouseUp = () => {
|
|
7917
|
-
resizeDragRef.current = null;
|
|
7918
|
-
};
|
|
7919
|
-
document.addEventListener("mousemove", onMouseMove);
|
|
7920
|
-
document.addEventListener("mouseup", onMouseUp);
|
|
7921
|
-
return () => {
|
|
7922
|
-
document.removeEventListener("mousemove", onMouseMove);
|
|
7923
|
-
document.removeEventListener("mouseup", onMouseUp);
|
|
7924
|
-
};
|
|
7925
|
-
}, []);
|
|
7926
|
-
const [tableContextMenu, setTableContextMenu] = useState(null);
|
|
7927
|
-
const [tableSelection, setTableSelection] = useState(null);
|
|
7928
|
-
const cellDragRef = useRef(null);
|
|
7929
|
-
const colResizeRef = useRef(null);
|
|
7930
|
-
const state = useEditorState(engine);
|
|
7931
|
-
const inTableCellPos = state.selection ? findCellPosition(state.doc, state.selection.anchor.path) : null;
|
|
7932
|
-
useLayoutEffect(() => {
|
|
7933
|
-
stateRef.current = state;
|
|
7934
|
-
}, [state]);
|
|
7935
|
-
useLayoutEffect(() => {
|
|
7936
|
-
const container = containerRef.current;
|
|
7937
|
-
if (!container) return;
|
|
7938
|
-
isRenderingRef.current = true;
|
|
7939
|
-
renderDocument(state.doc, container);
|
|
7940
|
-
if (!container.contains(document.activeElement)) {
|
|
7941
|
-
container.focus({ preventScroll: true });
|
|
7942
|
-
}
|
|
7943
|
-
if (state.selection) {
|
|
7944
|
-
restoreSelection(container, state.selection);
|
|
7945
|
-
scrollCaretIntoView();
|
|
7946
|
-
}
|
|
7947
|
-
isRenderingRef.current = false;
|
|
7948
|
-
if (onHTMLChange) {
|
|
7949
|
-
onHTMLChange(htmlSerializer.serialize(state.doc));
|
|
7950
|
-
}
|
|
7951
|
-
if (onJSONChange) {
|
|
7952
|
-
onJSONChange(jsonSerializer.serialize(state.doc));
|
|
7953
|
-
}
|
|
7954
|
-
}, [state.doc]);
|
|
7955
|
-
useLayoutEffect(() => {
|
|
7956
|
-
const container = containerRef.current;
|
|
7957
|
-
if (!container || !state.selection) return;
|
|
7958
|
-
restoreSelection(container, state.selection);
|
|
7959
|
-
scrollCaretIntoView();
|
|
7960
|
-
}, [state.selection]);
|
|
7961
|
-
useEffect(() => {
|
|
7962
|
-
const container = containerRef.current;
|
|
7963
|
-
if (!container || readOnly) return;
|
|
7964
|
-
return attachClipboardHandlers(container, engine);
|
|
7965
|
-
}, [engine, readOnly]);
|
|
7966
|
-
useEffect(() => {
|
|
7967
|
-
const container = containerRef.current;
|
|
7968
|
-
if (!container) return;
|
|
7969
|
-
container.querySelectorAll(".editor-cell-selected").forEach((el) => {
|
|
7970
|
-
el.classList.remove("editor-cell-selected");
|
|
7971
|
-
});
|
|
7972
|
-
if (!tableSelection) return;
|
|
7973
|
-
const { tablePath, anchorCell, focusCell } = tableSelection;
|
|
7974
|
-
const minRow = Math.min(anchorCell[0], focusCell[0]);
|
|
7975
|
-
const maxRow = Math.max(anchorCell[0], focusCell[0]);
|
|
7976
|
-
const minCol = Math.min(anchorCell[1], focusCell[1]);
|
|
7977
|
-
const maxCol = Math.max(anchorCell[1], focusCell[1]);
|
|
7978
|
-
for (let r = minRow; r <= maxRow; r++) {
|
|
7979
|
-
for (let c = minCol; c <= maxCol; c++) {
|
|
7980
|
-
const cellPath = [...tablePath, r, c];
|
|
7981
|
-
const td = container.querySelector(
|
|
7982
|
-
`[data-block-path="${JSON.stringify(cellPath)}"]`
|
|
7983
|
-
);
|
|
7984
|
-
if (td) td.classList.add("editor-cell-selected");
|
|
7985
|
-
}
|
|
7986
|
-
}
|
|
7987
|
-
}, [tableSelection, state.doc]);
|
|
7988
|
-
useEffect(() => {
|
|
7989
|
-
const container = containerRef.current;
|
|
7990
|
-
if (!container) return;
|
|
7991
|
-
container.querySelectorAll(".editor-cell-active").forEach((el) => {
|
|
7992
|
-
el.classList.remove("editor-cell-active");
|
|
7993
|
-
});
|
|
7994
|
-
if (!inTableCellPos) {
|
|
7995
|
-
setTableSelection(null);
|
|
7996
|
-
return;
|
|
7997
|
-
}
|
|
7998
|
-
if (tableSelection) {
|
|
7999
|
-
const { anchorCell, focusCell, tablePath } = tableSelection;
|
|
8000
|
-
const sameTable = JSON.stringify(tablePath) === JSON.stringify(inTableCellPos.tablePath);
|
|
8001
|
-
const singleCell = anchorCell[0] === focusCell[0] && anchorCell[1] === focusCell[1];
|
|
8002
|
-
if (!sameTable || singleCell) {
|
|
8003
|
-
setTableSelection(null);
|
|
8004
|
-
}
|
|
8005
|
-
}
|
|
8006
|
-
const cellPath = [...inTableCellPos.tablePath, inTableCellPos.row, inTableCellPos.col];
|
|
8007
|
-
const td = container.querySelector(
|
|
8008
|
-
`[data-block-path="${JSON.stringify(cellPath)}"]`
|
|
8009
|
-
);
|
|
8010
|
-
if (td) td.classList.add("editor-cell-active");
|
|
8011
|
-
}, [inTableCellPos, state.doc]);
|
|
8012
|
-
useEffect(() => {
|
|
8013
|
-
const onSelectionChange = () => {
|
|
8014
|
-
if (isRenderingRef.current) return;
|
|
8015
|
-
const container = containerRef.current;
|
|
8016
|
-
if (!container) return;
|
|
8017
|
-
if (!isSelectionInContainer(container)) return;
|
|
8018
|
-
const captured = captureSelection(container);
|
|
8019
|
-
if (!captured) return;
|
|
8020
|
-
const currentSelection = engine.getState().selection;
|
|
8021
|
-
if (currentSelection && JSON.stringify(currentSelection.anchor) === JSON.stringify(captured.anchor) && JSON.stringify(currentSelection.focus) === JSON.stringify(captured.focus)) {
|
|
8022
|
-
return;
|
|
8023
|
-
}
|
|
8024
|
-
const tr = createTransaction();
|
|
8025
|
-
tr.steps.push(tr_setSelection(captured));
|
|
8026
|
-
engine.dispatch(tr);
|
|
8027
|
-
};
|
|
8028
|
-
document.addEventListener(
|
|
8029
|
-
"selectionchange",
|
|
8030
|
-
onSelectionChange
|
|
8031
|
-
);
|
|
8032
|
-
return () => {
|
|
8033
|
-
document.removeEventListener(
|
|
8034
|
-
"selectionchange",
|
|
8035
|
-
onSelectionChange
|
|
8036
|
-
);
|
|
8037
|
-
};
|
|
8038
|
-
}, [engine]);
|
|
8039
|
-
const handleFocus = useCallback(() => {
|
|
8040
|
-
const currentState = engine.getState();
|
|
8041
|
-
if (!currentState.selection) {
|
|
8042
|
-
const tr = createTransaction();
|
|
8043
|
-
tr.steps.push(
|
|
8044
|
-
tr_setSelection(
|
|
8045
|
-
makeCollapsedSelection({ path: [0], offset: 0 })
|
|
8046
|
-
)
|
|
8047
|
-
);
|
|
8048
|
-
engine.dispatch(tr);
|
|
8049
|
-
}
|
|
8050
|
-
}, [engine]);
|
|
8051
|
-
const handleKeyDown = useCallback(
|
|
8052
|
-
(e) => {
|
|
8053
|
-
var _a, _b, _c;
|
|
8054
|
-
if (readOnly) return;
|
|
8055
|
-
if ((e.ctrlKey || e.metaKey) && e.key === "f") {
|
|
8056
|
-
e.preventDefault();
|
|
8057
|
-
setFindReplaceMode("find");
|
|
8058
|
-
setFindReplaceOpen(true);
|
|
8059
|
-
return;
|
|
8060
|
-
}
|
|
8061
|
-
if ((e.ctrlKey || e.metaKey) && e.key === "h") {
|
|
8062
|
-
e.preventDefault();
|
|
8063
|
-
setFindReplaceMode("replace");
|
|
8064
|
-
setFindReplaceOpen(true);
|
|
8065
|
-
return;
|
|
8066
|
-
}
|
|
8067
|
-
if ((e.ctrlKey || e.metaKey) && e.key === "k") {
|
|
8068
|
-
e.preventDefault();
|
|
8069
|
-
setLinkPopupOpen(true);
|
|
8070
|
-
onOpenLinkPopup == null ? void 0 : onOpenLinkPopup();
|
|
8071
|
-
return;
|
|
8072
|
-
}
|
|
8073
|
-
if ((e.ctrlKey || e.metaKey) && (e.key === "a" || e.key === "A")) {
|
|
8074
|
-
e.preventDefault();
|
|
8075
|
-
const container = containerRef.current;
|
|
8076
|
-
if (container) {
|
|
8077
|
-
const spans = container.querySelectorAll("[data-path]");
|
|
8078
|
-
const first = spans[0];
|
|
8079
|
-
const last = spans[spans.length - 1];
|
|
8080
|
-
if (first && last) {
|
|
8081
|
-
const sel = window.getSelection();
|
|
8082
|
-
const range = document.createRange();
|
|
8083
|
-
range.setStart((_a = leafTextNode(first, "first")) != null ? _a : first, 0);
|
|
8084
|
-
const lastLeaf = leafTextNode(last, "last");
|
|
8085
|
-
range.setEnd(lastLeaf != null ? lastLeaf : last, (_c = (_b = lastLeaf == null ? void 0 : lastLeaf.textContent) == null ? void 0 : _b.length) != null ? _c : 0);
|
|
8086
|
-
sel == null ? void 0 : sel.removeAllRanges();
|
|
8087
|
-
sel == null ? void 0 : sel.addRange(range);
|
|
8088
|
-
}
|
|
8089
|
-
}
|
|
8090
|
-
return;
|
|
8091
|
-
}
|
|
8092
|
-
if (selectedImagePath && (e.key === "Delete" || e.key === "Backspace")) {
|
|
8093
|
-
e.preventDefault();
|
|
8094
|
-
deleteImageAtPath(selectedImagePath)(engine);
|
|
8095
|
-
setSelectedImagePath(null);
|
|
8096
|
-
return;
|
|
8097
|
-
}
|
|
8098
|
-
if (selectedImagePath) setSelectedImagePath(null);
|
|
8099
|
-
if (e.key === "Tab") {
|
|
8100
|
-
const { doc, selection: sel } = engine.getState();
|
|
8101
|
-
if (sel) {
|
|
8102
|
-
const bp = findContentBlockPath(doc, sel.anchor.path);
|
|
8103
|
-
const block = bp ? getNodeAtPath(doc, bp) : null;
|
|
8104
|
-
if ((block == null ? void 0 : block.type) === "code_block") {
|
|
8105
|
-
e.preventDefault();
|
|
8106
|
-
insertText(" ")(engine);
|
|
8107
|
-
return;
|
|
8108
|
-
}
|
|
8109
|
-
if ((block == null ? void 0 : block.type) === "list_item") {
|
|
8110
|
-
e.preventDefault();
|
|
8111
|
-
if (e.shiftKey) {
|
|
8112
|
-
outdentListItem(engine);
|
|
8113
|
-
} else {
|
|
8114
|
-
indentListItem(engine);
|
|
8115
|
-
}
|
|
8116
|
-
return;
|
|
8117
|
-
}
|
|
8118
|
-
if (findCellPosition(doc, sel.anchor.path)) {
|
|
8119
|
-
e.preventDefault();
|
|
8120
|
-
}
|
|
8121
|
-
}
|
|
8122
|
-
}
|
|
8123
|
-
const handled = engine.handleKeyDown(
|
|
8124
|
-
e.nativeEvent
|
|
8125
|
-
);
|
|
8126
|
-
if (handled) return;
|
|
8127
|
-
if (e.key === "Enter" && !e.shiftKey) {
|
|
8128
|
-
e.preventDefault();
|
|
8129
|
-
handleEnter(engine);
|
|
8130
|
-
return;
|
|
8131
|
-
}
|
|
8132
|
-
if (e.key === "Backspace") {
|
|
8133
|
-
e.preventDefault();
|
|
8134
|
-
handleBackspace(engine);
|
|
8135
|
-
return;
|
|
8136
|
-
}
|
|
8137
|
-
if (e.key === "Delete") {
|
|
8138
|
-
e.preventDefault();
|
|
8139
|
-
handleDelete(engine);
|
|
8140
|
-
return;
|
|
8141
|
-
}
|
|
8142
|
-
if (e.key === "Enter" && e.shiftKey) {
|
|
8143
|
-
e.preventDefault();
|
|
8144
|
-
insertText("\n")(engine);
|
|
8145
|
-
return;
|
|
8146
|
-
}
|
|
8147
|
-
},
|
|
8148
|
-
[engine, readOnly, onOpenLinkPopup, selectedImagePath]
|
|
8149
|
-
);
|
|
8150
|
-
useEffect(() => {
|
|
8151
|
-
const container = containerRef.current;
|
|
8152
|
-
if (!container || readOnly) return;
|
|
8153
|
-
const onBeforeInput = (e) => {
|
|
8154
|
-
if (e.isComposing || isComposingRef.current) return;
|
|
8155
|
-
switch (e.inputType) {
|
|
8156
|
-
case "insertText":
|
|
8157
|
-
case "insertReplacementText": {
|
|
8158
|
-
if (!e.data) return;
|
|
8159
|
-
e.preventDefault();
|
|
8160
|
-
insertText(e.data)(engine);
|
|
8161
|
-
return;
|
|
8162
|
-
}
|
|
8163
|
-
case "insertParagraph": {
|
|
8164
|
-
e.preventDefault();
|
|
8165
|
-
handleEnter(engine);
|
|
8166
|
-
return;
|
|
8167
|
-
}
|
|
8168
|
-
case "insertLineBreak": {
|
|
8169
|
-
e.preventDefault();
|
|
8170
|
-
insertText("\n")(engine);
|
|
8171
|
-
return;
|
|
8172
|
-
}
|
|
8173
|
-
case "deleteContentBackward":
|
|
8174
|
-
case "deleteWordBackward":
|
|
8175
|
-
case "deleteSoftLineBackward": {
|
|
8176
|
-
e.preventDefault();
|
|
8177
|
-
handleBackspace(engine);
|
|
8178
|
-
return;
|
|
8179
|
-
}
|
|
8180
|
-
case "insertFromPaste":
|
|
8181
|
-
case "insertFromDrop":
|
|
8182
|
-
return;
|
|
8183
|
-
default:
|
|
8184
|
-
e.preventDefault();
|
|
8185
|
-
}
|
|
8186
|
-
};
|
|
8187
|
-
container.addEventListener("beforeinput", onBeforeInput);
|
|
8188
|
-
return () => container.removeEventListener("beforeinput", onBeforeInput);
|
|
8189
|
-
}, [engine, readOnly]);
|
|
8190
|
-
useEffect(() => {
|
|
8191
|
-
const container = containerRef.current;
|
|
8192
|
-
if (!container || readOnly) return;
|
|
8193
|
-
const onCompositionStart = () => {
|
|
8194
|
-
isComposingRef.current = true;
|
|
8195
|
-
};
|
|
8196
|
-
const onCompositionEnd = (e) => {
|
|
8197
|
-
isComposingRef.current = false;
|
|
8198
|
-
if (e.data) insertText(e.data)(engine);
|
|
8199
|
-
};
|
|
8200
|
-
container.addEventListener("compositionstart", onCompositionStart);
|
|
8201
|
-
container.addEventListener("compositionend", onCompositionEnd);
|
|
8202
|
-
return () => {
|
|
8203
|
-
container.removeEventListener("compositionstart", onCompositionStart);
|
|
8204
|
-
container.removeEventListener("compositionend", onCompositionEnd);
|
|
8205
|
-
};
|
|
8206
|
-
}, [engine, readOnly]);
|
|
8207
|
-
useEffect(() => {
|
|
8208
|
-
const container = containerRef.current;
|
|
8209
|
-
if (!container) return;
|
|
8210
|
-
const onMouseOver = (e) => {
|
|
8211
|
-
var _a;
|
|
8212
|
-
const anchor = e.target.closest("a.editor-link");
|
|
8213
|
-
if (!anchor) return;
|
|
8214
|
-
const href = (_a = anchor.getAttribute("href")) != null ? _a : "";
|
|
8215
|
-
if (!href) return;
|
|
8216
|
-
if (linkTooltipTimerRef.current) clearTimeout(linkTooltipTimerRef.current);
|
|
8217
|
-
linkTooltipTimerRef.current = setTimeout(() => {
|
|
8218
|
-
const rect = anchor.getBoundingClientRect();
|
|
8219
|
-
const containerRect = container.getBoundingClientRect();
|
|
8220
|
-
setLinkTooltip({
|
|
8221
|
-
href,
|
|
8222
|
-
x: rect.left - containerRect.left,
|
|
8223
|
-
y: rect.bottom - containerRect.top + 6
|
|
8224
|
-
});
|
|
8225
|
-
}, 200);
|
|
8226
|
-
};
|
|
8227
|
-
const onMouseOut = (e) => {
|
|
8228
|
-
const anchor = e.target.closest("a.editor-link");
|
|
8229
|
-
if (!anchor) return;
|
|
8230
|
-
if (linkTooltipTimerRef.current) clearTimeout(linkTooltipTimerRef.current);
|
|
8231
|
-
setLinkTooltip(null);
|
|
8232
|
-
};
|
|
8233
|
-
const onClick = (e) => {
|
|
8234
|
-
if (!(e.ctrlKey || e.metaKey)) return;
|
|
8235
|
-
const anchor = e.target.closest("a.editor-link");
|
|
8236
|
-
if (!anchor) return;
|
|
8237
|
-
e.preventDefault();
|
|
8238
|
-
const href = anchor.getAttribute("href");
|
|
8239
|
-
if (href) window.open(href, "_blank", "noopener,noreferrer");
|
|
8240
|
-
};
|
|
8241
|
-
container.addEventListener("mouseover", onMouseOver);
|
|
8242
|
-
container.addEventListener("mouseout", onMouseOut);
|
|
8243
|
-
container.addEventListener("click", onClick);
|
|
8244
|
-
return () => {
|
|
8245
|
-
container.removeEventListener("mouseover", onMouseOver);
|
|
8246
|
-
container.removeEventListener("mouseout", onMouseOut);
|
|
8247
|
-
container.removeEventListener("click", onClick);
|
|
8248
|
-
if (linkTooltipTimerRef.current) clearTimeout(linkTooltipTimerRef.current);
|
|
8249
|
-
};
|
|
8250
|
-
}, []);
|
|
8251
|
-
useEffect(() => {
|
|
8252
|
-
const onMouseMove = (e) => {
|
|
8253
|
-
const drag = colResizeRef.current;
|
|
8254
|
-
if (!drag) return;
|
|
8255
|
-
const delta = e.clientX - drag.startX;
|
|
8256
|
-
const newWidth = Math.max(40, drag.startWidth + delta);
|
|
8257
|
-
const container = containerRef.current;
|
|
8258
|
-
if (container) {
|
|
8259
|
-
const colEl = container.querySelector(
|
|
8260
|
-
`col[data-col-index="${drag.colIndex}"]`
|
|
8261
|
-
);
|
|
8262
|
-
if (colEl) colEl.style.width = `${newWidth}px`;
|
|
8263
|
-
}
|
|
8264
|
-
};
|
|
8265
|
-
const onMouseUp = (e) => {
|
|
8266
|
-
const drag = colResizeRef.current;
|
|
8267
|
-
if (!drag) return;
|
|
8268
|
-
const delta = e.clientX - drag.startX;
|
|
8269
|
-
const newWidth = Math.max(40, drag.startWidth + delta);
|
|
8270
|
-
colResizeRef.current = null;
|
|
8271
|
-
setColumnWidth(drag.tablePath, drag.colIndex, newWidth)(engine);
|
|
8272
|
-
};
|
|
8273
|
-
document.addEventListener("mousemove", onMouseMove);
|
|
8274
|
-
document.addEventListener("mouseup", onMouseUp);
|
|
8275
|
-
return () => {
|
|
8276
|
-
document.removeEventListener("mousemove", onMouseMove);
|
|
8277
|
-
document.removeEventListener("mouseup", onMouseUp);
|
|
8278
|
-
};
|
|
8279
|
-
}, [engine]);
|
|
8280
|
-
useEffect(() => {
|
|
8281
|
-
const onMouseMove = (e) => {
|
|
8282
|
-
var _a, _b;
|
|
8283
|
-
const drag = cellDragRef.current;
|
|
8284
|
-
if (!drag) return;
|
|
8285
|
-
const td = e.target.closest("[data-cell-row]");
|
|
8286
|
-
if (!td) return;
|
|
8287
|
-
const row = parseInt((_a = td.dataset.cellRow) != null ? _a : "0");
|
|
8288
|
-
const col = parseInt((_b = td.dataset.cellCol) != null ? _b : "0");
|
|
8289
|
-
const tdTablePath = td.dataset.cellTablePath ? JSON.parse(td.dataset.cellTablePath) : null;
|
|
8290
|
-
if (!tdTablePath || JSON.stringify(tdTablePath) !== JSON.stringify(drag.tablePath)) return;
|
|
8291
|
-
setTableSelection({
|
|
8292
|
-
tablePath: drag.tablePath,
|
|
8293
|
-
anchorCell: [drag.startRow, drag.startCol],
|
|
8294
|
-
focusCell: [row, col]
|
|
8295
|
-
});
|
|
8296
|
-
};
|
|
8297
|
-
const onMouseUp = () => {
|
|
8298
|
-
cellDragRef.current = null;
|
|
8299
|
-
};
|
|
8300
|
-
document.addEventListener("mousemove", onMouseMove);
|
|
8301
|
-
document.addEventListener("mouseup", onMouseUp);
|
|
8302
|
-
return () => {
|
|
8303
|
-
document.removeEventListener("mousemove", onMouseMove);
|
|
8304
|
-
document.removeEventListener("mouseup", onMouseUp);
|
|
8305
|
-
};
|
|
8306
|
-
}, []);
|
|
8307
|
-
const handleContextMenu = useCallback(
|
|
8308
|
-
(e) => {
|
|
8309
|
-
var _a, _b, _c, _d, _e, _f;
|
|
8310
|
-
const td = e.target.closest("[data-cell-row]");
|
|
8311
|
-
if (!td) return;
|
|
8312
|
-
e.preventDefault();
|
|
8313
|
-
const row = parseInt((_a = td.dataset.cellRow) != null ? _a : "0");
|
|
8314
|
-
const col = parseInt((_b = td.dataset.cellCol) != null ? _b : "0");
|
|
8315
|
-
const tablePath = td.dataset.cellTablePath ? JSON.parse(td.dataset.cellTablePath) : null;
|
|
8316
|
-
if (!tablePath) return;
|
|
8317
|
-
const cellNode = getNodeAtPath(
|
|
8318
|
-
engine.getState().doc,
|
|
8319
|
-
[...tablePath, row, col]
|
|
8320
|
-
);
|
|
8321
|
-
const isMerged = !!cellNode && (((_d = (_c = cellNode.attrs) == null ? void 0 : _c.colspan) != null ? _d : 1) > 1 || ((_f = (_e = cellNode.attrs) == null ? void 0 : _e.rowspan) != null ? _f : 1) > 1);
|
|
8322
|
-
setTableContextMenu({ x: e.clientX, y: e.clientY, tablePath, row, col, isMerged });
|
|
8323
|
-
},
|
|
8324
|
-
[engine]
|
|
8325
|
-
);
|
|
8326
|
-
const handleMouseDown = useCallback(
|
|
8327
|
-
(e) => {
|
|
8328
|
-
var _a, _b, _c, _d;
|
|
8329
|
-
if (readOnly) return;
|
|
8330
|
-
const target = e.target;
|
|
8331
|
-
if (target.dataset.resizeImagePath) {
|
|
8332
|
-
e.preventDefault();
|
|
8333
|
-
const imagePath = JSON.parse(target.dataset.resizeImagePath);
|
|
8334
|
-
const corner = (_a = target.dataset.resizeImagePos) != null ? _a : "se";
|
|
8335
|
-
const container = containerRef.current;
|
|
8336
|
-
let startWidth = 300;
|
|
8337
|
-
if (container) {
|
|
8338
|
-
const fig2 = container.querySelector(`[data-image-path="${JSON.stringify(imagePath)}"]`);
|
|
8339
|
-
const img = fig2 == null ? void 0 : fig2.querySelector("img");
|
|
8340
|
-
if (img) startWidth = img.getBoundingClientRect().width || 300;
|
|
8341
|
-
}
|
|
8342
|
-
imageResizeRef.current = {
|
|
8343
|
-
imagePath,
|
|
8344
|
-
corner,
|
|
8345
|
-
startX: e.clientX,
|
|
8346
|
-
startY: e.clientY,
|
|
8347
|
-
startWidth,
|
|
8348
|
-
startHeight: 0
|
|
8349
|
-
};
|
|
8350
|
-
return;
|
|
8351
|
-
}
|
|
8352
|
-
const fig = target.closest("[data-image-path]");
|
|
8353
|
-
if (fig == null ? void 0 : fig.dataset.imagePath) {
|
|
8354
|
-
e.preventDefault();
|
|
8355
|
-
setSelectedImagePath(JSON.parse(fig.dataset.imagePath));
|
|
8356
|
-
return;
|
|
8357
|
-
}
|
|
8358
|
-
setSelectedImagePath(null);
|
|
8359
|
-
if (target.dataset.resizeTable) {
|
|
8360
|
-
e.preventDefault();
|
|
8361
|
-
const tablePath = JSON.parse(target.dataset.resizeTable);
|
|
8362
|
-
const colIndex = parseInt((_b = target.dataset.resizeCol) != null ? _b : "0");
|
|
8363
|
-
const container = containerRef.current;
|
|
8364
|
-
let startWidth = 120;
|
|
8365
|
-
if (container) {
|
|
8366
|
-
const colEl = container.querySelector(
|
|
8367
|
-
`col[data-col-index="${colIndex}"]`
|
|
8368
|
-
);
|
|
8369
|
-
if (colEl) startWidth = colEl.getBoundingClientRect().width || 120;
|
|
8370
|
-
}
|
|
8371
|
-
colResizeRef.current = { tablePath, colIndex, startX: e.clientX, startWidth };
|
|
8372
|
-
return;
|
|
8373
|
-
}
|
|
8374
|
-
const td = target.closest("[data-cell-row]");
|
|
8375
|
-
if (!td) setTableSelection(null);
|
|
8376
|
-
if (td && !readOnly) {
|
|
8377
|
-
const row = parseInt((_c = td.dataset.cellRow) != null ? _c : "0");
|
|
8378
|
-
const col = parseInt((_d = td.dataset.cellCol) != null ? _d : "0");
|
|
8379
|
-
const tdTablePath = td.dataset.cellTablePath ? JSON.parse(td.dataset.cellTablePath) : null;
|
|
8380
|
-
if (tdTablePath) {
|
|
8381
|
-
cellDragRef.current = { tablePath: tdTablePath, startRow: row, startCol: col };
|
|
8382
|
-
setTableSelection({
|
|
8383
|
-
tablePath: tdTablePath,
|
|
8384
|
-
anchorCell: [row, col],
|
|
8385
|
-
focusCell: [row, col]
|
|
8386
|
-
});
|
|
8387
|
-
}
|
|
8388
|
-
}
|
|
8389
|
-
const checkTarget = target.dataset.checkPath ? target : target.closest("[data-check-path]");
|
|
8390
|
-
if (checkTarget == null ? void 0 : checkTarget.dataset.checkPath) {
|
|
8391
|
-
e.preventDefault();
|
|
8392
|
-
toggleCheckItemAt(JSON.parse(checkTarget.dataset.checkPath))(engine);
|
|
8393
|
-
}
|
|
8394
|
-
},
|
|
8395
|
-
[engine, readOnly]
|
|
8396
|
-
);
|
|
8397
|
-
const handleClick = useCallback(() => {
|
|
8398
|
-
const container = containerRef.current;
|
|
8399
|
-
if (!container) return;
|
|
8400
|
-
const selection = captureSelection(container);
|
|
8401
|
-
if (!selection) return;
|
|
8402
|
-
const tr = createTransaction();
|
|
8403
|
-
tr.steps.push(
|
|
8404
|
-
tr_setSelection(selection)
|
|
8405
|
-
);
|
|
8406
|
-
engine.dispatch(tr);
|
|
8407
|
-
}, [engine]);
|
|
8408
|
-
const imageResizeRef = useRef(null);
|
|
8409
|
-
useEffect(() => {
|
|
8410
|
-
const onMouseMove = (e) => {
|
|
8411
|
-
const drag = imageResizeRef.current;
|
|
8412
|
-
if (!drag) return;
|
|
8413
|
-
const deltaX = e.clientX - drag.startX;
|
|
8414
|
-
const isRight = drag.corner === "ne" || drag.corner === "se";
|
|
8415
|
-
const newWidth = Math.max(60, drag.startWidth + (isRight ? deltaX : -deltaX));
|
|
8416
|
-
const container = containerRef.current;
|
|
8417
|
-
if (container) {
|
|
8418
|
-
const fig = container.querySelector(
|
|
8419
|
-
`[data-image-path="${JSON.stringify(drag.imagePath)}"]`
|
|
8420
|
-
);
|
|
8421
|
-
const img = fig == null ? void 0 : fig.querySelector("img");
|
|
8422
|
-
if (img) img.style.width = `${newWidth}px`;
|
|
8423
|
-
}
|
|
8424
|
-
};
|
|
8425
|
-
const onMouseUp = (e) => {
|
|
8426
|
-
const drag = imageResizeRef.current;
|
|
8427
|
-
if (!drag) return;
|
|
8428
|
-
const deltaX = e.clientX - drag.startX;
|
|
8429
|
-
const isRight = drag.corner === "ne" || drag.corner === "se";
|
|
8430
|
-
const newWidth = Math.max(60, drag.startWidth + (isRight ? deltaX : -deltaX));
|
|
8431
|
-
imageResizeRef.current = null;
|
|
8432
|
-
setImageAttr(drag.imagePath, { width: newWidth })(engine);
|
|
8433
|
-
};
|
|
8434
|
-
document.addEventListener("mousemove", onMouseMove);
|
|
8435
|
-
document.addEventListener("mouseup", onMouseUp);
|
|
8436
|
-
return () => {
|
|
8437
|
-
document.removeEventListener("mousemove", onMouseMove);
|
|
8438
|
-
document.removeEventListener("mouseup", onMouseUp);
|
|
8439
|
-
};
|
|
8440
|
-
}, [engine]);
|
|
8441
|
-
useEffect(() => {
|
|
8442
|
-
const container = containerRef.current;
|
|
8443
|
-
if (!container) return;
|
|
8444
|
-
container.querySelectorAll("[data-image-path]").forEach((el) => {
|
|
8445
|
-
el.removeAttribute("data-selected");
|
|
8446
|
-
});
|
|
8447
|
-
if (selectedImagePath) {
|
|
8448
|
-
const fig = container.querySelector(
|
|
8449
|
-
`[data-image-path="${JSON.stringify(selectedImagePath)}"]`
|
|
8450
|
-
);
|
|
8451
|
-
if (fig) fig.setAttribute("data-selected", "true");
|
|
8452
|
-
}
|
|
8453
|
-
}, [selectedImagePath, state.doc]);
|
|
8454
|
-
useEffect(() => {
|
|
8455
|
-
const container = containerRef.current;
|
|
8456
|
-
if (!container || readOnly) return;
|
|
8457
|
-
const onDragOver = (e) => {
|
|
8458
|
-
var _a;
|
|
8459
|
-
if ((_a = e.dataTransfer) == null ? void 0 : _a.types.includes("Files")) e.preventDefault();
|
|
8460
|
-
};
|
|
8461
|
-
const onDrop = (e) => {
|
|
8462
|
-
var _a;
|
|
8463
|
-
e.preventDefault();
|
|
8464
|
-
const file = (_a = e.dataTransfer) == null ? void 0 : _a.files[0];
|
|
8465
|
-
if (!file || !file.type.startsWith("image/")) return;
|
|
8466
|
-
const blobUrl = URL.createObjectURL(file);
|
|
8467
|
-
insertImage(blobUrl, file.name.replace(/\.[^.]+$/, ""))(engine);
|
|
8468
|
-
};
|
|
8469
|
-
container.addEventListener("dragover", onDragOver);
|
|
8470
|
-
container.addEventListener("drop", onDrop);
|
|
8471
|
-
return () => {
|
|
8472
|
-
container.removeEventListener("dragover", onDragOver);
|
|
8473
|
-
container.removeEventListener("drop", onDrop);
|
|
8474
|
-
};
|
|
8475
|
-
}, [engine, readOnly]);
|
|
8476
|
-
const isVisualEmpty = state.doc.children.length === 1 && state.doc.children[0].type === "paragraph" && state.doc.children[0].children.length === 0;
|
|
8477
|
-
return /* @__PURE__ */ jsxs(
|
|
8478
|
-
"div",
|
|
8479
|
-
{
|
|
8480
|
-
ref: editorRootRef,
|
|
8481
|
-
className: `editor-root relative flex flex-col ${className}`,
|
|
8482
|
-
style: editorHeight ? { minHeight: editorHeight } : void 0,
|
|
8483
|
-
children: [
|
|
8484
|
-
!readOnly && /* @__PURE__ */ jsx(
|
|
8485
|
-
Toolbar,
|
|
8486
|
-
{
|
|
8487
|
-
engine,
|
|
8488
|
-
onFindReplace: (mode) => {
|
|
8489
|
-
setFindReplaceMode(mode);
|
|
8490
|
-
setFindReplaceOpen(true);
|
|
8491
|
-
},
|
|
8492
|
-
linkPopupOpen,
|
|
8493
|
-
onLinkPopupClose: () => setLinkPopupOpen(false),
|
|
8494
|
-
isSourceMode,
|
|
8495
|
-
onToggleSource: handleToggleSource
|
|
8496
|
-
}
|
|
8497
|
-
),
|
|
8498
|
-
/* @__PURE__ */ jsx("div", { className: "relative", children: isSourceMode ? /* @__PURE__ */ jsx(
|
|
8499
|
-
"textarea",
|
|
8500
|
-
{
|
|
8501
|
-
"aria-label": "HTML source editor",
|
|
8502
|
-
value: sourceHTML,
|
|
8503
|
-
onChange: (e) => {
|
|
8504
|
-
setSourceHTML(e.target.value);
|
|
8505
|
-
const t = e.target;
|
|
8506
|
-
t.style.height = "auto";
|
|
8507
|
-
t.style.height = t.scrollHeight + "px";
|
|
8508
|
-
},
|
|
8509
|
-
ref: (el) => {
|
|
8510
|
-
if (el) {
|
|
8511
|
-
el.style.height = "auto";
|
|
8512
|
-
el.style.height = el.scrollHeight + "px";
|
|
8513
|
-
}
|
|
8514
|
-
},
|
|
8515
|
-
spellCheck: false,
|
|
8516
|
-
className: "w-full block px-6 py-4 font-mono text-sm leading-relaxed bg-gray-950 text-green-300 outline-none resize-none border-0 overflow-hidden"
|
|
8517
|
-
}
|
|
8518
|
-
) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
8519
|
-
isVisualEmpty && /* @__PURE__ */ jsx(
|
|
8520
|
-
"div",
|
|
8521
|
-
{
|
|
8522
|
-
className: "\r\n absolute\r\n top-6\r\n left-6\r\n pointer-events-none\r\n select-none\r\n text-base\r\n text-gray-400\r\n dark:text-gray-500\r\n ",
|
|
8523
|
-
"aria-hidden": "true",
|
|
8524
|
-
children: placeholder
|
|
8525
|
-
}
|
|
8526
|
-
),
|
|
8527
|
-
/* @__PURE__ */ jsx(
|
|
8528
|
-
"div",
|
|
8529
|
-
{
|
|
8530
|
-
ref: containerRef,
|
|
8531
|
-
contentEditable: !readOnly,
|
|
8532
|
-
suppressContentEditableWarning: true,
|
|
8533
|
-
role: "textbox",
|
|
8534
|
-
"aria-multiline": "true",
|
|
8535
|
-
"aria-label": "Rich text editor",
|
|
8536
|
-
spellCheck: true,
|
|
8537
|
-
onMouseDown: handleMouseDown,
|
|
8538
|
-
onContextMenu: handleContextMenu,
|
|
8539
|
-
onFocus: handleFocus,
|
|
8540
|
-
onClick: handleClick,
|
|
8541
|
-
onKeyDown: handleKeyDown,
|
|
8542
|
-
className: [
|
|
8543
|
-
"editor-canvas",
|
|
8544
|
-
"min-h-[300px]",
|
|
8545
|
-
"px-6",
|
|
8546
|
-
"py-4",
|
|
8547
|
-
"outline-none",
|
|
8548
|
-
"text-base",
|
|
8549
|
-
"leading-relaxed",
|
|
8550
|
-
"text-gray-900",
|
|
8551
|
-
"dark:text-gray-100",
|
|
8552
|
-
readOnly ? "cursor-default" : "cursor-text"
|
|
8553
|
-
].join(" ")
|
|
8554
|
-
}
|
|
8555
|
-
),
|
|
8556
|
-
linkTooltip && /* @__PURE__ */ jsxs(
|
|
8557
|
-
"div",
|
|
8558
|
-
{
|
|
8559
|
-
role: "tooltip",
|
|
8560
|
-
style: { left: linkTooltip.x, top: linkTooltip.y },
|
|
8561
|
-
className: "absolute z-50 pointer-events-none flex items-center gap-1.5 bg-gray-900 dark:bg-gray-700 text-white text-xs px-2.5 py-1.5 rounded shadow-lg max-w-xs",
|
|
8562
|
-
children: [
|
|
8563
|
-
/* @__PURE__ */ jsxs("svg", { width: "11", height: "11", viewBox: "0 0 16 16", fill: "none", className: "shrink-0 opacity-70", "aria-hidden": "true", children: [
|
|
8564
|
-
/* @__PURE__ */ jsx("path", { d: "M6.5 3.5H4A2.5 2.5 0 0 0 4 8.5h2", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round" }),
|
|
8565
|
-
/* @__PURE__ */ jsx("path", { d: "M9.5 3.5H12A2.5 2.5 0 0 1 12 8.5h-2", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round" }),
|
|
8566
|
-
/* @__PURE__ */ jsx("path", { d: "M5.5 6h5", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round" })
|
|
8567
|
-
] }),
|
|
8568
|
-
/* @__PURE__ */ jsx("span", { className: "truncate", children: linkTooltip.href }),
|
|
8569
|
-
/* @__PURE__ */ jsx("span", { className: "shrink-0 opacity-50 ml-1", children: "Ctrl+click to open" })
|
|
8570
|
-
]
|
|
8571
|
-
}
|
|
8572
|
-
)
|
|
8573
|
-
] }) }),
|
|
8574
|
-
inTableCellPos && !readOnly && /* @__PURE__ */ jsx(
|
|
8575
|
-
TableToolbar,
|
|
8576
|
-
{
|
|
8577
|
-
engine,
|
|
8578
|
-
tablePath: inTableCellPos.tablePath,
|
|
8579
|
-
cellPos: { row: inTableCellPos.row, col: inTableCellPos.col },
|
|
8580
|
-
tableSelection,
|
|
8581
|
-
editorContainer: containerRef
|
|
8582
|
-
}
|
|
8583
|
-
),
|
|
8584
|
-
tableContextMenu && /* @__PURE__ */ jsx(
|
|
8585
|
-
TableContextMenu,
|
|
8586
|
-
{
|
|
8587
|
-
x: tableContextMenu.x,
|
|
8588
|
-
y: tableContextMenu.y,
|
|
8589
|
-
tablePath: tableContextMenu.tablePath,
|
|
8590
|
-
row: tableContextMenu.row,
|
|
8591
|
-
col: tableContextMenu.col,
|
|
8592
|
-
isMerged: tableContextMenu.isMerged,
|
|
8593
|
-
tableSelection,
|
|
8594
|
-
engine,
|
|
8595
|
-
onClose: () => setTableContextMenu(null)
|
|
8596
|
-
}
|
|
8597
|
-
),
|
|
8598
|
-
findReplaceOpen && /* @__PURE__ */ jsx(
|
|
8599
|
-
FindReplaceModal,
|
|
8600
|
-
{
|
|
8601
|
-
engine,
|
|
8602
|
-
editorContainer: containerRef,
|
|
8603
|
-
initialMode: findReplaceMode,
|
|
8604
|
-
onClose: () => setFindReplaceOpen(false)
|
|
8605
|
-
}
|
|
8606
|
-
),
|
|
8607
|
-
selectedImagePath && !readOnly && /* @__PURE__ */ jsx(
|
|
8608
|
-
ImageToolbar,
|
|
8609
|
-
{
|
|
8610
|
-
engine,
|
|
8611
|
-
imagePath: selectedImagePath,
|
|
8612
|
-
editorContainer: containerRef,
|
|
8613
|
-
onClose: () => setSelectedImagePath(null)
|
|
8614
|
-
}
|
|
8615
|
-
),
|
|
8616
|
-
!readOnly && /* @__PURE__ */ jsx(
|
|
8617
|
-
"div",
|
|
8618
|
-
{
|
|
8619
|
-
title: "Drag to resize",
|
|
8620
|
-
onMouseDown: (e) => {
|
|
8621
|
-
e.preventDefault();
|
|
8622
|
-
const rootEl = editorRootRef.current;
|
|
8623
|
-
if (!rootEl) return;
|
|
8624
|
-
resizeDragRef.current = {
|
|
8625
|
-
startY: e.clientY,
|
|
8626
|
-
startHeight: rootEl.getBoundingClientRect().height
|
|
8627
|
-
};
|
|
8628
|
-
},
|
|
8629
|
-
className: "absolute bottom-0 right-0 w-4 h-4 cursor-s-resize flex items-end justify-end pr-0.5 pb-0.5 select-none",
|
|
8630
|
-
"aria-hidden": "true",
|
|
8631
|
-
children: /* @__PURE__ */ jsx("svg", { width: "8", height: "8", viewBox: "0 0 8 8", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M7 1L1 7M7 4L4 7M7 7L7 7", stroke: "#9ca3af", strokeWidth: "1.2", strokeLinecap: "round" }) })
|
|
8632
|
-
}
|
|
8633
|
-
)
|
|
8634
|
-
]
|
|
8635
|
-
}
|
|
8636
|
-
);
|
|
8637
|
-
}
|
|
8638
|
-
var BLOCK_TAGS = "p|h[1-6]|ul|ol|li|blockquote|pre|figure|table|thead|tbody|tr|th|td";
|
|
8639
|
-
var _OPEN_ONLY_RE = new RegExp(`^<(${BLOCK_TAGS})(?:\\s[^>]*)?>$`, "i");
|
|
8640
|
-
var _CLOSE_ONLY_RE = new RegExp(`^<\\/(${BLOCK_TAGS})>$`, "i");
|
|
8641
|
-
var _HR_RE = /^<hr(\s[^>]*)?\/?>$/i;
|
|
8642
|
-
function leafTextNode(el, end) {
|
|
8643
|
-
let node = el;
|
|
8644
|
-
while (node.hasChildNodes()) {
|
|
8645
|
-
node = end === "first" ? node.firstChild : node.lastChild;
|
|
8646
|
-
}
|
|
8647
|
-
return node.nodeType === Node.TEXT_NODE ? node : null;
|
|
8648
|
-
}
|
|
8649
|
-
function prettyPrintHTML(html) {
|
|
8650
|
-
const blockOpen = new RegExp(`(<(?:${BLOCK_TAGS})(?:\\s[^>]*)?>)`, "gi");
|
|
8651
|
-
const blockClose = new RegExp(`(<\\/(?:${BLOCK_TAGS})>)`, "gi");
|
|
8652
|
-
const spread = html.replace(blockOpen, "\n$1").replace(blockClose, "$1\n").replace(/<hr(\s[^>]*)?\/?>(\s*)/gi, "\n<hr />\n");
|
|
8653
|
-
let depth = 0;
|
|
8654
|
-
const result = [];
|
|
8655
|
-
for (const raw of spread.split("\n")) {
|
|
8656
|
-
const line = raw.trim();
|
|
8657
|
-
if (!line) continue;
|
|
8658
|
-
if (_HR_RE.test(line)) {
|
|
8659
|
-
result.push(" ".repeat(depth) + line);
|
|
8660
|
-
continue;
|
|
8661
|
-
}
|
|
8662
|
-
if (_CLOSE_ONLY_RE.test(line)) {
|
|
8663
|
-
depth = Math.max(0, depth - 1);
|
|
8664
|
-
result.push(" ".repeat(depth) + line);
|
|
8665
|
-
continue;
|
|
8666
|
-
}
|
|
8667
|
-
if (_OPEN_ONLY_RE.test(line)) {
|
|
8668
|
-
result.push(" ".repeat(depth) + line);
|
|
8669
|
-
depth++;
|
|
8670
|
-
continue;
|
|
8671
|
-
}
|
|
8672
|
-
result.push(" ".repeat(depth) + line);
|
|
8673
|
-
}
|
|
8674
|
-
return result.join("\n");
|
|
8675
|
-
}
|
|
8676
7834
|
function EditorCore({
|
|
8677
7835
|
engine,
|
|
8678
7836
|
placeholder = "Start writing...",
|
|
@@ -8689,6 +7847,7 @@ function EditorCore({
|
|
|
8689
7847
|
}) {
|
|
8690
7848
|
const containerRef = useRef(null);
|
|
8691
7849
|
const isRenderingRef = useRef(false);
|
|
7850
|
+
const skipRestoreSelectionRef = useRef(false);
|
|
8692
7851
|
const scrollCaretIntoView = useCallback(() => {
|
|
8693
7852
|
var _a, _b;
|
|
8694
7853
|
const sel = window.getSelection();
|
|
@@ -8742,7 +7901,7 @@ function EditorCore({
|
|
|
8742
7901
|
const handleToggleSource = useCallback(() => {
|
|
8743
7902
|
if (!isSourceMode) {
|
|
8744
7903
|
const html = htmlSerializer.serialize(engine.getState().doc);
|
|
8745
|
-
setSourceHTML(
|
|
7904
|
+
setSourceHTML(prettyPrintHTML(html));
|
|
8746
7905
|
setIsSourceMode(true);
|
|
8747
7906
|
} else {
|
|
8748
7907
|
const newDoc = htmlSerializer.deserialize(sourceHTML);
|
|
@@ -8801,8 +7960,14 @@ function EditorCore({
|
|
|
8801
7960
|
useLayoutEffect(() => {
|
|
8802
7961
|
const container = containerRef.current;
|
|
8803
7962
|
if (!container || !state.selection) return;
|
|
7963
|
+
if (skipRestoreSelectionRef.current) {
|
|
7964
|
+
skipRestoreSelectionRef.current = false;
|
|
7965
|
+
return;
|
|
7966
|
+
}
|
|
7967
|
+
isRenderingRef.current = true;
|
|
8804
7968
|
restoreSelection(container, state.selection);
|
|
8805
7969
|
scrollCaretIntoView();
|
|
7970
|
+
isRenderingRef.current = false;
|
|
8806
7971
|
}, [state.selection]);
|
|
8807
7972
|
useEffect(() => {
|
|
8808
7973
|
const container = containerRef.current;
|
|
@@ -8867,6 +8032,7 @@ function EditorCore({
|
|
|
8867
8032
|
if (currentSelection && JSON.stringify(currentSelection.anchor) === JSON.stringify(captured.anchor) && JSON.stringify(currentSelection.focus) === JSON.stringify(captured.focus)) {
|
|
8868
8033
|
return;
|
|
8869
8034
|
}
|
|
8035
|
+
skipRestoreSelectionRef.current = true;
|
|
8870
8036
|
const tr = createTransaction();
|
|
8871
8037
|
tr.steps.push(tr_setSelection(captured));
|
|
8872
8038
|
engine.dispatch(tr);
|
|
@@ -8930,8 +8096,8 @@ function EditorCore({
|
|
|
8930
8096
|
if (first && last) {
|
|
8931
8097
|
const sel = window.getSelection();
|
|
8932
8098
|
const range = document.createRange();
|
|
8933
|
-
range.setStart((_a =
|
|
8934
|
-
const lastLeaf =
|
|
8099
|
+
range.setStart((_a = leafTextNode(first, "first")) != null ? _a : first, 0);
|
|
8100
|
+
const lastLeaf = leafTextNode(last, "last");
|
|
8935
8101
|
range.setEnd(lastLeaf != null ? lastLeaf : last, (_c = (_b = lastLeaf == null ? void 0 : lastLeaf.textContent) == null ? void 0 : _b.length) != null ? _c : 0);
|
|
8936
8102
|
sel == null ? void 0 : sel.removeAllRanges();
|
|
8937
8103
|
sel == null ? void 0 : sel.addRange(range);
|
|
@@ -9249,10 +8415,9 @@ function EditorCore({
|
|
|
9249
8415
|
if (!container) return;
|
|
9250
8416
|
const selection = captureSelection(container);
|
|
9251
8417
|
if (!selection) return;
|
|
8418
|
+
skipRestoreSelectionRef.current = true;
|
|
9252
8419
|
const tr = createTransaction();
|
|
9253
|
-
tr.steps.push(
|
|
9254
|
-
tr_setSelection(selection)
|
|
9255
|
-
);
|
|
8420
|
+
tr.steps.push(tr_setSelection(selection));
|
|
9256
8421
|
engine.dispatch(tr);
|
|
9257
8422
|
}, [engine]);
|
|
9258
8423
|
const imageResizeRef = useRef(null);
|
|
@@ -9487,36 +8652,36 @@ function EditorCore({
|
|
|
9487
8652
|
}
|
|
9488
8653
|
);
|
|
9489
8654
|
}
|
|
9490
|
-
var
|
|
9491
|
-
var
|
|
9492
|
-
var
|
|
9493
|
-
var
|
|
9494
|
-
function
|
|
8655
|
+
var BLOCK_TAGS = "p|h[1-6]|ul|ol|li|blockquote|pre|figure|table|thead|tbody|tr|th|td";
|
|
8656
|
+
var _OPEN_ONLY_RE = new RegExp(`^<(${BLOCK_TAGS})(?:\\s[^>]*)?>$`, "i");
|
|
8657
|
+
var _CLOSE_ONLY_RE = new RegExp(`^<\\/(${BLOCK_TAGS})>$`, "i");
|
|
8658
|
+
var _HR_RE = /^<hr(\s[^>]*)?\/?>$/i;
|
|
8659
|
+
function leafTextNode(el, end) {
|
|
9495
8660
|
let node = el;
|
|
9496
8661
|
while (node.hasChildNodes()) {
|
|
9497
8662
|
node = end === "first" ? node.firstChild : node.lastChild;
|
|
9498
8663
|
}
|
|
9499
8664
|
return node.nodeType === Node.TEXT_NODE ? node : null;
|
|
9500
8665
|
}
|
|
9501
|
-
function
|
|
9502
|
-
const blockOpen = new RegExp(`(<(?:${
|
|
9503
|
-
const blockClose = new RegExp(`(<\\/(?:${
|
|
8666
|
+
function prettyPrintHTML(html) {
|
|
8667
|
+
const blockOpen = new RegExp(`(<(?:${BLOCK_TAGS})(?:\\s[^>]*)?>)`, "gi");
|
|
8668
|
+
const blockClose = new RegExp(`(<\\/(?:${BLOCK_TAGS})>)`, "gi");
|
|
9504
8669
|
const spread = html.replace(blockOpen, "\n$1").replace(blockClose, "$1\n").replace(/<hr(\s[^>]*)?\/?>(\s*)/gi, "\n<hr />\n");
|
|
9505
8670
|
let depth = 0;
|
|
9506
8671
|
const result = [];
|
|
9507
8672
|
for (const raw of spread.split("\n")) {
|
|
9508
8673
|
const line = raw.trim();
|
|
9509
8674
|
if (!line) continue;
|
|
9510
|
-
if (
|
|
8675
|
+
if (_HR_RE.test(line)) {
|
|
9511
8676
|
result.push(" ".repeat(depth) + line);
|
|
9512
8677
|
continue;
|
|
9513
8678
|
}
|
|
9514
|
-
if (
|
|
8679
|
+
if (_CLOSE_ONLY_RE.test(line)) {
|
|
9515
8680
|
depth = Math.max(0, depth - 1);
|
|
9516
8681
|
result.push(" ".repeat(depth) + line);
|
|
9517
8682
|
continue;
|
|
9518
8683
|
}
|
|
9519
|
-
if (
|
|
8684
|
+
if (_OPEN_ONLY_RE.test(line)) {
|
|
9520
8685
|
result.push(" ".repeat(depth) + line);
|
|
9521
8686
|
depth++;
|
|
9522
8687
|
continue;
|
|
@@ -9988,6 +9153,117 @@ function execCommand(engine, command, ...args) {
|
|
|
9988
9153
|
function registerCommand(name, fn) {
|
|
9989
9154
|
REGISTRY[name] = fn;
|
|
9990
9155
|
}
|
|
9156
|
+
var Editor = forwardRef(function Editor2({
|
|
9157
|
+
value,
|
|
9158
|
+
onChange,
|
|
9159
|
+
defaultValue,
|
|
9160
|
+
toolbar,
|
|
9161
|
+
theme: _theme,
|
|
9162
|
+
placeholder = "Start writing...",
|
|
9163
|
+
className = "",
|
|
9164
|
+
style,
|
|
9165
|
+
minHeight,
|
|
9166
|
+
maxHeight,
|
|
9167
|
+
readOnly = false,
|
|
9168
|
+
plugins,
|
|
9169
|
+
onFocus,
|
|
9170
|
+
onBlur,
|
|
9171
|
+
onReady,
|
|
9172
|
+
onUploadImage
|
|
9173
|
+
}, ref) {
|
|
9174
|
+
const engine = useEditorEngine();
|
|
9175
|
+
const pluginsRegisteredRef = useRef(false);
|
|
9176
|
+
if (!pluginsRegisteredRef.current && (plugins == null ? void 0 : plugins.length)) {
|
|
9177
|
+
pluginsRegisteredRef.current = true;
|
|
9178
|
+
for (const plugin of plugins) {
|
|
9179
|
+
engine.registerPlugin(plugin);
|
|
9180
|
+
}
|
|
9181
|
+
}
|
|
9182
|
+
const defaultLoadedRef = useRef(false);
|
|
9183
|
+
useEffect(() => {
|
|
9184
|
+
if (defaultLoadedRef.current) return;
|
|
9185
|
+
defaultLoadedRef.current = true;
|
|
9186
|
+
if (defaultValue) {
|
|
9187
|
+
const doc = htmlSerializer.deserialize(defaultValue);
|
|
9188
|
+
const tr = createTransaction();
|
|
9189
|
+
tr.steps.push(tr_replaceDoc(doc));
|
|
9190
|
+
engine.dispatch(tr);
|
|
9191
|
+
}
|
|
9192
|
+
}, []);
|
|
9193
|
+
const lastExternalValueRef = useRef(void 0);
|
|
9194
|
+
useEffect(() => {
|
|
9195
|
+
if (value === void 0) return;
|
|
9196
|
+
if (value === lastExternalValueRef.current) return;
|
|
9197
|
+
lastExternalValueRef.current = value;
|
|
9198
|
+
const doc = htmlSerializer.deserialize(value);
|
|
9199
|
+
const tr = createTransaction();
|
|
9200
|
+
tr.steps.push(tr_replaceDoc(doc));
|
|
9201
|
+
engine.dispatch(tr);
|
|
9202
|
+
}, [value, engine]);
|
|
9203
|
+
const api = {
|
|
9204
|
+
getHTML: () => htmlSerializer.serialize(engine.getState().doc),
|
|
9205
|
+
setHTML: (html) => {
|
|
9206
|
+
const doc = htmlSerializer.deserialize(html);
|
|
9207
|
+
const tr = createTransaction();
|
|
9208
|
+
tr.steps.push(tr_replaceDoc(doc));
|
|
9209
|
+
engine.dispatch(tr);
|
|
9210
|
+
},
|
|
9211
|
+
getJSON: () => engine.getState().doc,
|
|
9212
|
+
getMarkdown: () => markdownSerializer.serialize(engine.getState().doc),
|
|
9213
|
+
focus: () => {
|
|
9214
|
+
const el = document.querySelector('[role="textbox"]');
|
|
9215
|
+
el == null ? void 0 : el.focus();
|
|
9216
|
+
},
|
|
9217
|
+
blur: () => {
|
|
9218
|
+
const el = document.querySelector('[role="textbox"]');
|
|
9219
|
+
el == null ? void 0 : el.blur();
|
|
9220
|
+
},
|
|
9221
|
+
clear: () => {
|
|
9222
|
+
const doc = htmlSerializer.deserialize("");
|
|
9223
|
+
const tr = createTransaction();
|
|
9224
|
+
tr.steps.push(tr_replaceDoc(doc));
|
|
9225
|
+
engine.dispatch(tr);
|
|
9226
|
+
},
|
|
9227
|
+
undo: () => undo(engine),
|
|
9228
|
+
redo: () => redo(engine),
|
|
9229
|
+
execCommand: (command, ...args) => execCommand(engine, command, ...args),
|
|
9230
|
+
registerCommand: (name, fn) => registerCommand(name, fn),
|
|
9231
|
+
getEngine: () => engine
|
|
9232
|
+
};
|
|
9233
|
+
useImperativeHandle(ref, () => api);
|
|
9234
|
+
useEffect(() => {
|
|
9235
|
+
onReady == null ? void 0 : onReady(api);
|
|
9236
|
+
}, []);
|
|
9237
|
+
const containerStyle = { ...style };
|
|
9238
|
+
if (minHeight !== void 0) {
|
|
9239
|
+
containerStyle.minHeight = typeof minHeight === "number" ? `${minHeight}px` : minHeight;
|
|
9240
|
+
}
|
|
9241
|
+
if (maxHeight !== void 0) {
|
|
9242
|
+
containerStyle.maxHeight = typeof maxHeight === "number" ? `${maxHeight}px` : maxHeight;
|
|
9243
|
+
containerStyle.overflow = "auto";
|
|
9244
|
+
}
|
|
9245
|
+
const hideToolbar = toolbar === false;
|
|
9246
|
+
const toolbarConfig = toolbar === false || toolbar === void 0 ? void 0 : Array.isArray(toolbar) ? toolbar : void 0;
|
|
9247
|
+
return /* @__PURE__ */ jsx("div", { style: containerStyle, children: /* @__PURE__ */ jsx(
|
|
9248
|
+
EditorCore,
|
|
9249
|
+
{
|
|
9250
|
+
engine,
|
|
9251
|
+
placeholder,
|
|
9252
|
+
className,
|
|
9253
|
+
readOnly,
|
|
9254
|
+
onHTMLChange: (html) => {
|
|
9255
|
+
lastExternalValueRef.current = html;
|
|
9256
|
+
onChange == null ? void 0 : onChange(html);
|
|
9257
|
+
},
|
|
9258
|
+
onFocus,
|
|
9259
|
+
onBlur,
|
|
9260
|
+
hideToolbar,
|
|
9261
|
+
toolbarConfig,
|
|
9262
|
+
onUploadImage
|
|
9263
|
+
}
|
|
9264
|
+
) });
|
|
9265
|
+
});
|
|
9266
|
+
Editor.displayName = "Editor";
|
|
9991
9267
|
|
|
9992
9268
|
export { BASIC_TOOLBAR, DEFAULT_TOOLBAR, Editor, EditorCore, EditorEngine, MINIMAL_TOOLBAR, TablePlugin, Toolbar, addColumnLeft, addColumnRight, addMarkToNode, addRowAbove, addRowBelow, applyMarkToRange, applySearchHighlights, applyTransaction, attachClipboardHandlers, attachPasteHandler, captureSelection, clearSearchHighlights, collectText, comparePaths, comparePositions, createEmptyDocument, createHistoryManager, createHistoryPlugin, createParagraph, createPastePlugin, createTableCell, createTableNode, createTransaction, deleteColumn, deleteImageAtPath, deleteRange, deleteRow, deleteTable, deleteTableColumn, deleteTableRow, deleteTextAtPath, execCommand, findCellPosition, findContentBlockPath, findMatches, findTablePath, getActiveAlignment, getActiveBlockType, getActiveFontFamily, getActiveFontSize, getActiveHighlightColor, getActiveLinkHref, getActiveLinkRange, getActiveMarks, getActiveTextColor, getCellFirstPosition, getCellLastPosition, getDocumentLength, getDocumentMarkAttrValues, getNodeAtPath, getTableDimensions, getTextNodesBetween, htmlSerializer, insertImage, insertLink, insertTable, insertTableColumnAfter, insertTableColumnBefore, insertTableRowAfter, insertTableRowBefore, insertText, insertTextAtPath, isBlockNode, isContainerBlock, isSelectionInContainer, isTextNode, joinBlocks, jsonSerializer, makeCollapsedSelection, makePosition, markdownSerializer, marksEqual, mergeAdjacentTextNodesInDoc, mergeCells, mergeTableCells, normalizeRange, redo, registerCommand, removeMarkFromNode, removeMarkFromRange, renderDocument, replaceAllMatches, replaceMatch, restoreSelection, scrollMatchIntoView, setAlignment, setBlockType2 as setBlockType, setCodeBlockLanguage, setColumnWidth, setFontFamily, setFontSize, setHighlightColor, setImageAttr, setMarkOnRange, setTextColor, splitBlock, splitCell, splitTableCell, toggleCheckItemAt, toggleHeaderRow, toggleMark, undo, updateColumnWidth, useEditorEngine, useEditorState, walkDocument };
|
|
9993
9269
|
//# sourceMappingURL=index.mjs.map
|